메시징 큐의 개념과 기본 구조
A 서비스에서 B 서비스로 데이터를 넘겨야 하는데, B가 일시적으로 죽어 있으면 그 데이터는 어디로 가는 걸까?
메시징 큐란?
메시징 큐(Messaging Queue)는 서비스 간 데이터(메시지)를 임시로 저장하고 순차적으로 전달하는 중간 매개체 입니다. 생산자와 소비자 사이에 큐가 위치하기 때문에, 양쪽이 동시에 동작하지 않아도 데이터를 안전하게 전달할 수 있습니다.
왜 메시징 큐가 필요한가
여러 서비스가 협력하는 구조에서는, 서비스 간 데이터를 주고받는 방식이 시스템 전체의 안정성과 확장성을 결정합니다. 동기 방식으로 직접 호출하면 소비자가 죽는 순간 생산자까지 영향을 받습니다. 큐를 사이에 두면 이 문제가 해결됩니다.
메시징 큐의 핵심 가치는 시스템 간 결합도를 낮추고, 비동기 처리를 가능하게 하며, 장애 상황에서도 메시지를 보존하는 것 입니다.
동기 vs 비동기: 왜 비동기가 필요한가
메시징 큐를 이해하려면, 먼저 동기 처리 방식의 한계를 살펴봐야 합니다.
| 비교 항목 | 동기(Synchronous) | 비동기(Asynchronous, 메시징 큐) |
|---|---|---|
| 처리 방식 | 생산자가 메시지를 보내면 소비자가 즉시 처리해야 함 | 큐에 메시지를 적재하고, 소비자가 자신의 속도로 처리 |
| ** 결합도** | 생산자와 소비자가 강하게 결합됨 | 큐가 매개체 역할을 하여 느슨한 결합 |
| ** 장애 대응** | 소비자 장애 시 생산자도 영향을 받음 | 소비자 장애 시에도 메시지가 큐에 보존됨 |
| ** 트래픽 급증** | 병목 현상 발생, 시스템 장애 가능 | 큐가 버퍼 역할을 하여 부하를 흡수 |
| ** 확장성** | 수평 확장이 어려움 | 소비자를 독립적으로 확장 가능 |
동기 방식의 문제 시나리오
A 서비스(생산자)가 B 서비스(소비자)에 직접 메시지를 전달하는 동기 구조를 가정해 봅니다.
- ** 속도 차이 문제 **: A가 초당 1,000건의 메시지를 생산하지만, B가 초당 500건만 처리할 수 있다면 나머지 500건은 대기 상태로 쌓이게 됩니다.
- ** 트래픽 폭증 **: 특정 시간대에 트래픽이 몰리면 B의 처리 능력을 초과하여 시스템 전체에 지연이 발생합니다.
- ** 장애 전파 **: B에 장애가 발생하면 A도 메시지를 보낼 수 없게 되어, 단일 서비스 장애가 전체 시스템으로 전파됩니다.
이러한 한계 때문에 ** 대규모 트래픽, 마이크로서비스 아키텍처, 이벤트 기반 시스템 **에서는 비동기 메시징 큐가 사실상 필수적입니다.
메시징 큐의 실무 활용 사례
메시징 큐는 다양한 실무 시나리오에서 활용됩니다.
- ** 주문/결제 처리 **: 주문 접수 후 결제, 재고 차감, 알림 발송을 각각 비동기로 처리
- ** 로그 수집 **: 대량의 로그 데이터를 큐에 적재한 뒤, 분석 시스템이 자신의 속도로 처리
- ** 이메일/푸시 알림 **: 사용자 요청에 대한 응답은 즉시 반환하고, 알림은 큐를 통해 비동기 발송
- ** 작업 분산 **: 이미지 리사이징, 영상 인코딩 등 무거운 작업을 워커에게 분산 위임
RabbitMQ 기본 구조와 동작 원리
RabbitMQ의 기본 구조
RabbitMQ는 AMQP(Advanced Message Queuing Protocol)를 구현한 오픈소스 메시지 브로커입니다.
**AMQP 한 줄 정의 **: 메시지의 생산, 라우팅, 큐잉, 소비 방식을 표준화한 개방형 애플리케이션 계층 프로토콜. 브로커 구현체(RabbitMQ, ActiveMQ 등)와 클라이언트가 서로 다른 언어/플랫폼이어도 동일한 규칙으로 통신할 수 있게 해줍니다.
기본 구조는 네 가지 핵심 요소로 구성됩니다.
Producer → Exchange → Queue → Consumer
(생산) (라우팅) (저장) (소비)
| 구성 요소 | 역할 | 비유 |
|---|---|---|
| Producer (생산자) | 메시지를 생성하여 Exchange로 전송 | 택배를 보내는 사람 |
| Exchange (교환기) | 라우팅 규칙에 따라 메시지를 큐로 분배 | 물류 센터 |
| Queue (큐) | 메시지를 임시 저장하는 FIFO 버퍼 | 택배 보관함 |
| Consumer (소비자) | 큐에서 메시지를 꺼내 처리 | 택배를 받는 사람 |
Exchange란?
Exchange는 RabbitMQ 설계의 핵심입니다. 생산자가 큐에 직접 메시지를 발행하지 않고, ** 반드시 Exchange를 경유 **하도록 설계된 이유가 있습니다.
만약 생산자가 큐로 직접 메시지를 보낸다면, 생산자는 큐의 이름과 위치를 정확히 알아야 합니다. 큐가 추가되거나 라우팅 규칙이 변경될 때마다 생산자 코드를 수정해야 하므로, 시스템의 유연성과 확장성이 크게 떨어집니다.
RabbitMQ는 생산자와 큐 사이에 Exchange를 두어 이 문제를 해결합니다. 생산자는 Exchange 이름과 라우팅 키만 지정하면 되고, 메시지를 어떤 큐로 보낼지는 Exchange가 결정합니다.
RabbitMQ 공식 문서: "In RabbitMQ, producers publish messages to exchanges, not directly to queues. The exchange routes the messages into zero or more queues based on rules called bindings. This decouples producers from queues and allows for flexible routing logic." — AMQP 0-9-1 Model Explained
Exchange 타입 요약
RabbitMQ는 기본적으로 네 가지 Exchange 타입을 제공합니다.
| Exchange 타입 | 라우팅 방식 | 적합한 시나리오 |
|---|---|---|
| Direct | 라우팅 키가 정확히 일치하는 큐에 전달 | 특정 서비스로의 1:1 메시지 전달 |
| Fanout | 바인딩된 모든 큐에 브로드캐스트 | 전체 알림, 로그 복제 |
| Topic | 패턴(와일드카드)으로 매칭되는 큐에 전달 | 카테고리별 이벤트 분배 |
| Headers | 메시지 헤더 속성으로 매칭 | 복합 조건 라우팅 |
각 Exchange 타입의 상세한 동작 방식과 코드 예제는 여러 가지 Exchange 타입과 라우팅 방식 글에서 다룹니다.
주의할 점
Exchange를 생략하면 안 된다
RabbitMQ 입문자들이 흔히 하는 실수는 기본 Exchange("")만 사용하는 것입니다. 기본 Exchange는 라우팅 키를 큐 이름과 정확히 매칭하는 Direct Exchange인데, 이렇게 하면 생산자가 큐 이름을 직접 알아야 합니다. 큐가 추가되거나 라우팅 규칙이 변경될 때마다 생산자 코드를 수정해야 하므로, 프로덕션에서는 반드시 명시적인 Exchange를 선언해서 사용해야 합니다.
메시지 유실 가능성
기본 설정만으로는 브로커가 재시작되면 큐와 메시지가 모두 사라집니다. Durable(큐 지속성)과 Persistent(메시지 지속성)를 모두 설정해야 안전합니다. 이 부분은 메시지 지속성과 생명주기 관리 글에서 상세히 다룹니다.
정리
| 항목 | 설명 |
|---|---|
| 메시징 큐 | 서비스 간 비동기 메시지 전달을 위한 중간 매개체 |
| 핵심 가치 | 결합도 감소, 비동기 처리, 장애 시 메시지 보존 |
| 기본 흐름 | Producer → Exchange → Queue → Consumer |
| Exchange | 생산자와 큐 사이에서 라우팅 규칙에 따라 메시지를 분배 |
| 동기 vs 비동기 | 동기 방식은 장애 전파/병목 위험, 비동기 방식은 큐가 버퍼 역할 |
** 공식 문서 참고 **: RabbitMQ Tutorials | AMQP Concepts