Kafka를 고려하기 전에 내가 선택하는 메시징 백본, NATS
요약
메시징 시스템 선택 시 Kafka의 대안으로 NATS를 제안합니다. NATS는 Core NATS의 빠른 pub/sub 기능과 JetStream의 지속성 기능을 결합하여 통신 패브릭으로서 강력한 성능을 제공합니다.
핵심 포인트
- NATS는 pub/sub, 큐, request/reply를 하나의 바이너리로 처리 가능
- JetStream을 통해 지속성, 스트림, KV 버킷 등 내구성 기능 제공
- 데이터 플랫폼보다 통신 패브릭이 필요한 경우 강력한 기본값
- Kafka는 데이터 생태계와 신뢰할 수 있는 단일 원천이 중요할 때 적합
대부분의 팀은 메시징 시스템을 선택하지 않습니다. 대신 하나씩 쌓아갑니다. 작업용 큐(queue), 캐시 무효화(cache invalidation)를 위한 pub/sub, 서비스를 위한 request/reply, 그리고 분석을 위한 내구성이 있는 이벤트 로그(durable event log)까지 말이죠. 갑자기 다이어그램에는 SQS, SNS, RabbitMQ, Kafka, 그리고 "messaging"이라고 불리는 세 개의 내부 라이브러리가 등장하게 됩니다.
이것이 바로 NATS가 주목받아야 하는 이유입니다.
Core NATS는 빠른 pub/sub, 큐 그룹(queue groups), 그리고 request/reply를 제공합니다. JetStream은 지속성(persistence), 스트림(streams), 컨슈머(consumers), 승인(acknowledgements), 재생(replay), 키-값 버킷(key-value buckets), 그리고 오브젝트 스토리지(object storage)를 추가합니다. 하나의 바이너리로 종종 세 개의 제품이 되어버리는 세 가지 작업을 모두 처리할 수 있습니다.
그렇다고 해서 NATS가 작은 Kafka라는 뜻은 아닙니다. 만약 당신의 이벤트 로그가 신뢰할 수 있는 단일 원천(source of truth)이거나, 몇 달 전의 기록을 재생(replay)하는 것이 일반적이거나, 혹은 데이터 생태계가 지연 시간(latency)이나 토폴로지(topology)보다 더 중요하다면, 여전히 Kafka가 명확한 정답입니다.
더 나은 주장은 더 좁은 범위에 있습니다. NATS는 데이터 플랫폼이 필요하기 전에 통신 패브릭(communication fabric)이 필요한 경우 강력한 기본값(default)이 됩니다.
형태 (the shape)
만약 코어 프로토콜에서 내구성이 있는 메시징(durable messaging)을 기대했다면 이 말이 나쁘게 들릴 수도 있습니다. 하지만 실시간 신호(live signals), 캐시 무효화(cache invalidations), 서비스 디스커버리(service discovery), request/reply, 그리고 컨트롤 플레인(control-plane)의 대화(chatter)를 위해서는 이것이 핵심입니다.
JetStream은 내구성이 도입되는 지점입니다. 스트림(stream)은 하나 이상의 주제(subjects)에 대해 메시지를 저장합니다. 컨슈머(consumer)는 해당 스트림에 대한 상태 저장 뷰(stateful view)입니다. 서버는 전달(delivery), 승인(acknowledgements), 재전달(redelivery), 필터(filters), 시작 위치(starting position), 그리고 보존(retention)을 추적합니다.
작은 설정은 생소하지 않습니다:
jetstream {
store_dir: "/var/lib/nats/jetstream"
max_mem_store: 1G
...
그리고 스트림 정의는 보통 비즈니스 대상을 이름으로 지정합니다:
nats stream add ORDERS \--subjects "orders.*" \--storage file \...
JetStream KV와 Object Store는 유용하지만, 이는 스트림 추상화(stream abstractions)이지 Postgres, S3, 또는 검색 엔진의 대체제가 아닙니다.
비교 (the comparison)
| 축 (Axis) | NATS 및 JetStream | SQS | RabbitMQ | Kafka |
|---|---|---|---|---|
| 전달 (Delivery) | Core NATS는 최대 한 번 (at-most-once) 전달입니다. JetStream은 설정된 윈도우 내에서 발행자 중복 제거 (publisher deduplication) 기능을 갖춘 최소 한 번 (at-least-once) 전달 방식입니다. | 표준 큐 (Standard queues)는 최소 한 번 (at-least-once) 전달입니다. FIFO 큐는 순서 보장 및 5분간의 중복 제거 기능을 추가합니다. | 확인 응답 (Ack) 기반 큐입니다. 쿼럼 큐 (Quorum queues)는 복제된 내구성 (replicated durability)을 추가합니다. | 컨슈머 오프셋 (consumer offsets)을 가진 내구성 있는 로그 (Durable log)입니다. 트랜잭션을 통해 정확히 한 번 (Exactly-once) 전달이 존재하지만, 이는 설계 방식에 영향을 미칩니다. |
| ... |
SQS는 작업이 단순하고 팀이 AWS를 사용 중일 때 제가 원하는 선택지입니다. 메시지를 수신하고, 처리하고, 삭제합니다. 처리에 실패하면 가시성 타임아웃 (visibility timeout)을 통해 메시지가 다시 나타납니다. 재시도 횟수가 소진되면 데드 레터 큐 (dead-letter queue)를 추가합니다. 이는 가장 좋은 의미에서 지루한 방식이지만, 통신 백본 (communication backbone)은 아닙니다.
RabbitMQ는 라우팅 (routing)이 애플리케이션의 일부일 때 제가 원하는 선택지입니다. 익스체인지 (Exchanges), 바인딩 (bindings), 라우팅 키 (routing keys), 프리페치 (prefetch), 확인 응답 (acknowledgements), 그리고 AMQP 계약 (contracts) 등이 유용합니다. 현재의 이야기는 과거의 전설과는 다릅니다. RabbitMQ 4.x에서는 클래식 큐 (classic queues)가 복제되지 않는 반면, 쿼럼 큐 (quorum queues)와 스트림 (streams)이 진지한 워크로드를 위한 내구성 있는 복제 구조입니다.
Kafka는 로그 (log) 자체가 제품일 때 제가 원하는 선택지입니다. 컨슈머가 나중에 참여하여 이력을 재생 (replay)해야 하거나, 컴팩션된 토픽 (compacted topics)이 상태를 재구축하거나, 동일한 스트림에서 분석 (analytics)이 이루어진다면 Kafka는 그 비용만큼의 가치를 합니다. 계층형 스토리지 (Tiered storage)는 보관 경제성 (retention economics)을 도와주지만, Kafka를 가볍게 만들어주지는 않습니다.
NATS는 서비스 통신 (service communication) 자체가 제품일 때 제가 원하는 선택지입니다. 요청/응답 (Request/reply), 팬아웃 (fanout), 큐 그룹 (queue groups), 그리고 내구성 있는 운영 이벤트 (durable operational events)가 하나의 주제 공간 (subject space)을 공유할 수 있습니다. 이는 소규모 플랫폼 팀에게 매우 매력적입니다.
멀티 리전 논거 (the multi-region argument)
NATS의 가장 강력한 논거는 토폴로지 (topology)입니다.
실제 시스템은 더 이상 하나의 깔끔한 리전으로 존재하지 않습니다. 에지 디바이스 (edge devices), 프라이빗 네트워크 (private networks), 컴플라이언스 경계 (compliance boundaries), 그리고 선택적 공유와 함께 로컬 자율성이 필요한 워크로드들을 가지고 있습니다.
NATS 리프 노드(leaf nodes)는 그러한 형태에 적합합니다. 리프(leaf)는 에지 사이트(edge site)에서 허브(hub)로 다이얼 아웃(dial out)할 수 있습니다. 로컬 클라이언트들은 로컬 서버에 연결됩니다. 관심사(interest)는 리프 연결을 통해 브릿지(bridge)되므로, 더 큰 서브젝트 공간(subject space)이 에지에 도달할 수 있습니다.
슈퍼클러스터(Superclusters)는 게이트웨이를 통해 클러스터들을 연결합니다. 리프 노드는 로컬 도메인을 해당 토폴로지(topology)로 연결합니다. JetStream 도메인, 미러(mirrors), 그리고 소스(sources)가 내구성이 있는 데이터(durable data)가 어디에 머물지를 결정합니다.
이러한 명시적인 로컬리티(locality)가 핵심입니다. 어떤 메시지는 로컬에 있고, 어떤 메시지는 중앙으로 미러링되며, 어떤 메시지는 그저 실시간 신호일 뿐입니다. Kafka는 멀티 리전(multi-region)을 수행할 수 있지만, 이제 당신은 복제(replication)를 직접 설계해야 합니다. SQS는 리전(regional) 단위입니다. RabbitMQ는 페더레이션(federation)과 샤벨(shovel) 기능을 가지고 있습니다. NATS는 네트워크 형태가 마치 네이티브(native)인 것처럼 느껴지게 만듭니다.
각각을 선택하는 시점
AWS를 사용 중이고, 단순한 작업 큐(work queue)가 필요하며, 아무것도 직접 실행하고 싶지 않다면 SQS를 선택하세요. 14일의 보관 기간 제한과 FIFO 그룹 모델을 기억하세요.
복잡한 라우팅(routing), AMQP 규약(contracts), 메시지별 확인 응답(acknowledgements), 또는 워크플로 상태(workflow state)를 직접 모델링하는 브로커가 필요하다면 RabbitMQ를 선택하세요.
이벤트 로그(event log)가 신뢰할 수 있는 단일 원천(source of truth)인 경우 Kafka를 선택하세요. 재생(replay)이 일반적이고, 보관 기간이 길며, 분석(analytics)이 중요하다면 Kafka는 지루할 정도로 정확한 정답입니다.
마이크로서비스 RPC와 이벤트(events)가 동시에 필요하고, 낮은 지연 시간(low latency), 멀티 리전 또는 에지 토폴로지, 작은 운영 오버헤드(ops footprint), 그리고 제한된 범위의 내구성이 있는 재생(durable replay)이 필요하다면 NATS를 선택하세요.
핀테크 버전
핀테크에서 결정의 핵심은 대부분 멱등성(idempotency), 순서 보장(ordering), 그리고 감사(audit)에 관한 것입니다.
순서 보장은 보통 계정, 카드, 결제 또는 원장(ledger) 단위로 범위가 지정되어야 합니다. 전역 순서 보장(Global ordering)은 비용이 많이 들며 종종 가짜(fake)인 경우가 많습니다. NATS 서브젝트(subjects)와 JetStream 컨슈머(consumers)는 키(keys)와 동시성(concurrency)이 잘 관리된다면 범위가 지정된 순서 보장(scoped ordering)을 모델링할 수 있습니다.
멱등성은 타협할 수 없는 요소입니다. JetStream 발행자 중복 제거(publisher deduplication)가 도움이 되며, 이는 SQS FIFO 중복 제거가 도움이 되는 것과 비슷하지만 특정 윈도우(window) 내에서만 작동합니다. 비즈니스 작업은 여전히 기록 시스템(system of record) 내에 멱등성 키(idempotency key)를 가지고 있어야 합니다.
Kafka 대신 NATS를 선택함으로써 포기하게 되는 것은 매우 거대한 재생 가능한 로그 (replayable log)가 주는 편안함입니다. 만약 수년간 쌓인 불변의 계정 이벤트 (immutable account events)로부터 프로젝션 (projections)을 다시 구축해야 한다면, 저는 여전히 Kafka를 원할 것입니다. 하지만 빠른 서비스 조정 (service coordination), 지역적 팬아웃 (regional fanout), 그리고 제한된 재생 (bounded replay)을 가진 내구성이 있는 운영 이벤트 (operational events)가 필요하다면, NATS가 강력한 적합성을 가집니다.
저의 기본 질문은 간단합니다: 핵심 NATS가 실시간 통신 (live communication)을 처리할 수 있는가, 그리고 JetStream이 정직한 보존 제한 (retention limits)을 가지고 내구성이 필요한 부분들을 처리할 수 있는가? 만약 그렇다면, 거기서부터 시작하십시오. 로그 자체가 하나의 제품 (product)이 될 때 Kafka를 추가하십시오.
references
- NATS 및 NATS documentation
- NATS Server releases, 2026-08-02 확인, 현재 GitHub 릴리스 라인은 2.14.x이며 v2.14.4가 최신 릴리스로 나열되어 있음
- NATS JetStream documentation
- NATS leaf node topology documentation
- Amazon SQS Developer Guide
- RabbitMQ quorum queues, classic queues, 및 streams
- Apache Kafka documentation
제 프로젝트를 테스트하기 위해 저는 Railway를 사용합니다. 시작하기 위해 20달러(USD)를 받고 싶다면, 이 링크를 사용하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기