"RabbitMQ랑 Kafka 중에 뭘 써야 하나요?"라는 질문은, 사실 "택시와 버스 중에 뭘 타야 하나요?"와 비슷합니다. 목적지가 같더라도 용도가 다릅니다.

RabbitMQ vs Kafka — 메시지 브로커 vs 이벤트 스트리밍

먼저 알아야 할 것: 둘은 근본적으로 다른 물건이다

RabbitMQ와 Kafka를 비교하는 글은 많지만, 공부하다 보니 가장 중요한 포인트는 둘이 같은 카테고리의 제품이 아니라는 것 이었습니다.

  • RabbitMQ — 전통적인 ** 메시지 브로커** (Message Broker)
  • Kafka — ** 이벤트 스트리밍 플랫폼** (Event Streaming Platform)

이름부터가 다릅니다. 하나는 "브로커"고 하나는 "플랫폼"입니다. 이 차이를 모르면 비교 자체가 의미 없어집니다.


1. 설계 철학 — 큐 모델 vs 분산 로그 모델

RabbitMQ: 스마트 브로커, 심플 컨슈머

RabbitMQ는 AMQP 프로토콜 기반의 메시지 브로커입니다. 브로커가 메시지의 라우팅, 필터링, 전달을 모두 책임집니다.

PLAINTEXT
Producer → Exchange → Binding → Queue → Consumer

                          브로커가 라우팅 결정
  • 메시지가 소비자에게 전달되고 ACK되면 ** 큐에서 삭제 **됩니다
  • 브로커가 "똑똑한" 역할을 하고, 소비자는 단순히 받아서 처리하면 됩니다

Kafka: 심플 브로커, 스마트 컨슈머

Kafka는 분산 커밋 로그(Distributed Commit Log)입니다. 브로커는 로그를 저장하는 역할만 하고, 소비자가 자신의 오프셋을 관리합니다.

PLAINTEXT
Producer → Topic (Partition 0) → [msg0][msg1][msg2][msg3]...

                                  Consumer가 오프셋 관리
  • 메시지가 소비되어도 ** 삭제되지 않습니다** (보존 기간까지 유지)
  • 소비자가 "똑똑한" 역할을 하고, 브로커는 단순히 로그를 저장합니다

이 차이가 나머지 모든 차이의 원인입니다. "소비 후 삭제 vs 소비 후 보존"이라는 한 줄로 기억하면, 나머지 특성들이 자연스럽게 따라옵니다.


2. 핵심 비교표

비교 항목RabbitMQKafka
** 모델**메시지 큐 (Queue)분산 커밋 로그 (Log)
** 프로토콜**AMQP 0-9-1자체 TCP 프로토콜
** 전달 방식**Push (브로커 → 소비자)Pull (소비자 → 브로커)
** 메시지 소비 후**큐에서 삭제보존 (retention 기간까지)
** 라우팅**Exchange + Binding (유연)Topic + Partition (단순)
** 순서 보장**큐 단위파티션 단위
** 소비자 확장**Competing ConsumersConsumer Group
** 처리량**수만 msg/s수백만 msg/s
** 메시지 재처리**기본적으로 불가 (이미 삭제)오프셋 리셋으로 가능
** 주요 언어**ErlangScala + Java

3. 메시지 소비 방식 — Push vs Pull

RabbitMQ: Push 모델

PLAINTEXT
[브로커]  --push-->  [Consumer A]
          --push-->  [Consumer B]
          --push-->  [Consumer C]

브로커가 소비자에게 메시지를 ** 밀어넣습니다 **. 메시지가 도착하면 즉시 전달되므로 ** 지연(latency)이 낮습니다 **.

하지만 소비자의 처리 속도보다 빠르게 밀어넣으면 과부하가 발생할 수 있습니다. 이를 방지하기 위해 prefetch count로 한 번에 받을 메시지 수를 제한합니다.

JAVA
// RabbitMQ — prefetch로 소비자 과부하 방지
channel.basicQos(10); // 최대 10개까지만 미리 전달

Kafka: Pull 모델

PLAINTEXT
[Consumer A]  --poll-->  [브로커]
[Consumer B]  --poll-->  [브로커]
[Consumer C]  --poll-->  [브로커]

소비자가 자신의 속도에 맞춰 브로커에서 메시지를 ** 가져갑니다 **. 처리가 느려도 과부하가 발생하지 않습니다.

대신, 새 메시지가 없을 때도 계속 폴링하면 리소스 낭비가 발생합니다. Kafka는 이를 long polling으로 해결합니다.

JAVA
// Kafka — 소비자가 자신의 속도로 메시지를 가져감
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
// 1초 동안 새 메시지가 없으면 빈 결과 반환

Push 모델은 실시간성이 중요할 때, Pull 모델은 대량 데이터를 안정적으로 처리할 때 유리합니다. 면접에서 단순히 "Push vs Pull"만 말하면 절반만 아는 겁니다. ** 트레이드오프까지 설명해야 합니다.**


4. 메시지 삭제 vs 보존

이 차이가 실무에서 가장 큰 영향을 미칩니다.

RabbitMQ: ACK하면 삭제

PLAINTEXT
Queue: [msg1][msg2][msg3]
            ↓ Consumer가 msg1을 ACK
Queue: [msg2][msg3]          ← msg1 사라짐
  • 한 번 소비된 메시지는 큐에서 사라집니다
  • 다른 소비자가 같은 메시지를 읽을 수 없습니다 (Fanout Exchange로 복제하면 가능)
  • 메시지 재처리가 필요하면? 프로듀서가 다시 보내야 합니다

Kafka: 오프셋 기반 보존

PLAINTEXT
Partition: [msg0][msg1][msg2][msg3][msg4][msg5]
                          ↑ Consumer A 오프셋 = 2
                                    ↑ Consumer B 오프셋 = 4
  • 소비해도 메시지가 삭제되지 않습니다 (retention 기간까지 보존)
  • 여러 Consumer Group이 독립적으로 같은 메시지를 읽을 수 있습니다
  • 오프셋을 되감으면 과거 메시지를 재처리할 수 있습니다
JAVA
// Kafka — 오프셋을 처음으로 되감아 전체 재처리
consumer.seekToBeginning(consumer.assignment());

이 특성 때문에 ** 이벤트 소싱 **, ** 감사 로그 , ** 데이터 파이프라인 같은 용도에서 Kafka가 압도적으로 유리합니다.


5. Consumer Group vs Competing Consumers

소비자를 수평 확장하는 방식도 다릅니다.

RabbitMQ: Competing Consumers

PLAINTEXT
              ┌→ [Consumer 1] ← msg1, msg3, msg5
[Queue] ──────┼→ [Consumer 2] ← msg2, msg4, msg6
              └→ [Consumer 3] ← msg7, msg8, msg9
  • 하나의 큐를 여러 소비자가 경쟁적으로 소비합니다
  • 각 메시지는 하나의 소비자만 받습니다 (라운드 로빈)
  • 같은 메시지를 여러 소비자가 받으려면? Fanout Exchange 로 큐를 복제해야 합니다

Kafka: Consumer Group

PLAINTEXT
Topic (3 파티션)
├── Partition 0 → [Consumer 1]  ← Group A
├── Partition 1 → [Consumer 2]  ← Group A
├── Partition 2 → [Consumer 3]  ← Group A

├── Partition 0 → [Consumer X]  ← Group B (독립적)
├── Partition 1 → [Consumer X]  ← Group B
├── Partition 2 → [Consumer Y]  ← Group B
  • **같은 그룹 내 **: 파티션이 컨슈머에게 분배됩니다 (하나의 파티션은 하나의 컨슈머만 소비)
  • ** 다른 그룹 간 **: 같은 메시지를 독립적으로 소비합니다
  • 같은 그룹 내 컨슈머 수가 파티션 수를 초과하면? 초과분은 ** 유휴 상태 **가 됩니다
패턴RabbitMQKafka
1:1 (하나의 메시지를 하나의 소비자가)기본 큐 소비같은 Consumer Group
1:N (하나의 메시지를 여러 소비자가)Fanout Exchange서로 다른 Consumer Group
수평 확장소비자 추가 (제한 없음)파티션 수 이하로 소비자 추가

6. 메시지 순서 보장

RabbitMQ: 큐 단위 순서 보장

하나의 큐 내에서는 메시지 순서가 보장됩니다. 하지만 여러 소비자가 경쟁적으로 소비하면, 처리 완료 순서는 보장되지 않습니다.

PLAINTEXT
Queue: [A][B][C]
Consumer 1이 A를 받고 3초 걸림
Consumer 2가 B를 받고 1초 걸림
→ B가 A보다 먼저 처리 완료됨

Kafka: 파티션 단위 순서 보장

하나의 파티션 내에서는 메시지 순서가 ** 절대적으로 보장 **됩니다. 같은 키를 가진 메시지는 항상 같은 파티션에 들어가므로, 키 기반 순서 보장이 가능합니다.

JAVA
// 같은 orderId를 가진 메시지는 항상 같은 파티션으로
producer.send(new ProducerRecord<>("orders", orderId, orderEvent));

하지만 ** 파티션 간** 순서는 보장되지 않습니다. 글로벌 순서가 필요하면 파티션을 1개로 제한해야 하는데, 그러면 병렬 처리를 포기하는 것이므로 신중하게 판단해야 합니다.


7. 전달 보장 — At-most-once, At-least-once, Exactly-once

전달 보장 수준의미RabbitMQKafka
At-most-once최대 한 번 (유실 가능)Auto ACK오프셋 자동 커밋
At-least-once최소 한 번 (중복 가능)Manual ACK + Publisher Confirm오프셋 수동 커밋 + acks=all
Exactly-once정확히 한 번네이티브 미지원Idempotent Producer + Transactional API

Exactly-once는 정말 가능한가?

공부하다 보니 이 부분이 가장 많이 헷갈렸습니다.

Kafka 는 0.11 버전부터 Idempotent Producer와 Transactional API를 통해 브로커 레벨의 exactly-once 를 지원합니다.

JAVA
// Kafka — Idempotent Producer 활성화
props.put("enable.idempotence", "true");

// Transactional API로 원자적 쓰기
producer.initTransactions();
producer.beginTransaction();
producer.send(new ProducerRecord<>("topic", "key", "value"));
producer.commitTransaction();

하지만 이것은 프로듀서 → 브로커 구간의 exactly-once입니다. ** 브로커 → 컨슈머 → 외부 시스템** 구간까지 포함한 엔드투엔드 exactly-once는 소비자 측에서도 ** 멱등성(idempotency)**을 보장해야 합니다.

"Exactly-once가 가능한가?"라는 질문에 "Kafka에서 가능합니다"라고만 대답하면 부족합니다. 브로커 내부 exactly-once와 엔드투엔드 exactly-once를 구분해서 설명할 수 있어야 합니다.

RabbitMQ 는 네이티브 exactly-once를 지원하지 않습니다. Publisher Confirm + Manual ACK 조합으로 at-least-once를 구현하고, 소비자 측 멱등성으로 중복을 제거하는 방식이 일반적입니다.


8. 처리량(Throughput) 차이

Kafka가 높은 처리량을 달성하는 이유는 설계 자체가 다르기 때문입니다.

요소RabbitMQKafka
I/O 방식메시지별 개별 처리시퀀셜 I/O + 배치 쓰기
** 압축**메시지별 (선택)배치 단위 압축
** 데이터 전송**브로커가 복사 후 전달Zero-copy (sendfile 시스템콜)
** 저장 구조**큐 (인메모리 + 디스크)파티션별 세그먼트 파일 (디스크)
PLAINTEXT
RabbitMQ 처리량:
  ├── 단일 큐: ~50,000 msg/s
  └── 클러스터: ~수십만 msg/s

Kafka 처리량:
  ├── 단일 파티션: ~수십만 msg/s
  └── 클러스터: ~수백만 msg/s

다만 처리량이 높다고 항상 좋은 것은 아닙니다. 메시지 하나하나의 ** 라우팅 유연성 **이 필요하면 RabbitMQ가 적합하고, 대량의 이벤트를 빠르게 ** 스트리밍 **해야 하면 Kafka가 적합합니다.


9. 프로토콜 — AMQP vs Custom TCP

RabbitMQ: AMQP 0-9-1

  • 표준 프로토콜이라 다양한 언어/클라이언트가 존재합니다
  • Exchange, Queue, Binding 등의 개념이 프로토콜 레벨에서 정의되어 있습니다
  • STOMP, MQTT 등 플러그인으로 다른 프로토콜도 지원합니다

Kafka: 자체 TCP 프로토콜

  • Kafka 전용 바이너리 프로토콜입니다
  • 표준이 아니므로 Kafka 클라이언트 라이브러리가 반드시 필요합니다
  • 대신 배치, 압축, 파티셔닝에 최적화되어 있습니다

최종 정리

기준RabbitMQ를 선택Kafka를 선택
** 메시지 패턴**복잡한 라우팅이 필요할 때단순 pub/sub, 대량 스트리밍
** 메시지 보존**처리 후 삭제해도 될 때이벤트 재처리, 감사 로그가 필요할 때
** 처리량**수만 msg/s 이하수백만 msg/s 이상
** 순서 보장**큐 단위면 충분할 때키 기반 파티션 순서가 필요할 때
** 전달 보장**At-least-once면 충분할 때브로커 레벨 exactly-once가 필요할 때
** 지연(Latency)**밀리초 단위 실시간 필요약간의 지연 허용 가능
** 소비자 패턴**작업 큐 (Task Queue)이벤트 소싱, 데이터 파이프라인
** 운영 복잡도**상대적으로 단순ZooKeeper/KRaft 등 추가 구성 필요

둘 다 각자의 영역에서 최고의 도구입니다. "어떤 게 더 좋냐"가 아니라 "어떤 문제를 풀어야 하냐" 로 접근해야 올바른 선택을 할 수 있습니다.


다음 편에서는 RabbitMQ와 Kafka를 함께 사용하는 공존 패턴 과 실무 선택 기준 체크리스트 를 정리합니다.

댓글 로딩 중...