312. 사가 (Saga) 패턴의 코레오그래피 (Choreography) vs 오케스트레이션 (Orchestration)
핵심 인사이트 (3줄 요약)
- 본질: 사가(Saga) 패턴은 마이크로서비스(MSA)에서 불가능해진 분산 트랜잭션(2PC)의 문제를 해결하기 위해, 거대한 트랜잭션을 여러 개의 작고 독립적인 로컬 트랜잭션의 연속적인 릴레이로 쪼개어 '결과적 일관성(Eventual Consistency)'을 맞추는 아키텍처 패턴이다.
- 가치: 중간에 한 서비스가 에러를 뱉어 트랜잭션이 실패할 경우, 이전에 성공했던 서비스들에게 **"방금 했던 거 다 취소해!"라는 '보상 트랜잭션(Compensating Transaction)'**을 역순으로 발행하여 더럽혀진 데이터를 무결하게 롤백(Rollback)시키는 마법을 부린다.
- 융합: 이 연속적인 릴레이를 통제하는 지휘 체계에 따라, 중앙 지휘자가 멱살을 잡고 명령하는 오케스트레이션(Orchestration) 방식과, 지휘자 없이 각자가 이벤트를 듣고 눈치껏 다음 행동을 이어가는 코레오그래피(Choreography) 방식으로 완벽히 양분된다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념: "Saga"는 본래 긴 서사시나 연대기를 뜻한다. 한 방에 짧게 끝나는 DB 트랜잭션(
BEGIN-COMMIT)을 쓰지 못하니, 긴 시간을 들여 여러 서비스의 로컬 DB를 순서대로 변경해 나가는 긴 여정(Saga)을 의미한다. -
필요성: [1. 고객의 돈을 차감한다] -> [2. 재고를 줄인다] -> [3. 배달을 요청한다]. 3번 배달 요청을 보내려다 에러가 났다. 모놀리식 시절에는 DB가 하나라서
Rollback한 줄 치면 1, 2번 데이터가 마법처럼 되돌아갔다. 하지만 MSA 환경에서는결제 DB와재고 DB가 물리적으로 남남이다. 이미 커밋(Commit)되어 버린 남의 집 DB를 어떻게 되돌릴 것인가? 누군가가 결제 서버에 "아까 뺀 돈 다시 더해놔(+)", 재고 서버에 "아까 뺀 재고 다시 더해놔(+)"라고 **역방향 API(보상 트랜잭션)**를 날려주어야만 시스템이 망가지지 않는다. -
💡 비유: 호텔 방, 비행기, 렌터카를 세 군데 예약 사이트에서 차례대로 예약하는 것과 같습니다. 호텔과 비행기는 성공했는데, 마지막 렌터카 예약이 매진으로 취소됐습니다. 이때 시스템이 알아서 호텔에 "아까 예약한 거 취소해주세요", 비행기에 "아까 끊은 표 환불해주세요"라고 차례대로 역주문을 넣어주는 구조가 바로 Saga 패턴의 보상 트랜잭션입니다.
-
등장 배경 및 발전 과정:
- 2PC(Two-Phase Commit)의 한계: 초기 분산 DB 환경에선 중앙 코디네이터가 모든 DB에게 "커밋할 준비 됐어?" 묻고, 100% "네" 할 때까지 모든 시스템에 락(Lock)을 걸어두는 2PC를 썼다. 마이크로서비스 수십 개가 얽히자 이 락(Lock) 때문에 시스템이 극도로 느려져 폐기되었다.
- 1987년 Hector Garcia-Molina의 Saga 논문: 긴 수명의 트랜잭션을 다루기 위해 하위 트랜잭션과 보상(취소) 트랜잭션의 쌍을 제안한 고전 논문이 발굴되었다.
- MSA 트랜잭션의 구원자로 등극: 클라우드 시대에 데이터베이스 퍼 서비스(DB per Service) 패턴이 강제되면서, NoSQL이나 다양한 이기종 DB를 느슨하게 묶어 트랜잭션을 일치시키는 유일한 탈출구로 사가 패턴이 부활했다.
-
📢 섹션 요약 비유: 옛날엔 3명의 친구가 동시에 점프해야만 성공하는 '줄넘기(2PC)'였다면, 사가 패턴은 1번 친구 뛰고, 2번 뛰고, 3번 뛰는 '징검다리 릴레이'입니다. 중간에 3번이 넘어지면 2번, 1번 친구가 뒤로 한 칸씩 다시 물러서서(보상) 처음 상태로 예쁘게 되돌려 놓는 영리한 룰입니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
사가 패턴의 릴레이를 통제하는 두 가지 거대한 아키텍처 진영이 있다. 중앙 집권(Orchestration)이냐, 각자도생(Choreography)이냐의 차이다.
1. 오케스트레이션 (Orchestration) : "명령형 지휘"
중앙에 **'사가 오케스트레이터(Saga Orchestrator)'**라는 강력한 컨트롤 타워 객체를 둔다. 오케스트레이터가 순서대로 각 마이크로서비스에게 API를 호출하여 "너 이거 해, 저거 해"라고 멱살을 잡고 명령(Command)한다.
┌─────────────────────────────────────────────────────────────┐
│ Saga Orchestration (오케스트레이션) 작동 흐름 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [ 1. 주문 생성 요청 ] │
│ │ │
│ ▼ (명령) ┌─────────┐ │
│ ┌───────────────────┐ ── 2 ─▶ │ 결제 MSA │ (결제 승인) │
│ │ Saga Orchestrator │ ◀─ 3 ── └─────────┘ │
│ │ (주문 서비스 내부) │ │
│ │ │ ── 4 ─▶ ┌─────────┐ │
│ │ [상태 기계] │ ◀─ 5 ── │ 재고 MSA │ (재고 깎음) │
│ │ - 결제됨 │ └─────────┘ │
│ │ - 재고깎음 │ ── 6 ─▶ ┌─────────┐ (에러 발생!!) │
│ │ - 배송실패 (롤백!) │ ◀─ 7 ── │ 배달 MSA │ ◀── X_X │
│ └───────────────────┘ └─────────┘ │
│ │ │
│ ▼ (오케스트레이터가 보상 명령 발동) │
│ 8. 재고 MSA에 "재고 롤백해(+)" 명령 전송 │
│ 9. 결제 MSA에 "결제 환불해(+)" 명령 전송 │
└─────────────────────────────────────────────────────────────┘
- 장점: 전체 트랜잭션의 흐름(상태)을 중앙 지휘자(Orchestrator) 한 놈만 보면 파악할 수 있어 코드가 직관적이고 디버깅이 매우 쉽다. 마이크로서비스들은 서로의 존재를 알 필요가 없다(결합도 낮음).
- 단점: 중앙 지휘자가 모든 비즈니스 로직(순서)을 알게 되어 **스마트한 파이프(Smart Pipe)와 멍청한 엔드포인트(Dumb Endpoints)**라는 안티패턴에 빠지기 쉽다. 지휘자가 SPOF(단일 장애점)가 될 수 있다.
2. 코레오그래피 (Choreography) : "이벤트 주도"
중앙 지휘자가 없다. 각 마이크로서비스는 자기가 할 일만 끝나면 허공(Kafka 등 Event Bus)에 **"나 결제 끝났어(Event)"**라고 방송(Publish)만 하고 끝낸다. 이 방송을 기다리던 다음 서비스가 듣고(Subscribe) 자기 할 일을 시작한다.
┌─────────────────────────────────────────────────────────────┐
│ Saga Choreography (코레오그래피) 작동 흐름 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [ 1. 주문 생성 이벤트 (OrderCreated) ] │
│ │ │
│ ▼ (구독) │
│ ┌─────────────┐ │
│ │ 결제 MSA │ │
│ └──────┬──────┘ │
│ │ 2. 결제 완료 이벤트 발행 │
│ ▼ (BilledEvent) │
│ ┌─────────────┐ │
│ │ 재고 MSA │ │
│ └──────┬──────┘ │
│ │ 3. 재고 감소 실패 이벤트 발행 │
│ ▼ (InventoryFailedEvent) │
│ │
│ ( 역방향 이벤트 수신 시 각자 보상 로직 실행 ) │
│ ┌───────────────────┴─────────────────────┐ │
│ ▼ (실패 이벤트 구독) ▼ (구독) │
│ ┌────────┐ "어? 재고 실패했네?" ┌────────┐ │
│ │ 결제 MSA│ ──▶ [환불 로직 롤백 수행] │ 주문 MSA│ │
│ └────────┘ └────────┘ │
│ "어? 재고 실패네 주문 취소" │
└─────────────────────────────────────────────────────────────┘
-
장점: 완전히 디커플링된 느슨한 구조(Loose Coupling)다. 중앙 병목이 없고 이벤트 큐 기반이므로 스케일 아웃에 압도적으로 강력하다.
-
단점: 트랜잭션 단계가 4단계를 넘어가면, 이벤트들이 핑퐁을 치며 거미줄처럼 얽혀서 도대체 누가 먼저고 누가 롤백을 뱉는지 흐름 파악(디버깅)이 불가능한 재앙에 빠진다. (이를 막기 위해 CQRS나 분산 추적(Zipkin)이 강제된다.)
-
📢 섹션 요약 비유: 오케스트레이션은 '오케스트라 지휘자'가 바이올린에게 켜라, 피아노에게 쳐라 지시하는 것이고, 코레오그래피는 악보 없이 춤을 추는 '비보이 크루(Choreography=안무)'들이 앞 친구가 윈드밀을 끝내는 걸 보고(이벤트) 눈치껏 자기가 튀어나가 헤드스핀을 도는 환상적이지만 복잡한 퍼포먼스입니다.
Ⅲ. 융합 비교 및 다각도 분석
1. 두 패턴의 치명적인 딜레마 (어떤 방식을 선택할 것인가?)
실무에서 아키텍트는 서비스의 크기와 복잡도에 따라 두 진영을 반드시 선택해야 한다.
| 비교 척도 | 코레오그래피 (Choreography) | 오케스트레이션 (Orchestration) |
|---|---|---|
| 트랜잭션 참여자 수 | 2~4개의 적은 수에 적합 (간단할 때) | 4개 이상의 복잡한 스텝에 필수적 |
| 추가 인프라 비용 | Kafka 같은 무거운 메시지 브로커 필수 | 별도의 지휘자 서비스(또는 Camunda 등 툴) 개발 필요 |
| 순환 참조 위험 | 이벤트들이 핑퐁 치며 무한 루프 위험 매우 높음 | 지휘자가 통제하므로 순환 참조 0% |
| 결합도 (Coupling) | 완벽한 디커플링 (최고 장점) | 지휘자가 모든 서비스를 알아야 하는 강결합 발생 (최대 단점) |
최근 엔터프라이즈의 거대 트랜잭션 흐름은 모니터링의 한계로 인해, 결국 오케스트레이션(Orchestration) 진영이 승리하는 추세다. 다만 지휘자가 직접 REST API를 찌르는 대신 지휘자도 Kafka 메시지로 명령을 내리는(Hybrid) 방식으로 진화하고 있다.
과목 융합 관점
-
소프트웨어 공학 (SE): 디자인 패턴 중 '커맨드(Command)' 패턴과 '메디에이터(Mediator)' 패턴이 MSA 트랜잭션으로 스케일 업 된 것이 오케스트레이션이고, '옵저버(Observer)' 패턴이 분산화된 것이 코레오그래피다.
-
데이터베이스 (DB): 보상 트랜잭션(환불, 재고 원복)은 100% 무조건 성공해야 한다. 만약 환불 API를 날렸는데 네트워크 에러가 나면? 사가 패턴에서는 보상 로직이 완료될 때까지 **무한 재시도(Infinite Retry)**해야 한다. 따라서 모든 보상 트랜잭션 API는 100번을 재시도해도 데이터가 1번만 깎이도록 수학적 **멱등성(Idempotency)**을 완벽히 보장하도록 DB 스키마와 락을 설계해야 한다.
-
📢 섹션 요약 비유: 2명이서 여행을 가면 서로 눈빛만 봐도 일정이 돌아가지만(코레오그래피), 10명이서 유럽 여행을 가면 눈치껏 하다가는 다 길을 잃습니다. 깃발을 든 가이드(오케스트레이터)가 멱살을 잡고 "다음은 에펠탑입니다!"라고 통제해야만 대규모 여행(트랜잭션)이 무사히 끝납니다.
Ⅳ. 실무 적용 및 기술사적 판단
실무 시나리오
-
시나리오 — 데이터 불일치(Eventual Consistency)의 혼돈: 오케스트레이션 사가 패턴으로 결제 로직을 구현했다. 사용자가 돈을 지불하고(1단계 성공), 재고 서버가 재고를 깎는 중(2단계 진행 중)이었다. 그런데 그 찰나의 1초 동안, 다른 탭에서 사용자가 새로고침을 누르자 "돈은 빠져나갔는데, 주문 내역에는 아직 안 뜨는" 기괴한 화면이 나타났다. 사용자는 "내 돈 먹튀했다!"라며 소리를 지른다.
- 아키텍트의 해결책: 사가 패턴은 '격리성(Isolation)'을 포기한 아키텍처임을 명심해야 한다. 전통적 DB의
Lock이 없기 때문에, 트랜잭션이 다 끝나기 전의 반쪽짜리 더러운 상태(Dirty Read)가 사용자에게 노출된다. 아키텍트는 1단계 성공 직후 주문 상태를 '주문됨'이 아니라[결제 완료_재고 확인 중 (PENDING)]이라는 과도기적 상태(Semantic Lock)로 명확히 마킹하여 DB에 쓰고, 프론트엔드는 이 PENDING 상태일 때 뺑글뺑글 도는 로딩 스피너 UI를 띄워주어 사용자가 기괴함을 느끼지 않도록(UX 융합) 디자인해야 한다.
- 아키텍트의 해결책: 사가 패턴은 '격리성(Isolation)'을 포기한 아키텍처임을 명심해야 한다. 전통적 DB의
-
시나리오 — 코레오그래피의 이벤트 지옥(Event Hell): 10개의 마이크로서비스가 얽힌 쇼핑몰을 코레오그래피(이벤트 구독)로 짰다. 배송팀 신입이 로직을 잘못 짜서
배송실패이벤트대신결제취소이벤트를 발행했다. 1초 뒤 이 이벤트를 듣고 결제 서버가 환불을 때려버렸고, 유저 서버가 쿠폰을 뱉어냈다. 장애가 터졌는데 수백만 개의 이벤트 로그 속에서 "도대체 어느 미친 서버가 첫 폭탄(이벤트)을 던졌는지" 원인 파악에만 3일이 걸렸다.- 아키텍트의 해결책: 전형적인 코레오그래피의 관찰 가능성(Observability) 붕괴다. 복잡한 트랜잭션에는 코레오그래피를 지양하고 **오케스트레이터(중앙 통제)**를 둬야 했다. 만약 코레오그래피를 썼다면, 최소한 이벤트 발행 시
Correlation ID(고유 추적 번호)를 헤더에 박아서 분산 추적(Zipkin/Jaeger)으로 이 요청이 1번 서버부터 10번 서버까지 어떻게 핑퐁 쳤는지 그 궤적을 1초 만에 시각화해 내는 인프라를 먼저 깔아뒀어야 한다.
- 아키텍트의 해결책: 전형적인 코레오그래피의 관찰 가능성(Observability) 붕괴다. 복잡한 트랜잭션에는 코레오그래피를 지양하고 **오케스트레이터(중앙 통제)**를 둬야 했다. 만약 코레오그래피를 썼다면, 최소한 이벤트 발행 시
도입 체크리스트
- 기술적: 보상 트랜잭션 코드(
cancelPayment())를 짤 때, 이 함수가 여러 번 호출되어도 데이터가 안전한가(Idempotency)? 분산 네트워크에서 취소 메시지 큐가 2번 전송되는 일(At-least-once)은 비일비재하다. 취소 API는 반드시 "이미 환불 처리된 고유 ID입니다"라며 우아하게 중복을 씹어버리는(Ignore) 방어 코드가 세팅되어야 한다. - 경영적: 2PC를 쓰면 데이터는 100% 깔끔하지만 서버 응답 속도가 느리다. 반면 사가 패턴은 응답 속도는 날아가듯 빠르지만 로직 복잡도로 개발 비용이 폭증하고 과도기적 불일치가 생긴다. "빠른 비즈니스(Saga)" vs "완벽한 정합성(2PC/모놀리식)", 이 회사가 어느 가치를 위해 돈을 태울 것인지 아키텍트가 결정을 받아내야 한다.
안티패턴
-
스프링 @Transactional로 MSA 묶기: 결제 서버 개발자가 자바의
@Transactional어노테이션 블록 안에서 HTTP RestTemplate으로 외부 배송 서버 API를 찌르는 대재앙. 밖에서 5초 타임아웃이 나면, 내 서버의 DB 커넥션 1개가 5초 동안 허공에 물린 채로 서버가 터져나간다. 분산 환경에선 로컬 트랜잭션 안에서 절대로 외부 I/O 통신을 섞어 짜면 안 된다. -
📢 섹션 요약 비유: 사가 패턴은 이사 갈 때 '일단 짐을 다 싸서 트럭에 넣고(로컬 커밋), 다음 집 문이 안 열리면 짐을 다시 우리 집에 풀어놓는(보상 트랜잭션)' 짓입니다. 이사 내내 집 안이 난장판(격리성 파괴)이 되는 꼴을 견뎌야 하지만, 결국에는 깔끔하게 정리가 완료(결과적 일관성)된다는 믿음으로 버티는 전략입니다.
Ⅴ. 기대효과 및 결론
정량/정성 기대효과
| 구분 | 분산 환경에서 기존 2PC(동기 락) 사용 시 | 사가(Saga) 패턴 적용 시 (비동기) | 개선 효과 |
|---|---|---|---|
| 정량 | 여러 시스템의 락(Lock) 대기로 인한 응답 3초 | 로컬 커밋 후 즉각 응답(0.1초) 및 비동기 처리 | 트랜잭션 지연 시간 95% 이상 극단적 단축 (TPS 극대화) |
| 정량 | 1개 DB 장애 시 타 서버 스레드 전멸 (가동률 급락) | 장애 시 해당 보상 이벤트만 큐에 쌓여 대기 | 단일 시스템 장애 파급률(Cascading Failure) 제로화 |
| 정성 | DB 스키마가 다른 NoSQL 등과는 트랜잭션 불가 | 각자 로컬 DB만 통제하면 되므로 이기종 연동 자유 | 폴리글랏 퍼시스턴스(Polyglot Persistence)의 완벽한 실현 |
미래 전망
- 오케스트레이션 프레임워크의 표준화: 개발자가 보상 트랜잭션
try-catch로직을 짜다 지치는 것을 막기 위해, Uber의 Cadence, Zeebe, AWS Step Functions 같은 '워크플로우 오케스트레이션 특화 엔진'들이 대거 등장했다. 이제는 GUI 환경에서 1단계, 2단계 흐름과 실패 시 화살표(보상)를 드래그 앤 드롭으로 그리면, 인프라가 사가 패턴을 100% 자동 대행해 주는 시대로 진입했다. - Microservices의 환상 붕괴와 타협: MSA로 다 쪼개고 나니 사가 패턴을 유지하는 개발 유지보수 비용(보상 트랜잭션 코드 작성)이 회사를 파산시킬 지경에 이른 기업들이 속출하고 있다. 다시 "강한 트랜잭션이 묶이는 도메인(주문-결제)은 억지로 찢지 말고 하나의 덩어리(모듈러 모놀리식)로 다시 합치자"는 반성(Pragmatism)의 회귀 현상이 최신 아키텍처의 트렌드다.
참고 표준
- Microservices Patterns (Chris Richardson): 사가 패턴의 코레오그래피와 오케스트레이션, 보상 트랜잭션 개념을 실무적/수학적으로 완벽히 정립한 MSA의 절대 바이블.
- Eventual Consistency (결과적 일관성): CAP 정리에서 일관성(C)을 100% 보장하는 대신, 가용성(A)을 취하면서 데이터가 '결국에는 동기화됨'을 믿는 현대 분산 시스템의 이론적 기반.
사가(Saga) 패턴은 마이크로서비스라는 멋진 신대륙으로 넘어온 개발자들이 반드시 치러야 하는 **'피 묻은 영수증(비용)'**이다. 쪼개진 자유(Database per service)를 얻은 대가로, 우리는 잃어버린 트랜잭션을 땀과 코드로 직접 기워 맞춰야 한다. 기술사는 마법 같은 해법에 현혹되지 않고, "이 도메인을 쪼갤 때 발생하는 보상 트랜잭션 코드 수천 줄의 개발 비용을 우리가 감당할 수 있는가?"를 가장 뼈아프게 계산하여 분리(Decoupling)의 칼을 내려쳐야 하는 고독한 재무 설계사여야 한다.
- 📢 섹션 요약 비유: 사가 패턴은 식당에서 손님이 짜장면(주문), 탕수육(결제), 군만두(배송)를 한 번에 시켰을 때, 만약 주방에 군만두 재료가 떨어졌다고 해서 이미 먹고 있던 짜장면을 손님 입에서 억지로 뺏어오는 것(롤백)이 아니라, "죄송합니다, 만두가 안 돼서 전체 주문을 취소하고 돈을 환불해 드릴게요(보상 트랜잭션)"라고 우아하게 사과하고 상황을 수습하는 고도화된 고객 서비스 매뉴얼입니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| 데이터베이스 퍼 서비스 (DB per Service) | MSA에서 각 서버가 독립된 DB를 가지는 원칙으로, 이 원칙 때문에 기존의 트랜잭션이 불가능해져 사가 패턴이 필연적으로 탄생하게 되었다. |
| 보상 트랜잭션 (Compensating Transaction) | 사가 패턴의 심장. 앞서 이미 Commit 해버린 남의 DB 데이터를 역방향 수학 연산(더하기->빼기)으로 되돌리는 눈물겨운 수작업 코드. |
| 2PC (Two-Phase Commit) | 사가 패턴 이전에 쓰이던 분산 DB 동기화 기술. 중앙 통제자가 모든 DB에 Lock을 걸고 한 번에 승인하는 빡빡하지만 완벽한 القد 방식. |
| 트랜잭셔널 아웃박스 (Transactional Outbox) | 사가 패턴에서 "내 DB에 커밋함"과 "카프카에 메시지 쏨"이라는 두 행위 사이에 에러가 나서 꼬이는 걸 막기 위해 메시지를 DB에 담아두는 쌍둥이 패턴. |
| 이벤트 소싱 (Event Sourcing) | 상태 값을 덮어쓰지 않고 변경 이력(Event) 전체를 저장하는 기법으로, 사가 패턴의 보상 트랜잭션(이력 역재생) 구현을 구조적으로 가장 편하게 만들어준다. |
👶 어린이를 위한 3줄 비유 설명
- 친구 3명(주문, 결제, 배송)이 도미노 블록을 차례대로 하나씩 세우기로 약속했어요. (트랜잭션)
- 1번, 2번 친구는 잘 세웠는데 3번 친구가 실수로 블록을 못 세웠어요! 그럼 도미노 전체가 실패니까 2번, 1번 친구가 자기가 세운 블록을 뒤에서부터 하나씩 치워서(보상) 다시 깨끗한 원래 상태로 되돌려야 해요.
- 이렇게 여러 명이 이어달리기를 하다가 누군가 넘어지면, 거꾸로 돌아가면서 자기가 한 일을 취소하고 완벽하게 치우는 룰을 **'사가 패턴'**이라고 부른답니다!