312. 사가 (Saga) 패턴의 코레오그래피 (Choreography) vs 오케스트레이션 (Orchestration)

핵심 인사이트 (3줄 요약)

  1. 본질: 사가(Saga) 패턴은 마이크로서비스(MSA)에서 불가능해진 분산 트랜잭션(2PC)의 문제를 해결하기 위해, 거대한 트랜잭션을 여러 개의 작고 독립적인 로컬 트랜잭션의 연속적인 릴레이로 쪼개어 '결과적 일관성(Eventual Consistency)'을 맞추는 아키텍처 패턴이다.
  2. 가치: 중간에 한 서비스가 에러를 뱉어 트랜잭션이 실패할 경우, 이전에 성공했던 서비스들에게 **"방금 했던 거 다 취소해!"라는 '보상 트랜잭션(Compensating Transaction)'**을 역순으로 발행하여 더럽혀진 데이터를 무결하게 롤백(Rollback)시키는 마법을 부린다.
  3. 융합: 이 연속적인 릴레이를 통제하는 지휘 체계에 따라, 중앙 지휘자가 멱살을 잡고 명령하는 오케스트레이션(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 패턴의 보상 트랜잭션입니다.

  • 등장 배경 및 발전 과정:

    1. 2PC(Two-Phase Commit)의 한계: 초기 분산 DB 환경에선 중앙 코디네이터가 모든 DB에게 "커밋할 준비 됐어?" 묻고, 100% "네" 할 때까지 모든 시스템에 락(Lock)을 걸어두는 2PC를 썼다. 마이크로서비스 수십 개가 얽히자 이 락(Lock) 때문에 시스템이 극도로 느려져 폐기되었다.
    2. 1987년 Hector Garcia-Molina의 Saga 논문: 긴 수명의 트랜잭션을 다루기 위해 하위 트랜잭션과 보상(취소) 트랜잭션의 쌍을 제안한 고전 논문이 발굴되었다.
    3. 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명이서 유럽 여행을 가면 눈치껏 하다가는 다 길을 잃습니다. 깃발을 든 가이드(오케스트레이터)가 멱살을 잡고 "다음은 에펠탑입니다!"라고 통제해야만 대규모 여행(트랜잭션)이 무사히 끝납니다.


Ⅳ. 실무 적용 및 기술사적 판단

실무 시나리오

  1. 시나리오 — 데이터 불일치(Eventual Consistency)의 혼돈: 오케스트레이션 사가 패턴으로 결제 로직을 구현했다. 사용자가 돈을 지불하고(1단계 성공), 재고 서버가 재고를 깎는 중(2단계 진행 중)이었다. 그런데 그 찰나의 1초 동안, 다른 탭에서 사용자가 새로고침을 누르자 "돈은 빠져나갔는데, 주문 내역에는 아직 안 뜨는" 기괴한 화면이 나타났다. 사용자는 "내 돈 먹튀했다!"라며 소리를 지른다.

    • 아키텍트의 해결책: 사가 패턴은 '격리성(Isolation)'을 포기한 아키텍처임을 명심해야 한다. 전통적 DB의 Lock이 없기 때문에, 트랜잭션이 다 끝나기 전의 반쪽짜리 더러운 상태(Dirty Read)가 사용자에게 노출된다. 아키텍트는 1단계 성공 직후 주문 상태를 '주문됨'이 아니라 [결제 완료_재고 확인 중 (PENDING)] 이라는 과도기적 상태(Semantic Lock)로 명확히 마킹하여 DB에 쓰고, 프론트엔드는 이 PENDING 상태일 때 뺑글뺑글 도는 로딩 스피너 UI를 띄워주어 사용자가 기괴함을 느끼지 않도록(UX 융합) 디자인해야 한다.
  2. 시나리오 — 코레오그래피의 이벤트 지옥(Event Hell): 10개의 마이크로서비스가 얽힌 쇼핑몰을 코레오그래피(이벤트 구독)로 짰다. 배송팀 신입이 로직을 잘못 짜서 배송실패이벤트 대신 결제취소이벤트를 발행했다. 1초 뒤 이 이벤트를 듣고 결제 서버가 환불을 때려버렸고, 유저 서버가 쿠폰을 뱉어냈다. 장애가 터졌는데 수백만 개의 이벤트 로그 속에서 "도대체 어느 미친 서버가 첫 폭탄(이벤트)을 던졌는지" 원인 파악에만 3일이 걸렸다.

    • 아키텍트의 해결책: 전형적인 코레오그래피의 관찰 가능성(Observability) 붕괴다. 복잡한 트랜잭션에는 코레오그래피를 지양하고 **오케스트레이터(중앙 통제)**를 둬야 했다. 만약 코레오그래피를 썼다면, 최소한 이벤트 발행 시 Correlation ID(고유 추적 번호)를 헤더에 박아서 분산 추적(Zipkin/Jaeger)으로 이 요청이 1번 서버부터 10번 서버까지 어떻게 핑퐁 쳤는지 그 궤적을 1초 만에 시각화해 내는 인프라를 먼저 깔아뒀어야 한다.

도입 체크리스트

  • 기술적: 보상 트랜잭션 코드(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줄 비유 설명

  1. 친구 3명(주문, 결제, 배송)이 도미노 블록을 차례대로 하나씩 세우기로 약속했어요. (트랜잭션)
  2. 1번, 2번 친구는 잘 세웠는데 3번 친구가 실수로 블록을 못 세웠어요! 그럼 도미노 전체가 실패니까 2번, 1번 친구가 자기가 세운 블록을 뒤에서부터 하나씩 치워서(보상) 다시 깨끗한 원래 상태로 되돌려야 해요.
  3. 이렇게 여러 명이 이어달리기를 하다가 누군가 넘어지면, 거꾸로 돌아가면서 자기가 한 일을 취소하고 완벽하게 치우는 룰을 **'사가 패턴'**이라고 부른답니다!