최종프로젝트(응답하라 추억시대)

chat.server.js 서비스 코드 해석

김민커 2023. 10. 16. 20:46
import WebSocket from 'ws';
import { ChatMessage, ChatLog, getTimes } from './db/mongoose';

채팅을 위한 소켓 연결은 npm으로 설치한 ws 모듈로 합니다.

socket.io가 아니라 ws인 이유는 ws의 데이터는 따로 파싱할 필요가 없고 socket.io보다 데이터가 가볍기 때문에 차용했습니다.

mongodb와의 상호작용을 위한 모듈을 import했습니다.

ChatMessage는 메세지의 저장, ChatLog는 메세지의 저장 시간, getTimes는 과거의 채팅 이력을 불러오는 기능을 합니다.

 

export default class ChatServer {
  constructor(port, onMessage) {
    this.port = port;
    this.onMessage = onMessage;
    this.clients = new Set(); // 연결된 클라이언트를 추적하기 위한 Set

    this.server = new WebSocket.Server({ port });

    this.server.on('connection', async (socket, request) => {
      const nickname = request.url.split('?')[1].split('=')[1];
      if (nickname !== undefined) {
        const chatLog = new ChatLog({
          nickname: nickname,
        });
        await chatLog.save();
      }
      this.clients.add(socket); // 클라이언트 추가

      console.log('Client connected:', socket._socket.remoteAddress, socket._socket.remotePort);
      const arrMessage = await getTimes(nickname);
      for (const messageData of arrMessage) {
        socket.send(JSON.stringify(messageData));
      }
      socket.on('message', async (message) => {
        try {
          const stringMessage = message.toString();

          console.log('Server Received:', stringMessage);

          // 채팅 메시지를 MongoDB에 저장
          const chatMessage = new ChatMessage({
            message: stringMessage,
            timestamp: Date.now(), // Date.now()를 사용하여 현재 시간을 저장
          });
          await chatMessage.save(); // await를 사용하여 저장 작업을 기다림
          console.log('채팅 메시지가 성공적으로 저장되었습니다.');
          this.onMessage(socket, stringMessage);
        } catch (err) {
          console.error('채팅 메시지 저장 오류:', err);
        }
      });

      socket.on('close', async () => {
        console.log(
          'Client disconnected:',
          socket._socket.remoteAddress,
          socket._socket.remotePort,
        );

        this.clients.delete(socket); // 클라이언트 제거
      });

      socket.on('error', (err) => {
        console.error('Client error:', err);
      });
    });
  }
  broadcastMessage(message) {
    this.server.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  }
}

export default class ChatServer는 ChatServer를 클래스로 만들어서 유연함과 가용성을 높혔습니다.

 

ChatServer의 구조는 constructor(port, onMessage)로서 port는 채팅서버의 포트 번호,
onMessage는 소켓의 메세지 데이터입니다.

 

this.clients = new Set()은 Set 모듈의 기능을 사용한 클라이언트의 추적 기능입니다.

Set()은 중복되지 않는 value값만을 저장하는 Collection입니다.
이 Set의 기능을통해서 소켓의  connection 이벤트와 close 이벤트를 보장합니다.

 

 this.server = new WebSocket.Server({ port }); 이 코드는 ws 모듈의 특징입니다.
socket.io와는 다르게 new WebSocket.Server를 통해서 바로 소켓의 객체를 생성하는 것이 가능합니다.

 this.server.on('connection', async (socket, request) => {
      const nickname = request.url.split('?')[1].split('=')[1];
      if (nickname !== undefined) {
        const chatLog = new ChatLog({
          nickname: nickname,
        });
        await chatLog.save();
      }
      this.clients.add(socket); // 클라이언트 추가

on메서드는 이벤트를 연결해주는 기능을 합니다, 그래서 소켓의 connection 이벤트를 감지해서 소켓에서 보내주는 url의
값에서 클라이언트의 닉네임을 추출합니다. if문을 통해서 에러를 핸들링하고   
this.clients.add(socket)를 통해서 set에 클라이언트의 정보를 저장합니다.

 

const arrMessage = await getTimes(nickname);
      for (const messageData of arrMessage) {
        socket.send(JSON.stringify(messageData));
      }

 

그 다음 getTimes를 통해서 닉네임의 데이터에 기반한 과거의 채팅 내역을 불러와서 for 반복문을 통해서 socket으로 메세지 데이터를 전송합니다.

 socket.on('message', async (message) => {
        try {
          const stringMessage = message.toString();

          console.log('Server Received:', stringMessage);

          // 채팅 메시지를 MongoDB에 저장
          const chatMessage = new ChatMessage({
            message: stringMessage,
            timestamp: Date.now(), // Date.now()를 사용하여 현재 시간을 저장
          });
          await chatMessage.save(); // await를 사용하여 저장 작업을 기다림
          console.log('채팅 메시지가 성공적으로 저장되었습니다.');
          this.onMessage(socket, stringMessage);
        } catch (err) {
          console.error('채팅 메시지 저장 오류:', err);
        }
      });

 

socket의 message 이벤트 때 메세지 데이터를 String으로 변환하고 ChatMessage의 모듈을 통해서 메세지를 mongodb에 저장합니다.

      socket.on('close', async () => {
        console.log(
          'Client disconnected:',
          socket._socket.remoteAddress,
          socket._socket.remotePort,
        );

        this.clients.delete(socket); // 클라이언트 제거
      });


socket의 close이벤트 때 set에  this.clients.delete(socket) 코드를 통해서 저장된 소켓의 데이터를 제거합니다.

 

broadcastMessage(message) {
    this.server.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(message);
      }

이 코드는 WebSocket에 연결된 클라이언트 모두에게 메세지를 보내는 기능을 담당합니다.