<aside>
<img src="/icons/reorder_gray.svg" alt="/icons/reorder_gray.svg" width="40px" />
목차
</aside>
1. 배경
프로젝트 초기에 구현한 실시간 채팅 기능은 WebSocket과 STOMP 프로토콜을 기반으로, 클라이언트로부터 메시지를 수신하면 DB에 동기적으로 저장한 후 해당 채팅방 구독자들에게 메시지를 전달하는 비교적 단순한 구조였습니다. 이 방식은 빠른 초기 개발에는 유리했지만, 서비스 사용자가 늘어나면서 다음과 같은 한계점들이 드러났습니다.
- 메시지 지연 (Latency): 메시지를 보낸 후 DB 저장 트랜잭션이 완료될 때까지 서버의 응답이 지연되어, 사용자 입장에서는 메시지가 즉각적으로 전달되지 않는 답답함을 느낄 수 있습니다. 실시간성이 중요한 채팅 기능의 사용자 경험을 저해하는 요인이라고 생각했습니다.
- 확장성 부족: 모든 메시지 처리 로직(수신, DB 저장, 전송)이 단일 애플리케이션 서버 인스턴스 내에서 동기적으로 처리되었습니다. 이는 트래픽 증가 시 서버 부하를 가중시키고, 서버 인스턴스를 늘리는 수평 확장(Scale-out)이 불가능한 구조였습니다.
- 장애 전파 위험 (Risk of Cascading Failures): DB 연결 지연이나 장애가 발생할 경우, 동기 처리 방식 때문에 메시지 저장 로직이 멈추면 전체 채팅 메시지 발송 기능까지 마비될 수 있는 위험이 존재했습니다.
이러한 문제들을 해결하고 더 안정적이고 확장 가능한 실시간 메시징 구조를 구축하기 위해, 메시지 브로커 도입을 검토하게 되었습니다.
2. 선택
- Kafka
- 특징: 대용량 데이터 스트림 처리에 특화된 분산 메시징 플랫폼입니다. 높은 처리량(High Throughput)과 영속성(Durability)을 보장하며, 메시지를 디스크 기반 로그에 순차적으로 저장하여 데이터 유실에 강합니다. 파티셔닝을 통한 병렬 처리와 뛰어난 수평 확정성이 강점입니다.
- 고려 사항: 강력한 기능만큼 초기 구축 및 운영의 복잡도가 높습니다. 'DB 저장 로직 분리'라는 명확한 목표에 비해서는 무거운 솔루션으로 판단되었습니다. 리소스 요구량도 상대적으로 높습니다.
- RabbitMQ
- 특징: 안정적인 오픈소스 메시지 브로커로 다양한 메시징 프로토콜을 지원합니다. 유연한 라우팅기능과 메시지 전송 보장, 데드 레터 큐 등 신뢰성 높은 메시지 처리를 위한 다양한 기능을 제공합니다.
- 고려 사항: Kafka보다는 가볍지만, 여전히 별도의 브로커 서버 구축 및 관리가 필요합니다. 다양한 기능은 장점이지만, 저희의 요구사항은 특정 토픽(채팅방)에 메시지를 발행하고, 이를 구독하여 DB에 저장하는 비교적 단순한 패턴이었습니다. RabbitMQ의 복잡한 라우팅 기능이나 강력한 전송 보장 메커니즘은 현재 단계에서는 필수적이지 않았고, 도입 및 운영에 추가적인 학습 곡선과 관리 부담이 발생할 수 있었습니다.
- Redis Pub/Sub
- 특징: In-Memory 데이터 저장소인 Redis가 제공하는 간단한 메시지 발행/구독 기능입니다. 별도의 설치 없이 기존 Redis 인프라를 활용할 수 있으며, 메모리 기반으로 동작하여 매우 빠른 메시지 전송 속도를 제공합니다. 구현이 간단합니다.
- 고려 사항: 메시지 영속성을 보장하지 않습니다. 즉, Redis 서버가 다운되거나 구독자가 없는 상태에서 발행된 메시지는 유실될 수 있습니다. 또한, Kafka나 RabbitMQ처럼 복잡한 기능은 제공하지 않습니다. 하지만 우리의 경우, 메시지 자체의 영속성보다는 **'클라이언트 응답 지연 해소'와 'DB 저장 로직의 비동기 분리'**가 주 목적이었고, DB 저장은 별도의 구독 서비스에서 안정적으로 처리하면 되므로 메시지 유실 가능성은 수용 가능한 수준이었습니다. (DB 저장 실패 시 재시도 로직 등으로 보완 가능)
3. 결정 및 이유
종합적인 검토 끝에, Redis Pub/Sub를 도입하기로 결정했습니다. 이유는 다음과 같습니다.
- 낮은 도입 비용과 기술 적합성:
저희는 이미 세션 관리 및 캐싱 목적으로 Redis를 사용 중이었습니다. 따라서 Kafka나 RabbitMQ처럼 별도의 인프라를 구축하고 운영하는 부담 없이, 기존 Redis 설정에 약간의 코드만 추가하여 Pub/Sub 기능을 즉시 활용할 수 있었습니다. 이는 개발 시간과 비용을 절약하는 이점이었습니다.
- 성능: Redis는 In-Memory 기반으로 동작하므로 메시지 발행 속도가 매우 빠릅니다. 이는 기존의 동기 방식에서 발생했던 클라이언트 메시지 전송 지연 문제를 직접적으로 해결할 수 있었습니다. 채팅 메시지를 Redis에 빠르게 발행하고 즉시 클라이언트에 응답을 보낸 뒤, 별도의 구독로세스가 비동기적으로 DB 저장을 처리하는 구조는 요구사항에 부합했습니다.
- 구현 난이도 낮음: Kafka나 RabbitMQ에 비해 API가 훨씬 간단하여 학습 곡선이 낮고 빠르게 기능을 구현할 수 있었습니다. 복잡한 설정이나 개념 없이 '발행'과 '구독'이라는 명확한 로직에만 집중할 수 있었습니다.