이번에는 웹소켓을 사용해서 실제로 백엔드와 프론트엔드간에 통신을 하는 것을 시도해보도록 하겠습니다!

 

※ 본 내용은 줌 클론코딩의 1.0~1.5 까지의 수업 내용을 담고 있습니다!

 

목차


    0. 웹소켓의 기본

     

    출처 : https://blog.scaleway.com/iot-hub-what-use-case-for-websockets/

     

    HTTP웹소켓에는 두 가지의 큰 차이점이 존재합니다.

     

    HTTP는 클라이언트가 요청을 보내면 서버는 응답하는 방식으로밖에 메시지를 전달할 수 없습니다.

    서버가 먼저 클라이언트에게 메시지를 전달할 수 없다는 것이죠.

    또한 서버는 어떤 유저가 요청을 보냈는지 기억하지 않습니다.

    매번 요청에 담긴 응답 위치에다가 응답할 뿐이죠.

     

    반면 웹소켓은 서로가 연결이 되어야 합니다.

    그 연결을 바탕으로 양쪽에서 언제든지 메시지를 전달할 수 있죠.

     

    이 개념을 바탕으로 웹소켓을 이용해서 통신을 해보도록 하겠습니다.

    먼저 server.js의 내용을 수정해줍니다.

     

    - server.js

    // server.js
    
    import http from "http"; // 이미 기본 설치되어있음
    import WebSocket from "ws"; // 기본설치!
    import express from "express"; // npm i express 설치
    
    const app = express(); // app이라는 변수에 가져와서 사용
    
    app.set("view engine", "pug"); // 뷰 엔진을 pug로 하겠다
    app.set("views", __dirname + "/views"); // 디렉토리 설정
    app.use("/public", express.static(__dirname + "/public")); // public 폴더를 유저에게 공개 (유저가 볼 수 있는 폴더 지정)
    app.get("/", (req, res) => res.render("home")); // 홈페이지로 이동할 때 사용될 템플릿을 렌더
    app.get("/*", (req, res) => res.redirect("/")) // 홈페이지 내 어느 페이지에 접근해도 홈으로 연결되도록 리다이렉트 (다른 url 사용 안할거라)
    
    const handleListen = () => console.log(`Listening on http://localhost:3000`)
    // app.listen(3000, handleListen); // 3000번 포트와 연결
    
    const server = http.createServer(app); // app은 requestlistener 경로 - express application으로부터 서버 생성
    
    const wss = new WebSocket.Server({ server }); // http 서버 위에 webSocket서버 생성, 위의 http로 만든 server는 필수 X - 이렇게 하면 http / ws 서버 모두 같은 3000번 포트를 이용해서 돌릴 수 있다!
    
    // 임시로 만든 함수
    function handleConnection(socket) { // 여기서 socket은 연결된 브라우저
        console.log(socket) // 여기 있는 소켓이 frontend와 real-time으로 소통할 수 있다!
    };
    
    // on method에서는 event가 발동되는 것을 기다린다
    // event가 connection / 뒤에 오는 함수는 event가 일어나면 작동
    // 그리고 on method는 backend에 연결된 사람의 정보를 제공 - 그게 socket에서 옴
    wss.on("connection", handleConnection) // socket을 callback으로 받는다! webSocket은 서버와 브라우저 사이의 연결!!!
    
    
    server.listen(3000, handleListen); // 서버는 ws, http 프로토콜 모두 이해할 수 있게 된다!

     

    주석을 상세하게 달아놓았는데, 여기서 가장 먼저 알아야 할 것은 바로 server과 wss 변수입니다.

    server 변수를 이용해서 http 서버를 열었고, wss라는 변수를 이용해서 웹소켓 서버를 연 것이죠.

    같은 주소에 http/wss 서버를 모두 열었다는 점이 특징입니다.

     

    그리고 wss의 on 메서드를 이용해서 connection이 발생하면 handleConnection 함수가 작동하도록 만들었습니다.

    handleConnection함수는 socket이라는 매개변수를 받아서 {} 안에서 각종 작업을 수행하게 되죠.

     

    하지만 이 상태로는 실행되지 않습니다. connection event가 발생하지 않았기 때문인데요,

    이 이벤트를 발생시키기 위해서는 app.js(frontend)내에서 연결을 해 줘야 합니다.

    클라이언트와 서버를 연결시키는 과정이라고 생각하시면 됩니다.

     

    - app.js

    // app.js
    
    alert("hi"); // frontend에서 backend랑 연결해 달라고 해야 앞에서 만든 wss.on의 connection이 작동한다!
    
    const socket = new WebSocket(`ws://${window.location.host}`); // 이제 서버로 접속 가능! - 여기 socket을 이용해서 frontend에서 backend로 메세지 전송 가능!
    // 여기 socket은 서버로의 연결

     

    app.js에서는 socket 변수를 이용해서 WebSocket과 연결해줍니다.

    window.location.host는 주소를 직접 불러와서 사용한 것이죠. (localhost:3000과 현재는 동일합니다!)

     

    여기까지 만들고 작동시키면 app.js와 server.js가 서로 연결되고, 우리가 만든 대로 console.log를 통해 socket이 출력되게됩니다!

     

     


    1. 메시지 주고받기

    위에서 연결을 성공했다면, 이번에는 메시지를 실제로 주고받아보겠습니다.

     

    - server.js

    // server.js
    
    import http from "http"; // 이미 기본 설치되어있음
    import WebSocket from "ws"; // 기본설치!
    import express from "express"; // npm i express 설치
    
    const app = express(); // app이라는 변수에 가져와서 사용
    
    app.set("view engine", "pug"); // 뷰 엔진을 pug로 하겠다
    app.set("views", __dirname + "/views"); // 디렉토리 설정
    app.use("/public", express.static(__dirname + "/public")); // public 폴더를 유저에게 공개 (유저가 볼 수 있는 폴더 지정)
    app.get("/", (req, res) => res.render("home")); // 홈페이지로 이동할 때 사용될 템플릿을 렌더
    app.get("/*", (req, res) => res.redirect("/")) // 홈페이지 내 어느 페이지에 접근해도 홈으로 연결되도록 리다이렉트 (다른 url 사용 안할거라)
    
    const handleListen = () => console.log(`Listening on http://localhost:3000`)
    // app.listen(3000, handleListen); // 3000번 포트와 연결
    
    const server = http.createServer(app); // app은 requestlistener 경로 - express application으로부터 서버 생성
    
    const wss = new WebSocket.Server({ server }); // http 서버 위에 webSocket서버 생성, 위의 http로 만든 server는 필수 X - 이렇게 하면 http / ws 서버 모두 같은 3000번 포트를 이용해서 돌릴 수 있다!
    
    // on method에서는 event가 발동되는 것을 기다린다
    // event가 connection / 뒤에 오는 함수는 event가 일어나면 작동
    // 그리고 on method는 backend에 연결된 사람의 정보를 제공 - 그게 socket에서 옴
    // 익명함수로 바꾸기
    wss.on("connection", socket => { // 여기의 socket이라는 매개변수는 새로운 브라우저를 뜻함!! (wss는 전체 서버, socket은 하나의 연결이라고 생각!!)
        console.log("Connected to Browser ✅");
        socket.on("close", () => console.log("Disconnected to Server ❌")); // 서버를 끄면 동작
        socket.on("message", message => {
            const utf8message = message.toString("utf8"); // 버퍼 형태로 전달되기 때문에 toString 메서드를 이용해서 utf8로 변환 필요!
            console.log(utf8message);
        }); // 프론트엔드로부터 메시지가 오면 콘솔에 출력
        socket.send("hello!!!"); // hello 메시지 보내기 - send는 socket의 전송용 메서드!!
    }) // socket을 callback으로 받는다! webSocket은 서버와 브라우저 사이의 연결!!!
    
    
    server.listen(3000, handleListen); // 서버는 ws, http 프로토콜 모두 이해할 수 있게 된다!

     

    새로 추가된 부분은 wss.on에 붙은 콜백함수입니다.

    원래는 밖에서 함수를 만들어서 넣어줬는데, 이번에는 익명함수를 이용해서 바로 만들었죠?

    () => {} 형태로, 이름이 없는 함수가 바로 익명함수입니다.

    socket이라는 매개변수를 받아서, on이라는 메서드를 이용해 서버를 켜고(connection) 끌 때(close) 메시지가 나오도록 했고, 메시지(message)를 받으면 출력하도록 했습니다.

    그리고 send를 이용해 프론트엔드로 메시지를 전달할 수 있도록 만들어줬죠.

     

    특이점이라면 메시지를 받을 때 버퍼형태로 전달받기 때문에 이걸 utf8 형태로 바꿔주기 위해서 추가적인 작업을 해줬습니다.

    만약 그대로 받으면 버퍼형태로 메시지를 받아서 어떤 내용인지 파악하기가 어렵겠죠?!!

     

    버퍼 형태로 받으면..?!

     

    이제 서버쪽 작업이 끝났습니다.

    메시지를 주고받기 위해서는 반대쪽(클라이언트쪽)에서도 무언가 해 줘야겠죠?

     

    - app.js

    // app.js
    
    alert("hi"); // frontend에서 backend랑 연결해 달라고 해야 앞에서 만든 wss.on의 connection이 작동한다!
    
    const socket = new WebSocket(`ws://${window.location.host}`); // 이제 서버로 접속 가능! - 여기 socket을 이용해서 frontend에서 backend로 메세지 전송 가능!
    // 여기 socket은 서버로의 연결
    
    socket.addEventListener("open", ()=>{ // open되면 동작
        console.log("Connected to Server ✅");
    })
    
    socket.addEventListener("message", message => {
        console.log("New message: ", message.data);
    });
    
    socket.addEventListener("close", () => {
        console.log("Disconnected to Server ❌");
    });
    
    setTimeout(() => {
        socket.send("hello from the browser!"); // backend로 메시지 보내기!
    }, 10000); // 10초 후에 작동

     

    app.js에서는 addEventLister를 사용합니다.

    특정 이벤트가 발생하면 콜백함수가 작동되도록 하는건데요!

    서버가 열리거나(open) 닫히면(close) 메시지를 띄우도록 했고, 메시지가 도착하면(message) 메시지를 출력하도록 했습니다.

    마찬가지로 send를 이용해서 메시지를 전송하도록 했는데, setTimeout 함수를 이용해서 10초 후에 전송하도록 했습니다.

     

    이제 새로고침을 해보면? 서로 연결되고 메시지도 잘 전달되는 모습을 확인하실 수 있습니다!

    (서버를 종료하는 것까지 해서 브라우저쪽 console에 연결이 끊어졌다고 출력되었습니다!)

     

    서버쪽 console

     

    브라우저쪽 console

     


    실제로 메시지가 전달되는 것을 보니 굉장히 신기합니다!

    챌린지 기간에 구현했을 때 서버쪽에서 클라이언트쪽으로 메시지를 보내는 것에 어려움을 느꼈었는데, 복습이 되는 느낌이네요!

    그렇다면 다음에는 실제로 익명의 유저가 채팅창에서 채팅을 작성하고 그 내용이 출력되는 것까지 만들어보도록 하겠습니다!!

    반응형
    • 네이버 블로그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기