AI VIDEO BRIEFING
메시징 패턴 정리 - 아웃박스, 사가, 경쟁 소비자, 백프레셔로 분산 시스템 살리기
발행-구독만으로 만든 분산 시스템이 운영에서 갈라지는 지점을 짚고, 트랜잭셔널 아웃박스와 사가, 경쟁 소비자, 백프레셔와 에러 큐 관리까지 실전 메시징 패턴을 정리한 NDC 코펜하겐 2026 강연을 소개한다.

핵심 메시지
쉽게 이해하기
덴마크 코펜하겐에서 열린 NDC 콘퍼런스에서 파티큘러 소프트웨어의 소프트웨어 엔지니어 겸 아키텍트 푸르니마 나야르가 발행-구독 이후의 메시징 패턴을 다뤘다. 모놀리스나 HTTP 기반 서비스에서 메시징으로 넘어오는 팀이 가장 먼저 잡는 것이 발행-구독이다. 발행자와 구독자가 시간과 공간 모두에서 분리되고, 구독자가 내려가 있어도 발행자는 계속 동작한다.
여기서 오가는 메시지를 이벤트라고 부르는데, 이벤트는 이미 일어난 일을 세상에 알리는 것이며 따라서 불변인 사실이다. 절대 지시여서는 안 된다. 발표자는 지시를 이벤트로 위장한 경우를 수동공격적 이벤트라 부르며 안티패턴으로 못 박는다. 가방이 마음에 든다고 말하는 것은 사실이지만, 그 말에 가게에 들어가 사오라는 지시를 숨겨 두었다면 그것은 더 이상 사실 통보가 아니라는 비유다.
문제는 팀이 발행-구독에서 만족하고 멈출 때 생긴다. 주문 서비스가 이벤트를 항상 정확히 한 번 발행한다는 보장은 없고, 모든 구독자가 동시에 받는다는 보장도 없다. 이벤트가 순서대로 도착한다는 가정은 특히 큰 착각이다. 네트워크는 신뢰할 수 없고 대역폭은 유한하며 지연은 늘 존재한다. 재시도는 부작용을 중복시킬 수 있고, 주문이 지금 어디까지 왔는지 누구도 확답하지 못한다. 로열티 서비스 하나를 새로 붙이려면 나머지 서비스 대부분이 함께 바뀌어야 하는 지경이 되면 결국 더 나쁜 분산 모놀리스로 되돌아온 셈이다.
첫 번째 처방은 트랜잭셔널 아웃박스다. 분산 시스템의 데이터 손실은 대개 브로커나 데이터베이스 안이 아니라 그 둘의 경계에서 일어난다. 결제를 저장한 뒤 이벤트를 발행하려는 순간 네트워크가 끊기면 아무도 모르는 좀비 레코드가 남고, 순서를 바꾸면 결제되지 않았는데 결제됐다고 전체 시스템에 알린 꼴이 된다. 아웃박스는 업무 데이터와 보낼 메시지를 같은 데이터베이스의 단일 트랜잭션으로 저장한 뒤, 백그라운드 인프라가 실제 발송을 맡는 방식이다. 브로커가 최소 한 번 전달을 보장하는 이상 수신 측 멱등성과 중복 제거가 함께 필요하다.
두 번째는 사가다. 주문의 수명은 밀리초로 끝나지 않는다. 물리적 배송이라면 며칠이나 몇 주가 걸린다. 본질적으로 비동기인 흐름을 동기로 밀어 넣을 이유가 없다는 것이다. 사가는 상태를 가진 메시지 핸들러로, 매 단계마다 무엇을 했고 무엇이 남았는지를 영속 저장소에 기록한다. 메시지가 어떤 순서로 도착하든 경로를 바로잡아 주문 완료나 주문 취소 같은 최종 상태에 도달하게 만든다. 다만 사가 안에 비즈니스 로직이 들어가면 그 자체가 모놀리스가 되므로, 사가는 결정만 내리고 커맨드를 보내는 데 그쳐야 한다.
분산 트랜잭션을 쓰지 않는 이유도 분명하다. 원자성과 강한 일관성을 주지만 강결합과 빠른 네트워크, 단일 팀 통제라는 전제를 깔고 있는데 이는 분산 시스템이 아니라 모놀리스의 특징이다. 게다가 비즈니스 현실과 맞지 않는다. 환불은 주문의 롤백이 아니라 환불이라는 비즈니스 결정이고, 재고 백오더도 마찬가지다. 코디네이터와 시간이라는 두 개의 단일 장애점도 남는다.
운영 측면에서는 경쟁 소비자 패턴과 백프레셔가 다뤄진다. 같은 큐를 여러 소비자가 바라보며 각 메시지를 한 소비자만 처리하는 구조로 처리량을 끌어올리되, 순서 보장은 없고 작업 간 의존이 있으면 쓰면 안 된다. 처리량을 올리면 그 압력이 하류로 넘어가므로 소비자 스로틀링, 레이트 리미팅, 서킷 브레이커, 벌크헤드, 지터를 넣은 재시도 같은 저항 장치가 필요하다. 발표자는 영국의 스마트 모터웨이를 예로 든다. 혼잡할 때 제한 속도를 낮추면 느려지지만 사고가 줄어 차선이 막히지 않고, 결국 전체는 계속 흐른다.
재시도는 시간이 문제를 고치게 두는 장치로 정리된다. 데이터베이스 락이나 순간적인 네트워크 끊김에는 즉시 재시도, 서비스 재시작이나 페일오버처럼 조금 더 시간이 필요한 경우에는 고정 간격이나 지수 백오프를 쓰는 지연 재시도, 버그나 배포 실패처럼 시스템 자체의 오류에는 에러 큐 이동이 맞는다. 다만 한 번에 수만 건을 몰아서 재시도하면 자기 시스템을 스스로 마비시키는 재시도 폭풍이 되므로 상한과 지터가 필수다.
주요 인사이트
- 발행-구독이 주는 것은 시간적 결합 해제 하나뿐이다. 충돌 시 데이터 보존, 소비자 실패 처리, 순서 뒤바뀜, 일관성 보장, 보상 액션 중 어느 것도 발행-구독은 답하지 않는다.
- 오케스트레이션과 코레오그래피는 양자택일이 아니라 공존한다. 판단 기준은 보상 액션의 양이다. 되돌려야 할 결정이 많고 최종 상태를 소유한 서비스가 모호하면 오케스트레이터가 필요하다.
- 보상 액션도 1급 워크플로 상태로 다뤄야 한다. 환불 역시 실패할 수 있기 때문이다.
- 발행-구독과 경쟁 소비자는 다른 질문에 답한다. 전자는 누가 이 사실을 알고 싶은가, 후자는 누가 이 일을 해야 하는가다. 둘을 함께 쓰려면 토픽 뒤에 구독 큐가 있는 브로커가 필요하다.
- 큐 깊이는 시스템 건강의 초기 경고 신호다. 큐가 무한히 늘어난다는 것은 시스템이 압력을 못 견디고 있다는 뜻이다.
- 패턴은 한꺼번에 적용하지 말고 점진적으로 도입해야 하며, 존재 이유를 설명할 수 없는 패턴은 제거하는 편이 낫다.
자주 묻는 질문
정확히 한 번 처리는 정말 불가능한가요?
발표에서는 착시라고 설명합니다. 실제로 일어나는 일은 브로커의 최소 한 번 전달에 멱등성을 더한 것이며, 그 조합이 정확히 한 번처럼 보이게 만들 뿐입니다. 아웃박스 자체는 발행자를 신뢰할 수 있게 만들고 멱등성의 필요를 강제할 뿐, 정확히 한 번 전달을 보장하지 않습니다.
트랜잭셔널 브로커를 쓰면 아웃박스가 필요 없나요?
아닙니다. 애저 서비스 버스 같은 트랜잭셔널 브로커는 수신 메시지와 송신을 하나의 트랜잭션으로 묶을 수 있지만, 여러분의 업무 데이터까지 그 트랜잭션에 넣지는 못합니다. 업무 데이터와 메시지의 일관성을 위해서는 아웃박스가 필요합니다.
오케스트레이션이 필요한지 어떻게 판단하나요?
너무 많은 서비스가 너무 많은 이벤트에 반응하는지, 최종 상태가 모호해 어느 서비스도 그것을 소유하지 못하는지, 보상 로직이 여러 서비스에 얇게 흩어져 있는지, 순서가 뒤바뀐 이벤트가 시스템을 뒤엎는지, 타임아웃이 필요한지를 점검하라고 제시합니다. 이벤트 하나를 재생했을 때 여러 서비스에서 예상치 못한 반응이 일어난다면 이미 무언가 잘못된 것입니다.
메시징을 도입할 때 가장 먼저 해야 할 일은 무엇인가요?
에러 큐와 에러 큐 모니터링, 그리고 아웃박스와 복구 가능성입니다. 발표자는 에러 큐 관리가 없으면 메시징을 쓰는 시스템은 설계상 미완성이라고 못 박습니다. 실패한 메시지가 에러 큐에 앉아 만료되는데 재생할 방법이 없는 상황을 피해야 하기 때문입니다.
원문과 출처
이 글은 원본 영상의 자막을 바탕으로 한국어 독자를 위해 요약했습니다. 전체 맥락과 최신 정보는 원문에서 확인하세요.
YouTube 원본 영상 보기 ↗