어째서 메시지 브로커가 왜 필요한가?
- 개발
- 2026. 2. 13. 23:40
카프카나 레빗 MQ같은 MQ시스템들을 학습하다보면 브로커라는 개념이 등장합니다. 인터넷에 브로커를 검색해보니 판매자와 구매자 사이에서 정보를 교환하고 중개하여 수수료를 받는 개인 또는 법인이라고 합니다. 그렇다는 이야기는 메시지를 발송하는 프로듀서와 메시지를 수신하는 컨슈머사이에서 중개하는 역할을 담당하는걸로 추정이 되어집니다. 그렇다면 어째서 메시지 브로커라는게 필요할까요? 이것에 초점을 맞춰서 학습해봅시다.
만약에, 메시지 브로커가 존재하지 않는다면?
조금 단순하게 생각해보면 이런 구조입니다.
OrderService → InventoryService
주문이 들어오면 재고를 바로 차감합니다. 이게 제일 직관적이고 이해하기도 쉽습니다.
그런데 이 구조는 생각보다 위험합니다.
재고 서비스가 느리면 주문도 같이 느려집니다.
재고 서비스가 죽으면 주문도 실패합니다. 트래픽이 몰리면 둘 다 같이 무너질 수 있습니다.
이건 서비스가 서로 강하게 묶여 있는 상태입니다. 한쪽이 흔들리면 다른 쪽도 영향을 받습니다.
왜냐하면 두 시스템은 실시간으로 연결되어 있기 때문입니다.
OrderService는 InventoryService의 응답을 받아야만 다음 단계로 넘어갈 수 있습니다.
동기 호출이든, 코드상 비동기 호출이든 본질은 같습니다.
지금 당장 재고 처리가 끝나야 주문이 끝나는 구조입니다.
즉, 처리 시간이 공유되고 네트워크 연결이 공유되고 리소스 압박이 전파됩니다.
재고 서비스가 느려지면 응답 대기가 길어지고 연결이 오래 유지되고 리소스 점유 시간이 늘어나고 그 영향이 주문 시스템까지 번집니다.
결국 브로커가 존재하지 않는다면 두 시스템은 실시간 네트워크 호출로 묶여 있는 상태입니다.
비동기든 동기든 중요한 건 호출 방식이 아니라, 지금 당장 처리되어야만 한다는 관계가 유지된다는 점입니다.
그래서 한쪽 병목은 다른 쪽으로 전파될 수밖에 없습니다. 그래서 브로커가 필요한 이유입니다.
브로커 기본 용어 부터 알아봅시다.
Topics and Queues
메시지 브로커는 데이터를 Topic과 Queue라는 두 가지 구조로 나눕니다.
단순히 이름이 다른 게 아니라, 메시지를 "어떻게 소비하게 만들 것인가"에 대한 설계 차이입니다.
Queue

Queue는 포인트 투 포인트 통신 구조입니다. 기본적으로 FIFO(선입선출) 버퍼입니다.
메시지를 하나 넣으면, 그 메시지는 하나의 소비자만 처리합니다.
여러 소비자가 붙어 있어도 결국 한 메시지는 한 소비자에게만 전달됩니다.
Topic
반면 Topic은 Pub/Sub 구조입니다.
메시지를 하나 발행하면 구독하고 있는 모든 소비자가 그 메시지를 받습니다.

예를 들어 주문 완료 이벤트가 발생하면 재고 서비스, 배송 서비스, 정산 서비스, 알림 서비스 모두가 그 이벤트를 받아야 할 수 있습니다.
이럴때 적합한것이 Toplc입니다.
Topic은 이벤트 전파를 위한 구조입니다.
Producer
producer은 이벤트를 터트리는 주체입니다.
분산 시스템에서는 IoT 센서가 온도 값을 보내는 경우도 있고,
1. 웹 서버가 사용자 행동 로그를 남길 수도 있고,
2. 결제 시스템이 결제 완료 이벤트를 발생시킬 수도 있습니다.
3. 결국 생산자는 어떤 일이 발생했다는 사실을 외부로 알리는 역할을 합니다.
프로듀서의 역할은 단순 전송이 아닙니다.
먼저 비즈니스 이벤트를 직렬화해야 합니다. JSON이든, Avro든, Protobuf든 결국 브로커는 바이트 덩어리로 받습니다.
그 다음 중요한 건 메타데이터입니다.
- 어떤 토픽으로 보낼 것인지
- 어떤 파티션으로 보낼 것인지
- 라우팅 키는 무엇인지
- 헤더에 어떤 정보가 붙는지
즉, 생산자는 메시지를 "어디로, 어떻게 흐르게 할지"를 결정합니다.
브로커가 모든 걸 판단하는 게 아니라, 흐름의 출발점에서 이미 방향이 정해집니다.
생산자는 전송 방식도 결정해야 합니다.
- 브로커의 ACK를 기다릴 것인가?
- 그냥 보내고 넘어갈 것인가?
ACK를 기다리면 안전하지만 느려집니다. 기다리지 않으면 빠르지만 유실 가능성이 생깁니다.

이 지점이 첫 번째 트레이드오프입니다.
처리량(throughput)을 선택할지, 신뢰성(reliability)을 선택할지..
이 결정은 단순 구현 문제가 아니라 시스템의 성격을 결정하는 선택입니다.
Consumers
소비자는 브로커에 쌓인 메시지를 가져와서 실제로 처리하는 주체입니다.
producer가 "이벤트를 터트리는 주체"이라면, consumer는 "그 이벤트를 받아서 처리하는 주체"입니다.
예를 들어, 주문 완료 메시지를 받아
재고를 차감하거나 결제 완료 메시지를 받아 정산을 처리하거나
알림 이벤트를 받아 푸시를 발송하는 역할을 합니다.
결국 메시지를 읽는 순간부터가 진짜 비즈니스 로직의 시작입니다.
Pull 방식 vs Push 방식
이는 브로커마다 방식이 조금씩 상이합니다. 예를 들어, 카프카는 pull방식이고 레빗 MQ는 push방식입니다.
소비자는 브로커 구조에 따라 메시지를 가져오는 방식이 다릅니다.
어떤 브로커는 메시지를 밀어주고(push), 어떤 브로커는 소비자가 직접 가져옵니다(pull).
하지만 중요한 건 방식이 아니라 이겁니다. 누가 처리 속도를 조절하느냐입니다.
Pull 구조에서는 consumer가 속도를 조절합니다. Push 구조에서는 브로커가 전달 타이밍을 주도합니다.
데이터를 땡겨서(pull 방식)이라는건 cunsumer가 데이터를 어떻게 가져올지를 정할 수 있습니다.
예를 들면, 얼마나 자주 호출할지,한 번에 얼마나 가져올지,언제 멈출지등을 정할 수 있습니다. 그래서 대용량 트래픽 환경에서 더 유리하다고 하는 이유도 그 이유때문입니다.
반면 push 구조에서는 브로커가 메시지를 먼저 전달합니다. consumer는 기본적으로 브로커가 보내는 메시지를 받아야 합니다.
다만, 완전히 무제한으로 받는 것은 아닙니다. 예를 들어 RabbitMQ에서는 prefetch 설정을 통해
"한 번에 몇 개까지 받을지" 제한할 수 있습니다.
즉, push 구조는 브로커가 전달을 시작하고, consumer는 수용량을 제한하는 방식입니다.
Pull은 읽는 쪽이 주도권을 갖고, Push는 보내는 쪽이 전달을 시작합니다. 결국 차이는 "누가 흐름을 시작하느냐"입니다.
Consumer Group
여기서 중요한 개념이 소비자 그룹입니다.
소비자 그룹은 단순히 소비자 여러 개가 아니라, 같은 작업을 나눠 처리하기 위한 집합입니다.
여러 소비자가 하나의 큐나 토픽을 함께 소비하면 각 메시지는 한 소비자에게만 전달됩니다.
이걸 경쟁 소비자(Competing Consumers) 패턴이라고 합니다.
이 구조의 장점은 단순합니다.
- 소비자가 하나면 처리 속도는 한계가 있습니다.
- 소비자를 늘리면 처리량도 늘어납니다.
즉, 메시지 처리도 수평 확장이 가능합니다. 이게 브로커를 사용하는 중요한 이유 중 하나입니다.
Brokers
브로커는 메시지를 중간에서 받아서 전달해주는 존재입니다. 그런데 단순한 중계 서버라고 보면 오해입니다.
브로커는 메시지를 수신하고 저장하고 적절한 소비자에게 전달합니다.
여기서 중요한 건 "저장"입니다.
브로커가 단순히 네트워크 패킷을 흘려보내는 게 아니라, 메시지를 내부에 기록하고 관리합니다.
그래서 브로커는 그냥 중간 서버가 아니라 하나의 작은 데이터 저장 시스템에 가깝습니다.
브로커는 무엇을 관리할까?
브로커는 단순 전달 외에도 많은 일을 합니다.
- 인증 (누가 보냈는가?)
- 권한 확인 (이 토픽에 써도 되는가?)
- 리소스 제한 (트래픽 제한)
- 메시지 지속성 (디스크에 저장할 것인가?)
- 복제 (다른 노드에 복사할 것인가?)
즉, 브로커는 메시지의 전체 라이프사이클을 관리합니다.
브로커의 복잡성은 다르다
모든 브로커가 같은 수준은 아닙니다.
어떤 브로커는 메모리에만 저장하는 단순 큐일 수도 있고,
어떤 브로커는 디스크에 기록하고 여러 노드에 복제하고 파티션으로 나누고 장애가 나도 살아남도록 설계된
거의 분산 데이터베이스 수준의 시스템일 수도 있습니다.
그래서 브로커는 단순한 "중개자"라기보다, 메시지를 저장하고, 보호하고, 분배하는 분산 인프라입니다.
이제 간략하게 용어들에 대해 알아봤습니다. 대략적으로 브로커가 중간에서 메시지를 저장하고 보호하고 분배하는 인프라 환경인것은 이해를 하였습니다. 그래서 본론적인 이야기인 그래서 메시지 브로커는 왜 사용하는 걸까요?
그래서 메시지 브로커를 사용하는 이유가 뭔데?
메시지 브로커를 쓰는 이유는 단순합니다.
분산 시스템에서 발생하는 구조적인 문제를 줄이기 위해서입니다.
서비스가 많아질수록, 서로 직접 호출하는 구조는 점점 복잡해집니다.
그때 브로커가 들어옵니다.
디커플링 (결합도 감소)
브로커가 존재하지 않는다면,
A → B → C → D
이렇게 연결되어질겁니다.
만약, 한 서비스가 변경되면 연쇄적으로 영향이 갑니다.
하지만 브로커가 들어오면 구조가 이렇게 바뀝니다.
A → Broker ← B
C → Broker ← D
A,B,C,D모두 브로커를 바라보고 있습니다.
결국, 서로를 직접 알 필요가 없습니다.
생산자는 소비자가 누구인지 몰라도 되고, 소비자는 생산자가 어떻게 동작하는지 몰라도 됩니다.
이것이 디커플링입니다.
그리고 이 구조 덕분에 독립 배포, 독립 확장, 독립 수정이 가능해집니다.
비동기 처리
브로커가 없으면 처리 시점이 공유됩니다. 브로커가 있으면 처리 시점이 분리됩니다.
생산자는 메시지를 남기고 바로 다음 작업을 할 수 있습니다.
즉, 기다림이 제거됩니다. 이건 단순 성능 문제가 아니라 시스템 안정성 문제와 연결됩니다.
신뢰성
HTTP 호출은 실패하면 끝입니다. 하지만 브로커는 다릅니다.
위에서 브로커는 메시지를 저장한다고 하였습니다. 그 뜻은 소비가가 죽어도 유지가 될 수 있다는 뜻입니다.
이 말은 언제든지 나중에 다시 처리할 수 있습니다.
이건 네트워크 호출을 "재처리 가능한 작업 단위"로 바꾸는 효과가 있습니다.
로드 밸런싱
여러 소비자가 같은 큐를 소비하면 자연스럽게 작업이 분산됩니다.
소비자를 늘리면 처리량도 늘어납니다. 생산자 코드를 수정할 필요도 없습니다.
즉, 메시지 기반 구조에서는 수평 확장이 자연스럽습니다.
트래픽 완충 (버퍼 역할)
이게 실제로 매우 중요합니다. 트래픽이 갑자기 몰리면 직접 호출 구조는 바로 터집니다.
브로커는 메시지를 일단 받아서 저장합니다. 소비자가 느려도 생산자는 멈추지 않습니다.
물론 무한정은 아닙니다. 하지만 최소한 연쇄 장애는 막을 수 있습니다.
결론
원문에는 내용이 더 있긴하지만, 본론적으로 브로커가 왜 필요한지에 대해 이야기를 하고 싶었습니다. 대부분 내용이 글에서 발취한 내용을 다시 바꾼거지만 아무튼 브로커가 왜 필요한지에 대해 어느정도 알것 같다는 생각이 듭니다.
대략적으로 브로커는 저장 공간입니다. 브로커를 사용하는 이유는 여러가지가 있겠지만 대표적으로 결합도를 낮추는것과, 수신자가 죽어도 메시지를 언제든지 다시 발송할 수 있다는 것에 있다고 생각이 듭니다.
출처
https://blog.bytebytego.com/p/message-brokers-101-storage-replication
'개발' 카테고리의 다른 글
| 분산락을 적용해보자! (0) | 2026.02.18 |
|---|---|
| 어째서 JVM을 알아야 하는가? (0) | 2026.02.15 |
| read-only일때 replica 사용하게 하기 (0) | 2026.02.12 |
| master-slave 구조 이해하기 (0) | 2026.02.11 |
| 샤딩이란 무엇일까? (1) | 2026.02.10 |