MSA 환경에서 하나의 사용자 요청은 여러 서비스에 걸쳐 수행된다.
주문 -> 재고 -> 결제 -> 배송처럼 독립된 서비스들이 순차적으로 연결되며 전체 프로세스가 완성된다.
문제는, 이 과정이 하나의 트랜잭션처럼 보이지만 실제로는 분산된 여러 로컬 트랜잭션으로 구성된다는 점이다.
이때 전통적인 ACID 트랜잭션으로는 데이터 일관성을 유지할 수 없기 때문에 등장한 해결책이 SAGA 패턴이다.
Choreography 방식이란?
이러한 SAGA를 구현하는 방식은 두 가지가 있다.
- 오케스트레이션(Orchestration): 중앙 조정자가 전체 흐름을 통제
- 코레오그래피(Choreography): 중앙 조정자 없이 이벤트 기반으로 처리
Choreography 방식은 서비스들이 이벤트를 통해 "너 다음!" 식으로 자연스럽게 동작이 이어지는 구조이다.

OrderCreated → InventoryReserved → PaymentCompleted
특징은 다음과 같다.
- 어떤 서비스도 전체의 “지휘자(중앙 조정자)”가 아니다
- 각 서비스는 자신이 구독하는 이벤트를 보고 반응한다
- 처리 후 다음 서비스가 필요한 이벤트를 발행한다
구조가 단순해지고 서비스 간 결합도가 낮아지는 장점이 있다.
Choreography 기반 주문 처리의 핵심 특징 정리
성공 시나리오: Choreography 주문 처리 흐름
아래 시퀀스 다이어그램은 Choreography 방식이 실제로 어떻게 동작하는지 잘 보여준다.
핵심은 각 마이크로서비스가 서로를 호출하지 않고, 오직 “이벤트”를 통해서만 연결된다는 점이다.

각 마이크로서비스는 자신의 로컬 트랜잭션만 책임진다
Choreography에서는 주문 서비스, 재고 서비스, 결제 서비스, 배송 서비스가 각자의 로컬 트랜잭션을 독립적으로 실행한다.
- 주문 서비스는 주문을 생성하고 커밋한 뒤 OrderCreated 이벤트 발행
- 재고 서비스는 재고 예약 로컬 트랜잭션을 커밋한 뒤 InventoryReserved 이벤트 발행
- 결제 서비스는 결제를 시도하고 성공 시 PaymentCompleted 발행
- 배송 서비스는 배송 정보를 생성하고 ShippingReady 발행
이때, 각 서비스는 다른 서비스의 상태를 전혀 신경 쓰지 않고, 오직 “내 로컬 트랜잭션이 성공했는가?” 만 판단한다.
이벤트는 로컬 트랜잭션이 완료된 이후에만 발행된다
Choreography 패턴에서 가장 중요한 원칙은 다음과 같다.
이벤트는 도메인의 상태가 DB에 정상적으로 반영된 후에만 발행해야 한다.
즉, “재고 예약이 성공했으면, 예약 상태를 DB에 커밋한 뒤 → InventoryReserved 이벤트를 발행”
이 순서를 지키지 않으면 SAGA 전체 흐름이 무너진다.
이 원칙을 안전하게 지키기 위해 실무에서는 Outbox 패턴을 활용한다.
(트랜잭션 내에서 outbox 테이블에 이벤트를 기록하고, 별도 프로세스가 이를 브로커로 발행)
Choreography는 완전한 느슨한 결합(loose coupling)을 제공한다
이벤트를 발행한 서비스는 다른 서비스가 어떻게 반응하는지 기다리지 않는다.
- 주문 서비스는 재고 서비스가 성공/실패했는지 모른다
- 재고 서비스는 결제 서비스의 상태를 모른다
- 결제 서비스는 배송 서비스가 정상 처리하는지 모른다
각 서비스는 자신의 이벤트만 발행하고 끝이며, 이로 인해 얻을 수 있는 장점은 다음과 같다.
- 서비스 간 직접 호출이 없어 장애 전파가 적다
- 서비스 수가 늘어나도 구조가 단순하다
- 특정 서비스가 느려도 전체 흐름이 블로킹되지 않는다
Message Broker의 “at-least-once” 전달 특성
Kafka, RabbitMQ 같은 브로커는 메시지 최소 한 번(at-least-once) 전달을 보장한다.
이것은 이벤트가 정상적으로 전달될 수 있으며, 네트워크 문제로 두 번 이상 중복 전달될 수 있음을 의미한다.
- InventoryReserved 이벤트가 재고 서비스에서 여러 번 발행될 수 있음
- PaymentCompleted 이벤트가 결제 서비스에서 두 번 이상 전달될 수도 있음
따라서 Choreography 방식에서는 모든 이벤트 처리 로직이 멱등(idempotent)해야 한다.
https://yenjjun187.tistory.com/1139
SAGA 패턴과 멱등성 – 분산 트랜잭션에서 꼭 짚고 넘어가야 할 관계
MSA 환경에서 트랜잭션을 다루다 보면 반드시 마주치는 키워드 두 개가 있다.분산 트랜잭션을 “비즈니스적으로” 풀어내기 위한 SAGA 패턴같은 작업이 여러 번 실행되어도 안전하게 만들기 위한
yenjjun187.tistory.com
실패 시나리오: 보상 트랜잭션 Choreography 흐름
Choreography 방식의 SAGA에서는 각 단계가 이벤트를 통해 연결되고, 어떤 서비스에서 오류가 발생하더라도 직전 단계의 상태를 되돌릴 수 있어야 한다.
아래 시퀀스 다이어그램은 “결제 실패(PaymentFailed)” 상황에서 각 서비스가 어떻게 보상 트랜잭션을 수행하는지를 보여준다.

결제 실패 이벤트 발생
결제 시점에 외부 결제 시스템 장애, 카드 승인 실패 등으로 인해 결제 서비스가 결제를 완료하지 못하면 다음 단계가 수행된다.
- 결제 서비스는 로컬 트랜잭션에서 실패 처리
- 그 상태를 반영한 뒤 PaymentFailed 이벤트 발행
Choreography 방식에서는 실패 정보를 중앙에서 취합하지 않는다.
각 서비스가 “나는 실패했다”는 이벤트를 직접 브로커(MQ)에 알릴 뿐이다.
보상 트랜잭션(Compensation) 시작 — 재고 서비스
PaymentFailed 이벤트가 메시지 브로커를 통해 재고 서비스로 전달되면, 재고 서비스는 다음 작업을 수행한다.
- 예약 테이블에서 해당 주문의 예약 레코드를 조회
- 재고 수량을 원상태로 복구 (예약된 수량만큼 다시 증가)
- 예약 상태를 CANCELED 로 변경
- 보상 이벤트인 InventoryReleased 발행
이 흐름이 바로 SAGA 패턴에서 말하는 보상 트랜잭션(rollback logic)이며, 이며, 초기에 재고를 실차감하지 않았기 때문에 복구도 매우 단순하다.
주문 서비스는 결제 실패 이벤트로 주문 상태를 취소로 변경
보상 과정이 끝나면 InventoryReleased 이벤트가 다시 주문 서비스로 전달된다.
주문 서비스는
- 주문 상태를 취소 로 변경
- 취소 사유(예: 결제 실패)를 함께 저장
- 최종적으로 “해당 주문은 실패 처리되었다”는 정보를 사용자에게 전달
이로써 하나의 SAGA 사이클이 마무리된다.
재고 서비스에서 ‘예약(Reservation)’을 사용하는 이유
Choreography 기반 SAGA에서는 재고를 바로 차감하지 않고, 먼저 “예약(Reservation)”을 생성한다.
그 이유는 아주 단순하다.
- ❗ 결제나 배송 등 뒤 단계에서 실패가 발생할 수 있기 때문에
- ❗ 재고를 초기에 실차감하면 “원상 복구(보상)”가 매우 어렵기 때문이다.
즉, 예약은 재고의 소프트 락(Soft Lock) 역할이며, 결제가 최종 성공할 때만 실제 재고를 차감한다.
예약이란?
예약은 “이 재고는 주문 X가 사용할 예정이니, 다른 사람이 못 쓰게 잠시 잡아두겠다”는 의미이며, 예약을 생성해도 실제 재고(stock)는 줄어들지 않는다.
대신 상태만 다음과 같이 관리된다.
| stock | 실제 창고 재고 (변하지 않음) |
| reserved | 예약된 재고 (결제 대기 중) |
| available | 실제로 판매 가능한 수량 |
예약 가능 여부 판단
예약이 가능한지 여부는 다음 공식 하나로 판단한다.
available = 총 재고(stock) − 취소되지 않은 예약(reserved)
예약 가능? → available >= 주문 수량
예시
- 총 재고: 10
- 예약(RESEVED): 3
- 예약(CANCELED): 1
available = 10 − 3 = 7
사용자가 2개 주문 → 예약 가능
사용자가 8개 주문 → 예약 불가능
예약 후 결제가 성공하는 경우
- 재고 서비스: 예약 생성
- 결제 서비스: 결제 성공
- 재고 서비스: 실제 재고(stock)를 차감하고 예약 상태를 CONFIRMED로 변경
stock = stock − reserved_qty
👉 이 순간에만 재고가 실제로 줄어든다.
결제가 실패하면?
PaymentFailed 이벤트가 재고 서비스로 전달되면
- 예약 상태를 CANCELED 로 변경
- stock은 건드리지 않음
- 즉시 재고 복구 완료
왜 이렇게 처리해야 하나?
전통 방식(주문 초기에 재고 실차감)과 비교해보면 이유가 선명해진다.
주문 초기 재고 실차감 방식의 문제
- 결제 실패 시 stock +1 복구해야 함
- 복구 시점에 다른 주문이 이미 재고를 가져갔을 수 있음
- 동시성 문제가 심각해지고 되돌리기가 어렵다
예약 기반 방식의 장점
- 실재고는 결제 성공 시점에만 차감
- 결제 실패 시 재고는 이미 그대로 → 예약만 취소하면 끝
- 보상 트랜잭션이 압도적으로 단순해짐
- 중간 상태(예약)를 명확하게 관리할 수 있음
- 동시성 제어가 훨씬 쉬워짐
https://yenjjun187.tistory.com/manage/newpost/1140
티스토리
좀 아는 블로거들의 유용한 이야기, 티스토리. 블로그, 포트폴리오, 웹사이트까지 티스토리에서 나를 표현해 보세요.
www.tistory.com
Choreography 환경에서 보상 트랜잭션이 중요한 이유
결제 실패 시 보상 트랜잭션이 자연스럽게 이어질 수 있는 이유는 다음 두 가지 원칙 덕분이다.
각 서비스는 자신의 상태만 책임지고 반영한다
주문 서비스는 주문 상태만, 재고 서비스는 재고와 예약 상태만 관리한다.
다른 서비스의 상태를 직접 변경하거나 기다릴 필요가 없다.
이벤트 기반 비동기 흐름
서로 직접 호출하지 않기 때문에 결제 실패 시에도 전체 시스템이 블로킹되지 않는다.
Choreography 방식의 장단점
Choreography의 장점
1. 완전한 느슨한 결합(Loose Coupling)
Choreography의 가장 큰 장점이다.

- 각 서비스는 다른 서비스의 상태나 응답을 알 필요 없이, 오직 이벤트만 구독하고 처리할 뿐이다.
- 어떤 서비스가 다운되어도, 메시지 브로커(MQ)에 기록된 이벤트는 누락되지 않으며, 복구 후 그대로 이어서 처리할 수 있다.
- 신규 도메인이나 기능을 추가하는 것이 매우 쉽다: 새로운 서비스가 이벤트를 구독하기만 하면 기존 시스템을 건드릴 필요가 없다.
- 개방-폐쇄 원칙(OCP)에 매우 적합한 구조.
2. 단순한 구조와 뛰어난 확장성
- 중앙 조정자(Orchestrator)를 두지 않기 때문에 단일 장애점(SPOF) 이 없다.
- 이벤트 브로커만 확장하면 대규모 트래픽도 자연스럽게 분산된다.
- 특정 서비스가 확장되어도 다른 서비스들과 직접 연결되어 있지 않으므로 종속적 확장이 필요 없다.
3. 병목(Bottleneck) 발생 가능성이 적다
각 서비스는 독립적인 로컬 트랜잭션을 처리하고 다음 이벤트를 발행하면 즉시 작업을 끝낸다.
즉
- 서비스 간 호출 지연 없음
- 동기식 HTTP 호출로 인한 병목 없음
- 병렬 처리 능력이 자연스럽게 확보됨
이벤트 처리량이 매우 높은 서비스에서도 안정적인 동작이 가능하다.
4. 서비스별 독립 확장 가능
주문량이 증가하면 주문 서비스만 확장하면 되고, 결제가 몰리면 결제 서비스만 스케일 아웃하면 된다.
서비스들끼리 직접 의존하지 않기 때문에 도메인별 독립 확장(Independent Scaling) 이 가능한 것이 큰 장점이다.
Choreography의 단점
1. 시스템이 복잡해지기 쉽다
Choreography는 구조는 단순하지만, 서비스 수가 많아질수록 비즈니스 로직이 이벤트 흐름에 흩어져 복잡해진다.

- 어디서 어떤 이벤트가 발행되는지 추적하기 어렵고
- 장애 시 어떤 순서로 이벤트가 처리되었는지 파악하기 어렵다
- 분산 시스템 전체 상태 파악이 쉽지 않다
특히 주문, 재고, 결제, 쿠폰, 포인트 등 여러 도메인이 얽힌 시나리오에서는 이벤트 흐름이 쉽게 “스파게티 구조”가 될 수 있다.
2. 이벤트 순환 의존성 위험(이벤트 루프)
이벤트 간 의존성을 잘못 설계하면
서비스 A → 서비스 B → 서비스 A 형태의 무한 루프가 발생할 수 있다.
예시
OrderUpdated → InventoryAdjusted → OrderUpdated → InventoryAdjusted → ...
그래서 Choreography에서는 “어떤 이벤트가 누구를 트리거할 것인가?”를 매우 신중하게 설계해야 한다.
3. 전체 트랜잭션 상태 추적이 어렵다
각 서비스는 자기 로컬 트랜잭션 결과만 알고, 다른 서비스의 진행 상태를 모른다.
그 결과
- 특정 주문이 지금 어느 단계에 있는가?
- 재고는 예약되었는데 결제는 진행 중인가?
- 보상 트랜잭션이 필요한 상태인가?
같은 질문에 답하기 위해선 추가적인 모니터링 시스템이 필요하다.
게다가 상태 파악 도중 다른 서비스에서 장애가 발생하면 보상 트랜잭션이 연쇄적으로 발생할 수도 있다.
'아키텍처 & 설계(Architecture & Design) > MSA' 카테고리의 다른 글
| SAGA: Orchestration vs Choreography 비교 (1) | 2025.11.28 |
|---|---|
| Orchestration 기반 SAGA 패턴 (0) | 2025.11.23 |
| SAGA 패턴과 에서의 격리성(Isolation) 문제 (0) | 2025.11.10 |
| SAGA 패턴과 멱등성 – 분산 트랜잭션에서 꼭 짚고 넘어가야 할 관계 (0) | 2025.11.10 |
| 분산 트랜잭션의 한계와 SAGA 패턴 (0) | 2025.11.07 |