3PC와 Saga 패턴 이해하기

송현진·2025년 5월 6일
0

Architecture

목록 보기
5/18

❓3PC(Three-Phase Commit, 3단계 커밋)란?

3PC는 분산 시스템에서 데이터 일관성을 유지하기 위한 트랜잭션 처리 방식 중 하나다. 기존에 존재하던 2PC의 단점을 보완하기 위해 만들어졌고 Coordinator(조정자)가 갑자기 장애가 발생해도 각 시스템이 자체적으로 트랜잭션을 마무리하거나 롤백할 수 있게 설계되어 있다.

쉽게 말하면 2PC에서 발생할 수 있는 Blocking 문제를 해결하기 위해 단계가 하나 더 추가된 방식이다.

🛠️ 3PC 동작 원리

3PC는 아래와 같은 3단계로 진행된다.

1단계: canCommit 단계 (가능 확인 단계)

조정자가 각 참여 시스템에게 트랜잭션 실행 가능 여부를 물어본다. 참여 시스템은 가능 여부만을 응답하고 실제 작업은 수행하지 않는다.

조정자 : 이 작업 가능해?
모든 시스템 : 응 가능해 (다음 단계로)
하나라도 불가능 : 아니 안돼! (바로 취소)

2단계: preCommit 단계 (준비 완료 단계)

모든 시스템이 가능하다고 하면 조정자는 참여 시스템들에게 임시로 작업을 준비하라고 지시한다. 참여 시스템들은 트랜잭션 로그에 기록만 하고 대기 상태에 들어간다.

조정자 : 작업 곧 할 거니까 준비만 해둬.
모든 시스템 : 알겠어, 준비 완료! (대기 중)

3단계: doCommit 단계 (실행 단계)

모든 참여 시스템이 준비 완료가 되면 조정자는 실제 트랜잭션을 실행하라고 지시한다. 참여 시스템들은 실제 작업을 반영하고 트랜잭션을 완료한다.

조정자 : 이제 진짜 작업 실행해! (Commit)
모든 시스템 : 알겠어! 실행 완료!

❌ 단점

  • 성능 문제
    단계가 2PC보다 많아 처리 속도가 느리다.

  • 네트워크 분할에 취약
    네트워크 장애 상황에서는 두 개 이상의 조정자가 각각 독립적으로 동작할 위험이 있다.

  • 실제 사용 사례가 거의 없음
    이론적 모델이며 실무 환경에서 거의 사용하지 않는다. 대부분의 실전 환경에서는 2PC보다 복잡한 3PC보다는 Saga 패턴이나 이벤트 기반 패턴으로 대체된다.


❓Saga 패턴이란?

Saga 패턴은 모든 작업을 하나의 큰 트랜잭션으로 묶지 않고 각 서비스에서 로컬 트랜잭션을 개별적으로 실행한 뒤 실패가 발생하면 보상 트랜잭션(Compensation Transaction)을 통해 되돌리는 방식이다.

즉, Saga는 하나의 거대한 트랜잭션 대신 여러 작은 트랜잭션으로 나눠 관리하고 실패가 발생하면 보상 작업을 통해 최종적 일관성(Eventual Consistency) 을 유지한다. Saga 패턴은 특히 마이크로서비스 환경에서 독립적인 서비스 관리에 적합하다.

왜 Saga가 필요한가?

전통적 트랜잭션의 한계

모놀리식 애플리케이션에서는 @Transactional과 같은 어노테이션으로 하나의 트랜잭션을 간단하게 관리할 수 있다. 하지만 마이크로서비스 아키텍처에서는 다음과 같은 복잡성이 발생한다.

  • 각 서비스가 별도의 데이터베이스를 가짐
  • Kafka, RabbitMQ 같은 메시지 브로커와도 연동
  • 네트워크 지연 및 장애 자주 발생

이러한 분산 환경에서는 2PC는 블로킹/가용성 문제, 기술 제약으로 인해 적합하지않다. 특히 Kafka나 MongoDB는 2PC를 지원하지 않으며 이를 강제로 적용하면 시스템의 복잡도가 급격히 증가한다.

🛠️ Saga 동작 원리

Saga는 다음과 같은 흐름을 가진다.

  1. 작업 시작: 첫 번째 서비스가 로컬 트랜잭션을 수행하고 다음 작업을 위한 메시지를 발행한다.
  2. 다음 단계 진행: 메시지를 받은 다음 서비스가 로컬 트랜잭션을 처리하고 다시 메시지를 발행한다.
  3. 실패 발생 시 보상: 특정 단계에서 실패하면 이전에 성공한 단계들을 보상 트랜잭션으로 취소한다.

이러한 흐름은 다음 두 가지 스타일로 구현된다.

1. Orchestration 방식 (중앙 집중 관리 방식)

하나의 오케스트레이터(Orchestrator)가 전체 Saga 흐름을 관리한다. 각 서비스에 직접 트랜잭션 실행 명령을 내리고 결과에 따라 다음 작업으로 진행하거나 보상 트랜잭션을 실행할지 결정한다.

예시 흐름
1. 오케스트레이터 -> 주문 서비스 호출 → 주문 생성
2. 주문 생성 완료 -> 오케스트레이터 -> 결제 서비스 호출
3. 결제 완료 -> 오케스트레이터 -> 배송 서비스 호출
4. 배송 완료 -> 오케스트레이터 -> 주문 상태 완료로 업데이트

장점

  • 흐름이 명확하고 추적이 쉽다.
  • 서비스 간 직접 호출이 없어 느슨한 결합이 유지된다.

단점

  • 중앙 서비스가 복잡해지고 병목(bottleneck)이 될 수 있다.
  • 오케스트레이터가 장애 시 전체 흐름에 영향을 준다 (단일 장애 지점).

2. Choreography 방식 (분산 이벤트 기반 방식)

각 서비스가 작업을 끝낸 후 결과를 이벤트로 발행한다. 다른 서비스는 이 이벤트를 구독해 다음 작업을 진행한다. 실패가 발생하면 이전에 성공한 작업들이 이벤트를 받아 보상 작업을 스스로 수행한다.

예시 흐름
1. 주문 서비스 -> OrderCreated 이벤트 발행
2. 결제 서비스 -> OrderCreated 수신 -> 결제 수행 후 PaymentCompleted 이벤트 발행 3. 배송 서비스 -> PaymentCompleted수신 -> 배송 수행 후ShippingCompleted이벤트 발행 4. 주문 서비스 ->ShippingCompleted` 수신->→ 주문 상태 '완료'로 업데이트

장점

  • 중앙 관리자가 없고 완전한 분산 구조이다.
  • 서비스 간 결합도가 낮고 유연성(확장성)이 뛰어나다.

단점

  • 전체 흐름이 이벤트로 분산되어 있어 추적 및 디버깅이 어렵다.
  • 이벤트가 많아지면 관리 복잡성이 커진다.

❓보상 트랜잭션(Compensation Transaction)이란?

Saga 패턴에서 중간 단계에서 실패가 발생할 경우 이전에 성공한 작업들을 취소하기 위해 실행되는 반대 작업을 말한다. 즉, 전체 트랜잭션의 정합성을 맞추기 위해 작업을 되돌리는 트랜잭션이다.

예를 들어 쇼핑몰 주문 흐름에서 주문 생성 -> 결제 승인 -> 배송 예약 단계 중 결제 승인에서 실패했다면 앞선 주문 생성주문 취소로 보상 트랜잭션 처리해야 한다.

주의할 점은 단순한 롤백이 아니라는 점이다. 보상 트랜잭션은 일반 로컬 트랜잭션처럼 개발자가 명시적으로 직접 구현해야 하고 각 작업에 대해 적절한 반대 로직이 존재해야만 적용 가능하다. 비즈니스 로직 관점에서의 취소 작업이다. 모든 작업이 완벽하게 원상 복구되기 어려운 경우도 많아 최대한 유사한 상태로 되돌리는 게 목적이다.

❌ Saga 패턴의 단점

  • 최종적 일관성(Eventual Consistency)
    즉각적인 데이터 일관성 보장이 아닌 일정 시간 후 일관성이 보장되는 방식이라서 강한 일관성이 필요하면 부적합하다.

  • 보상 트랜잭션 직접 구현 필요
    각 서비스가 실패 시 원상 복귀를 위한 보상 트랜잭션을 구현해야 하므로 로직이 복잡해질 수 있다.

  • 추적 및 디버깅 어려움
    분산된 작업으로 인해 문제가 발생했을 때 원인을 찾기가 어렵다.

전통 트랜잭션 vs Saga

항목ACID 트랜잭션Saga 패턴
원자성전체 작업 한꺼번에 성공/실패각 단계 성공/실패 후 보상으로 복구
고립성중간 상태 숨김중간 상태 노출 가능
복구ROLLBACK보상 트랜잭션 직접 호출
가용성낮음 (동기적)높음 (비동기 메시지)
기술 제약JDBC, RDB 중심메시지 브로커, NoSQL 등과 호환

📝 배운점

3PC는 2PC의 블로킹 문제를 해결하려고 고안되었지만 여전히 복잡성이나 성능 문제로 실제 사용되는 경우가 거의 없다는 것을 배웠다.

반면 Saga는 2PC/3PC와 달리 중앙 조정자 없이 마이크로서비스가 비동기 이벤트를 통해 협업하는 현실적인 트랜잭션 처리 방식이다. Kafka나 메시지 큐 같은 이벤트 기반 아키텍처와 궁합이 뛰어나다. 비록 보상 트랜잭션 구현과 최종적 일관성 등의 단점이 있지만 마이크로서비스 환경에서는 필수적인 트랜잭션 처리 방식이라는 것을 명확히 알게 되었다.

profile
개발자가 되고 싶은 취준생

0개의 댓글