알림 시스템 아키텍처 선택에 대한 고찰
알림 시스템을 설계할 때 중요한 것은 메시지의 신뢰성과 처리의 원자성이다. 초기에는 Java의 CompletableFuture를 활용한 비동기 처리로 시스템을 구현했으나, 알림 발송과 로그 저장 과정에서 여러 문제점이 발생했다. 특히 파티셔닝된 로그 테이블에 대한 트랜잭션 처리와 실패 시의 재시도 로직 구현에서 어려움을 겪었다.
이러한 문제를 해결하기 위해 메시지 큐 도입을 검토하던 중, 일부 기업들이 Kafka를 사용하여 알림 시스템을 구현했다는 사례를 발견했다. 이는 메시지 큐(RabbitMQ)와 분산 이벤트 스트리밍 플랫폼(Kafka)의 차이점을 더 깊이 이해하고, 우리 시스템에 더 적합한 솔루션을 선택해야 할 필요성을 제기했다.
두 시스템의 특성을 비교 분석하고, 현재 구현하고자 하는 알림 시스템의 요구사항을 고려한 결과, RabbitMQ가 더 적합한 선택이라 판단했다. 이러한 판단의 근거와 상세한 분석 내용을 아래와 같이 정리했다.
java
@Service
@Slf4j
public class NotificationService {
@Async
public CompletableFuture<Void> sendNotification(NotificationHistory notification) {
return CompletableFuture.runAsync(() -> {
try {
sendPushNotification(notification);
// 알림 로그 파티셔닝 저장
saveNotificationLog(notification);
} catch (Exception e) {
// 실패 처리 및 로그 저장이 각각 별도 트랜잭션으로 발생
// 알림 실패와 로그 저장의 원자성 보장이 어려움
log.error("Failed to send notification: {}", e.getMessage());
}
}, Executors.newVirtualThreadPerTaskExecutor());
}
private void saveNotificationLog(NotificationHistory notification) {
// 파티셔닝된 테이블에 로그 저장
// 알림 실패시 로그 상태 관리가 복잡
}
}
이런 문제점으로 메세지 큐를 도입해 아래와 같은 이점을 얻을 수 있다고 생각했다.
하지만!!
알림 시스템을 설계 할때 특정 상황에서는 kafka를 통해 구현한다는 레퍼런스를 보고 메세지큐를 이용한 설계와 분산 이벤트 스트리밍을 이용한 설계가 어떤 점에서 다른지 궁금해졌다.
알림 시스템의 주요 특성:
참고 : kafka vs rabbitmq
결론: 알림 전송과 로그 저장 시스템에 RabbitMQ가 적합한 이유
1. 트랜잭션 특성
- 알림 전송과 로그 저장이 하나의 트랜잭션으로 처리되어야 함
- RabbitMQ의 메시지 확인(ACK) 메커니즘으로 원자성 보장
- 실패 시 자동으로 Dead Letter Queue로 이동하여 재처리 가능
2. 로그 저장 목적
- 알림 전송 결과를 단순히 기록하는 것이 목적
- 로그의 장기 보관이나 재처리가 필요없음
- Kafka의 영구 저장 특성은 이 경우 오버스펙
3. 즉시성
- 알림 전송 즉시 로그가 저장되어야 함
- RabbitMQ의 푸시 기반 모델이 이 요구사항에 적합
- Kafka의 풀 모델은 불필요한 지연 발생 가능
4. 구현 복잡도
- 단순한 알림 전송과 로그 저장에는 RabbitMQ의 단순한 구조가 적합
- Kafka의 복잡한 아키텍처는 현재 요구사항에 과도