이번 챕터에서는 websocket으로 구현했던 내용을 socket.io를 이용해서 쉽고 빠르게 업그레이드해서 만들어보도록 하겠습니다!

 

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

 

목차


    0. socket.io란

    지금까지 했던 내용들은 socket.io를 이용해서 모두 구현할 수 있습니다.

    socket.io아주 쉽게 실시간 기능을 만들어주는 framework이죠.

     

     

    Socket.IO

    SOCKET.IO 4.0 IS HERE ~/Projects/tweets/index.js const io = require('socket.io')(80); const cfg = require('./config.js

    socket.io

     

    socket.io는 실시간, 양방향, 이벤트 기반의 통신을 가능하게 합니다.

    websocket과 굉장히 비슷하죠?

    출처 : socket.io

     

    그렇다면 차이점이 무엇이냐!!

    websocketsocket.io실시간, 양방향, 이벤트 기반의 통신을 제공하는 방법 중 하나입니다.

    이 말은 socket.io는 websocket의 부가기능이 아니라는 이야기가 되겠죠!

    socket.io는 가끔 websocket을 이용할 뿐입니다.

    websocket 이용이 불가능한 상황이라면 다른 방법을 이용해서 계속 작동할 것입니다.

     

    아래 사진에서 밑줄친 부분을 보면, 만약 websocket이 작동이 불가능한 상황이라면 HTTP long-polling을 사용한다고 나와있죠?!!

     

     

    둘의 차이점을 먼저 알아봤습니다.

    이렇게 차이점만 있는 것은 아니고 비슷한 점도 많은데, 그 중에서도 각종 API들(connection, send, ...)을 사용하는 방법이 거의 유사합니다.

    websocket에서 send를 사용했다면 socket.io에서는 emit를 사용하는 것처럼 말이죠.

    이름만 다르지 같은 기능을 하기 때문에 쉽게 익힐 수 있습니다.

     

    출처 : https://www.npmjs.com/package/socket.io

     


    1. 방 만들기 준비

    사용을 위해서는 먼저 npm install socket.io 를 터미널에 쳐서 다운로드 받아줍니다.

     

    이렇게 다운로드 받는것만으로도 url을 하나 받아오게 됩니다.

    서버가 켜진 상태로 아래와 같은 사이트를 브라우저에 치면, 다양한 정보들이 나오게 되죠.

    (이런게 있다 정도로만 넘어가셔도 무방합니다!)

     

     

    socket.io로 기존에 만들었던 내용을 싹 다 바꾸기 때문에 더이상 기존 websocket내용은 필요가 없습니다.

    다만 코드가 굉장히 유사하기 때문에 이해에는 크게 문제 없을 것이라고 생각됩니다.

    (비교를 위해 server.js의 이전 코드는 주석처리를 통해 남겨두었습니다.)

     

    이전과 다르게 채팅창이 아닌 방을 만드는 것부터 시작하고, 현재 강의 단계에서는 데이터가 전송되는 것까지만 확인이 가능합니다.(~2.3 Recap)

     

    먼저 html부터 수정해주겠습니다.

    간단한 form 태그와 input, button을 통해 방을 만드는 웹페이지를 만들어줍니다.

     

    //- home.pug
    
    doctype html
    html(lang="en")
        head
            meta(charset="UTF-8")
            meta(http-equiv="X-UA-Compatible", content="IE=edge")
            meta(name="viewport", content="width=device-width, initial-scale=1.0")
            title Noom
            //- 덜 못생기게 만들어주는 요소
            link(rel="stylesheet", href="https://unpkg.com/mvp.css") 
        body 
            header
                h1 Noom
            main
                div#welcome
                    form
                        input(placeholder="room name", required, type="text")
                        button Enter Room 
            //- 프론트엔드에 웹소켓 설치
            script(src="/socket.io/socket.io.js") 
            script(src="/public/js/app.js")

     

    약간 찌그러졌네..?

     

    그 다음엔 app.js와 server.io입니다.

    기존의 복잡한 작업들을 한번에 해결해 주는 함수인 io를 사용하였습니다.

    io는 자동적으로 클라이언트와 back-end socket.io를 연결해주는 함수입니다.

     

    그리고 send 대신 emit을 사용하였습니다.

    emit의 특징은 만들고 싶은 어떤 이벤트명도 맨 앞 매개변수로 적어주면 되고, string이 아닌 다른 요소(object)들도 전송이 가능하고, 백엔드에서 함수 실행이 가능하다는 점이 있습니다.

     

    중간에 setTimeout이 있는데, 이건 밀리초단위로 시간이 흐른 뒤에 안의 내용이 실행되게 하는 함수입니다.

    1000이 1초이기 때문에 15000이면 15초가 되겠죠. 

     

    12번 방을 만들면 서버에는 아래와 같이 input value가 전달되고, 작업이 끝나고 SetTimeout이 작동하게 되고, 15초 후에 backendDone 함수가 작동함으로써 클라이언트의 콘솔창에 미리 작성해둔 내용이 뜨게 됩니다.

     

     

    가장 인상적인 점은 서버에서 done의 매개변수로 넣었던 글씨가 그대로 나온다는 것이 되겠네요.

    이름은 done이지만, 클라이언트로부터 받은 마지막 함수에다가 done의 매개변수를 넣어서 front-end에서 실행할 수 있도록 어떤 임의의 버튼을 눌러준 것이라고 생각하면 됩니다. (결국 프론트엔드에서 함수가 돌아간 것입니다!)

     

    프론트엔드에서 전송한 함수가 백엔드에서 돌아가게 되면 보안에 있어서 큰 문제가 발생하기 때문에 이런 식으로 작동하는 것이죠. (만약 함수가 백엔드에서 돌아간다면 임의의 코드가 서버의 데이터를 다 뒤집어 놓을 수도 있겠죠?!

     

    // server.js
    
    import http from "http"; // 이미 기본 설치되어있음
    import WebSocket from "ws"; // 기본설치!
    import express from "express"; // npm i express 설치
    import SocketIO from "socket.io"; 
    import { doesNotMatch } from "assert";
    
    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 httpServer = http.createServer(app); // app은 requestlistener 경로 - express application으로부터 서버 생성
    const wsServer = SocketIO(httpServer); // localhost:3000/socket.io/socket.io.js로 연결 가능 (socketIO는 websocket의 부가기능이 아니다!!)
    
    // websocket에 비해 개선점 : 1. 어떤 이벤트든지 전달 가능 2. JS Object를 보낼 수 있음
    wsServer.on("connection", socket => {
        socket.on("enter_room", (roomName, done) => {
            console.log(roomName);
            setTimeout(()=>{
                done("hello from the backend"); // 여기 있는 done 함수는 여기서 실행하지 않는다 - 사용자로부터 함수를 받아서 사용하면 보안문제가 생길 수 있기 때문에
            }, 15000);
        });
    })
    
    
    // 웹소켓 사용한 부분 주석처리!
    // const wss = new WebSocket.Server({ server }); // http 서버 위에 webSocket서버 생성, 위의 http로 만든 server는 필수 X - 이렇게 하면 http / ws 서버 모두 같은 3000번 포트를 이용해서 돌릴 수 있다!
    
    // const sockets = []; // 누군가 우리 서버에 연결하면 그 connection을 여기에 넣을 것이다!!
    
    
    // // on method에서는 event가 발동되는 것을 기다린다
    // // event가 connection / 뒤에 오는 함수는 event가 일어나면 작동
    // // 그리고 on method는 backend에 연결된 사람의 정보를 제공 - 그게 socket에서 옴
    // // 익명함수로 바꾸기
    // wss.on("connection", socket => { // 여기의 socket이라는 매개변수는 새로운 브라우저를 뜻함!! (wss는 전체 서버, socket은 하나의 연결이라고 생각!!)
    //     sockets.push(socket); // 파이어폭스가 연결되면 sockets 배열에 firefox를 넣어줌! (다른 브라우저도 마찬가지!)
    //     socket["nickname"] = "Anonymous"; // 익명 소켓인 경우 처리 - 맨 처음 닉네임은 Anonymous
    //     console.log("Connected to Browser ✅");
    //     socket.on("close", () => console.log("Disconnected to Server ❌")); // 서버를 끄면 동작
    //     socket.on("message", msg => {
    //         const message = JSON.parse(msg);
    //         // new_message일 때 모든 브라우저에 payload를 전송!
    //         // if / else if로 해도 잘 돌아간다!
    //         // 받아온 String 형태의 메시지(바로 출력하면 Buffer로 뜨지만..!)를 parse로 파싱한 후 구분해서 출력
    //         switch(message.type){
    //             case "new_message":
    //                 sockets.forEach((aSocket) => aSocket.send(`${socket.nickname}: ${message.payload}`));
    //             case "nickname":
    //                 socket["nickname"] = message.payload; // socket은 기본적으로 객체라 새로운 아이템 추가 가능! : 닉네임을 socket 프로퍼티에 저장중!
    //         }
    //         // const utf8message = message.toString("utf8"); // 버퍼 형태로 전달되기 때문에 toString 메서드를 이용해서 utf8로 변환 필요!
    //         // sockets.forEach(aSocket => aSocket.send(utf8message)); // 연결된 모든 소켓에 메시지를 전달!!
    //         // socket.send(utf8message);
    //     }); // 프론트엔드로부터 메시지가 오면 콘솔에 출력
    // }) // socket을 callback으로 받는다! webSocket은 서버와 브라우저 사이의 연결!!!
    
    
    httpServer.listen(3000, handleListen); // 서버는 ws, http 프로토콜 모두 이해할 수 있게 된다!

     

    // app.js
    
    const socket = io(); // io function은 알아서 socket.io를 실행하고 있는 서버를 찾을 것이다!
    
    // 방을 만들것!! (socket IO에는 이미 방기능이 있다!)
    
    const welcome = document.getElementById("welcome");
    const form = welcome.querySelector("form");
    
    function backendDone(msg) { // Front-end에서 작성된 코드는 back-end가 실행시킬 것이다 (백엔드 작업이 다 끝나면 call됨!!)
        console.log(`The backend says: `, msg);
    }
    
    function handleRoomSubmit(event){
        event.preventDefault();
        const input = form.querySelector("input");
        // argument 보내기 가능 (socketIO는 Object 전달가능)
        // 첫 번째는 이벤트명(아무거나 상관없음), 두 번째는 front-end에서 전송하는 object(보내고 싶은 payload), 세 번째는 서버에서 호출하는 function
        socket.emit( // emit의 마지막 요소가 function이면 가능
            "enter_room",
            {payload: input.value},
            backendDone // 백엔드에서 끝났다는 사실을 알리기 위해 function을 넣고 싶다면 맨 마지막에 넣자!
        ); // 1. socketIO를 이용하면 모든 것이 메세지일 필요가 없다! / 2. client는 어떠한 이벤트든 모두 emit 가능 / 아무거나 전송할 수 있다(text가 아니어도 되고 여러개 전송 가능!)
        input.value = "";
    }
    
    // 서버는 back-end에서 function을 호출하지만 function은 front-end에서 실행됨!!
    
    form.addEventListener("submit", handleRoomSubmit);

     

     

    ※ 현재까지의 결과

     

     


     

     

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