멀티 인스턴스 환경에서 채팅 메시지가 유실되는 문제 해결
문제 발견
카카오테크캠퍼스 최종 프로젝트에서 팀 채팅 기능을 WebSocket으로 구현했다. 로컬에서 테스트할 때는 아무 문제가 없었다.
그런데 AWS ECS에 배포하니까 오토스케일링으로 인스턴스가 2개 이상이 되면서 이상한 현상이 생겼다.
- A 사용자가 보낸 메시지가 B 사용자에게 도착하지 않았다
- 어떤 메시지는 도착하고 어떤 메시지는 안 오는 식이었다
원인 분석
WebSocket 연결은 특정 인스턴스와 맺어진다.
1
2
Client A → Instance 1에 연결
Client B → Instance 2에 연결
Client A가 메시지를 보내면 Instance 1에서 처리되는데, Client B는 Instance 2에 연결되어 있다. Instance 1 입장에서는 Client B가 어디 있는지 알 수가 없다.
[이미지 필요] 멀티 인스턴스 환경에서 WebSocket 연결 분산 다이어그램 (Client A-Instance 1, Client B-Instance 2, 메시지 유실 표시)
해결책 선택
인스턴스 간에 메시지를 동기화해야 했다. 여러 방법을 검토했다.
| 방법 | 장점 | 단점 |
|---|---|---|
| DB 폴링 | 구현이 간단하다 | 실시간성이 떨어지고 DB에 부하가 간다 |
| Kafka | 메시지 영속성이 있다 | 이 정도 규모에는 오버엔지니어링이다 |
| Redis Pub/Sub | 실시간이고 간단하다 | 메시지 영속성이 없다 |
채팅 메시지는 DB에도 별도로 기록하고 있어서 Pub/Sub의 메시지 영속성 문제는 신경 쓰지 않아도 됐다. Redis Pub/Sub를 선택했다.
ADR 작성
이 결정을 팀에 공유하기 위해 ADR(Architecture Decision Record)을 작성했다.
핵심 결정 사항:
- 각 ECS 인스턴스는 Redis 채널을 구독한다
- 메시지가 발생하면 채널에 publish한다
- Redis는 메시지를 실시간으로 모든 구독 인스턴스에 전달한다
- 세션은 각 인스턴스에서 관리하고, Redis는 오직 Pub/Sub 브로커 역할만 한다
리스크:
- Redis 장애 시 메시지 전달이 안 된다 → 채팅 기능 전체에 영향이 있다
- 트래픽이 급증하면 Redis Pub/Sub이 병목이 될 수 있다
[이미지 필요] Redis Pub/Sub 기반 멀티 인스턴스 메시지 동기화 아키텍처 다이어그램 (Client → Instance → Redis Pub/Sub → 모든 Instance → 해당 Client)
검증: JMeter로 부하 테스트
구현만 해서는 안 되고, 실제 오토스케일링 상황에서 메시지가 유실되지 않는지 검증해야 했다.
ALB + Fargate + ECS 구조에서 JMeter로 동시에 100명이 채팅하는 상황을 시뮬레이션했다.
- 정상 상황: 인스턴스 2개에서 메시지 정합성 100% 확인
- 스케일 아웃: 트래픽 급증 → 인스턴스 3개로 확장 → 신규 인스턴스도 메시지 수신 확인
- 스케일 인: 트래픽 감소 → 인스턴스 축소 → 남은 인스턴스에서 정상 동작 확인
배운 점
- 단일 인스턴스에서 잘 돌아가는 코드가 분산 환경에서도 잘 돌아가리란 보장은 없다
- “돌아간다”와 “안정적으로 돌아간다”는 다르다 — 부하 상황에서도 검증해야 한다
- 아키텍처 결정에는 근거가 필요하다 — ADR로 기록해두면 나중에 왜 이렇게 했는지 설명할 수 있다
카카오테크캠퍼스 3기 최종 프로젝트(대학생 맞춤형 일정 관리 서비스)에서 경험한 내용 정리.