development

RabbitMQ vs Kafka: 당신의 시스템에 최적화된 메시징 솔루션은?

분산 시스템이나 마이크로서비스 아키텍처(MSA)를 설계할 때, 서비스 간의 비동기 통신을 위해 가장 먼저 고민하게 되는 것이 바로 메시징 시스템입니다. 시장에는 다양한 옵션이 있지만, 그중에서도 RabbitMQ와 Kafka는 가장 널리 사용되면서도 성격이 극명하게 갈리는 대표적인 기술들입니다.

많은 개발자들이 “Kafka가 대용량 처리에 좋으니 무조건 Kafka를 쓰자”는 식의 결정을 하곤 합니다. 하지만 이는 자칫 프로젝트의 본질적인 요구사항을 간과하고 오버스펙을 초래하거나, 반대로 필요한 기능을 놓칠 수 있습니다. RabbitMQ와 Kafka는 해결하고자 하는 문제의 본질 자체가 다르기 때문입니다.

이 포스팅에서는 두 기술의 탄생 배경과 핵심 아키텍처 차이를 심도 있게 살펴보고, 여러분의 비즈니스 요구사항과 팀의 운영 역량에 최적화된 도구를 선택하는 실질적인 기준을 제시해 드리겠습니다.


📑 목차


1. 탄생 배경과 철학의 차이: 메시지 브로커 vs 이벤트 스트리밍

RabbitMQ와 Kafka는 둘 다 ‘메시지’를 다루는 시스템이지만, 그 근본적인 철학과 목적이 다릅니다.

RabbitMQ: 전통적인 메시지 브로커

RabbitMQ는 AMQP(Advanced Message Queuing Protocol) 표준을 구현한 대표적인 **메시지 브로커(Message Broker)**입니다. 그 목적은 메시지를 안전하고 신뢰성 있게 한 지점에서 다른 지점으로 전달하는 것에 있습니다. 이는 마치 “배달 사고 없는 확실한 택배 서비스”와 유사합니다.

  • 주요 특징: 메시지 전달 보장, 복잡한 라우팅 규칙, 큐(Queue) 기반.
  • 활용 예시: 작업 큐(Task Queue), 비동기 알림, RPC(원격 프로시저 호출) 대체.

Kafka: 분산 이벤트 스트리밍 플랫폼

Kafka는 LinkedIn에서 대규모 데이터 로그를 실시간으로 처리하고 저장하기 위해 개발된 **분산 이벤트 스트리밍 플랫폼(Distributed Event Streaming Platform)**입니다. 단순히 메시지를 전달하는 것을 넘어, 대용량 데이터를 순차적으로 기록하고, 여러 구독자가 원하는 시점에 데이터를 다시 읽을 수 있도록 하는 “중단 없는 실시간 데이터 중계 및 저장소”에 가깝습니다.

  • 주요 특징: 고처리량, 낮은 지연 시간, 영구적인 이벤트 로그 저장, 분산 처리.
  • 활용 예시: 로그 수집, 사용자 행동 추적, 실시간 데이터 파이프라인, 이벤트 소싱.

2. 핵심 아키텍처 비교: 라우팅 주체와 메시지 처리 방식

두 시스템의 가장 근본적인 아키텍처 차이는 메시지 라우팅 및 상태 관리의 주체가 누구냐에 있습니다.

RabbitMQ (스마트 브로커, Dumb Consumer)

RabbitMQ는 브로커(Broker)가 대부분의 지능적인 역할을 수행합니다. 즉, 메시지가 어떤 큐로 가야 할지, 컨슈머에게 메시지가 전달되었는지(Ack), 전달된 메시지는 큐에서 삭제할지 등을 브로커가 모두 관리합니다. 컨슈머는 메시지를 받아 처리하는 단순한 역할을 합니다.

  • Exchange: 메시지를 받아 라우팅 키와 바인딩 규칙에 따라 특정 큐로 전달하는 역할을 합니다. 다양한 타입(Direct, Topic, Fanout, Headers)으로 복잡한 라우팅이 가능합니다.
  • Queue: 메시지를 저장하고 컨슈머에게 전달합니다. 메시지가 컨슈머에 의해 처리되면 큐에서 삭제되는 것이 일반적입니다.

Kafka (스마트 컨슈머, Dumb Broker)

Kafka는 브로커(Broker)가 상대적으로 단순한 역할을 합니다. 메시지를 받아 단순히 **토픽(Topic)의 파티션(Partition)**에 순차적으로 기록합니다. 메시지의 소비 상태나 라우팅 로직은 컨슈머(Consumer)가 스스로 관리합니다. 즉, 컨슈머가 어디까지 읽었는지(Offset)를 직접 추적하고 제어합니다.

  • Topic: 메시지를 분류하는 논리적인 단위로, 하나 이상의 파티션으로 구성됩니다.
  • Partition: 토픽을 구성하는 물리적인 단위로, 메시지들이 순서대로 append-only 방식으로 저장되는 불변(immutable) 로그 파일입니다.
  • Offset: 컨슈머가 파티션 내에서 어디까지 메시지를 읽었는지 나타내는 포인터입니다. 컨슈머 그룹마다 독립적으로 관리됩니다.
RabbitMQ의 Exchange-Queue 라우팅 모델과 Kafka의 토픽-파티션 로그 모델 비교

3. 메시지 영속성 및 확장성 관점의 차이

메시지 영속성 (Message Durability)

  • RabbitMQ: 메시지가 컨슈머에게 전달되고 ACK를 받으면, 브로커는 해당 메시지를 큐에서 삭제하려는 경향이 있습니다. 즉, 메시지는 기본적으로 **일시적(Transient)**이며, 큐에 메시지가 쌓이는 것은 일반적으로 부하 또는 처리 지연의 징후로 간주됩니다.
  • Kafka: 메시지는 컨슈머에 의해 소비되어도 삭제되지 않고 설정된 보관 기간(Retention Policy) 동안 토픽의 파티션에 영구적으로 저장됩니다. 이는 데이터의 영구적인 로그(Log) 역할을 수행하며, 과거 데이터를 언제든 다시 읽어와(Replay) 처리할 수 있는 강력한 기능을 제공합니다.
// Kafka 컨슈머 예시: 특정 시점(Offset)부터 다시 읽기
import { Kafka } from 'kafkajs';

const kafka = new Kafka({ 
  clientId: 'my-app',
  brokers: ['localhost:9092'] 
});
const consumer = kafka.consumer({ groupId: 'my-processing-group' });

async function consumeMessages() {
  await consumer.connect();
  // 'user-events' 토픽을 구독. fromBeginning: true로 설정하면 가장 오래된 메시지부터 다시 읽습니다.
  // 실제 애플리케이션에서는 last committed offset부터 시작하는 것이 일반적입니다.
  await consumer.subscribe({ topic: 'user-events', fromBeginning: true }); 

  await consumer.run({
    eachMessage: async ({ topic, partition, message }) => {
      // Kafka는 컨슈머가 장애 후 복구되어도 마지막 offset부터 다시 처리가 가능하여 데이터 손실 위험이 적습니다.
      console.log(`[${topic}:${partition}] 이벤트 처리 완료 (Offset: ${message.offset}): ${message.value.toString()}`);
      // 처리 완료 후 offset은 자동으로 커밋되거나, 수동으로 커밋할 수 있습니다.
    },
  });
}

consumeMessages().catch(console.error);

위 Kafka 컨슈머 코드처럼 fromBeginning: true 옵션은 개발 시 테스트 용도로 유용하며, 실제 프로덕션에서는 컨슈머 그룹의 마지막 커밋된 오프셋(last committed offset)부터 메시지를 읽어오게 됩니다. 이는 Kafka의 **내결함성(Fault Tolerance)**과 재처리(Replay) 능력의 핵심입니다.

확장성 (Scalability)

  • RabbitMQ: 큐와 컨슈머를 추가하여 확장할 수 있지만, 메시지 라우팅 로직이 브로커에 집중되어 있어 수평 확장에 다소 제약이 있습니다. 큐당 처리량에 한계가 명확할 수 있습니다.
  • Kafka: 토픽을 여러 파티션으로 나누고, 각 파티션을 클러스터 내의 다른 브로커에 분산하여 저장합니다. 컨슈머 그룹의 컨슈머들은 각기 다른 파티션을 병렬로 처리하여 높은 처리량과 선형적인 수평 확장성을 제공합니다.

4. 시니어의 실무 팁: 프로젝트에 맞는 메시징 시스템 선택 가이드

수많은 프로젝트를 경험하며 제가 보통 팀원들에게 메시징 시스템 선택 시 던지는 질문들입니다.

💡 시니어의 팁: 도구 선택의 결정적 질문들

  1. “메시지 ‘하나하나’의 안전한 전달과 순서 보장이 가장 중요한가?”
    • 예: 결제 완료 알림, 주문 처리, 특정 사용자에 대한 즉각적인 작업.
    • -> RabbitMQ가 더 적합합니다. 복잡한 라우팅과 메시지 전달 보장이 강점입니다.
  2. “초당 수십만~수백만 건의 대량 데이터를 처리하며, 이 데이터를 나중에 다시 분석하거나 재처리할 가능성이 있는가?”
    • 예: 모든 사용자 행동 로그 수집, 실시간 스트리밍 분석, 이벤트 소싱 아키텍처.
    • -> Kafka가 더 적합합니다. 고처리량, 영구 저장, Replay 기능이 핵심입니다.
  3. “우리 팀이 분산 시스템 운영에 대한 충분한 경험과 역량을 갖추고 있는가?”
    • Kafka는 RabbitMQ에 비해 클러스터 구성, 파티션 관리, Zookeeper(또는 KRaft) 운영 등 초기 설정 및 유지보수 난이도가 높습니다.
    • 운영 역량이 부족하다면, 상대적으로 직관적인 RabbitMQ로 시작하여 점진적으로 확장하는 것이 현명할 수 있습니다.
  4. “메시지 기반의 RPC(원격 프로시저 호출)와 같은 동기적/반동기적 통신 패턴이 필요한가?”
    • RabbitMQ의 RPC 패턴은 특정 서비스에 대한 요청-응답 처리에 유용합니다. Kafka는 기본적으로 비동기 이벤트 스트리밍에 최적화되어 있어, 이러한 패턴 구현이 더 복잡할 수 있습니다.

5. 자주 묻는 질문 (FAQ)

Q1. Kafka가 RabbitMQ보다 항상 빠른가요?

처리량(Throughput) 관점에서는 Kafka가 압도적으로 우수합니다. 하지만 단일 메시지의 지연 시간(Latency) 관점에서는 RabbitMQ가 더 유리할 수 있습니다. 수 밀리초 단위의 낮은 지연 시간이 중요한 단순 메시지 전송 작업에는 RabbitMQ가 더 적합할 때도 있습니다.

Q2. RabbitMQ에서도 메시지 재처리가 가능한가요?

Kafka처럼 과거 메시지를 다시 ‘읽어오는’ 기능은 기본적으로 제공하지 않습니다. 대신 **DLX(Dead Letter Exchange)**를 설정하여 처리에 실패한 메시지를 별도의 큐로 보내고, 이 큐에 쌓인 메시지를 수동으로 재전송하는 로직을 직접 구현해야 합니다. 이는 Kafka의 Replay 기능과는 목적과 구현 복잡도가 다릅니다.

Q3. 운영 난이도는 어떤 것이 더 높나요?

일반적으로 Kafka의 운영 난이도가 더 높습니다. Zookeeper(또는 KRaft 모드) 관리, 파티션 분산 전략, 복제 계수 설정, 클러스터 모니터링 등 신경 쓸 요소가 많습니다. 반면 RabbitMQ는 설치와 기본 사용이 매우 직관적이며, 클러스터링도 상대적으로 간단합니다.


마치며

RabbitMQ와 Kafka 중 무엇이 더 우월한지는 중요하지 않습니다. 중요한 것은 “우리 팀의 역량으로 감당할 수 있는 운영 복잡도인가?” 그리고 **“우리의 비즈니스 데이터가 1회성 전달이 핵심인가, 지속적인 이벤트 스트림으로 분석 및 재처리가 필요한가?”**를 먼저 자문해 보는 것입니다. 이 질문들에 대한 답을 찾고 적절한 도구를 선택하는 것이 견고하고 확장 가능한 시스템 설계의 첫걸음이 될 것입니다.


이 글이 마음에 드셨나요?

로딩 중...