나는 왜 RabbitMQ 을 채택했는가?

goyo·2026년 4월 26일
post-thumbnail

최근 파일 변환 프로젝트를 마무리하며,
프로젝트에 사용된 기술 스택에 대한 정리를 해보고자 한다.

1. 메시지 큐란 무엇이고 왜 쓰는가?

1-1 메시지 큐(Message Queue) 란?

메시지 큐는 프로세스 또는 서비스 간에 데이터를 비동기적으로 교환하기 위해 사용하는 중간 저장소이다.


1-2 왜 쓰는가?

시스템 간의 의존성을 줄이고 비동기 처리를 통해 대규모 트래픽을 효율적으로 처리하여 시스템 전체의 안정성과 응답 속도를 높이기 위해 사용한다. 그 밖에도 트래픽 부하 분산, 작업의 백그라운드 처리, 데이터 유실 방지가 있다.


1-3 메시지 큐 종류 및 차이점

구분KafkaRabbitMQRedis Pub/Sub
주요 사례로그 수집,빅데이터 스트리밍비동기 작업(파일 변환)채팅,실시간 알림
메시지 영속성영구 보존(설정 기간동안)Ack 전까지 보존(메모리/디스크)X (전달 후 삭제)
전달 방식Pull 기반(consumer 중심)Push 기반(Smart Routing)Push 기반(Fire & forget)
처리량매우 높음(수백만 RPS)중간(수만 RPS)높음 (단일 노드 강세)
순서 보장파티션 단위큐 단위발행 순서
보장 범위동일 키를 가진 메시지 간 순서 유지하나의 큐에 쌓인 순서대로 전달발행된 순서대로 전달

2. RabbitMQ 를 채택, 비동기 아키텍처를 설계하다.

2-1 RabbitMQ 채택 이유와 특징

  • 개별 메시지의 생명 주기 직접 관리

    • 파일 변환은 변환 처리 과정에서 실패를 할 수도 있다. 이 경우 파일별로 변환 실패에 대한 처리를 개별적으로 해야한다.
      RabbitMQ 에서는 개발자가 메시지 단위의 Ack/Nack 을 직접 메뉴얼로 설정 가능하다. 특정 파일 변환이 실패하면 재시도를 하거나, DLQ(DeadLetterQueue) 로 전송하여 격리하거나 재처리 또한 가능하다.
  • 순서가 보장될 필요가 없다

    • kafka 의 경우 파티션 내 순서 보장을 위해 분산 구조의 복잡성과 오버헤드를 수반하지만, 파일 변환 요청의 경우 독립적인 작업이므로 특별히 요청 간의 순서를 보장해줄 필요는 없었으므로 불필요한 비용이라 판단했다.
  • 유연한 라우팅 전략

    • 현재는 통합 큐를 사용하더라도, 훗날 서비스의 트래픽이 급증한다면 파일의 확장자별로 다른 워커 서버 or 큐의 구성이 필요할 때 Exchange(Direct, Topic) 설정을 활용하면 심플하게 원하는 큐로 전달할 수 있다.



2-2 비동기 아키텍처 UML 다이어그램

2-3 메시지 큐를 활용한 비동기 아키텍처의 특징

1. 비동기 처리

  • 메시지 큐를 사용하면 프로세스 간의 작업 요청이 큐에 저장되고, 각작업은 독립적으로 수행될 수 있다.
  • 클라이언트는 작업을 큐에 넣은 후 바로 다음 작업을 수행할 수 있으며, 작업이 완료될 때까지 기다릴 필요가 없음, 이로 인해 높은 처리 속도와 반응성이 유지됨

2. 느슨한 결합

  • 메시지 큐는 서로 다른 서비스가 직접 통신하는 대신, 메시지를 큐에 넣어 간접적으로 통신하게 하여, 서비스간의 의존성을 줄여준다.
  • 예를 들어 파일 변환 시스템에서 사용자의 요청을 수신하는 서비스와 파일을 변환을 서비스가 독립적으로 운영될 수 있다. 이 덕분에 시스템의 한 서비스가 장애가 나더라도 다른 서비스는 정상 작동할 수 있다.

3. 확장성

  • 메시지 큐는 다수의 소비자(Consumer) 가 큐에 쌓인 메시지를 병렬로 처리할 수 있게 한다. 이를 통해 시스템의 부하가 쌓아질 경우, 소비자를 추가해 쉽게 수평 확장할 수 있다.
  • 예를 들어 변환할 파일이 많아지면 소비자 프로세스를 추가하여 병목 현상을 줄이고 작업 속도를 높일 수 있다.

4. 신뢰성

  • 메시지 큐는 메시지의 손실을 방지하기 위해, 메시지를 안전하게 저장하고 전달하는 역할을 한다.
  • 예를 들어 RabbitMQ 나 Kafka 같은 메시지 큐 시스템은 메시지의 복제,메시지의 순서 보장, 메시지 재전송 등의 기능을 제공하여 신뢰성 높은 메시지 전달을 지원한다.
  • 메시지 큐에 메시지를 넣고, 소비자가 메시지를 받아 처리한 후 '확인(Acknowledgement)'을 보내야 메시지가 큐에서 삭제된다. 이렇게 하면 메시지가 손실되거나 중복되지 않고 안전하게 전달된다.

5. 장애 격리

  • 메시지 큐 아키텍처는 한 서비스의 장애가 다른 서비스로 전파되지 않도록 방지한다.
  • 서비스 간의 작업이 큐를 통해 비동기로 처리되기 때문에, 한 서비스가 일시적으로 중단되더라도 큐에 메시지가 쌓여 다른 서비스에 영향을 미치지않는다. 장애가 복구된 후, 쌓인 메시지를 순차적으로 처리하면 된다.

6. 트래픽 스파이크 완화

  • 메시지 큐는 일시적으로 발생하는 높은 트래픽을 완화하는 버퍼 역할을 한다.
  • 트래픽이 갑자기 증가하더라도 요청을 일시적으로 큐에 저장해 처리할 수 있으며, 시스템이 요청을 순차적으로 처리하므로, 과부하에 따른 시스템 다운타임을 방지할 수 있다.

7. 작업 재시도 및 지연 처리

  • 비동기 아키텍처에서는 메시지 큐를 통해 특정 작업을 일정 시간 이후로 재시도하거나, 작업 지연 처리를 할 수 있다.
  • 예를 들어 파일 변환 서비스에서 일시적 오류가 발생한 경우 메시지 큐에 지연 시간을 설정해 재시도하면 자동으로 재전송할 수 있다. 이를 통해 시스템 안정성을 높일 수 있다.




최종 설계된 데이터 흐름 구조는 이러하다.

1. 사용자는 변환 요청한다.
2.사용자는 서버로부터 즉시 요청 성공 응답을 받는다.
3.서버는 메시지 브로커로 변환 요청 데이터를 전송하고 변환 작업을 진행한다.


3. Exchange 전략

3-1 RabbitMQ Exchange 전략 비교

전략 구분라우팅 방식매칭 규칙장점단점서비스 적용 예시
Direct1:1 매칭Routing Key가 정확히 일치해야 전달단순하고 명확하며 처리가 빠름확장 시 매번 키를 추가해야 하므로 관리가 번거로움pdf-queue, jpg-queue 등 고정된 작업 분배
Fanout전체 방송키를 무시하고 연결된 모든 큐에 전달메시지 복제 전달이 빠르고 설정이 매우 간단함불필요한 메시지도 모든 컨슈머가 받게 되어 리소스 낭비 발생공지사항 알림, 서버 간 상태 동기화(Heartbeat)
Topic패턴 매칭와일드카드(*, #)를 이용한 패턴 일치유연성과 확장성이 가장 높음. 계층적 설계 가능라우팅 로직이 앞선 방식들에 비해 복잡할 수 있음file.event.# (파일 관련 모든 이벤트 처리), 로그 레벨별 수집

💡 Topic 전략 와일드카드 규칙

  • * (Star): 정확히 단어 1개를 대체 (예: file.*.pdf)
  • # (Hash): 0개 이상의 단어를 대체 (예: file.event.#)

  • 위와 같은 Exchange 전략들 중에서 추후 다양한 기능들이 추가될 경우를 대비하여 워커 서버의 컨슈머에서 필터링하여 처리할 수 있도록 패턴 매칭 전략을 채택했다.
  • 번외로 프로듀서와 컨슈머의 연결상태를 명시적으로 줄 수 있는 Heartbeat 는 Fanout 전략으로 채택해도 좋을 것 같다.

4. 메시징 트랜잭션

4-1 비동기 메시징 트랜잭션 관리 기법 비교

기법핵심 메커니즘주요 장점단점 및 고려사항
Transaction OutboxDB 로컬 트랜잭션 내에 Outbox 테이블에 메시지 저장 후, 별도 Relay가 큐로 발행서비스 중단 시에도 데이터 일관성(Atomic) 보장, 2PC 없이 구현 가능별도의 메시지 발행기(Relay) 구현 필요, DB 부하 증가 가능성
Publisher Confirms브로커(RabbitMQ 등)가 메시지를 안전하게 수신했는지 발행자에게 확인(ACK) 응답발행 단계에서의 메시지 유실 방지, 높은 신뢰성네트워크 왕복(RTT)으로 인한 발행 성능(Throughput) 저하
TCC (Try-Confirm-Cancel)비즈니스 로직을 시도(Try), 확정(Confirm), 취소(Cancel) 단계로 분리하여 실행강한 일관성이 필요한 결제/주문 시스템에 적합모든 서비스에 보상 트랜잭션(Cancel) 로직을 구현해야 하므로 개발 복잡도 매우 높음

4-2 채택한 컨슈머 트랜잭션 메시징 흐름 구조

[ Main Queue ] 
      │
      ▼
[ Consumer (Manual Mode) ]
      │
      ├─ (Try 1, 2, 3) ───▶ [ Success ] ───▶ basicAck (Main Queue 메시지 삭제)
      │
      └─ (All Fails) ─────▶ [ Recoverer ] ──▶ basicNack ──▶ [ DLQ 이관 ] ──▶ [ Slack 로그 전송] 

Transaction outbox & Menual Ack/Nack 처리

  • Consumer(Worker Server) 의 Ack/Nack 처리 방식은 위와 같은 흐름으로 진행했다.

4-3 Why?

비즈니스 도메인 특성

  • 파일 변환은 서버 자원 소모가 크고 처리 시간이 가변적이며, 파일 손상, 미지원 포맷같은 데이터 자체의 결함으로 인한 실패가 빈번하기때문에, 이러한 환경에서 Manual Ack + Recoverer 조합을 사용하면, 무거운 변환 작업 중 서버가 다운되더라도 메시지 유실을 방지함과 동시에, 특정 불량 파일이 전체 시스템을 마비시키는 '병목 현상을 방지하기 위해 해당 메시지만 DLQ로 신속히 격리할 수 있다.

데이터 유실 차단

  • 단순 Auto Ack 의 경우 Consumer 가 메시지를 받자마자 브로커에서 삭제를 해서, 처리 도중 서버가 뻗으면 데이터 영구 유실되기 때문에, 비즈니스 로직을 묶어서 성공 시 Ack 처리되도록 했다.

효율적인 오류 필터링

  • 단순 네트워크 지연 또는 일시적인 오류는 재시도만으로도 충분히 해결될 수 있으나 파일 손상, 로직 버그의 경우 Recoverer 를 통해 따로 DLQ 로 모으는 필터링이 효율적이다.

5. 성능 측정 및 결과 분석 (Artillery + Prometheus + Grafana)

// 시나리오 페이즈 예시
  phases:
    - duration: 30
      arrivalRate: 1
      name: "1단계: Warm Up(초당 1명)"

    - duration: 60
      arrivalRate: 1
      rampTo: 10
      name: "2단계: 점진적 기본 트래픽 (1명부터 시작해서 10명까지)"

    - duration: 120
      arrivalRate: 5
      name: "3단계: 긴 지속적인 트래픽 (초당 5명)"

    - duration: 30
      arrivalRate: 20
      name: "4단계: 한계 돌파 스트레스 테스트 (초당 20명)"

이미지 파일은 jpg / 50MB 으로 진행.



5-1 부하테스트 결과 성능 지표

  • 메시지 건당 평균 0.08의 Laytency
  • 1분당 평균 300건대Throughput 을 뽑아냈다.

출처
https://yoon-ssi.tistory.com/109
https://dongwooklee96.github.io/post/2021/03/29/%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%81%90%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-%EB%B0%8F-%EC%97%90%EB%9F%AC-%EC%B2%98%EB%A6%AC.html

0개의 댓글