[아키텍처] MSA 데이터 일관성 문제, SAGA 패턴으로 해결하기

2025. 11. 23. 23:19·아키텍처
마이크로서비스 아키텍처(MSA)를 도입할 때 가장 먼저 마주치는 기술적 난관 중 하나는 바로 데이터 일관성(Data Consistency) 문제이다.

모놀리식 환경에서는 하나의 데이터베이스에서 ACID 트랜잭을 통해 일관성을 보장 받았지만, 
MSA에서는 "Database per Service"  패턴으로 인해 데이터가 여러 서비스에 분산되어 있다.

분산 환경에서 데이터 일관성을 유지하기 위한 전통적인 방법과, 현대적인 해결책인 SAGA 패턴에 대해 알아보자.

1. 2PC (Two-Phase Commit)

과거 분산 데이터베이스 환경에서 주로 사용된 방법으로,
코디네이터(Coordinator)가 모든 참여자(Participants)의 트랜잭션 준비 상태를 확인하고 트랜잭션 커밋/롤백을 결정하는 방식

 

Two-phase commits (출처: linkedin)

1단계: Prepare (준비)

  • Coordinator가 모든 참여자에게 “커밋할 준비 되었는가?”를 묻는다.
  • 참여자는 준비되었다면 “Yes”, 실패하거나 준비 불가라면 “No”를 응답한다.
  • “Yes” 응답 시 로컬 로그에 트랜잭션을 영구 기록한다.

 

2단계: Commit (커밋)

  • 모든 참여자가 “Yes”라면 Coordinator가 “commit”을 지시한다.
  • 하나라도 “No”거나 타임아웃이 발생하면 전체가 “rollback”된다.

 

MSA에서 2PC가 적합하지 않은 이유

  • 성능 저하 (Blocking): 모든 서비스가 트랜잭션을 완료할 때까지 락(Lock)을 잡고 있어 처리량이 급격히 떨어집니다.
  • 단일 실패 지점: 코디네이터가 다운되면 전체 트랜잭션 처리가 불가능해집니다.
  • NoSQL 미지원: 현대적인 NoSQL 데이터베이스들은 대부분 2PC를 지원하지 않습니다.

2. SAGA 패턴

SAGA 패턴은 긴 트랜잭션을 여러 개의 짧은 로컬 트랜잭션으로 나누어 순차적으로 실행하는 방식으로,
각 로컬 트랜잭션은 해당 서비스의 DB를 업데이트하고, 다음 단계를 트리거하는 이벤트나 메시지를 발행한다.

핵심: 보상 트랜잭션 (Compensating Transaction)

SAGA 패턴의 핵심은 "실패 시 어떻게 되돌릴 것인가?"이다.

중간에 특정 단계가 실패하면, 이미 성공한 이전 단계들의 변경 사항을 취소하기 위해 보상 트랜잭션을 역순으로 실행해야 한다.

 

  • 정상 흐름: 주문 생성(성공) → 결제 승인(성공) → 배송 요청(성공)
  • 실패 흐름: 주문 생성(성공) → 결제 승인(실패!) → [보상 트랜잭션] 주문 취소 실행

3. SAGA 구현 방식

SAGA 패턴을 구현하는 방법은 크게 두 가지가 있다.

 

3-1. Choreography

중앙 제어 장치 없이, 각 서비스가 이벤트를 주고받으며 자율적으로 트랜잭션을 진행하는 방식

 "이벤트를 던질 테니, 알아서 반응해"

 

A. 주문 서비스 (Event Publisher)

@Service
public class OrderService {
    @Transactional
    public void createOrder(OrderRequest request) {
        Order order = orderRepository.save(request.toEntity());
        // 1. 주문 생성 이벤트 발행
        kafkaTemplate.send("order-created", new OrderCreatedEvent(order.getId(), order.getCustomerId(), order.getTotalAmount()));
    }
    
    // 4. (성공/실패) 결제/신용 결과에 따라 상태 변경
    @KafkaListener(topics = {"credit-reserved", "credit-limit-exceeded"})
    public void handleCreditResult(CreditResultEvent event) {
        if (event.isSuccess()) {
            orderRepository.approveOrder(event.getOrderId());
        } else {
            orderRepository.rejectOrder(event.getOrderId());
        }
    }
}

B. 고객 서비스 (Event Consumer & Publisher)

@Service
public class CustomerService {
    @KafkaListener(topics = "order-created")
    public void reserveCredit(OrderCreatedEvent event) {
        try {
            // 2. 신용 한도 확인 및 예약
            customerRepository.reserveCredit(event.getCustomerId(), event.getAmount());
            // 3-1. 성공 이벤트 발행
            kafkaTemplate.send("credit-reserved", new CreditReservedEvent(event.getOrderId()));
        } catch (CreditLimitExceededException e) {
            // 3-2. 실패 이벤트 발행
            kafkaTemplate.send("credit-limit-exceeded", new CreditLimitExceededEvent(event.getOrderId()));
        }
    }
}

 

특징:

  • Decentralized Control (분산 제어): 중앙 관리자가 없고, 각 서비스가 자신의 역할을 수행한다.
  • Event-Driven (이벤트 기반): 서비스 간 통신이 비동기 이벤트로 이루어진다.

중앙 제어 없이 이벤트를 통해 통신

  • 장점: 구성이 간단하고 서비스 간 결합도가 낮다.
  • 단점: 트랜잭션 흐름이 복잡해지면 추적하기 어렵고, 순환 의존성(Cyclic Dependency)이 발생할 수 있습니다.

 

3-2. Orchestration

중앙의 Saga Orchestrator가 각 서비스에 명령(Command)을 내리고 상태를 관리하는 방식

"내가 순서를 관리하니, 서비스들은 명령 수행 후 나에게 결과만 알려라”

A. Saga Orchestrator (중앙 관리자)

@Service
@RequiredArgsConstructor
public class OrderSagaOrchestrator {

    private final PaymentClient paymentClient;   // 결제 서비스 API 호출용
    private final InventoryClient inventoryClient; // 재고 서비스 API 호출용
    private final OrderRepository orderRepository;

    public void processOrder(Long orderId) {
        // 1. 결제 요청 (Command)
        boolean paymentResult = paymentClient.processPayment(orderId);
        
        if (!paymentResult) {
            // 결제 실패 시 즉시 종료 (주문은 이미 PENDING 상태라 가정)
            orderRepository.cancelOrder(orderId);
            return;
        }

        // 2. 재고 차감 요청 (Command)
        boolean inventoryResult = inventoryClient.decreaseStock(orderId);
        
        if (!inventoryResult) {
            // 3. 재고 실패 시 -> [보상 트랜잭션] 결제 취소 요청!
            paymentClient.refundPayment(orderId); 
            orderRepository.cancelOrder(orderId);
            return;
        }

        // 4. 모두 성공 시 주문 완료 처리
        orderRepository.completeOrder(orderId);
    }
}

 

특징: 

  • Centralized Control (중앙 제어): Orchestrator가 전체 트랜잭션의 흐름을 제어한다.
  • Command-Driven (커맨드 기반): Orchestrator가 각 서비스에 명시적으로 명령(Command) 으로 동작한다.

Orchestrator가 신용 예약을 요청하고 결과를 처리

  • 장점: 트랜잭션의 흐름과 상태를 한곳에서 파악하고 관리하기 쉽다. 복잡한 비즈니스 로직에 적합하다.
  • 단점: 오케스트레이터 서비스에 로직이 집중되며, 서비스 간 결합도가 높아질 수 있다.

 

3-3. 비교

비교 항목 Choreography(이벤트 기반) Orchestration(중앙 제어)
통신 방식 Event Pub/Sub Command / Reply
결합도 매우 낮음 (Loose Coupling) 높음
복잡성 관리 어려움 (흐름 파악 힘듦) 쉬움 (중앙 코드만 보면 됨)
적합한 규모 소규모, 단순한 흐름 대규모, 복잡한 흐름

4. 결론

MSA 환경에서 데이터 일관성은 포기할 수 없는 가치이지만, 2PC와 같은 강한 일관성(Strong Consistency)을 고집하면 분산 시스템의 장점인 가용성과 성능을 잃게 됩니다.

따라서 결과적 일관성(Eventual Consistency)을 목표로 하는 SAGA 패턴이 현실적인 대안이다.

 

비즈니스 로직이 단순하다면 Choreography를, 복잡하고 흐름 관리가 중요하다면 Orchestration을 선택하는 것이 좋아 보인다.


5. 참고

  • https://www.linkedin.com/pulse/two-phase-commit2pc-distributed-designpatterns-pratik-pandey
    https://learn.microsoft.com/ko-kr/azure/architecture/patterns/saga
  • https://sangyunpark99.tistory.com/entry/Saga-Pattern%EC%82%AC%EA%B0%80-%ED%8C%A8%ED%84%B4

'아키텍처' 카테고리의 다른 글

[아키텍처] DDD를 이해하기  (1) 2025.12.05
[아키텍처] MSA는 정답이고 Monolithic은 오답일까?  (0) 2025.11.21
'아키텍처' 카테고리의 다른 글
  • [아키텍처] DDD를 이해하기
  • [아키텍처] MSA는 정답이고 Monolithic은 오답일까?
코드피터
코드피터
능동적으로 배우고, 적극적으로 해결하며, 성실하게 성장
  • 코드피터
    코드 읽어주는 피터
    코드피터
  • 전체
    오늘
    어제
    • 분류 전체보기 (10)
      • 이슈 (1)
      • 트러블 슈팅 (1)
      • 아키텍처 (3)
      • Backend (1)
      • 스파르타 자바 심화 4기 RushCrew Proj.. (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    RestClient
    #DDD
    Netflix Eureka
    트러블 슈팅
    Spring Boot
    N+1
    backend
    분산트랜잭션
    JEP 418
    SystemDesign
    fetch join
    MSA
    외부 API 연동
    SagaPattern
    springboot
    HHH000104
    이벤트스토밍
    Java 21 Features
    멱등성
    Monolithic
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
코드피터
[아키텍처] MSA 데이터 일관성 문제, SAGA 패턴으로 해결하기
상단으로

티스토리툴바