현재 진행 중인 토이프로젝트에 이전에 웹소켓을 이용해서 채팅을 구현하였습니다.
웹소켓이라는 건 HTTP와 같은 통신프로토콜이며 Http와 달리 양방향 통신이 가능하며 Connection을 끊지 않고 지속해서 유지하는 특성을 지니고 있습니다.
현재는 단일한 서버의 인스턴스만으로 실행하고 있지만, 트래픽이 몰려서 아래와 같이 Scale-Out된 상황을 가정해 봅시다.
유저 A(클라이언트)가 서버 인스턴스(1)와 웹소켓을 연결해서 채팅을 실행했다고 하자. 유저 A가 작성하는 채팅 내용은 서버 인스턴스(1)에는 기록이 되겠고 A는 실시간으로 확인이 가능할 것입니다.
유저 A와 대화를 하는 유저B는 서버 인스턴스(1)이 아니라 다른 서버 인스턴스(2)와 웹소켓을 연결해서 채팅을 한다고 하면 유저 A와 다른 서버의 인스턴스와 웹소켓이 연결되었기 때문에 A가 작성한 대화 내용을 확인할 수 없는 문제가 있습니다. 실시간 채팅 시스템의 서버 인스턴스 간의 상태 동기화가 이뤄지지 않는 심각한 문제는 사용자 경험을 크게 저하시킬 수 있습니다.
(트래픽을 가정하면) 사실상 실무 환경에서는 단일한 서버 인스턴스로 운영하는 것이 불가능하고 WAS를 스케일아웃하여 사용하기 때문에 이는 단순채팅 구현이 아닌 실무를 가정하면 아주 중요한 이슈사항이라고 할 수 있습니다.
이런 상황에서 아래와 같이 ‘메시징 큐’를 적절하게 도입하면 위와 같은 문제를 해결하는데 큰 도움이 될 수 있습니다.

메시징 큐란 서버 인스턴스가 여러 대일때, 각 인스턴스 간 메시지 전달을 중개하는 일종의 중개 시스템으로서 위와 같은 채팅과 관련된 도메인뿐 아니라 여러 도메인에 걸쳐서 데이터를 중개자로서 비동기적 통신을 지원합니다.
대표적인 메시징 큐는 Kafka, Redis Pub/Sub 등이 존재합니다.
각 메시징 큐의 특성을 간단하게 알아보자면 다음과 같습니다.

러프하게 이해하자면 큰 틀에서 메시징 큐의 기본 골조는 위의 그림과 같습니다.
네모 박스의 역할을 하는 것이 메시징큐(미들웨어)입니다.
좌측에 보이는 Publisher(Sender)와 우측에 보이는 Subscriber(Receiver)들이 각 인스턴스의 개념에 대응한다고 할 수 있습니다.
Publisher는 메시징큐 내부의 특정 Topic(또는 Channel)에 메시지를 전송하고 해당 Topic(또는 Channel)을 구독하는 Subscriber(Receiver)가 해당 메시지를 수신합니다.
큰 틀에서 메시징 큐가 하는 역할은 위와 같고 세부적으로 해당 메시지를 어떻게 처리하는지는 메시징 큐마다 제각각인데, 대표적 메시징 큐로는 Redis Pub/sub과 Kafka가 있습니다.
간략히 위와 같이 Kafka와 Redis pub/sub의 특징을 알아봤습니다.
위 내용으로 미뤄볼때 어떤 메시징 큐를 도입할 지는 다루고 있는 개발자가 풀고자 하는 문제의 요구사항이 무엇인지가 중요하다고 생각됩니다.
저의 토이프로젝트는 ‘채팅’이라는 도메인이 서버 내에서 수행하는 역할이 크지 않았습니다.
예를 들어서 치지직 혹은 디스코드 등과 같이 큰 다수의 사용자가 하나의 채팅방에 들어와서 채팅하는 다대다를 지원하지 않았습니다.
대부분의 쇼핑몰(예를 들어 네이버톡)에서 구매자와 사용자가 해당 상품에 대해서 1:1 문의를 하는 내용을 염두에 두고 채팅을 구상하였습니다.
현재 다루고자 하는 문제의 영역인 ‘채팅’ 역시 다른 도메인에 비춰볼때, 상대적으로 데이터가 유실되어도 상관이 없고(중요하지 않다는 것이 아니라 결제, 주문 등에 비춰봤을때 그렇습니다.), 데이터의 실시간성이 훨씬 중요합니다. 또 이 실시간성이 사용자 경험과 크게 연관되어 있다고 생각하였습니다.
이에 따라 저는 현재 토이 프로젝트에서 채팅을 구현함에 있어서 Redis Pub/Sub을 도입하고자 하였습니다. 미들웨어인 Redis Pub/sub로는 메시지 유실이 가능할 수 있기 때문에 채팅 메시지 내역에 대해서는 별도의 ChatMessage로서 DB에 저장하도록 하도록 하였습니다.
이에 따라 Redis Pub/Sub으로 실시간 전송 역할을 수행하고, 이후 DB를 통해서 메시지 로그를 저장하여 실시간성과 영속성을 분리해서 관리하기로 하였습니다.