개발일지

💬 실시간 채팅 시스템 개발기 – Socket.IO + NestJS + React 조합으로 방 기반 메시징 구현하기

makeviibe 2025. 7. 15. 15:51

안녕하세요. makeviibe입니다.

이번 글에서는 저희가 한 프로젝트에서 구현한 방 기반 실시간 채팅 기능에 대해 소개드리고자 합니다.

단순한 메시지 전송을 넘어서, 사용자가 특정 채팅방에 참여하고, 이전 대화 히스토리를 받아보고, 나가면 방 상태가 정리되는 흐름을 구현하는 것이 주요 목표였습니다.

기술적으로는 NestJS + Socket.IO + React를 조합하여 구축하였고, 그 과정에서 겪었던 다양한 이슈와 해결 방안을 공유드립니다.


🧱 채팅 시스템의 기본 구조

  1. 사용자는 특정 유저의 ID 또는 방 ID를 기반으로 joinRoom 요청을 서버에 전송합니다.
  2. 서버는 해당 요청을 받아 사용자를 방에 참여시키고, 기존 메시지 히스토리를 클라이언트에 전달합니다.
  3. 이후에는 message 이벤트를 통해 실시간 메시지를 주고받게 됩니다.
  4. 사용자가 채팅방을 나가거나 React 컴포넌트가 언마운트될 경우, 클라이언트는 leaveRoom을 통해 방에서 나가고, 서버는 해당 방의 사용자 목록에서 클라이언트를 제거합니다.

React에서는 useEffect를 활용해 소켓을 연결하고 해제하며, 서버에서는 NestJS의 @WebSocketGateway를 기반으로 각 방을 관리했습니다.


😵 겪었던 문제들과 해결 방법

1. 메시지 중복 수신 문제

초기 구현 당시, useEffect 내에서 socket.on('message') 리스너를 매번 새로 등록하다 보니, 일부 상황에서는 같은 메시지를 여러 번 수신하는 문제가 발생했습니다.

해결 방법

  • 리스너를 등록하기 전 socket.off('message')를 통해 기존 리스너 제거
  • useEffect의 의존성 배열을 설정하여, roomUser.id나 user.id 변경 시에만 재등록되도록 제한

2. React에서 소켓 연결 해제 타이밍

컴포넌트가 언마운트되거나 사용자가 방을 떠나더라도, 서버는 단순한 disconnect()만으로는 어떤 방에서 사용자가 나갔는지 알 수 없었습니다.

이로 인해 서버는 해당 사용자가 여전히 방에 남아 있는 것으로 인식하는 문제가 발생했습니다.

해결 방법

  • leaveRoom이라는 커스텀 이벤트를 만들어, 방 ID와 함께 명시적으로 방을 나가는 동작을 수행하도록 했습니다.
  • 클라이언트가 나가기 전 socket.emit('leaveRoom', { roomId })를 호출하고, 이후에 연결을 종료합니다.
  • 서버는 이를 수신하여, 해당 방에서 클라이언트를 제거하고, 방에 사용자가 더 이상 없다면 방 정보도 삭제합니다.

3. 채팅방 참여 시 히스토리 불러오기

유저가 채팅방에 입장했을 때, 기존 대화 내용(히스토리)을 불러오는 기능도 중요한 요구사항 중 하나였습니다.

처음에는 서버 메모리에 메시지를 임시 저장하는 방법을 고려했으나, 안정성과 데이터 영속성을 위해 DB에 저장하고 불러오는 방식으로 전환했습니다.

구현 방식

  • joinRoom 요청 시, userId와 roomId를 함께 서버에 전송
  • 서버는 해당 유저의 방 참여 기록을 확인하고, 필요한 경우 추가
  • 그 이후의 메시지를 불러와 chatHistory 이벤트로 클라이언트에 전송

이렇게 하면 새로운 유저도 자연스럽게 대화 흐름을 이해하고 참여할 수 있습니다.


💡 마무리하며

이번 실시간 채팅 시스템을 구현하면서, 단순한 WebSocket 연결이 아닌

  • 클라이언트 생명주기와의 연동,
  • 서버 내 방 상태 관리,
  • 메시지 리스너 중복 방지,
  • 채팅 히스토리 관리 등 다양한 요소를 고려하게 되었습니다.

Socket.IO는 강력하고 유연한 툴이지만,

모든 것을 자동으로 처리해주는 도구는 아닙니다.

클라이언트와 서버 양쪽에서 상태 관리와 책임을 명확히 정의해야,

유지보수성과 확장성을 모두 갖춘 시스템을 만들 수 있다는 것을 다시금 실감했습니다.

이 글이 실시간 채팅을 구현하고자 하는 분들께 실질적인 도움이 되기를 바랍니다.

궁금하신 점이나 의견은 언제든 환영합니다!