Zoom Clone Coding

part 1에서는 websockets을 이용해서 가벼운 채팅 프로그램을 개발했었는데, 이번에는 socket.io framework을 활용해서 이를 구현하자

socket.io는 websocket을 내부적으로 이용하는 framework으로 만약 websocket을 지원하지 않는 브라우져의 경우 다른 방식으로 대체하여 front-back 간에 연결을 할 수 있는 기능을 제공해준다. 뿐만 아니라, websocket을 이용해서 수동적으로 구현해야 했던 기능들을 내부 framework에서 제공해주기 때문에 매우 편리하다.

socket.io server

Server Initialization

server.js

import SocketIO from "socket.io";
//http server => to get access to server
const server=http.createServer(app);
//socket.io server
const io=SocketIO(server);

back-end 서버에서는 위 처럼 SocketIO server을 생성해서 http server와 같이 구동한다.

app.js

const socket=io();

home.pug

script(src="/public/js/app.js")

이렇게 하면 되는데, 이때 주의해야할 점은 socket.io/socket.io.js 파일을 html 상에서 포함시켜줘야 위의 코드가 정상적으로 동작한다. socket.io 서버를 작동하게 될 때, 자동적으로 socket.io.js을 client 쪽에 설치해서 socket.io framework가 정상적으로 작동될 수 있도록 한다.

Event Handling

Event Handling

socket.on(event_type, event_handler)

socket.io 의 event handler는 websocket 방식과 유사하게 작동하지만 event_type에 제한이 없다. 자기가 원하는 string type의 event를 작성해서 해당 event에 대한 handling function을 지정할 수 있다.

Message Send(Emits an event)

socket.emit(event_type, args ... , function)

emit가 실제로 하는 역할은 event을 전달하는 것이다. 만약 message을 넘겨주고 싶다? event_type을 “new_message”,로 설정하고 값을 전달해주면 된다. 이처럼, event_type에는 어떠한 정보가 들어가도 상관이 없다. 추가로, 전달해줄 수 있는 인자에는 제한이 없다.

예를 들어

socket.emit("new_message","hi",1,{123})

아래와 같이 인자의 자료형, 개수에 제한 없이 보낼 수 있다.

그러면, emit에 마지막 인자에 들어가는 function의 역할은 뭘까? 이 function을 이용하면, backend에서 해당 event에 대한 처리를 모두 마치고나서 front-end에서 실행시킬 function을 지정할 수 있다. 이는 나중에 예시를 통해 자세히 알아보자.

Real-Time Chatting

그러면 본격적으로 front-back 간에 실시간 채팅이 되도록 각각의 기능을 구현하는 부분에 대해 알아보자.

Room, Nickname Configuration

socket.io에서는 room을 생성,삭제, 초대, 등의 관리 기능을 내부적으로 지원해준다.

socket.join(roomName);

위와 같이 원하는 roomName을 지정하고 socket.join을 하면 해당 방에 접속할 수 있다.

server.js

  socket.on("enter_room",(nickName,roomName,done)=>{
            socket["nickname"]=nickName;
            //join room
            socket.join(roomName);
            done();
            //emit "welcome" event to everyone in the room
            socket.to(roomName).emit("welcome",socket.nickname);

            }
  );

front로 부터 enter_room 이라는 event을 전달받았을 때, 인자로 받은 nickname으로 socket object의 nickname을 추가하고, roomName을 이용해서 해당 방에 접속시킨다.또한, socket.to를 이용하게 되면 해당 방에 접속된 모든 client에게 message를 전달하는 것이 가능하다.

home.pug

div#welcome
    form
        input(placeholder="nickname",required,type="text")
        input(placeholder="room name",required, type="text")
        button Enter Room

app.js

const welcome=document.getElementById("welcome");
const form= welcome.querySelector("form");

//메세지를 ul tag에 추가하는 함수이다.
function addMessage(msg){
    const ul=room.querySelector("ul");
    const li=document.createElement("li");
    li.innerText=msg;
    ul.appendChild(li);
}
//채팅방에 접속하게 되면, 방 이름, 닉네임을 입력받는 form은 hidden 처리하고, 메세지를 주고 받을 수 있는 input form을 활성화한다. 
function showRoom(){
    welcome.hidden=true;
    room.hidden=false;
    const h3=room.querySelector("h3");
    h3.innerText=`Room ${roomName}`;
    const messageForm=room.querySelector("form");
    messageForm.addEventListener("submit",handleMessageSubmit);
}

function handleRoomSubmit(event){
    event.preventDefault();

    const nameInput=welcome.querySelector("input");
    const roomInput=welcome.querySelector("input:nth-child(2)");
    console.log(roomInput);

    //Emit anything + can send objects
    socket.emit("enter_room",nameInput.value,roomInput.value,showRoom);

    roomName=roomInput.value;
    roomInput.value="";
}
form.addEventListener("submit",handleRoomSubmit)

nickname과 roomName을 명시하고 submit을 작성하게 되면 handleRoomSubmit function이 실행된다. 그려면 함수 내부에서 nickname, roomName 값을 읽어들여서 backend server로 값을 넘겨준다.

socket.emit함수를 살펴보면, showRoom이라는 function를 마지막에 전달해주는 것을 알 수 있는데, 이렇게 function을 넘겨주게 되면 아래의 backend server에서 데이터에 대한 처리를 완료한 후에 front-end에서 해당 함수가 실행되도록 요청할 수 있다.

socket.on("enter_room",(nickName,roomName,done)=>{
            socket["nickname"]=nickName;
            //join room
            socket.join(roomName);
            done();

Sending Messages

server.js

socket.on("new_message",(msg,room,done)=>{
            socket.to(room).emit("new_message",`${socket.nickname}: ${msg}`);
            done();
        })

app.js

function handleMessageSubmit(event){
    event.preventDefault();
    const input=room.querySelector("input");
    const value=input.value;
    socket.emit("new_message",value,roomName,()=>{
        addMessage(`You: ${value}`)
    });
    input.value="";
}

socket.on("new_message",(msg)=>addMessage(msg));

front, back 각각에서 socket.on 함수를 이용해서 메세지 event을 받은 경우 대한 handler function들을 구현해준다.

References

link: nomadcoders

socket.io 공식 문서

link: socket.io

댓글남기기