LOL 전적 트래커 프로젝트에서 Redis Pub/Sub과 RabbitMQ를 동시에 사용합니다. 같은 프로젝트에서 두 기술을 함께 쓰다 보니 각각의 특성과 어울리는 상황이 자연스럽게 정리됐습니다.
어디에 무엇을 썼나
수집 서버(lol-collect)에서 게임 시작·종료를 감지하면 두 곳으로 이벤트를 내보냅니다.
- Redis Pub/Sub: 수집 서버 → lol-api → SSE로 브라우저 실시간 전달
- RabbitMQ: 수집 서버 → lol-api, lol-discord-api → 각각 후처리
같은 이벤트를 서로 다른 브로커로 두 곳에 보내는 구조입니다. 처음엔 하나로 통일하는 게 맞지 않나 싶었는데, 쓰다 보니 각자 잘하는 역할이 달랐습니다.
Redis Pub/Sub — 빠른 실시간 전달
Redis Pub/Sub은 채널에 메시지를 publish하면 구독 중인 모든 subscriber에게 즉시 전달합니다. 구조가 단순하고 속도가 빠릅니다.
// Publisher (lol-collect)
redisTemplate.convertAndSend("game-event", message);
// Subscriber (lol-api)
@Bean
MessageListenerAdapter listenerAdapter(GameEventSubscriber subscriber) {
return new MessageListenerAdapter(subscriber, "onMessage");
}
// 수신 후 SSE 브로드캐스트
public void onMessage(String message) {
sseEmitters.forEach(emitter -> emitter.send(message));
}
Redis Pub/Sub의 핵심 특징은 메시지를 저장하지 않는다는 점입니다. Subscriber가 연결되어 있지 않으면 메시지는 그냥 사라집니다. 이게 단점처럼 보이지만, SSE 실시간 전달 용도에서는 오히려 잘 맞습니다. 브라우저가 연결 중이 아닐 때 쌓인 이벤트를 나중에 재전송할 필요가 없기 때문입니다.
RabbitMQ — 신뢰성 있는 이벤트 처리
RabbitMQ는 메시지를 큐에 저장합니다. Consumer가 잠깐 다운되었다가 다시 올라와도 그 사이에 쌓인 메시지를 처리할 수 있습니다.
// Producer (lol-collect)
rabbitTemplate.convertAndSend("lol.exchange", "rank.changed", event);
// Consumer (lol-discord-api)
@RabbitListener(queues = "rank.changed.queue")
public void handleRankChanged(RankChangedEvent event) {
discordWebhook.send(formatMessage(event));
}
Discord 알림 전송 용도에는 RabbitMQ가 더 적합합니다. Discord Webhook 호출이 실패하거나 lol-discord-api가 잠깐 재시작되더라도 큐에 메시지가 남아있어서 재처리가 가능합니다. 랭크 변동 같은 이벤트를 놓치면 안 되는 상황에서는 메시지 보존이 중요합니다.
핵심 차이 정리
| 항목 | Redis Pub/Sub | RabbitMQ |
|---|---|---|
| 메시지 저장 | 없음 (fire-and-forget) | 큐에 저장, 재처리 가능 |
| 속도 | 매우 빠름 | 약간의 오버헤드 |
| 구독자 부재 시 | 메시지 유실 | 큐에 대기 |
| 운영 복잡도 | 낮음 (Redis 하나로 해결) | 별도 RabbitMQ 운영 필요 |
| 적합한 용도 | 실시간 브로드캐스트, SSE | 중요한 이벤트, 알림, 작업 큐 |
선택 기준 요약
메시지가 유실되어도 괜찮고 빠른 실시간 전달이 중요하다면 Redis Pub/Sub, 메시지를 반드시 처리해야 하고 재처리 가능성이 필요하다면 RabbitMQ를 선택하면 됩니다.
Redis를 이미 캐시 용도로 쓰고 있다면 간단한 실시간 이벤트는 Redis Pub/Sub으로 처리하고, 별도 인프라를 추가하기보다 Redis 하나로 해결하는 것이 관리 포인트를 줄이는 방법이기도 합니다.