마이크로서비스 아키텍처에서 서비스 간 통신은 시스템 전체 안정성과 확장성, 응답성에 지대한 영향을 미칩니다. 특히 내부 메시지 큐 (Message Queue) 설계는 비동기 처리, 이벤트 기반 흐름, 장애 내성 등을 결정짓는 핵심 요소입니다. 이번 글에서는 대표적인 메시징 시스템인 Apache Kafka와 NATS를 중심으로, 설계 관점에서 비교하고 보완할 수 있는 다른 옵션까지 함께 살펴보겠습니다. 이를 통해 어떤 상황에 어떤 메시지 큐가 적합한지 판단할 수 있는 기준을 제시하겠습니다.
왜 메시지 큐 선택이 중요한가
마이크로서비스는 느슨한 결합(loose coupling)이 핵심 설계 원칙 중 하나입니다.
동기 호출 방식(RPC, HTTP 등)만 사용하면 서비스 간 장애 전파, 고가용성 확보, 스케일 아웃 전략 등이 복잡해집니다.
이때 메시지 큐를 통합 미들웨어로 사용하면 다음과 같은 장점이 있습니다:
- 비동기 메시징을 통한 서비스 독립성 보장
- 메시지 버퍼링으로 부하 변화 흡수
- 이벤트 중심 설계로 확장성과 유연성 확보
- 장애 복구 및 재시도 로직 구현 가능
하지만 메시지 큐를 잘못 설계하면 지연(latency), 메시지 손실, 스케일 병목, 운영 복잡성 등이 문제가 됩니다.
따라서 Kafka, NATS 등 시스템별 특성과 트레이드오프를 명확히 이해하고 선택해야 합니다.
Kafka 개요 및 특징
Apache Kafka는 분산 로그 기반(event streaming) 메시징 플랫폼으로, 고속 처리와 내구성, 확장성에 특화되어 있습니다.
다음은 Kafka의 주요 특징입니다:
- 토픽(Topic)과 파티션(Partition)을 기반으로 메시지를 분산 저장
- 메시지를 디스크에 영구 저장하고 복제(replication) 가능
- 파티션 내 순서 보장
- 강한 내구성(durability)과 장애 복구 기능
- 풍부한 생태계 (Kafka Streams, Kafka Connect 등)
- 소비자 그룹(Consumer Group)을 통한 병렬 처리
Kafka는 높은 처리량과 실시간 스트리밍, 로그 수집, 이벤트 소싱 패턴 등에 자주 활용됩니다.
Kafka의 약점도 존재합니다:
- 초기 구성과 운영이 복잡
- 토픽과 파티션 관리, 브로커 동기화, 리밸런싱(rebalancing) 등이 부담
- 낮은 지연(latency)에는 한계가 있을 수 있음
- 메시지를 많이 보유할 경우 스토리지와 디스크 I/O 부담
NATS 개요 및 특징
NATS는 경량 메시징 시스템으로, 단순성과 고성능을 핵심 가치로 설계되었습니다.
특히 마이크로서비스 간 통신에 적합한 특성을 가집니다. 주요 특징은 다음과 같습니다:
- Subject 기반의 퍼블리시/구독(pub/sub) 모델
- Request–Reply 패턴과 큐 그룹(queue group) 지원
- 기본 버전은 메시지를 디스크에 저장하지 않는 비영속(non-persistent) 방식
- JetStream 기능을 통해 메시지 내구성, 재전송, 재생(replay), 지속 소비자 등 기능 확장
- 낮은 지연과 높은 처리량
- 운영과 설정이 단순하고 리소스 사용량이 낮음
NATS의 기본 버전은 메시지를 일시적으로 전달하고 사후 저장하지 않으므로, 메시지 손실 가능성이 있습니다.
JetStream을 이용하면 신뢰성 있는 메시징 패턴 구현이 가능합니다.
Kafka vs NATS 비교 관점
설계 관점에서 아래 기준을 중심으로 Kafka와 NATS를 비교해 보겠습니다.
비교 항목 | Kafka | NATS |
메시지 영속성 / 내구성 | 디스크 저장, 복제, 강한 내구성 보장 | 기본은 비영속, JetStream으로 영속성 확보 가능 |
순서 보장 | 파티션 내 순서 보장 | 주제(subject) 단위로만 순서 보장 가능하며 복수 퍼블리셔 간 순서 보장은 제한적 |
확장성 / 처리량 | 매우 높음, 파티션 기반 스케일링 | 고성능, 단순 구조 기반 확장 가능 |
지연(latency) | 낮지만 복잡도 및 디스크 I/O 영향 있음 | 매우 낮은 지연, 네트워크 홉 최소화 |
소비자 모델 | 소비자 그룹을 통한 병렬 처리 | 큐 그룹(queue groups)을 통한 로드 분산 |
운영 복잡성 | 브로커, 토픽/파티션 관리, 리밸런싱 등 높은 복잡도 | 간단한 설정, 작은 오버헤드, JetStream 설정만 추가 |
메시지 처리 보증 수준 | 최소 1회(at least once), 정확히 1회(exactly once) 지원 | JetStream를 통해 at least once, exactly once 가능 |
멀티 테넌시, 권한 제어 | 복잡한 구성이나 외부 시스템 연동 필요 | 계정 기반 멀티 테넌시, 주제 권한 제어 가능 |
토픽/주제 유연성 | 수십~수백 개 토픽 가능, 주제 생성/삭제에는 제약 | 수만 개 subject 사용 가능, wildcard 기반 동적 필터링 가능 |
메시지 보증 수준
Kafka는 기본적으로 최소 1회 전송(at least once) 모델을 지원하며, 최신 버전에서는 정확히 1회 전송(exactly once) 모델도 지원됩니다.
반면, NATS는 기본 모드에서는 메시지 손실 가능성이 있으며, JetStream을 사용하면 영속성, ACK, 전달 보증 등을 설정할 수 있습니다.
순서 유지
Kafka는 파티션 내 순서를 보장합니다. 여러 소비자가 같은 파티션을 읽을 경우 순서 일관성을 관리할 수 있습니다.
NATS는 단일 퍼블리셔의 순서는 보통 보장되지만, 여러 퍼블리셔 또는 복제 구조에서는 순서 혼동 가능성이 있습니다.
운영 복잡성과 유지보수
Kafka는 브로커 구성, 스케일링, 리밸런싱, 장애 복구 등을 포함하여 운영 부담이 큽니다.
NATS는 단일 바이너리 구성으로 시작이 쉽고, 설정과 유지보수 비용이 더 낮습니다.
메시지 큐 설계 관점의 고려 요소
Kafka나 NATS 선택 외에도 메시지 큐 설계 시 고려해야 할 요소는 다음과 같습니다:
- 요구 처리량 및 트래픽 패턴
- 메시지 양이 많고 절대 유실이 허용되지 않는 경우 Kafka가 유리
- 높은 빈도, 낮은 지연이 중요하면 NATS가 적합
- 메시지 보존 기간 및 재처리 가능성
- 장기 보관, 로그 재생, 이벤트 소싱이 필요하다면 Kafka
- 빠른 일회성 처리 위주라면 NATS + JetStream 조합
- 확장 전략과 파티셔닝
- Kafka에서는 토픽과 파티션 설계가 핵심
- NATS는 클러스터 확장이나 JetStream 스트림 분할 방식 선택
- 소비자 처리 패턴
- 병렬 처리, 멀티 컨슈머 구조 등 소비자 그룹 모델 고려
- ACK 기반 재시도, 지연 큐 등을 구현할 수 있는지
- 장애 복구와 내결함성
- 브로커 장애 시 failover, 메시지 손실 최소화
- 클러스터 복제, 리플리카 전략
- 운영 및 유지보수 비용
- 모니터링, 장애 진단, 백업, 확장(Scale-out) 전략
- 운영자의 역량 및 시스템 복잡성 허용 범위
- 하이브리드 아키텍처
때로는 Kafka와 NATS를 함께 사용하는 하이브리드 방식이 더 균형 잡힌 선택이 됩니다.
예컨대, 내부 서비스 간 실시간 통신은 NATS로 처리하고, 이벤트 저장 및 분석용 스트림은 Kafka로 처리하는 구조가 가능합니다.
설계 패턴 및 활용 예시
내부 서비스 간 명령(Command) 메시징
- 서비스 A가 서비스 B에 작업 요청 → 메시지를 발송
- B는 이를 구독하고 처리 후 응답 메시지 발행
- NATS의 Request–Reply 또는 Kafka의 요청/응답 조합 사용
이벤트 브로드캐스트 / 구동 흐름(Event Propagation)
- 특정 이벤트(예: 사용자 생성, 주문 처리 등)를 여러 서비스에 전파
- Kafka의 토픽 기반 또는 NATS의 subject + wildcard 구독 패턴 활용
로그 수집 및 분석 스트림
- 서비스가 생성하는 이벤트 로그를 Kafka로 수집
- 실시간 분석, ETL 파이프라인, 이벤트 저장소로 연결
지연 처리 및 재시도 큐
- 실패한 메시지나 지연 메시지를 별도 큐에 저장
- Kafka에서는 Dead-Letter Queue(DLQ)를 설계
- NATS JetStream에서도 만료 설정, 리트라이 로직 구현
선택 가이드 요약
- 지연이 중요하고 메시지 양이 크지 않은 경우
→ NATS (JetStream 포함) 선택
→ 낮은 운영 복잡성, 빠른 응답성 확보 가능 - 높은 처리량, 데이터 저장, 이벤트 소싱이 필요한 경우
→ Kafka 선택
→ 내구성, 로그 재생, 복합 이벤트 처리 가능 - 둘을 조합하는 하이브리드 방식 고려
→ 내부 통신은 NATS, 이벤트 저장 및 분석은 Kafka - 메시지 보증, 순서, 소비자 모델 요구사항을 분석하여 구조 설계
→ 첫 구동 시 간단한 구조로 시작하고 점진적으로 확장
마무리
마이크로서비스 내부 메시지 큐 설계는 단순히 한 솔루션을 선택하는 문제가 아닙니다. Kafka와 NATS는 각각 고유한 강점과 약점을 지니고 있으며, 프로젝트 특성, 처리량, 응답성 요구사항, 운영 여력 등을 모두 고려해야 합니다. 또한 두 시스템을 병합해 쓰는 하이브리드 전략이 오히려 현실적이고 유연한 대응이 될 수 있습니다. 이 글에서는 Kafka와 NATS 중심으로 메시지 큐 설계 관점에서 비교했으며, 지연, 내구성, 운영 복잡성 등 주요 고려 요소를 함께 설명했습니다.