사용자들이 실시간으로 1:1 혹은 그룹 채팅을 할 수 있는 시스템을 설계한다. 메시지는 안정적으로 전달되고 지연은 최소화되며 동시에 수백만 사용자가 접속해도 확장성과 안정성을 유지해야 한다.
(userId % N)
→ 특정 샤드에 메시지 저장POST /api/v1/users/{id}/messages
GET /api/v1/users/{id}/messages?cursor=<opaque>&limit=50
POST /api/v1/conversations
GET /api/v1/conversations/{id}/messages?cursor=<opaque>
-- 사용자
User(id PK, name, status, last_seen)
-- 대화방
Conversation(id PK, type, created_at)
-- 대화방 참가자
Participant(conversation_id, user_id, last_read_message_id, PRIMARY KEY(conversation_id, user_id))
-- 메시지
Message(id PK, conversation_id, sender_id, text, created_at, status)
last_read_message_id
로 읽음 표시 구현
메시지 상태: sent, delivered, read
[Client]
|
[Load Balancer]
|
[Connection Server] <-- WebSocket 유지
|
[Chat Service] <-- 라우팅, ACK 처리
|
+----------+----------------+
| | |
[Message Store] [Redis/Kafka] (실시간 큐)
| |
[Search/History Service] [Notification Service]
last_read_message_id
갱신 → 송신자에게 읽음 표시채팅 시스템은 단순히 메시지를 주고받는 구조가 아니라 실시간성과 안정성, 확장성의 균형을 맞추는 종합적인 문제였다. 특히 수백만 사용자가 동시에 접속하더라도 연결을 유지하려면 Connection Server와 메시지 라우팅 레이어를 분리해 확장성을 확보해야 하고 메시지를 잃지 않으면서도 빠르게 전달하기 위해 큐와 영구 저장소를 함께 사용하는 이중화 전략이 필수적이었다.
또한 읽음 표시나 오프라인 메시지 재전송 같은 기능은 겉보기에는 단순해 보이지만
내부적으로는 멱등성, 재시도, ACK 기반 신뢰성 같은 설계 원칙이 뒷받침되어야 한다는 점도 중요하게 배웠다. 결국 채팅 시스템은 “낮은 지연”만큼이나 “메시지 유실 없는 보장”이 핵심이며 이를 위해 프로토콜 선택(WebSocket), 데이터 모델링, 큐 아키텍처, 샤딩까지 다각적으로 고려해야 한다는 교훈을 얻었다.