Network/WebSocket

[Node.js] socket.io를 이용한 실시간 채팅방 기능 구현하기

범데이 2022. 6. 21. 00:57

1. 개요

본 포스팅을 읽기 위한 기본 전제 사항은 다음과 같다.

  • socket.io 라이브러리에 대한 기초 지식이 있다.(socket.io 기본 사용법은 이 포스팅 참고 )
  • 클라이언트 측 채팅방 UI구현은 NodeJS + React로 구현하였다.
    • 본 포스팅은 socket.io 라이브러리를 중점적으로 살피기 위한 포스팅이므로, UI 구현 코드는 다루지 않는다.

 

채팅방 기능 구현에 정답은 없지만, 기능 구현을 위해 내가 사용했던 방법을 공유하고자 한다.

 

 

 

 

2. 서버 설정

2.1 socket key 상수 선언

const SOCKET_KEY_CHAT_FROM_CLIENT = "SOCKET_KEY_CHAT_FROM_CLIENT";
const SOCKET_KEY_CHAT_FROM_SERVER = "SOCKET_KEY_CHAT_FROM_SERVER";
const SOCKET_KEY_CHAT_APPLY_ID = "SOCKET_KEY_CHAT_APPLY_ID";
const SOCKET_KEY_NOTIFY_USER_IN = "SOCKET_KEY_NOTIFY_USER_IN";
const SOCKET_KEY_NOTIFY_USER_OUT = "SOCKET_KEY_NOTIFY_USER_OUT";

socket통신에 사용할 키를 const로 선언한다, 

상수명을 보면 알겠지만, Client to Server, Server to Client 및 그외 이벤트에 사용할 키를 지정하고 있다.

 

 

2.2 socket io 기본 설정

express 및 socket.io 모듈들을 import해준다음에, 웹 소켓 서버를 띄울 포트를 지정해준다.

(아래 예제에서는 포트트 30001로, path는 기본으로 설정해 주었다.)

const express = require("express");
const app = express();
const socketIO = require("socket.io");

...

const server = app.listen(30001, () => {
  logger("Bumday Websocket Listening on port http 30001!");
});

// socket io 객체 생성: 1) http서버 연결. 2) path 설정(생략시 디폴트: /socket.io)
const io = socketIO(server, { path: "/socket.io" });

...

 

 

2.3 이벤트 설정

web socket의 각종 이벤트에 대한 설정을 해준다.

코드에 대한 설명은 주석으로 작성해 두었다.

io.on("connection", (socket) => {
  // connection시에 기본 동작
  // socket id의 일부를 추출하여, user ID로 부여한다.
  const applyId = `user-${socket.id.substring(0, 6)}`;
  const clients = io.engine.clientsCount;
  const emitData = {
    id: applyId,
    clients: clients,
    created_date: new Date().getTime(),
  };
  // 연결된 클라이언트에게 부여된 ID와 현재 클라이언트 수를 보낸다.(emit)
  socket.emit(SOCKET_KEY_CHAT_APPLY_ID, emitData);

  const emitData2 = {
    id: applyId,
    clients: clients,
    created_date: new Date().getTime(),
  };
  // 그외 클라이언트들에게 새로운 ID가 입장했음을 알리고, 현재 클라이언트 수를 공지한다.(broadcast)
  socket.broadcast.emit(SOCKET_KEY_NOTIFY_USER_IN, emitData2);

  socket.on("disconnect", (reason) => {
    // disconnect시에 다른 클라이언트들에게 나간 user ID를 알리고, 현재 클라이언트 수를 공지한다.(broadcast)
    console.log(reason);

    const clients = io.engine.clientsCount;
    const emitData = {
      id: applyId,
      clients: clients,
      created_date: new Date().getTime(),
    };
    socket.broadcast.emit(SOCKET_KEY_NOTIFY_USER_OUT, emitData);
  });

  // socket.io의 에러 발생시 에러 로그를 출력한다.
  socket.on("error", (error) => {
    console.log(`Error ocurred: ${error}`);
  });

  // Client로부터 chat 메시지를 받으면 
  // user ID와 메시지 및 받은시각을 기록하여 다른 유저들에게 공지한다.(broadcast)
  socket.on(SOCKET_KEY_CHAT_FROM_CLIENT, (data) => {
    console.log(`socket${SOCKET_KEY_CHAT_FROM_CLIENT} received! >> data=`, data);
    const emitData = {
      id: data.id,
      message: data.message,
      created_date: new Date().getTime(),
    };
    socket.broadcast.emit(SOCKET_KEY_CHAT_FROM_SERVER, emitData);
  });
});

 

서버측 socket.io 주요코드는 위와 같다. 생각보다 간단하며, 간단한 채팅방을 만들기 위해선 socket의 연결 및 해제 및 채팅 메시지 수신시 처리만 잘 해주면 된다.

 

 

 

 

3. 클라이언트 설정 

3.1 socket key 상수 선언

서버쪽과 같이 사용할 socket key를 상수로 선언해 준다.

const SOCKET_KEY_CHAT_FROM_SERVER = "SOCKET_KEY_CHAT_FROM_SERVER";
const SOCKET_KEY_CHAT_FROM_CLIENT = "SOCKET_KEY_CHAT_FROM_CLIENT";
const SOCKET_KEY_CHAT_APPLY_ID = "SOCKET_KEY_CHAT_APPLY_ID";
const SOCKET_KEY_NOTIFY_USER_IN = "SOCKET_KEY_NOTIFY_USER_IN";
const SOCKET_KEY_NOTIFY_USER_OUT = "SOCKET_KEY_NOTIFY_USER_OUT";

 

 

3.2 socket.io 기본 설정

아래와 같이 socket.io-client 모듈을 import해준 후, 연결할 URL을 지정해주어 socket.io 연결을 위한 기본 설정을 해준다.

import io from "socket.io-client";

const SOCKET_BASE_URL = "http://localhost:30001/"
socket = io.connect(SOCKET_BASE_URL, {
  path: "/socket.io",
  transports: ["websocket"],
});

 

3.3 socket.io 이벤트 설정

클라이언트 측 web socket 이벤트 처리를 다음과 같이 해준다.

코드에 대한 설명은 주석으로 작성해 두었다.

 

참고: appendChatLog function은 채팅방에 메시지를 추가하기 위한 함수이다.

    socket.on("connect", () => {
      // 최초 connect(연결) 시의 처리 과정
      this.appendChatLog(null, "채팅방에 입장하셨습니다. (채팅기록은 저장되지 않습니다.)");
    });

    socket.on(SOCKET_KEY_CHAT_APPLY_ID, (data) => {
      // 최초 connect(연결) 후 서버로부터 user ID를 부여받았을때의 처리 과정
      
      this.setState({ socketId: data.id });
      this.appendChatLog(null, `당신의 아이디는 "${data.id}" 입니다. (현재 인원:${data.clients})`);
    });

    socket.on(SOCKET_KEY_CHAT_FROM_SERVER, (data) => {
      // 메시지를 받았을때의 처리 과정

      if (data.id === this.state.socketId) {
        return;
      } else {
        this.appendChatLog(data.id, data.message);
      }
    });
    
    socket.on(SOCKET_KEY_NOTIFY_USER_IN, (data) => {
      // 새로운 USER가 입장하였을때의 처리 과정
      
      if (data.id === this.state.socketId) {
        return;
      } else {
        this.appendChatLog(null, `${data.id}님이 대화에 참가하셨습니다. (현재 인원:${data.clients})`);
      }
    });
    socket.on(SOCKET_KEY_NOTIFY_USER_OUT, (data) => {
      // 기존 USER가 퇴장하였을때의 처리 과정
    
      if (data.id === this.state.socketId) {
        return;
      } else {
        this.appendChatLog(null, `${data.id}님이 대화에서 퇴장하셨습니다. (현재 인원:${data.clients})`);
      }
    });

 

코드를 읽어보면 생각보다 간단하다. 서버에서 처리해준것과 같이 새로운 유저의 입장 및 퇴장, 메시지를 받을때의 처리를 해주면 된다.

 

 

 

3.4 채팅 메시지 발송 처리

send는 메시지를 발송할때 호출하는 함수이다.

나의 id와 함께 작성한 message를 발송하고, 작성하였던 input창의 메시지는 비워주는 역할을 한다.

 

  send() {
    const emitObj = {
      id: this.state.socketId,
      message: this.state.sendText,
    };
    socket.emit(SOCKET_KEY_CHAT_FROM_CLIENT, emitObj);

    this.appendChatLog(this.state.socketId, this.state.sendText, new Date().getTime(), true);
    this.setState({ sendText: "" });
  }

 

 

 

4. 결과

 

구현된 채팅방에 최초 접속하면 다음과 같이 출력된다.

 

 

새로운 유저가 접속했을 시 다음과 같이 출력된다.

 

 

메시지를 주고받아보자. "안녕하세요"라고 보내면 다음과 같이 처리된다.

 

이 메시지를 받은 상대방은 이렇게 출력된다.

 

이 상대방의 입장에서도 "빵갑습니다"라는 메시지를 보내보자.

좌측이 보낸 쪽, 우측이 받은 쪽이다.

 

 

 

이러다가 아쉽게도 상대방이 대화를 떠났다.

그러자 퇴장메시지와 함께 현재 인원이 1명이 되어, 나만 이 채팅방에 남았음을 알 수 있다.

 

 

 

물론, 예시로는 두명의 사용자만 대화를 했지만, 아래와 같이 여러명이서 동시에 채팅을 할 수도 있다.

각 유저의 ID들이 별도로 설정이 되어 분간이 가능하나, 추후에 알맞은 이름을 지어주어 더 직관성 있는 채팅방을 구현할 수도 있다.

 

 


 

5. 마무리

오늘은 이렇게 socket.io라이브러리를 이용하여 간단한 채팅방 기능을 구현해 보았다.

소켓 통신의 이해도가 어느정도 있다면, 이렇게 실시간으로 데이터를 주고받는 상황에서 유용하게 잘 사용할 수 있을 것이다.

 

그리고, 실생활에서 채팅 기능을 여럿 접하지만, 막상 구현해보고 나니 별거 없다. 실시간 웹 랜덤채팅 서비스인 가가라이브와 같은 채팅방 기능이 어렵지 않게 구현될 수 있음을 알 수 있다.

 

기본적인 채팅 기능이 정상적으로 구현 되었으니, 이에 더 개선한다면 위에서 더 나아가 멀티미디어 처리나, 채팅 로그 저장 및 방장의 기능 등등을 추가할 수 있겠다.

 

 

 

궁금하신점이나 내용에 지적할 점이 있다면 댓글로 남겨 주세요. 감사합니다.

 

반응형

'Network > WebSocket' 카테고리의 다른 글

[NodeJS] socket.io 웹 소켓 모듈 기본 사용법  (0) 2022.01.27