📘알림 저장과 전송 분리를 위한 구조 설계를 위한 RabbitMQ 정리
모임 기반 앱의 알림 시스템 구조를 설계하면서 메세지 큐 기능의 도입이 필요했다.
[도메인 이벤트 발행]
↓
[알림 허브 도메인: 메시지 가공]
↓
[알림 도메인: 알림 저장 + 사용자에게 전송]
알림은 빠르게 보내는 것도 중요하지만, 전송이 실패해도 유실되지 않아야 한다.
전송이 실패했다고 해서 알림 저장까지 같이 실패하면 안 된다.
지금은 WebSocket만 쓰지만, 나중에 푸시, 이메일, 문자 같은 기능이 생길 수 있다.
예상되는 알림 트래픽 (fan-out 1,000 기준, 하루 5시간 활동 기준)
| 알림 종류 | 주간 트리거 수 | fan-out | MQ 메시지 수 | 초당 MQ 메시지 수 |
|---|---|---|---|---|
| 모임 생성 | 15,000 | ×1,000 | 15,000,000 | 119.05 |
| 모임 수정 | 10,000 | ×10 | 100,000 | 0.79 |
| 모임 참가 + 결제 | 50,000 | ×2 | 100,000 | 0.79 |
| 모임 취소/환불 | 10,000 | ×1 | 10,000 | 0.08 |
| 팔로우 | 70,000 | ×1 | 70,000 | 0.56 |
| 총합 | 155,000 | - | 15,280,000 | 121.27 |
XPENDING, XCLAIM, 재시도 횟수 추적, 시간 체크 등 여러 작업을 수동으로 처리해야 한다| 항목 | RabbitMQ (초당 121건 기준) | Kafka (초당 121건 기준) | 차이 (배수) |
|---|---|---|---|
| 메모리 사용량 (MB) | 300 | 2000 | 6.7 |
| CPU 사용률 (%) | 15 | 35 | 2.3 |
| 디스크 사용량 (GB/일) | 1 | 10 | 10.0 |
| 네트워크 사용량 (MB/일) | 200 | 600 | 3.0 |
| 최대 처리량 (msg/sec) | 20,000 | 1,000,000 | 50.0 |
| 현재 트래픽 (msg/sec) | 121 | 121 | 1.0 |
| 처리량 대비 활용률 (%) | 0.6 | 0.0121 | 0.0 |
일반적으로 DB에 저장하고, 주기적으로 스케줄러로 전송하는 방식도 존재한다.
그러나 이 방식은 다음과 같은 한계를 갖는다:
| 항목 | 스케줄러 방식 | MQ 기반 방식 |
|---|---|---|
| 전송 지연 | 수초~수분 지연 발생 | 실시간 전송 가능 |
| 확장성 | 배치 단일 스레드 한계 | MQ 컨슈머 병렬 확장 가능 |
| 장애 대응 | 실패 처리 복잡 | 재시도/실패 큐 자동 분리 |
| 추적 가능성 | 직접 로그/상태 구현 | 전송 상태 필드와 DLQ 활용 |
최종적안 우리의 알림 시스템 예시
도메인 이벤트 (AFTER_COMMIT)
↓
알림 허브: 메시지 가공
↓
RabbitMQ 발행 (notification.exchange)
↓
[컨슈머1] 알림 저장 (DB)
├─ 성공: 전송 요청 발행
└─ 실패: 재시도 큐 또는 DLQ
↓
[컨슈머2] 전송 (SSE/FCM)
├─ 성공: 상태 업데이트
└─ 실패: 재시도 큐 또는 DLQ
| 처리량 수준 | 추천 MQ | 이유 |
|---|---|---|
| ~5,000건/초 | RabbitMQ | 간단하고 실시간성 우수 |
| ~10,000건/초 | RabbitMQ or Kafka | Kafka는 트래픽 지속 시 고려 |
| 10,000건 이상 | Kafka | 분산 처리 기반, 대규모 최적화 |