Kafka와 RabbitMQ 비교 (Message Broker)
백엔드를 공부하다 보면 Kafka와 RabbitMQ라는 이름을 자주 만나게 됨.
Message Broker가 왜 등장했는지부터 시작해서, Kafka와 RabbitMQ가 어떻게 다른지까지 차례대로 정리.
목차
- 직접 통신하는 방식
- 직접 통신이 힘들어지는 순간
- Message Broker (Producer와 Consumer 사이의 중간 객체)
- Message Broker가 어울리는 곳과 아닌 곳
- RabbitMQ (Smart Broker, Dumb Consumer)
- Kafka (Dumb Broker, Smart Consumer)
- 비교 정리
- 선택하는 기준
직접 통신하는 방식
서버와 서버, 클라이언트와 서버가 통신하는 방법에는 여러 가지가 존재하는데, REST API, SOAP, GraphQL, gRPC, WebSocket, SSE, WebRTC 등이 있음.
여러 방법이 있지만 모두 한쪽이 다른 한쪽에게 직접 메시지를 전달하는 구조라는 공통점이 존재.
여기서 직접 통신한다는 의미는
- 보내는 쪽이 받는 쪽의 주소를 알고, 직접 연결해서 보냄
- 메시지를 보내는 그 순간, 받는 쪽이 켜져 있고 듣고 있어야 함
즉, 내가 메시지를 전송하는 순간 상대도 해당 메시지를 받으려고 대기하고 있어야 통신이 성립함.
이런 성질을 보내는 순간과 받는 순간이 서로 묶여 있다는 뜻으로 시간적 결합(temporal coupling)이라고 부름.
그래서 보내는 쪽은 지금 상대가 받을 수 있는 상태인가까지 계속 신경 써야 함.

직접 통신이 힘들어지는 순간
받는 쪽이 항상 잘 살아 있다면 직접 통신으로 충분함. 문제는 받는 쪽에 장애가 생기는 순간 드러남.
클라이언트가 서버로 데이터를 보내는 바로 그 순간에 서버가 꺼져 있거나 과부하 상태라면, 그 데이터는 그대로 유실됨. 유실을 막으려면 보내는 쪽이 재시도 로직, 실패 처리, 타임아웃 관리를 전부 직접 떠안아야 함.
받는 쪽이 하나일 때는 그래도 감당할 만하지만, MSA처럼 서비스를 여러 개의 작은 서버로 쪼개서 운영하는 구조에서는 상황이 달라짐.
- 한쪽이 여러 대상에게 데이터를 보내야 하는 경우가 많음. 예를 들어 주문이 하나 들어오면 결제, 재고, 알림, 로그 서비스에 모두 알려야 함.
- 받는 쪽마다 필요한 데이터가 다름. 보내는 쪽이 대상별로 다른 내용을 만들어서 보내야 함.
- 받는 쪽이 계속 늘어남. 새 서비스가 추가될 때마다 보내는 쪽 코드를 고쳐야 함.
- 모두가 잘 받았는지도 보내는 쪽이 일일이 확인해야 함.
여기에 보내는 쪽까지 여럿이 되면 문제는 더 커짐. 보내는 쪽이 N개, 받는 쪽이 M개면 연결은 N×M개로 늘어남. 아키텍처는 복잡해지고, 서버 하나의 작은 장애가 전체로 번지기 쉬운 구조가 됨.

Message Broker (Producer와 Consumer 사이의 중간 객체)
Message Broker는 이 문제를 직접 만나서 전달하지 말고, 중간에 보관함을 하나 두어서 해결함.
메시지를 전달하면 받는 사람을 기다릴 필요 없이, Broker에 넣어두면 받는 사람이 편한 시간에 메시지를 가져감.
즉, 보내는 사람과 받는 사람이 같은 시간에 같은 장소에 있을 필요가 없어짐.
용어
- Producer: 보내는 쪽
- Consumer: 받는 쪽
- Broker: 둘 사이에서 메시지를 받아 보관하고 전달하는 중개자
동작 방식
- Producer는 전달할 것이 생길 때마다 Broker에게 메시지를 넘김. Consumer의 상태나 위치를 직접 알 필요는 없음. 대신 Broker가 안전하게 받았는지는 RabbitMQ의 publisher confirm, Kafka의 producer acks 같은 설정으로 확인할 수 있음.
- Consumer는 가능할 때마다 Broker에 접근해서 필요한 데이터를 가져감. Broker 안에는 종류별로 칸막이(RabbitMQ의 Queue, Kafka의 Topic/Partition)가 있어서, 각자 자기에게 필요한 것만 가져가면 됨.
- Broker는 전달받은 메시지를 정해진 시점까지 보관함. Consumer가 바로 찾아가지 않아도 메시지를 남겨둘 수 있지만, 실제 안전성은 durable/persistent 설정, replication, ack 설정 같은 저장/전달 보장 방식에 따라 달라짐.

이 구조가 주는 이점
- 유실 방지: Broker가 정상적으로 수신하고 저장했다면, 받는 쪽이 잠시 죽어 있어도 메시지를 나중에 처리할 수 있음
- 독립성: Producer와 Consumer가 서로의 존재, 위치, 상태를 몰라도 됨
- 확장성: 누가 늘어나든 연결은 항상 Broker 하나를 거침. N×M개였던 연결이 N+M개로 줄어들어서, 서버를 늘리는 수평 확장이 훨씬 수월해짐
Message Broker가 어울리는 곳과 아닌 곳
빠른 응답보다 안전한 전달, 노드 간 독립성, 확장성이 중요한 서비스에 잘 맞음. 이메일 발송, 로그 수집, 온라인 결제, 이미지 처리, 마이크로서비스 사이의 이벤트 전파가 대표적임.
반대로 비교적 단순한 시스템에서 요청에 대한 즉각적인 응답이 필요한 경우에는 적합하지 않음. 중간 단계가 하나 늘어나는 만큼 지연과 운영 부담이 추가되기 때문.
즉, Broker는 직접 통신을 대체하는 것이 아니라, 조금 늦게 처리돼도 괜찮은 흐름에만 골라서 적용하는 도구임.
RabbitMQ (Smart Broker, Dumb Consumer)
RabbitMQ는 AMQP라는 프로토콜을 기반으로 하는 메시지 브로커. 설계 철학은 Smart Broker, Dumb Consumer라는 말로 요약됨.
말 그대로 브로커가 똑똑하게 일함. 어떤 메시지를 어디로 보낼지, 잘 전달됐는지, 언제 지울지를 전부 브로커가 챙기고, 컨슈머는 받은 것을 처리하기만 하면 됨.

핵심 동작 방식은 아래와 같음.
- 메시지는 Queue라는 그룹으로 묶임. Producer가 보낸 메시지는 Exchange라는 관문을 거쳐서, 조건에 맞는 특정 큐로 가기도 하고(direct, topic) 모든 큐로 퍼지기도 함(fanout). 이렇게 브로커가 라우팅을 전담하기 때문에, 복잡한 메시지 라우팅이 필요한 서비스라면 Kafka보다 유리함.
- Consumer가 메시지를 성공적으로 처리하면 브로커에게 ack를 보내고, 브로커는 그때 해당 메시지를 큐에서 삭제함. 수동 ack 기준으로 브로커가 처리 완료 여부를 확인하는 구조라서, 결제처럼 한 건 한 건 확실하게 처리해야 하는 작업에 적합함.
- 시간이 걸리는 작업을 여러 워커가 나눠서 처리하는 Work Queue 패턴을 지원함. 워커들이 작업을 하나씩 가져가고, 끝나는 대로 ack를 보내서 메시지가 지워지도록 함. 여러 이미지 파일을 업로드받아 그래픽 처리를 해주는 서비스 같은 곳에 유용함.
- 메시지에 우선순위를 두는 기능, 일정 시간이 지나면 자동으로 만료시키는 기능(TTL)도 제공함. 다만 큐 타입에 따라 지원 범위와 동작 방식에는 차이가 있음.
물론 아쉬운 점도 이 구조에서 나옴.
- 일반 Queue 모델에서는 메시지가 컨슈머의 ack를 받는 순간 큐에서 사라짐. durable queue와 persistent message는 소비 전 메시지를 장애 이후에도 보존하기 위한 설정이지, ack 후 메시지를 다시 읽게 해주는 설정은 아님. 한 번 소비한 메시지를 되돌려서 다시 읽는 것은 일반 Queue의 기본 모델이 아니며, RabbitMQ Streams는 이와 다른 별도 모델임.
- 브로커가 각 메시지의 전달/처리 상태를 관리해야 함. 그래서 브로커를 여러 서버로 늘리면 큐의 복제, 리더 선출, ack 상태 관리까지 함께 고려해야 해서 Kafka의 append-only log 모델보다 클러스터링이 상대적으로 까다로울 수 있음.
Kafka (Dumb Broker, Smart Consumer)
Kafka는 정확히 반대 방향의 설계를 택함. Dumb Broker, Smart Consumer.
브로커는 메시지를 파티션 로그에 순서대로 쌓아두고 내어주는 일에 집중함. 대신 "내가 어디까지 읽었더라?"를 제어하는 일은 컨슈머와 컨슈머 그룹이 맡음.

핵심 동작 방식은 아래와 같음.
- 메시지는 Topic이라는 그룹으로 묶이고, Topic 안에서는 Partition 단위로 나뉘어 디스크에 로그(log) 형태로 순서대로 쌓임. RabbitMQ와 가장 다른 점은 컨슈머가 읽어가도 메시지가 바로 삭제되지 않는다는 것. 정해진 보존 기간(retention) 동안 유지됨.
- 컨슈머는 offset이라는 위치 값으로 다음에 읽을 지점을 제어함. 커밋된 offset은 보통 Kafka 내부 __consumer_offsets 토픽에 저장되지만, 언제 커밋하고 어디서 다시 읽을지는 컨슈머/컨슈머 그룹이 결정함. 그래서 필요하면 같은 메시지를 여러 번 받아갈 수 있고, 장애가 났을 때 문제가 생긴 지점부터 다시 읽어갈 수도 있음.
- 브로커 입장에서는 RabbitMQ Queue처럼 메시지마다 소비 완료 상태를 삭제 처리할 필요가 없어서 구조가 단순해지고, 그만큼 높은 처리량을 낼 수 있음.
- batch 기능을 제공해서, 메시지를 여러 개씩 모아서 받아오거나 보내는 일도 간편하게 처리됨.
- 브로커를 여러 서버에 분산해서 운영하는 클러스터링에도 유리함. 데이터는 파티션 로그 단위로 복제하고, 컨슈머의 읽기 위치는 offset으로 표현되기 때문에, 일반 Queue처럼 소비 후 삭제 상태를 메시지마다 맞출 필요가 없음.
대신 아쉬운 점은 라우팅 쪽에 있음. 브로커가 조건에 따라 메시지를 나눠주는 일을 해주지 않으므로, 어떤 토픽을 구독하고 무엇을 걸러낼지는 컨슈머가 알아서 해야 함. RabbitMQ가 기본으로 제공하는 우선순위 큐나 메시지 건별 TTL도 없음. 토픽 단위의 보존 기간(retention)이 시간 기반 만료 역할을 하지만, 메시지 하나하나에 만료를 거는 것과는 다름.
비교 정리
| 구분 | RabbitMQ | Kafka |
| 설계 철학 | Smart Broker, Dumb Consumer | Dumb Broker, Smart Consumer |
| 메시지 그룹 | Queue (Exchange가 라우팅) | Topic (파티션 단위로 분산) |
| 소비 후 메시지 | ack 시 삭제 | 보존 기간 동안 유지 |
| 재소비 | 기본 모델에서는 불가 | offset 조정으로 가능 |
| 읽은 위치 관리 | 브로커 | 컨슈머/컨슈머 그룹 (offset) |
| 라우팅 | 다양함 (direct, topic, fanout 등) | 단순함 (토픽 구독) |
| 우선순위 / TTL | 지원 (큐 타입별 차이 있음) | 우선순위 없음, 건별 TTL 없음 (토픽 단위 retention만) |
| 처리량 | 일반적으로 Kafka보다 낮음 | 매우 높음 (디스크 로그 + batch) |
| 클러스터링 | 큐 상태 관리 때문에 상대적으로 까다로움 | 분산 운영에 유리 |
| 대표 사용처 | 작업 큐, 건별 확실한 처리, 복잡한 라우팅 | 로그 수집, 이벤트 스트리밍, 대용량 파이프라인 |
- 직접 통신 방식들은 모두 "보내는 순간 상대가 듣고 있어야 함"이라는 제약을 가짐. 상대가 없으면 유실되거나, 보내는 쪽이 재시도 부담을 떠안게 됨.
- Message Broker는 중간에 보관함을 두어 이 제약을 줄임. Producer는 Consumer와 직접 맞물리지 않고 Broker에 넘기고, Consumer는 가능할 때 가져가면 됨.
- 대신 즉각적인 응답이 필요한 단순 요청-응답에는 맞지 않음. 조금 늦게 처리돼도 괜찮은 흐름에만 쓰는 것.
- RabbitMQ는 브로커가 똑똑한 쪽. 라우팅, ack 기반 삭제, 우선순위, TTL을 브로커가 챙겨줌.
- Kafka는 컨슈머가 똑똑한 쪽. 브로커는 파티션 로그를 쌓는 데 집중하고, 어디까지 읽을지는 컨슈머/컨슈머 그룹이 offset으로 제어함. 그 단순함이 처리량과 클러스터링의 장점으로 돌아옴.
선택 기준
- 메시지 하나하나가 완결된 작업이라면 RabbitMQ. 조건에 따라 다른 곳으로 보내야 하거나, 우선순위와 만료 시간을 걸어야 하거나, 한 건 한 건 확실히 처리됐는지 확인해야 하는 경우. 브로커가 전달을 책임져주는 구조가 그대로 장점이 됨.
- 메시지가 대량으로 흘러가는 이벤트 스트림이라면 Kafka. 여러 컨슈머가 같은 데이터를 각자의 속도로 읽어야 하거나, 지나간 데이터를 다시 읽어야 하는 경우. 지우지 않고 쌓아두는 로그 구조가 그대로 장점이 됨.
'백엔드' 카테고리의 다른 글
| Express app.param vs app.get (0) | 2026.07.07 |
|---|---|
| Socket과 WebSocket 정리 (RFC 6455) (0) | 2026.02.11 |
| Node.js 몽고디비 알아보기 (0) | 2023.08.10 |
| Node.js MySQL 다뤄보기 (0) | 2023.07.23 |
| Node.js 익스프레스 웹 서버 만들기 (0) | 2023.07.23 |