Moment Note

Kafka vs RabbitMQ: 언제 무엇을 선택할까

서버 & 인프라 ·

비동기 메시징을 도입할 때 가장 자주 마주치는 갈림길이 Kafka와 RabbitMQ 사이의 선택입니다. 둘 다 “메시지 큐”로 뭉뚱그려 불리지만, 설계 철학과 강점이 근본적으로 다릅니다. 잘못 고르면 운영 내내 결이 맞지 않는 도구와 싸우게 됩니다.

핵심을 먼저 말하면, RabbitMQ는 메시지 브로커(스마트 브로커·더미 컨슈머)에 가깝고 Kafka는 분산 로그(더미 브로커·스마트 컨슈머)에 가깝습니다. 전자는 “작업을 정확히 한 명에게 전달”하는 데 강하고, 후자는 “이벤트 스트림을 여러 소비자가 각자 페이스로 재생”하는 데 강합니다. 이 차이가 라우팅, 순서 보장, 재처리, 처리량 특성 전부를 결정합니다.

이 글에서는 두 시스템의 모델을 비교하고, 어떤 상황에서 무엇을 선택해야 하는지 실무 기준으로 정리합니다.

모델의 근본 차이: 큐 vs 로그

RabbitMQ는 AMQP 기반으로 익스체인지(exchange)가 라우팅 규칙에 따라 메시지를 큐로 분배합니다. 컨슈머가 메시지를 ack 하면 브로커에서 그 메시지는 사라집니다. 즉 큐는 “처리하면 비워지는 작업 목록”입니다.

Kafka는 append-only 로그입니다. 메시지는 토픽의 파티션에 순서대로 쌓이고, 소비되어도 지워지지 않습니다. 컨슈머는 자신이 어디까지 읽었는지를 나타내는 오프셋(offset)만 관리합니다. 덕분에 여러 컨슈머 그룹이 같은 데이터를 독립적으로 읽고, 오프셋을 되감아 재처리할 수 있습니다.

# Kafka: 컨슈머 그룹은 오프셋만 관리 — 되감기(재처리)가 자유롭다
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group billing --describe
# GROUP    TOPIC    PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG

# 특정 시각 이후로 오프셋 되감아 재처리
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group billing --topic orders \
  --reset-offsets --to-datetime 2026-08-05T00:00:00.000 --execute

이 한 가지 차이가 대부분의 선택 기준을 만듭니다. “이미 처리한 이벤트를 나중에 다시 읽어야 하는가?”라는 질문에 그렇다고 답한다면 로그 모델(Kafka)이 자연스럽고, “작업을 한 번 처리하고 버리면 되는가?”라면 큐 모델(RabbitMQ)이 단순합니다.

라우팅 유연성: RabbitMQ의 강점

RabbitMQ는 익스체인지 타입(direct, topic, fanout, headers)으로 복잡한 라우팅을 브로커 레벨에서 해결합니다. 라우팅 키 패턴으로 메시지를 원하는 큐에만 꽂을 수 있어, 컨슈머는 자기 큐만 신경 쓰면 됩니다.

# RabbitMQ: topic 익스체인지로 패턴 라우팅
# 라우팅 키 "order.kr.premium" 은 아래 두 바인딩에 모두 매칭
channel.exchange_declare(exchange="orders", exchange_type="topic")
channel.queue_bind(queue="kr_orders",       exchange="orders", routing_key="order.kr.*")
channel.queue_bind(queue="premium_orders",  exchange="orders", routing_key="order.*.premium")
channel.basic_publish(exchange="orders", routing_key="order.kr.premium", body=payload)

Kafka에서 같은 일을 하려면 토픽을 나누거나 컨슈머가 직접 필터링해야 합니다. 세밀한 조건부 분배가 잦은 워크로드(알림 라우팅, 작업 종류별 워커 분리 등)라면 RabbitMQ가 훨씬 편합니다.

순서 보장과 파티셔닝

Kafka는 파티션 단위로 순서를 보장합니다. 같은 키를 가진 메시지는 항상 같은 파티션으로 가므로, 예컨대 같은 주문 ID의 이벤트들은 생성 순서대로 처리됩니다. 대신 전체 토픽의 전역 순서는 보장하지 않습니다(파티션이 여러 개면).

# Kafka 프로듀서: 키로 파티션을 고정 → 같은 주문의 순서 보장
producer.send("orders", key=b"order-1042", value=event_bytes)
# 처리량을 늘리려면 파티션 수를 키 카디널리티에 맞춰 설계
kafka-topics.sh --create --topic orders --partitions 12 \
  --replication-factor 3 --bootstrap-server localhost:9092

RabbitMQ는 단일 큐에서는 순서가 유지되지만, 처리량을 위해 컨슈머를 여러 개 붙이면(경쟁 소비) 순서가 깨집니다. 순서가 중요한데 병렬 처리도 필요하다면 Kafka의 파티션 모델이 더 자연스럽습니다.

처리량과 지연시간

Kafka는 순차 디스크 쓰기와 배칭, 제로카피 전송으로 높은 처리량에 최적화돼 있습니다. 대량 이벤트 스트림(로그 수집, 클릭스트림, 메트릭)에서 강력합니다. 반면 배칭 특성상 개별 메시지의 꼬리 지연은 튜닝이 필요합니다.

RabbitMQ는 개별 메시지를 낮은 지연으로 전달하는 데 강하지만, 큐에 메시지가 수백만 건 쌓이면(백로그) 성능이 떨어지는 경향이 있습니다. RabbitMQ는 “큐를 빠르게 비우는” 워크로드를, Kafka는 “일단 쌓아두고 나눠 소비하는” 워크로드를 가정합니다.

# RabbitMQ 컨슈머: prefetch 로 처리량과 공정성 균형
# 너무 크면 한 컨슈머가 메시지를 독점, 너무 작으면 왕복 오버헤드↑
channel.basic_qos(prefetch_count=50)
channel.basic_consume(queue="tasks", on_message_callback=handle, auto_ack=False)

전달 보장과 재처리

두 시스템 모두 at-least-once가 기본이며, 따라서 컨슈머는 멱등(idempotent)하게 설계해야 합니다. 같은 메시지가 두 번 와도 결과가 같아야 합니다.

  • RabbitMQ: 처리 실패 시 nack/재큐잉, 데드레터 익스체인지(DLX)로 실패 메시지 격리. “실패한 작업을 재시도·격리”하는 워크플로가 직관적입니다.
  • Kafka: 메시지가 지워지지 않으므로 오프셋을 되감아 대량 재처리가 가능. 버그 수정 후 지난 이벤트를 다시 흘려보내는 시나리오에 강합니다.
# RabbitMQ: 데드레터로 실패 메시지 격리 후 별도 분석/재처리
channel.queue_declare(queue="tasks", arguments={
    "x-dead-letter-exchange": "dlx",
    "x-message-ttl": 60000,          # 처리 못 하면 60s 후 DLX 로
})

운영 복잡도

운영 부담도 선택에 큰 영향을 줍니다. Kafka는 브로커 클러스터와 파티션·복제·리밸런싱을 이해해야 하고(최근 버전은 KRaft로 ZooKeeper 의존을 걷어냈지만 여전히 학습 곡선이 있습니다), 파티션 수 같은 초기 설계가 이후 처리량 상한을 좌우합니다.

RabbitMQ는 상대적으로 시작이 가볍고 단일 노드로도 충분한 경우가 많습니다. 다만 클러스터링·미러링·대용량 백로그 상황에서는 나름의 함정이 있습니다. “지금 필요한 것이 작업 분배 정도”라면 RabbitMQ의 낮은 진입 비용이 유리합니다.

마무리: 선택 기준 요약

정리하면, RabbitMQ는 복잡한 라우팅, 작업 큐, 요청-응답성 워크플로, 낮은 진입 비용이 필요할 때 적합합니다. Kafka는 이벤트 스트리밍, 높은 처리량, 여러 소비자의 독립 소비와 재처리(이벤트 소싱·데이터 파이프라인)가 필요할 때 적합합니다.

실무에서는 둘을 함께 쓰는 경우도 많습니다. Kafka로 이벤트를 수집·보관하고, 특정 작업 분배는 RabbitMQ로 처리하는 식입니다. 도구를 이념적으로 하나만 고집하기보다, “이 워크로드가 큐에 가까운가 로그에 가까운가”를 먼저 묻는 것이 가장 빠른 판단법입니다. 재처리와 다중 소비가 핵심이면 로그, 정확한 일회 전달과 라우팅이 핵심이면 큐입니다.