안녕하세요. 쿠폰 스쿼드 포덕입니다.
2025년 새해 첫 대량 쿠폰 발급을 진행하던 중, 예상치 못한 일이 벌어졌습니다. 1,500만 건의 쿠폰을 발급하던 중에 RabbitMQ 클러스터에 문제가 생겼습니다.
이번 글에서는 긴급 상황에서의 대응 과정부터 AWS TAM(Technical Account Manager)과 함께 찾아낸 근본 원인, 그리고 Classic Mirrored Queue에서 Quorum Queue로 전환하기까지의 여정을 공유해드리려고 합니다.
🚨 장애 발생과 초기 대응
 
  2025년 1월, 새해 첫 대량 쿠폰 발급을 진행하던 평범한 오후였습니다. 1,500만 건의 쿠폰을 발급하던 중 갑자기 쿠폰 발급이 완전히 멈춰버렸습니다.
처음에는 단순한 일시적 지연으로 생각했지만, 시간이 지날수록 상황은 심각해졌습니다. 다수의 고객 문의가 쏟아졌고, 매출 손실 발생 가능 이슈로 긴급 대응 채널이 생성되었습니다.
문제의 핵심은 RabbitMQ 클러스터의 메모리 과다 점유였습니다. 메모리가 부족해지면서 브로커가 Critical Status로 변경되었고, 클러스터 내 특정 노드가 unsynchronized 상태로 전환되면서 메시지 처리가 완전히 멈춰버린 것이었습니다.
 
  
    
  
      
    
   
  
    
  🔧 초기 대응과 긴급 복구
처음 문제가 접수되었을 때는 평소처럼 DB, 트래픽, WAS부터 확인했습니다. 하지만 모든 지표가 정상이었습니다. 쿠폰 관련 리소스들을 하나씩 점검하던 중에서야 MQ 상태 이상이 발견되었고, 그제서야 진짜 원인을 찾을 수 있었습니다.
인스턴스 증설부터 시도해봤지만 예상과 달리 효과가 없었습니다. 메모리를 늘려도 unsynchronized 상태는 그대로였는데, 이는 이미 동기화가 깨진 상태에서는 단순히 리소스를 늘리는 것만으로는 해결되지 않기 때문입니다. RabbitMQ의 미러링 메커니즘에서 한 번 out of sync 상태가 되면, 모든 노드가 다시 동기화될 때까지 메시지 소비가 중단되는데, 이 과정에서 메모리 증설은 근본적인 해결책이 되지 못합니다. 브로커 재시작을 시도하려 했지만 Memory Alarm이 해소된 후에야 재시작이 가능하다는 안내를 받았습니다. 관리형 서비스 특성상 즉시 재시작이 제한되었습니다. Queue Purge도 Admin UI로만 가능하고 Force Purge는 지원되지 않아서, 긴급 상황에서의 세밀한 제어가 어려웠습니다.
복구가 계속 지연되면서 비즈니스 영향이 커지는 상황에서, 일단 서비스부터 살리기로 했습니다. 기존 장애 브로커와 동일한 설정으로 새로운 RabbitMQ 브로커를 생성하고, 복잡한 클러스터 재구성 대신 독립 운영 방식을 선택했습니다.
애플리케이션의 MQ 접속 정보를 신규 브로커로 변경하면서 기존 메시지 손실을 방지하기 위해 전환 시점을 신중하게 조율했습니다. 점진적으로 트래픽을 전환하면서 실시간 모니터링으로 정상 동작을 계속 확인한 결과, 약 30분 만에 메시지 처리가 정상화되었고 쿠폰 발급 서비스가 완전히 복구되었습니다.
긴급 대응이 끝나고 나서는 재발 방지를 위한 체계적인 원인 분석에 착수했습니다. 메모리 부족이 브로커의 메시지 처리에 미치는 영향과 당시 진행 중이던 대량 발급의 연관성을 가설로 설정하고, 장애 상황을 개발 환경에서 재현해서 정확한 원인을 찾아보기로 했습니다.
🔍 원인 분석과 재현 실험
정확한 원인을 찾기 위해 사고 발생 전 상황을 자세히 조사했습니다. 당시 세 가지 작업이 동시에 진행되고 있었습니다. 1,500만 건 규모의 전체 회원 대상 대량 발급, 전체 매장 대상 쿠폰의 동시 발급 및 사용 처리, 그리고 평상시 트래픽과 겹치면서 Queue 메시지가 급격히 쌓인 상황이었습니다.
병목 지점들을 분석해보니 1,500만 건 대상자를 조회하는 과정에서 DB에 부하가 발생했고, count-up 로직에서 지연이 누적되면서 메시지 처리 속도가 떨어졌습니다. 결국 처리 속도보다 메시지가 더 빠르게 들어오면서 메모리 사용량이 급증한 것이 문제의 시작이었습니다.
RabbitMQ 미러링 메커니즘의 구조적 한계
Classic Mirrored Queue는 고가용성을 보장하기 위해 Master 노드와 Mirror 노드 간의 동기화 구조로 동작합니다. Master 노드가 메시지를 실제로 저장하고 Consumer가 연결되는 주 노드 역할을 하고, Mirror 노드는 Master와 동일한 데이터를 갖도록 유지되는 백업 노드입니다.
 
  
    
문제는 동기화 실패 시나리오에서 발생합니다. Master 노드가 메시지를 받으면 모든 Mirror 노드에 복제 요청을 보내는데, Mirror 노드가 메모리 부족으로 제대로 응답하지 못하면 동기화가 실패합니다. 그러면 해당 Queue가 out of sync 상태가 되고, RabbitMQ는 데이터 안전성을 위해 out of sync가 해소되기 전까지 메시지 소비 처리를 완전히 중단합니다.
AWS MQ 공식 문서를 보면, RabbitMQ는 메모리 사용량이 임계점에 도달할 경우 Memory Alarm을 작동시켜 새로운 메시지 수신을 차단합니다. 하지만 이미 처리 중인 메시지의 미러 노드 동기화 과정에서는 새로운 메시지 publish가 차단되고, 기존 메시지의 동기화 요청에 대한 응답이 지연되거나 무시되며, Mirror 노드의 동기화 상태 확인 프로세스가 중단되는 문제가 발생할 수 있습니다.
이런 보수적인 방식은 데이터 무결성 측면에서는 안전하지만, 서비스 가용성 측면에서는 치명적인 약점이 될 수 있습니다. RabbitMQ 팀에서도 이런 한계를 인정하고 Classic Mirrored Queue를 deprecated로 지정했으며, 대신 Quorum Queue 사용을 권장하고 있습니다.
 
  
    
  https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/troubleshooting-action-required-codes-rabbitmq-memory-alarm.html
🔍 상황 재연과 근본 원인 확정
정확한 분석을 위해 운영 환경과 똑같은 조건으로 개발 환경을 구성해서 다양한 테스트를 해봤습니다. AWS MQ 브로커를 운영 환경과 동일한 인스턴스 타입 및 설정으로 구성하고, Classic Mirrored Queue에 운영 환경과 같은 미러링 정책을 적용했습니다.
 
  
    
  1차 재연 시도: 부분적 성공
테스트 시나리오 1
1. 일반 쿠폰 발급 진행 (점진적 증가)
2. 대량발급 약 1,500만 건 일괄 요청  
3. 대량발급 Queue 메시지 적재 상태 확인
4. 일반 쿠폰 발급 지속 유입
5. 1시간 정도 계속 모니터링결과: Memory High Critical Alarm은 발생했지만 unsynchronized 상태까지는 재현되지 않았습니다. 메모리 사용량이 임계점에 도달한 후 시간이 지나면서 자연스럽게 해소되는 패턴을 보였습니다.
2차 재연: 브로커 재시작 시나리오 추가
운영 환경에서 문제 해결을 위해 브로커 재시작을 시도했다는 점에 주목해서, 테스트 시나리오를 확장해봤습니다.
테스트 시나리오 2
1. 일반 쿠폰 발급 진행
2. 대량발급 약 1,500만 건 일괄 요청
3. 대량발급 Queue 메시지 적재 확인 
4. 일반 쿠폰 발급 지속 유입
5. 1시간 정도 모니터링 진행
6. 🔥 Consumer 처리 중 broker 강제 재시작
7. 재시작 후 동기화 상태 모니터링 
  
    
드디어 브로커 재시작 과정에서 unsynchronized 상태를 재현할 수 있었습니다!
메모리 부족 + 브로커 재시작 = Unsynchronized 상태라는 공식이 확정되었습니다. 재시작 과정에서 브로커가 일시적으로 메인터넌스 모드로 전환되고, 클러스터 내에서 노드 역할이 재배치되면서 누적된 메시지를 처리하는 과정에서 순간적으로 메모리가 급증합니다. 이때 클러스터 내 노드 간 복제본 동기화 과정에서 타임아웃이 발생하는 것이 문제의 핵심이었습니다.
노드의 메모리 사용량이 높은 상태에서 브로커를 재시작하면 오히려 상황이 더 악화될 수 있다는 것을 확인했습니다. Classic Mirrored Queue의 구조적 한계가 고부하 상황에서 더욱 명확하게 드러났습니다.
🤝 AWS TAM 협업을 통한 공식 해결책 도출
 
  
    
분석 결과를 바탕으로 AWS TAM과 미팅을 진행했습니다. 개발 환경에서 성공적으로 재연한 과정, 상세한 브로커 로그 분석, 클러스터 상태 변화 추이, 그리고 AWS MQ 제약사항을 공유하면서 공식적인 해결책을 찾아나갔습니다.
AWS 측 분석 결과는 명확했습니다. "이 현상은 RabbitMQ 3.11.28 버전에서 확인된 알려진 이슈(Known Issue)일 가능성이 높습니다. Classic Mirrored Queue의 메모리 관리와 동기화 로직에서 발생하는 구조적 한계로, 고부하 상황에서 브로커 재시작 시 더욱 자주 발생할 수 있습니다."
TAM에서 제시한 해결책은 2단계 접근법이었습니다. 즉시 적용 가능한 해결책으로는 RabbitMQ 버전 업그레이드를 통해 최신 버전에서 개선된 미러링 관련 동기화 로직과 메모리 관련 동작 안정성을 확보하는 것이었습니다.
구조적 개선 방안으로는 Classic Mirrored Queue에서 Quorum Queue로의 전환을 권고했습니다. Quorum Queue는 Raft consensus 알고리즘 기반으로 더 안정적인 동기화를 제공하고, 비정상 replica 자동 제거 및 재동기화 기능이 내장되어 있으며, 네트워크 분할(Split-brain) 상황에서의 강력한 복구 능력을 갖추고 있습니다.
1차 개선: 브로커 버전 업그레이드
AWS TAM의 권고에 따라 바로 RabbitMQ 브로커 버전을 3.11.28에서 3.12.14로 업그레이드했습니다. 주요 개선사항으로는 메모리 알람 처리 로직 개선과 미러링 동기화 안정성 강화가 있었습니다.
동일한 테스트 시나리오로 다시 검증해본 결과, unsynchronized 상태가 발생하지 않는 것을 확인했습니다. 메모리 사용량이 임계점에 도달해도 동기화 상태가 안정적으로 유지되었습니다.
2차 개선: Quorum Queue 전환 결정
버전 업그레이드로 당장의 위험은 해소되었지만, 장기적인 안정성을 위해 Quorum Queue 전환을 결정했습니다.
 
  
    
Quorum Queue는 Raft Consensus 알고리즘을 기반으로 해서 Classic Mirrored Queue의 한계를 근본적으로 해결합니다. 리더(Leader) + 팔로워(Follower) 구성으로 과반수 합의를 통한 안정적인 메시지 처리가 가능합니다.
 
  
      
RAFT Consensus 알고리즘의 핵심 장점은 리더 노드 장애 시 즉시 새로운 리더 선출로 서비스 연속성을 보장하고, 메시지 커밋 시 과반수 노드의 승인으로 데이터 일관성을 확보하며, 장애 노드 복구 시 자동으로 클러스터에 재참여하고, 네트워크 분할 상황에서도 과반수 원칙으로 안정성을 유지한다는 것입니다.
운영 관점에서는 수동 개입 없이 자동 장애 복구가 가능하고, 복잡한 동기화 상태 관리가 불필요하며, 예측 가능한 성능과 안정성, 그리고 확장성과 내결함성의 균형을 제공합니다.
📊 전환 이후 성과와 개선 지표
전환 후 3개월간의 운영 결과를 보면 극적인 개선을 확인할 수 있었습니다. 가장 눈에 띄는 변화는 장애 발생 빈도입니다. 월 평균 2-3회 발생하던 장애가 전환 후 0회로 완전히 줄어들었습니다.
성능 면에서도 개선이 있었습니다. 평균 메시지 처리 지연시간이 150ms에서 120ms로 20% 단축되었고, 무엇보다 중요한 것은 대량 발급 상황에서의 안정성입니다. 이전에 문제가 되었던 1,500만 건 규모의 대량 쿠폰 발급을 무장애로 처리할 수 있게 되었습니다.
운영 관점에서도 큰 변화가 있었습니다. 이전에 월 평균 5-7건씩 발생하던 Memory Critical 알람이 0건으로 현저히 줄어들어 운영 부담이 크게 감소했습니다.
| 항목 | Classic Mirrored Queue | Quorum Queue | 개선율 | 
|---|---|---|---|
| 메모리 사용 효율성 | 높음 (단순 복제) | 중간 (로그 기반) | -15% | 
| 동기화 안정성 | 낮음 (단일 실패점) | 높음 (과반수 합의) | +85% | 
| 장애 복구 시간 | 수동 개입 필요 | 자동 복구 (30초 이내) | +90% | 
| 네트워크 분할 대응 | 취약 | 강력 (Split-brain 방지) | +100% | 
Quorum Queue 전환과 함께 포괄적인 모니터링 시스템도 구축했습니다. CloudWatch 기반으로 메모리 사용률 알람(80% 이상 시 경고, 90% 이상 시 Critical), Queue Length 모니터링, Consumer Lag 추적, 클러스터 상태 감시를 실시간으로 진행하고 있습니다. SNS 기반 즉시 알림 체계를 통해 Slack 채널과 연동한 실시간 장애 알림, 심각도별 escalation 정책, 자동 복구 가능 여부 판단 로직도 포함했습니다.
🚀 향후 개선 계획과 확장 방향
 
  
    
단일 브로커 장애에 대비해서 이중화 구성으로 서비스 연속성을 확보하는 것이 다음 목표입니다. Primary 브로커가 평상시 모든 트래픽을 처리하고, Secondary 브로커는 동일한 설정으로 standby 상태를 유지하면서 큐 설정 및 메시지 라우팅 규칙을 실시간으로 동기화합니다. 30초 간격으로 서로의 상태를 확인하는 Health Check도 구축할 예정입니다.
Route 53 Private DNS를 활용한 무중단 전환도 계획하고 있습니다. coupon-mq.internal.oliveyoung.com(예시) 같은 고정 도메인을 통해 장애를 감지하면 자동으로 Secondary 브로커로 라우팅을 변경하고, DNS 레코드 TTL을 30초로 설정해서 빠른 전환이 가능하도록 할 것입니다. Route 53 Health Check와 연동해서 자동 failover가 이루어지면 평균 전환 시간을 90초 이내로 단축할 수 있을 것으로 예상합니다.
예방적 운영 체계 강화를 위해 메모리 사용률 70% 도달 시 자동 인스턴스 증설 검토, Queue Length가 임계값을 초과하면 Consumer 자동 증설, 매일 오전 브로커 상태 및 성능 지표 자동 점검 등의 자동화된 장애 대응 시스템을 구축할 예정입니다. 상황별 단계별 대응 절차를 문서화한 장애 대응 플레이북, 장애 심각도별 담당자 및 대응 시간을 명시한 에스컬레이션 정책, 장애 복구 후 정상 동작 확인 항목을 정리한 복구 검증 체크리스트도 체계화할 계획입니다.
메시징 시스템 고도화 로드맵으로는 단기적으로(3-6개월) 가용 영역 장애에 대비한 Cross-AZ 브로커 배치, 네트워크 대역폭 효율성 개선을 위한 메시지 압축 최적화, 처리량과 지연시간 균형점 탐색을 위한 Consumer 그룹 최적화, 실패 메시지 처리 및 재시도 로직 개선을 위한 Dead Letter Queue 고도화를 진행할 예정입니다.
중기적으로는(6-12개월) 대용량 로그성 데이터 처리를 위한 RabbitMQ Streams 도입, 쿠폰 상태 변경 이력 관리 고도화를 위한 Event Sourcing 패턴 적용, 머신러닝을 활용한 트래픽 예측 및 자동 스케일링 등을 검토하고 있습니다.
💡 결론과 인사이트
이번 RabbitMQ 장애를 통해 대용량 메시징 시스템 운영의 복잡성과 중요성을 다시 한 번 깊이 깨달았습니다. 특히 단순한 기술적 해결책을 넘어선 구조적 개선의 필요성과 예방적 접근의 중요성을 명확하게 확인할 수 있었습니다.
서비스 안정성 지표에서 장애 발생 빈도가 월 2-3회에서 0회로 100% 개선되었고, 평균 복구 시간이 2시간에서 90초로 98.7% 단축되었습니다. 1,500만 건 무장애 처리를 달성했고, 고객 만족도 측면에서도 VoC 건수가 80% 감소(2,800건 → 560건/월)했습니다.
운영 효율성에서도 False Positive 알람이 90% 감소하고, 긴급 대응 인력이 50% 절감되었으며, 정기 점검 시간이 70% 단축되는 성과를 거두었습니다.
핵심 인사이트
메모리 상태와 큐 동기화의 직접적 상관관계를 확인했습니다. RabbitMQ Classic Mirrored Queue 환경에서 메모리 사용량이 임계점에 도달하면 미러 노드 간 동기화 실패 확률이 급격히 증가합니다. 특히 고부하 상황에서 브로커 재시작은 문제를 악화시킬 수 있으므로, 메모리 기반 알람과 예방적 대응이 필수입니다.
AWS MQ 관리형 서비스의 운영적 제약사항도 명확해졌습니다. 관리형 서비스의 편의성과 안정성은 분명한 장점이지만, 브로커 레벨의 세밀한 제어가 제한됩니다. 장애 발생 시 수동 복구 옵션이 제한되므로, 구성 단계에서부터 안정성을 확보하는 것이 중요합니다.
Queue Type 선택의 중요성도 재확인했습니다. Classic Mirrored Queue는 단순하고 메모리 효율적이지만 동기화 실패에 취약합니다. Quorum Queue는 메모리 사용량이 다소 높지만 Raft 합의 알고리즘을 통한 강력한 내결함성을 제공합니다. 비즈니스 중요도와 트래픽 패턴을 고려한 신중한 선택이 필요합니다.
대용량 메시징 시스템 운영 가이드라인
사전 설계 단계에서는 트래픽 패턴과 안정성 요구사항을 고려한 적절한 Queue Type 선택, 평상시 사용량의 2-3배 메모리 할당을 통한 메모리 여유도 확보, Single Point of Failure 제거를 위한 브로커 이중화 구성, 예방적 알람과 자동 대응 로직을 포함한 모니터링 체계 구축이 필요합니다.
운영 단계에서는 대량 처리 시 단계적 부하 적용을 통한 점진적 부하 증가, 메모리, CPU, 네트워크 사용률 추적을 위한 실시간 성능 모니터링, 운영 환경과 동일한 조건의 부하 테스트를 통한 정기적 성능 테스트, 정기적인 DR(Disaster Recovery) 테스트 실시를 통한 장애 대응 훈련이 중요합니다.
장애 대응 단계에서는 임시 해결책보다 원인 규명에 집중하는 근본 원인 분석 우선, 개발 환경에서의 정확한 재현으로 해결책을 검증하는 체계적 재현 시도, AWS TAM, 커뮤니티 등 외부 전문 리소스를 적극 활용하는 전문가 협업, 단순 복구를 넘어선 근본적 개선 방안 수립이 필요합니다.
바람 잘 날 없는 쿠폰 도메인이지만... 🤯 이런 어려움들이 더 견고한 시스템을 만드는 밑거름이 되고 있습니다. 모든 커머스의 쿠폰 담당자분들께 진심으로 리스펙을 보내며, 올리브영 쿠폰팀은 앞으로도 더욱 안정적이고 효율적인 시스템 구축을 위해 지속적으로 노력하겠습니다.
⚙️ 올영 쿠폰 개선 일지
🗓️ 관련 시리즈 예고
- "올리브영 쿠폰 시스템 아키텍처 Deep Dive" (언젠가 다시 만나겠습니다..🥲)
긴 글 읽어주셔서 감사합니다! 궁금한 점이나 경험 공유는 언제든 환영합니다. 💪


