메세지 큐(Message Queue) 란 Queue 라는 자료구조를 채택해서 메세지를 전달하는 시스템이며, 메세지 지향 미들웨어(MOM) 을 구현한 시스템입니다.
메시지 지향 미들웨어(MOM)
메세지 지향 미들웨어란 응용 소프트웨어 간의 비동기적 데이터 통신을 위한 소프트웨어입니다. 즉, 비동기적(Asynchronous) 방식을 이용해서 프로세스간의 데이터를 주고 받는 기능을 위한 시스템입니다. 메세지 지향 미들웨어는 메세지를 전달하는 과정에서 보관하거나 라우팅 및 변환할 수 있다는 장점을 가집니다.
메세지 큐를 통해 메세지를 전달하려면 메세지를 전달하는 부분과 메세지를 받는 부분이 필요하겠죠?
여기서 메세지를 발행하고 전달하는 부분을 Producer 라고 하며, 메세지를 받아서 소비하는 부분을 Consumer 라고 합니다. 메세지 큐는 Producer 와 Consumer 의 메세지 전달 역할을 하는 매개체입니다.
메세지 큐에서 데이터를 운반하는 방식에 따라 메세지 브로커 와 이벤트 브로커 로 나눌 수 있습니다.
메세지 큐가 메세지 혹은 이벤트가 송신되고 수신되는 하나의 통신 통로라고하면 브로커는 메세지 큐에 메세지 혹은 이벤트를 넣어주고 중개하는 역할을 하는 주체입니다.
하지만, 메세지 큐 자체를 메세지 브로커 혹은 이벤트 브로커로 이해하면 좋을 것 같습니다.
메세지 브로커는 Producer 가 생산한 메세지를 메세지 큐에 저장하고, 저장된 메세지를 Consumer가 가져갈 수 있도록 합니다. 메세지 브로커는 Consumer 가 메세지 큐에서 데이터를 가져가게 되면 짧은 시간 내에 메세지 큐에서 삭제되는 특징이 있습니다.
ex) RabbitMQ, ActiveMQ, AWS SQS, Redis
이벤트 브로커 또한 기본적으로 메세지 브로커의 역할을 할 수 있습니다. 하지만 반대로 메세지 브로커는 이벤트 브로커의 기능을 하지 못합니다. 이벤트 브로커가 관리하는 데이터를 이벤트라고 하며 Consumer 가 메세지 큐에서 데이터를 가져가게 되면 짧은 시간 내에 메세지가 삭제되는 것과 달리, 이벤트 브로커 방식에서는 Consumer 가 소비한 데이터를 필요한 경우 다시 소비할 수 있습니다. 또한 메세지 브로커 보다 대용량 데이터를 처리할 수 있는 능력이 있습니다.
ex) Kafka
메세지큐가 없다고 가정해보겠습니다. 메세지를 발행하는 Producer 역할을 하는 애플리케이션은 자신의 메세지를 전달 받는 Consumer 역할을 하는 애플리케이션에게 직접적으로 메세지를 보내야합니다.
즉 End-To-End 통신을 통해 메세지 전달이 이루어집니다. 이 때문에 해당 과정이 완료되기 전까지는 다른 메세지 전달 과정을 이루어지지 못합니다.
이러한 방식을 동기(Synchronous) 라고 하며 전송속도가 빠르고 전송 결과를 신속하게 알 수 있는 장점이 있는 반면에, 대용량 트래픽이 발생하는 서버에서 이러한 방식은 매우 비효율적입니다.
이를 해결하기 위해 메세지큐를 중간에 배치한다면 Producer 는 메세지를 Consumer 에게 바로 보내지 않고 Queue 에 메세지를 넣어 관리합니다. 이를 통해 Consumer 는 비동기적으로 메세지를 처리할 수 있습니다.

메세지큐를 통해 하나의 서비스를 구성하는 애플리케이션끼리의 결합도를 낮출 수 있습니다. 만약, 애플리케이션끼리의 결합도가 높으면 어떨까요? 물론 결합도가 높을 때 얻을 수 있는 장점도 있지만 확장성,유연성,효율적인 유지 보수,장애 전파 방지 등에 결합도가 낮을 때 얻을 수 있는 장점이 많습니다.
탄력성(Resilience) 란 시스템이 예기치 않은 상황 또는 장애에 대응하고 유연하게 대처할 수 있는 능력을 의미합니다.
이는 앞서 살펴보았던 낮은 결합도(Decoupling) 을 통해 실현될 수 있습니다. 이해를 위해 한가지 예시를 들어보겠습니다.
은행 송금 시스템에서 A,B 라는 2가지의 프로세스가 있다고 가정해보겠습니다. A 프로세스는 회원이 요청한 송금을 처리하는 프로세스이며, B 프로세스는 회원이 보낸 송금을 받아 계좌에 반영하는 프로세스입니다.

여기서 메세지 큐가 없다고 가정해보겠습니다. 이때, 어떠한 이유로 B 프로세스에 장애가 발생했을 때 해당 장애는 A 프로세스에게도 전파되어 장애가 복구되는 시간동안 A,B 두 프로세스 모두 정상적으로 동작하지 못한채 Blocking 됩니다. 이러한 결과는 당연한 결과인데 B 프로세스가 더이상 A 프로세스에서 보내는 송금을 받아 처리할 수 없으니 A 프로세스도 덩달아 송금을 처리하는 작업을 하지 못하게 되는 것입니다.
하지만 메세지 큐가 있다면 이러한 문제를 효율적으로 해결할 수 있습니다. 위와 같이 동일한 상황일 때 A 프로세스는 B 프로세스의 장애 여부와 상관 없이 자신이 보내는 송금(메세지) 을 메세지 큐에게 전달만 해주면 됩니다. 송금(메세지) 을 받은 메세지 큐는 B 프로세스의 장애가 해결될 때까지 큐 내부에 A 에서 받아오는 송금(메세지) 를 보관할 수 있습니다. 이로써, B 서비스의 일시적인 불능 상태가 A 서비스에 직접적인 영향을 주지 않고, 시스템의 기능을 유지할 수 있습니다.
정리하자면 메세지 큐의 이러한 특성 덕분에 Producer 프로세스는 Consumer 프로세스가 다운되어 있어도 메세지를 정상적으로 발행할 수 있고, Consumer 는 구독한 메세지를 발행하는 Producer 프로세스가 다운되어 있어도 정상적으로 수신할 수 있습니다.
과잉(Redundancy) 란 정상적인 메세지 송/수신이 실패하는 경우 재실행이 가능하다는 뜻입니다. 메세지 큐가 없다고 가정해보겠습니다.
A 프로세스와 B 프로세스는 End-to-End 통신을 하는 데 만약 B 프로세스에서 장애가 발생해 A 프로세스에서 장애 기간동안 정상적으로 메세지를 송신할 수 없다면 A 프로세스를 사용하는 클라이언트는 장애 기간동안 큰 어려움을 겪을 수 있습니다.
또한 여기에는 애플리케이션 수준에서 큰 문제가 될 수 있는데 시스템 응답성 저하, 데이터 불일치, 메세지 유실 등 다양한 문제가 발생할 수 있습니다.
하지만 메세지 큐를 사용한다면 이러한 문제를 완화할 수 있습니다.
작업을 메세지로 메세지 큐에 넣어두면 일정 장애 기간동안 송신된 메세지는 큐에 남아있어 추후 장애 복구 시 정상적으로 재시도 및 복구가 가능합니다.
신뢰성(Guarantees) 이란 송신된 메세지의 안전하고 확실한 전달을 의미합니다. 앞에 살펴보았던 장점들과 비슷한 맥락인데, 결국 메세지 큐라는 시스템 덕분에 장애가 발생하더라도 송신되는 메세지를 안전하고 확실하게 Consumer 가 수신할 수 있습니다.
확장성(Scalable) 은 수평확장을 의미합니다. 만약 기존 메시지 큐를 이용한 통신에서 부하가 증가하거나 클라이언트의 동시다발적인 요청이 증가할 때, 메세지 큐에 Producer 와 Consumer 을 추가함으로 비교적 간단하고 쉽게 확장을 할 수 있습니다.
AMQP(Advanced Message Queue Protocol)
AMQP 는 메세지 지향 미들웨어(MOM) 을 위한 개방형 표준 응용 계층 프로토콜입니다.
AMQP는 일반적인 메세지 큐와 비슷하지만 Exchange 라는 라우터가 존재하며 Binding 이라는 개념이 존재합니다. AMQP 을 구성하는 것들을 정리해보겠습니다.
RabbitMQ 는 AMQP 프로토콜을 구현해 놓은 오픈 소스 메시지 브로커이며 여러가지 특성을 가집니다.
ActiveMQ 을 본격적으로 알아보기 전에 JMS(Java Message Service) 에 대해 알아보겠습니다.
JMS(Java Message Service)
JMS 는 메세지 큐 및 Publish - Subscribe 패턴과 같은 메세지 기반 통신을 추상화하고 표준화한 API입니다. Java MOM 표준 API 이며 소프트웨어 응용 프로그램 구성 요소가 소비하는 요청, 보고서 또는 이벤트로 메시지를 작성, 전송, 수신 및 읽을 수있는 메시징 표준입니다. JMS는 다른 시스템의 프로그램이나 다른 프로그래밍 언어로 작성된 프로그램이 메시지를 통해 서로 조정할 수 있도록합니다.
쉽게 이해하자면 Java 에서 채택한 대표적인 MOM 시스템이라고 생각하면 좋을 것 같습니다. 일반적인 메세지 큐 구조와 비슷한 것을 볼 수 있습니다.
ActiveMQ 는 JMS 스펙을 좀 더 사용하기 편리하게 구현한 오픈소스 메시지 브로커입니다. RabbitMQ 와 더불어 현재 대표적인 메세지 브로커로 사용되고 있습니다.

카프카(Kafka)는 Linked-in 에서 개발한 파이프라인, 스트리밍 분석, 데이터 통합 및 미션 크리티컬 애플리케이션을 위해 설계된 고성능 분산 이벤트 스트리밍 플랫폼입니다. Pub-Sub 모델의 메시지 큐 형태로 동작하며 분산환경에 특화되어 있습니다.
Kafka 는 앞서 살펴보았던 메세지 브로커인 RabbitMQ, ActiveMQ 와는 달리 이벤트 브로커입니다. 또한 다른 동작 원리를 가집니다.

대용량 실시간 로그처리에 특화되어 설계된 메시징 시스템으로 TPS가 매우 우수하고,
메시지를 메모리에 저장하는 기존 메시징 시스템과는 달리 파일에 저장을 하는데 그로 인해 카프카를 재시작해도 메시지 유실 우려가 감소됩니다.
기본 메시징 시스템(rabbitMQ, ActiveMQ)에서는 브로커(Broker)가 컨슈머(consumer)에게 메시지를 push해 주는 방식인데, 카프카는 컨슈머(Consumer)가 브로커(Broker)로부터 메시지를 직접 가져가는 PULL 방식으로 동작하기 때문에 컨슈머는 자신의 처리 능력만큼의 메시지만 가져와 최적의 성능을 낼 수 있습니다.
대용량처리에 특화 되었다는 것은 아마도 이러한 구조로 설계가 되어 가능하게 된게 아닌가 싶습니다.
일반적으로 파일보다 메모리가 성능이 우수한데 왜 카프카가 성능이 좋은 것일까요? 그 이유는 카프카의 파일 시스템을 활용한 고성능 디자인에 있습니다. 일반적으로 하드디스크는 메모리보다 수백배 느리지만 하드디스크의 순차적 읽기에 대한 성능은 메모리보다 크게 떨어지지 않는다고 합니다.
정리하자면 대용량 분산 시스템이 필요하면 Kafka 을 구축하면 되고 그 외에는 Queue 기능에서 신뢰성과 안전성을 좀 더 보장하는 ActiveMQ 혹은 RabbitMQ 을 사용하면 될 것 같습니다.
메시지 큐란 서로 다른 시스템 간에 데이터를 비동기적으로 전달하기 위한 시스템입니다. 생산자(Producer)가 메시지를 큐에 넣으면, 소비자(Consumer)가 준비됐을 때 꺼내서 처리합니다. 두 시스템이 동시에 실행 중일 필요가 없다는 게 핵심입니다. 주요 특징은 다음과 같습니다. 1. 비동기 처리 — 생산자는 소비자가 처리할 때까지 기다리지 않아도 됩니다. 2. 느슨한 결합(Loose Coupling) — 생산자와 소비자가 서로를 직접 알 필요가 없습니다. 결합도가 낮아져 장애 전파 방지, 확장성에 좋습니다. 3. 버퍼링 — 소비자가 느리거나 일시적으로 다운되어도 메시지가 큐에 쌓여 유실되지 않습니다. 4. 확장성 — 소비자를 여러 개 붙여서 처리량을 늘릴 수 있습니다.
실제 사용 예시: 이메일/알림 발송 (주문 완료 후 즉시 응답하고, 이메일은 나중에 발송), 대용량 파일 처리 (업로드 즉시 응답, 변환 작업은 백그라운드), 마이크로서비스 간 통신, 로그 수집
대표적인 메시지 큐 시스템: RabbitMQ, Apache Kafka, AWS SQS, Redis (Pub/Sub / Stream)
RabbitMQ ㅡ 전통적인 메시지 브로커. 메시지를 소비자가 읽으면 삭제됩니다. 복잡한 라우팅 규칙이 필요할 때 적합하고, 안정성이 높습니다. 소규모~중규모 시스템에 무난한 선택. Apache Kafka — 메시지를 로그처럼 디스크에 저장하고, 읽어도 삭제되지 않습니다. 초대용량 데이터 스트리밍, 이벤트 소싱에 강점. 설정이 복잡하지만 처리량이 압도적입니다. Redis (Pub/Sub / Stream) — 인메모리라 속도가 가장 빠릅니다. 다만 기본 Pub/Sub은 메시지를 저장하지 않아 유실 위험이 있고, Redis Stream을 쓰면 어느 정도 보완됩니다.