리서치

[MSA] SAGA 패턴

꾸준함. 2025. 2. 1. 02:18

개요

MSA (Microservices Architecture)는 하나의 커다란 애플리케이션 (Monolithic Architecture)를 독립적으로 배포되고 운영될 수 있는 작은 서비스들로 분리해 개발 운영하는 소프트웨어 아키텍처 스타일입니다.

MSA의 장점을 정리하면 다음과 같습니다.

  • 독립적 배포: 각 마이크로서비스는 자체적인 코드, 데이터베이스 등을 갖추고 있어서, 다른 서비스와 격리된 상태에서 수정 및 재배포 가능
  • 확장성: 트래픽이나 부하가 특정 서비스에서만 집중될 경우, 해당 서비스만 확장할 수 있어 자원을 효율적으로 사용 가능
  • 유연성: 서비스별로 독립적인 기술 스택(프로그래밍 언어, DB 등)을 선택해 적용할 수 있어, 새로운 기술을 쉽게 도입 가능
  • 장애 격리: 어느 한 서비스에 문제가 생겨도 전체 시스템이 중단되지 않도록 설계할 수 있으며, 장애 범위를 최소화

 

위 내용만 보면 당장 모든 서비스를 MSA로 전환해야 할 것 같지만 서비스를 분리함에 있어 다음과 같은 단점도 존재합니다.

  • 분산 시스템 적용에 따른 복잡성 증가: 모놀리식에서는 한 프로세스 내에서 이뤄졌던 호출이 MSA에서는 서비스 간 통신 프로토콜, 에러 처리, 재시도 로직, Circuit Breaker 등 분산 시스템에서 요구되는 고려사항이 많아지므로, 개발 및 운영 난이도 상승
  • 운영·배포 및 인프라 관리 부담: 마이크로서비스는 작은 서비스들이 많아지는 구조이므로 각각을 배포·모니터링·운영하는 비용 증가하고 CI/CD 파이프라인, 컨테이너, 오케스트레이션(Kubernetes 등), 서비스 디스커버리, API 게이트웨이, 서비스 메쉬 등 많은 인프라 요소를 구축·관리해야 함
  • 데이터 일관성 및 트랜잭션 처리 어려움: 각 서비스는 자체 DB를 갖도록 권장되며, 분산 트랜잭션을 지양하는 만큼 데이터 일관성을 보장하기가 쉽지 않음
    • 일관성을 보장하기 위해 SAGA 패턴, 이벤트 소싱(Event Sourcing), CQRS 등 분산 트랜잭션을 대체하기 위한 패턴들이 필요하고, 이를 구현 및 운영하는데 노력이 많이 드는 편

 

  • 테스트 및 디버깅 복잡성:  각 서비스가 독립적으로 동작하기 때문에 단위 테스트는 쉽지만, 통합 테스트(E2E)는 오히려 복잡해지고 문제가 발생했을 때 어느 서비스의 어느 부분에서 오류가 발생했는지 찾아내기 위해 분산 추적 등의 도구가 필요하며, 로그 관리도 까다로움

 

이번 게시글에서는 MSA의 단점 중 하나로 거론한 `데이터 일관성 및 트랜잭션 처리 어려움`을 해결하기 위해 도입된 SAGA 패턴에 대해 간단히 정리해 보겠습니다.

 

MSA 환경에서의 트랜잭션

앞서 언급했다시피 MSA 환경에서는 다음과 같은 이유로 트랜잭션을 구현하지 않는 것이 바람직합니다.

  • 관리 포인트가 늘어나고
  • 개발에 필요한 시간이 늘어나고
  • 러닝 커브도 매우 높은 편

 

위 이유 때문에 대부분의 케이스에서는 분산 트랜잭션을 최대한 피하면서도 데이터 일관성을 유지하기 위해 다양한 패턴과 기법들이 활용됩니다.

  • 단순 동기 재시도
  • DB에 상태 저장 후 비동기 처리
  • DLQ (Dead Letter Queue)
  • etc.

 

그럼에도 불구하고 분산 트랜잭션을 구현해야 할 경우 적용할 수 있는 방법은 다음과 같습니다.

  • 2PC (2 Phase Commit)
  • 보상 트랜잭션 (Compensating Transaction)

 

1. 2PC (2 Phase Commit)

초기에는 다수의 노드를 가지는 DB에서 커밋을 구현하기 위한 개념으로 시작되었으나 현재는 의미가 확장되어 분산 시스템 환경에서 트랜잭션을 구현하기 위한 해결 방안 중 하나입니다.

  • 2PC를 위해서는 분산 시스템 환경에서 분산 트랜잭션을 위해 데이터 변경을 수행하는 서버들의 Phase를 관리하는 Coordinator라는 추가적인 인프라 필요
  • 또한 트랜잭션을 위한 서비스들을 Participant라고 정의
  • 2 Phase는 각각 다음과 같음
    • Prepare (Phase #1)
    • Commit (Phase #2)

 

2PC가 구현되는 프로세스는 다음과 같습니다.

  • Phase #1 (Prepare)
    • 트랜잭션 시작 시 Coordinator가 Transaction ID 생성
    • Coordinator는 사전에 정의된 각 Participant 별로 실행해야 할 요청들에 대한 준비를 각 서비스에 요청
    • 모든 Participant가 준비되었다고 응답 (실질적 데이터 변경은 일어나지 않은 상태)

 

  • Phase #2 (Commit)
    • Coordinator는 Participant들에게 Commit 요청
    • Participant들은 이전에 준비했던 동작을 마저 진행하고, 각 서비스들은 실제 데이터 변경을 위한 Commit 진행\
    • 모든 Participant에게 OK 응답을 받게 되면 트랜잭션 완료

 

https://timilearning.com/posts/mit-6.824/lecture-12-distributed-transactions/
https://www.geeksforgeeks.org/three-phase-commit-protocol/

 

1.1 2PC의 한계 - 장애 취약성

  • Coordinator에 문제가 생기는 순간 모든 트랜잭션은 멈춰버리고 모든 비즈니스가 정지됨
    • Data에 대한 Lock이 걸리고
    • 문제가 생겼을 때 결정 주체가 없어져 wait or timeout 발생

 

  • Coordinator의 모든 결정은 취소 불가
    • 트랜잭션의 일관성을 위해 Commit/Abort 결정에 대해서는 그 사이에 어떤 문제가 생겼을지라도 어떤 방법을 써서라도 반드시 완료해야 함

 

https://systemdesignschool.io/domain-knowledge/distributed-transactions

 

2. 보상 트랜잭션 (Compensating Transaction)

보상 트랜잭션은 일련의 작업을 보상하기 위한 트랜잭션을 구현하기 위한 하나의 방법으로 개념 자체는 고전적인 데이터베이스 개념에서 따왔습니다.

  • `commit`된 데이터를 `commit` 되기 이전의 상태로 변경하기 위한 작업 (Rollback)
  • 데이터베이스 개념상으로는 작업(Do)하던 내용을 다시 Undo 할 때 `보상` (Compensate)라는 용어 사용
  • MSA 환경에서의 보상 트랜잭션은 `데이터 변경을 수반하는 요청이 정상적으로 종료된 서비스의 API 호출에 대해서 해당 데이터 변경 요청을 하기 이전의 상태로 되돌리기 위한 API 호출`

 

https://orkes.io/blog/compensation-transaction-patterns/

 

2.1 보상 트랜잭션의 한계

  • 전통적인 ACID 트랜잭션은 하나의 트랜잭션 블록 내에서 일괄적으로 성공/실패를 결정하는 반면 보상 트랜잭션은 사후 보상(Undo) 방식이므로, 실패 발생 시 이미 일부 시스템은 변경된 상태가 외부에 노출됨
  • 보상 트랜잭션도 네트워크 오류, 중복 메시지 등 여러 환경 문제에 의해 여러 번 호출될 수 있기 때문에 보상 로직 또한 멱등적이게 설계해야 하며 보상 중간에 다시 실패 발생 시 어떻게 처리할지 고민 필요
    • 지연 혹은 문제가 발생했을 때 각 서비스가 독립적으로 보상 트랜잭션의 상태를 알 수 있어야 함
    • Sync 방식으로 무작정 기다리기보다 큐잉(이벤트)을 활용해서 문제 시에도 복구 시 이어서 처리 가능해야 함

 

SAGA 패턴

  • MSA에서 분산 트랜잭션을 구현하고 데이터 일관성을 유지하기 위한 대표적인 접근 방식
  • 여러 개의 로컬 트랜잭션을 논리적으로 하나의 트랜잭션처럼 묶어 처리하되, 각 서비스가 독립적으로 커밋 및 롤백(보상)을 수행하도록 설계
  • SAGA 패턴을 구현하는 방법에는 크게 2 종류가 있고 기본적으로 두 패턴 모두 보상 트랜잭션을 사용한다는 공통점 존재
    • Choreography 패턴
    • Orchestration 패턴

 

1. Choreography 패턴

  • 이벤트 기반으로 동작하며, 각 서비스가 자신의 작업을 완료할 때 도메인 이벤트를 발행
  • 다른 서비스들은 해당 이벤트를 구독하고, 로직을 수행한 뒤 필요한 이벤트를 다시 발행하는 식으로 전체 프로세스를 이어감
  • 관리 주체가 따로 없어 구현이 비교적 간단하지만, 이벤트 흐름이 복잡해지면 전체 시나리오 파악이 어려워지는 단점 존재

 

1.1 Choreography 패턴 정상적인 상황 예시

 

https://livebook.manning.com/book/microservices-patterns/chapter-4/72

 

부연 설명

  • Order Service에서 주문 생성 → `Order Created` 이벤트 발행
  • Consumer Service에서 소비자 검증 → `Consumer Verified` 이벤트 발행
  • Kitchen Service에서 티켓 생성 → `Ticket Created` 이벤트 발행
  • Accounting Service에서 카드 승인 → `Credit Card Authorized` 이벤트 발행
  • `Credit Card Authorized` 이벤트를 기반으로 Kitchen Service가 티켓 확정, Order Service가 주문 승인
  • 모든 과정이 이벤트 기반으로 자연스럽게 연결되며, 중앙 오케스트레이터 없이도 각 단계가 완료됨

 

1.2 Choreography 패턴 문제 예시

 

https://livebook.manning.com/book/microservices-patterns/chapter-4/72

 

부연 설명

  • 그림의 상황은 다음과 같음
    • 주문이 생성되고(`Order Created` 이벤트)
    • 소비자 정보가 검증된 후(`Consumer Verified`)
    • 티켓 생성(`Ticket Created`)
    • 이후 결제를 시도하지만, 카드 승인 절차가 실패(`Credit card authorization failed`)
    • Kitchen Service는 티켓을 거절(`rejectTicket()`)
    • Order Service도 주문을 거절(`rejectOrder()`)

 

  • 정리하면 중간에 문제(결제 실패)가 발생하자, 각 서비스가 개별 이벤트를 통해 서로에게 ‘실패’를 알리며 롤백·보상 처리를 시도하는 상황
    • 이 과정에서 서비스마다 이벤트 순서를 맞추고, 누구부터 롤백·거절해야 하는지 조정하기가 복잡해짐
    • 이벤트 기반으로 처리되다 보니 서비스 간 로직이 분산되어 있고, 장애 원인을 추적하거나 중복 이벤트 처리(멱등성)를 보장하는 데 부담이 커짐
    • 중앙 오케스트레이터가 없음에 따라 발생하는 문제

 

2. Orchestration 패턴

  • 별도의 Orchestrator(추가적인 인프라)가 전체 트랜잭션 흐름을 중앙에서 통제
  • 오케스트레이터가 각 서비스에 명령(Command)을 보내면, 서비스는 로직을 수행 후 결과(성공/실패)를 오케스트레이터에게 알려줌
  • 흐름을 한 곳에서 관리하므로 시나리오가 명확해지지만, 오케스트레이터가 단일 장애점(Single Point of Failure)이 될 수 있는 단점 존재

 

2.1 Orchestration 패턴 정상적인 상황 예시

 

https://livebook.manning.com/book/microservices-patterns/chapter-4/65

 

부연 설명

  • Order Service(Orchestrator)가 Consumer Service에 소비자 검증 명령 → Consumer Service에서 검증 완료 후, `Consumer Verified` 응답
  • Orchestrator가 Kitchen Service에 티켓 생성 명령 → Kitchen Service에서 티켓 생성 후, `Ticket Created` 응답
  • Orchestrator가 Accounting Service에 카드 승인 명령 → Accounting Service에서 결제 승인 후, `Card Authorized` 응답
  • Orchestrator가 Kitchen Service에 주문 승인 명령 → Kitchen Service 승인 후, 응답
  • Orchestrator가 Order Service 내부 로직으로 최종 주문 승인
  • 이처럼 Orchestrator가 단계별 명령과 응답을 주고받으며 전체 트랜잭션 흐름을 관리하므로, 각 과정이 순차적·일관되게 진행

 

2.2 Orchestration 패턴에서 문제가 발생했을 때 상황 예시

  • 사용자가 결제 요청 → 오케스트레이터(Order Saga)가 시작됨
  • Orchestrator → Consumer Service에 확인 요청 → Consumer Service가 “OK”로 응답
  • Orchestrator → Kitchen Service에 티켓 생성 요청 → Kitchen Service가 “OK”로 응답
  • Orchestrator → Accounting Service에 결제 요청  Accounting Service 가 “Failed!” 응답
  • Orchestrator → Kitchen Service에 티켓 취소 요청 (보상 트랜잭션 실행)
  • Orchestrator → 주문에 결제 실패 알림 → Accounting Service는 “Saga 실패” 이벤트를 받아 후처리 수행
  • 이처럼 Orchestration 패턴은 추가적인 인프라 Orchestrator를 도입함에 따라 각 서비스에서의 구현은 비교적 간단해지고, 보상 트랜잭션만 구현하면 됨
    • 관심사 분리 측면에서 유지보수 효율성 올라감
    • 단, 트랜잭션에 대한 비즈니스 로직을 오케스트레이터에 포함시키는 것을 지양

 

3. 결론 및 정리

  • SAGA 패턴이란 기존의 2PC와 보상 트랜잭션의 부족한 부분을 보완하는 MSA 호나경에서 트랜잭션을 구현하기 위한 패턴
  • SAGA를 구현하는 방법에는 Choreography 패턴과 Orchestration 패턴 존재
    • Choreography 패턴은 비교적 단순하지만, 확장과 모니터링 등이 어렵다는 단점 존재
    • Orchestration 패턴은 확장과 모니터링이 쉬운 반면 비교적 구현이 어렵고 추가 인프라에 따른 관리 포인트가 늘어나는 단점 존재
    • 각 패턴은 비즈니스의 복잡도, 그리고 인프라 및 인력 등 많은 부분을 고려해야 하고 가장 좋은 방법은 트랜잭션 자체를 구현하지 않는 방법을 찾는 것

 

Axon Framework를 통해 SAGA 패턴 살펴보기

 

1. Axon Framework란?

  • 태생부터 DDD (Domain Driven Design), CQRS (Command and Query Responsibility Segregation), EDA (Event Driven Architecture), 그리고 SAGA를 쉽게 구현하기 위해 개발된 오픈소스 프레임워크
  • Apache 2.0 라이센스를 가지고 있어 상업적 사용이 가능

 

2. Axon Framework를 구성하는 요소

Axon Framework는 기본적으로 별도의 Orchestrator가 존재합니다.

 

2.1 Axon Framework

  • Event Sourcing 기반으로 데이터를 관리할 수 있는 도구를 제공하고 해당 도구들을 사용해서 CQRS, DDD를 구현할 수 있도록 지원
  • Springboot 프로젝트에 의존성으로 사용하여, 다양한 어노테이션 기반으로 위 기능을 구현 가능

 

2.2 Axon Server

  • Axon Framework를 사용하여 배포된 애플리케이션들의 Orchestrator 역할 수행
  • Axon Framework를 사용하는 어플리케이션들로부터 발행된 Event들을 저장하는 역할 수행
  • 각 어플리케이션들의 상태를 관리하고 embedding 된 queuing을 내재하여 유량 조절도 진행
  • 고가용성에 집중된 내부 구현이 되어있고 Observability 제공

 

https://medium.com/@knoldus/axon-part-1-an-introduction-to-axon-framework-6dfd164afa90

 

2.3 Command Bus

  • Command만이 지나갈 수 있는 통로

 

2.4 Command Handler

  • Command Bus로부터 받은 Comand를 처리할 수 있는 handler

 

2.5 Aggregate

  • Command를 할 수 있는 도메인의 단위

 

2.6 Event Bus

  • Event만이 지나갈 수 있는 통로

 

2.7 Event Handler

  • Event Bus로부터 받은 Event를 처리하는 handler

 

https://nexocode.com/blog/posts/smooth-implementation-cqrs-es-with-sping-boot-and-axon/
https://nexocode.com/blog/posts/smooth-implementation-cqrs-es-with-sping-boot-and-axon/

 

3. Axon Framework를 통해 구현하는 SAGA

  • Axon Framework의 Orchestrator인 Axon Server 구축
  • 각 서비스에 Axon Framework 의존성 추가
  • Event Sourcing 방식을 사용하여 기존의 Request-Response 모델을 Event Driven Model로 변경
  • 변경된 모델을 사용해서 Axon Framework이 지원하는 SAGA 구현
    • Axon Server가 Orchestrator 역할을 수행하므로 자연스럽게 Orchestration 방식의 SAGA를 비교적 간단하게 구현해 볼 수 있음

 

예시 코드는 같은 강의를 들으신 JaeHoney님의 블로그를 참고 부탁드립니다!

https://jaehoney.tistory.com/403

 

Axon Framework로 Orchestration-based Saga 구현하기!

해당 포스팅은 아래 강의 내용의 예시 코드를 포함하고 있습니다.https://fastcampus.co.kr/courses/216427강의 코드를 따라하면서 재해석한 부분으로 이해해주시면 감사하겠습니다~Axon Framework란?Axon Framewo

jaehoney.tistory.com

 

참고

패스트 캠퍼스 - 간편 결제 프로젝트로 한 번에 끝내는 실전 MSA 초격차 패키지 Online

 

반응형