본문 바로가기
Develop/Zoom만들기

ZOOM 만들기 9. socket.io 이용하여 채팅룸 구현 (2)

by 보보트레인 2023. 9. 1.

1. home.pug 수정하기

채팅룸을 구별할 수 있게 되었으니 같은 채팅룸에 접속한 사용자들끼리만 메시지를 주고받을 수 있게 만들어보자.

 

다음의 코드를 home.pug에 추가하자

 div#room
                ul
                form
                    input(placeholder="message", required, type="text")
                    button Send

<결과화면>


2. app.js 수정하기

메시지를 입력하는 폼을 추가했지만 처음에는 이 폼을 숨겨야 한다.

왜냐하면 채팅룸에 아직 접속하지 않은 상태이기 때문.

 

룸 이름을 입력해 채팅룸에 접속하고 나면 반대로 룸 이름 폼이 사라지고 메시지 폼이 보이도록 app.js를 수정하자.

const socket = io();

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

room.hidden = true;

function showRoom(){
    welcome.hidden = true;
    room.hidden = false;
}

function handleRoomSubmit(event){
    event.preventDefault();
    const input = form.querySelector("input");
    socket.emit("enter_room", input.value, showRoom);
    input.value = "";
}

form.addEventListener("submit", handleRoomSubmit);

처음에는 id가 room인 메시지 입력 폼을 숨겨 두었다가, socket의 emit 메서드를 통해 enter_room 이벤트가 발생하면 showRoom함수를 실행시켜 메시지 입력 폼을 보여주고, 이때 거꾸로 id가 welcome인 영역 안의 룸 이름 입력 폼을 숨김.


3. server.js 수정

emit을 통해 showRoom 함수를 서버로 전달 했으니 서버에서 enter_room 이벤트가 발생했을 때,

서버에서 이를 호출해 주어야 한다.

 

다음의 코드 추가

 done();

여기서 done();은 브라우저(app.js)가 요청한 콜백 매개체!

→ showRoom() 메서드를 실행시켜준다는 의미임을 이해해야함.


4. 채팅 룸 이름 표시하기

4-1)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 Zoom
        //MVP.css는 우리가 태그에 class나 id같은 특성을 추가하지 않아도 자동으로 스타일을 적용해주는 라이브러리다.
        link(rel="stylesheet", href="https://unpkg.com/mvp.css")
    body
        //h1 It works!
        header
            h1 Zoom
        main
            div#welcome
                form
                    input(placeholder="room name", required, type="text")
                    button Enter Room
            div#room
                h3
                ul
                form
                    input(placeholder="message", required, type="text")
                    button Send
        script(src="/socket.io/socket.io.js")
        script(src="/public/js/app.js")


h3요소를 추가하여 채팅룸 이름을 표시할 예정.

 

4-2) app.js코드 수정하기

const socket = io();

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

room.hidden = true;

let roomName;

function showRoom(){
    welcome.hidden = true;
    room.hidden = false;
    const h3 = room.querySelector("h3");
    h3.innerText  =`Room ${roomName}`;
}

function handleRoomSubmit(event){
    event.preventDefault();
    const input = form.querySelector("input");
    socket.emit("enter_room", input.value, showRoom);
    roomName = input.value;
    input.value = "";
}

form.addEventListener("submit", handleRoomSubmit);

roomName 변수를 만들고 룸 이름을 입력하면 값이 대입되어 h3에 표시하도록 했음

 

 

<결과화면>

처음에는 room name 칸만 뜬다.

룸 이름을 입력하면

룸 이름이 h3로 뜨며 메시지 칸으로 전환된다.


5. 채팅룸 안에서 메시지 교환하기

5-1) server.js 수정하기

socket.io가 제공하는 to 메서드를 사용할 예정

 

to 메서드는 이벤트를 통해 데이터를 전달하고 싶은 대상을 지정할 수 있게 해줌.

server.js 코드를 다음과 같이 수정

import http from 'http';
import SocketIO from "socket.io";
import express from 'express';

const app = express();

app.set("view engine", "pug");
//dirname은 Node.js기본 전역변수로, 현재 실행되는 폴더의 경로를 의미
app.set("views", __dirname + "/views");
app.use("/public", express.static(__dirname + "/public"));

// '/'를 받으면 home으로 가게 설정
app.get("/", (req, res) => res.render("home"));
//만약홈이 아닌 다른 주소로 get요청을 보내더라도 홈으로 리다이렉션하게 예외처리함
app.get("/*", (req, res) => res.redirect("/"));

const httpServer = http.createServer(app);
const wsServer = SocketIO(httpServer);

wsServer.on("connection", (socket) => {
    socket.on("enter_room", (roomName,done) => {

        //브라우저가 요청한 콜백을 의미 - showRoom()을 의미.
        done();
       //소켓 룸 생성 및 접속
       socket.join(roomName);
      //to메서드
       socket.to(roomName).emit("welcome");
    });
});

const handleListen = () => console.log("Listening on http://localhost:3000");
httpServer.listen(3000,handleListen);

여기서 핵심은

  socket.to(roomName).emit("welcome");

to 메서드에 해당하는 다음의 코드이다. 

 

설명 : 해당 채팅룸(roomName으로 구별)에 참가한 소켓들에 대해서만 이벤트가 발생

이벤트명은 welcome으로 지정

 

5-2) app.js 수정하기

프론트에서 welcome 이벤트에 반응하도록 만들어야함.

const socket = io();

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

room.hidden = true;

let roomName;

function addMessage(message){
    //메시지 받아서 저장
    const ul = room.querySelector("ul");
    //li 생성
    const li = document.createElement("li");
    //li에 저장해둔 메시지 담음
    li.innerText = message;
    //ul에 가져다 붙힘
    ul.appendChild(li);
}

function showRoom(){
    welcome.hidden = true;
    room.hidden = false;
    const h3 = room.querySelector("h3");
    h3.innerText  =`Room ${roomName}`;
}

function handleRoomSubmit(event){
    event.preventDefault();
    const input = form.querySelector("input");
    socket.emit("enter_room", input.value, showRoom);
    roomName = input.value;
    input.value = "";
}

form.addEventListener("submit", handleRoomSubmit);

//서버로부터 welcome받으면 메시지 날리도록 체킹
socket.on("welcome", () => {
    addMessage("someone joined!");
})

<최종 테스트>

브라우저 1 / 브라우저 2 를 킨다. 1,2로 부른다고 가정

 

1에서 채팅룸 이름 bobo 입력

2에서 바로 채팅룸 이름 bobo입력 시

 

1에서는 누군가가 접속했다는 메시지를 받을 것임.

→ bobo라는 채팅방은 이미 만들어져 있으니 2는 새로운 채팅방 개설 없이 bobo라는 소켓룸으로 join될 것이고,

기존접속자들은 'someone joined!'라는 메시지를 받을 것임

브라우저 1의 화면
브라우저 2의 화면


6. disconnecting 이벤트 추가하기

채팅 나갈때도 알람이 갈 수 있도록 disconnecting 이벤트를 사용하여 코드를 수정해 보자.

 

6-1) server.js 코드 수정

socket.on("disconnecting", () =>{
        socket.rooms.forEach(room => socket.to(room).emit("bye"));
    });

disconnect이벤트가 발생하면 소켓이 bye 이벤트르 발생시킴

※ 접속중이던 채팅룸에만 이벤트를 발생시키기 위해서 rooms 속성을 사용한 것을 이해해야함.

rooms 속성은 접속 중인 채팅룸 목록을 뜻하고 forEach로 개별 room에 접속해 콜백함수를 호출할 수 있다.

 

6-2) app.js 수정하기

bye이벤트가 발생하면 bye이벤트를 발생시킨 소켓은 채팅룸을 떠나버리니까, 이 이벤트를 처리하는 것은 남아있는 다른 사용자들의 소켓의 몫이 될 것임.

socket.on("bye", () => {
    addMessage("someone left!");
})

 

<결과 확인>

1이 접속을 나갈때 2의 화면에 뜨는 메시지


7. 메시지 보내기

7-1) 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 = "";
}

function showRoom(){
    welcome.hidden = true;
    room.hidden = false;
    const h3 = room.querySelector("h3");
    h3.innerText  =`Room ${roomName}`;
    const form = room.querySelector("form");
    form.addEventListener("submit", handleMessageSubmit);
}

설명은 뒤의 코드와 함께 설명

 

7-2) server.js 코드 추가하기

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

 

설명 : app.js에서 showRoom() 메서드가 발생하고 메시지를 입력해 submit이벤트가 발생하면

handleMessageSubmit 호출을 함. 그러면 소켓에서 new_message 이벤트를 발생시키고 server.js가 받아서 콜백함수 실행

 

server.js 추가된 socket코드를 보면 각각 msg,room,done 이라는 이름으로 받았는데, msg와 room은 보낸 인자(파라미터)를 의미하는 것이고 done은 콜백함수를 의미하는 것임을 알고 있어야 한다. ( emit 메서드의 이해 필요 )

 

7-3) 메시지 받기 

현재 채팅룸에 접속한 사용자가 메시지를 보내면, 서버가 그걸 받아서 채팅룸에 있는 모든 사용자에게 보내도록 해뒀지만, 정작 화면에는 출력이 안되는 상황임.

 

app.js의 코드에 다음 코드를 추가하자

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

미리 만들어둔 addMessage함수를 사용하여 생산성을 높힘

 

<결과 화면>

화면에 잘 출력된다.

반응형