본문 바로가기
Java , Spring/Spring

[MSA] Orchestration 기반 SAGA 패턴을 활용한 분산 트랜잭션 처리

by 방배킹 2025. 3. 2.

이전 글에서(https://bangbaeking.tistory.com/159) Orchestration 기반의 SAGA 패턴을 활용하여 분산 트랜잭션을 처리했다고 언급했는데, 이번에는 그 과정을 좀 더 자세히 정리하고자 한다. 전체적인 구조는 아래와 같다.

전체 아키텍처
분산 트랜잭션 로직

1. 결제 서비스의 트랜잭션 흐름

결제 프로세스 개요

현재 결제 관련 플로우는 다음과 같다.

  1. 클라이언트에서 외부 결제 API에 결제 요청을 보낸다.
  2. 결제가 성공하면 서버에서 비즈니스 로직이 실행된다.

비즈니스 로직 처리 단계

비즈니스 로직의 주요 단계는 다음과 같다.

  1. 결제 내역 저장 (결제 서버)
  2. 쿠폰 사용 업데이트 (쿠폰 서버)
  3. 회원 수업 횟수 증가 (회원 서버)

2. MSA 환경에서의 분산 트랜잭션 문제

현재 서비스는 결제, 쿠폰, 회원 서비스가 각각 독립된 MSA 구조로 되어 있다.

이러한 환경에서는 여러 개의 서비스가 하나의 트랜잭션에 관여하게 된다.

  1. 결제 서버에서 결제 내역을 저장한 후,
  2. 쿠폰 서버에서 쿠폰 사용을 업데이트하고,
  3. 회원 서버에서 수업 횟수를 증가시키는 과정이 진행된다.

하지만 이 과정에서 어느 한 단계라도 오류가 발생하면, 전체 트랜잭션을 되돌려야 한다.

문제는, MSA 환경에서는 모놀리식 아키텍처처럼 모든 작업을 한 번에 커밋하거나 롤백하는 것이 불가능하다는 점이다.

기존 2PC 방식의 한계

이를 해결하기 위해 기존 RDBMS에서 사용하는 2PC(Two-Phase Commit) 방식을 적용하는 것도 고려할 수 있다.

그러나 2PC는 다음과 같은 문제로 인해 MSA 환경에서 적합하지 않다.

  • 서비스 간 강한 결합을 유발하여 서비스 독립성이 약해짐
  • 성능 저하 문제 발생 (트랜잭션이 완료될 때까지 모든 서비스가 대기해야 함)
  • 확장성이 떨어짐, 장애 발생 시 전체 시스템이 영향을 받을 가능성 높음

3. SAGA 패턴을 활용한 분산 트랜잭션 해결

위와 같은 문제를 해결하기 위해 2PC 대신 SAGA 패턴을 적용하여 분산 트랜잭션을 관리했다.

SAGA 패턴이란?

SAGA 패턴은 하나의 전체 트랜잭션을 여러 개의 독립적인 로컬 트랜잭션으로 나누어 실행하는 방식이다.

각 서비스는 독립적으로 트랜잭션을 실행한 후, 실패 시 보상 트랜잭션을 실행하여 이전 상태로 되돌린다.

SAGA 패턴의 두 가지 방식

SAGA 패턴에는 다음과 같은 두 가지 방식이 있다.

  1. Orchestration 방식
    • 중앙에서 Orchestrator(조정자) 서버가 트랜잭션을 제어하며, 각 서비스에 명령을 내리고 응답을 받음.
    • 트랜잭션 흐름이 명확하며 관리 및 디버깅이 용이함.
  2. Choreography 방식
    • 각 서비스가 자율적으로 이벤트를 발행하고, 이를 구독하여 비즈니스 로직을 진행하는 방식.
    • 확장성이 뛰어나지만, 이벤트 흐름이 복잡해지고 디버깅이 어려움.

4. Orchestration 방식을 선택한 이유

이번 프로젝트에서는 결제 로직의 트랜잭션 일관성을 유지하고, 진행 과정을 명확하게 추적하기 위해 Orchestration 방식을 선택했다.

Orchestration 방식의 장점

  1. 트랜잭션 흐름을 중앙에서 관리하여 일관성을 유지할 수 있음
    • 결제 서버가 모든 트랜잭션을 제어하므로, 실패 시 보상 트랜잭션을 실행하기 용이함.
  2. 디버깅과 모니터링이 쉬움
    • Orchestrator가 각 단계의 상태를 관리하므로 장애 발생 시 문제 원인을 빠르게 분석 가능.
  3. 기존 REST API 기반 서비스와의 통합이 용이
    • Choreography 방식은 이벤트 중심 아키텍처로 변경해야 하지만, 현재 시스템과의 호환성을 고려하여 Orchestration 방식이 더 적절함.

5. 트랜잭션 처리 방식 (동기 vs 비동기)

동기 처리 (주요 트랜잭션)

결제 성공 이후의 주요 트랜잭션(결제 내역 저장, 쿠폰 사용 업데이트, 회원 수업 횟수 증가)은 사용자에게 빠른 응답을 제공해야 하므로, 동기 방식으로 처리하였다.

  • 결제 서버에서 HTTP 요청을 직접 보내어 동기적으로 처리.
  • 빠른 응답이 필요한 로직이므로, Kafka와 같은 메시지 브로커 대신 직접 API 호출을 사용.

비동기 처리 (보상 트랜잭션)

반면, 보상 트랜잭션의 경우 즉각적인 응답이 필요하지 않으므로, Kafka 이벤트를 발행하여 비동기적으로 처리하는 것이 성능 측면에서 유리하다고 판단했다. 그러나, 보상 트랜잭션이 발생하는 빈도를 고려했을 때 Kafka를 사용하는 것이 오버스펙일 가능성도 존재했다.

하지만 Kafka를 통해 영속성을 보장할 수 있다는 점, 그리고 현재 쿠폰, 채팅, 알림 서버에서도 이미 Kafka를 사용 중이라는 점 을 고려하여, 보상 트랜잭션 또한 Kafka를 활용해 비동기적으로 처리하기로 결정했다.

6. 아웃박스 패턴 추가

아웃박스 패턴이란?

아웃박스 패턴(Outbox Pattern)은 트랜잭션 내에서 메시지를 안전하게 저장한 후, 별도의 프로세스를 통해 메시지를 전송하는 방식이다.

이를 통해 데이터 유실을 방지하고, 멱등성을 보장하며, 메시지 전송의 신뢰성을 높일수 있다.

아웃박스 패턴을 도입한 이유

비즈니스 로직 실패 시 데이터 유실 방지 및 재시도 가능

  • 외부 결제 API에서 결제 성공 응답을 받았지만, 이후 비즈니스 로직이 실패할 경우 트랜잭션이 정상적으로 진행되지 않을 수 있다.
  • 이를 방지하기 위해 아웃박스 테이블을 활용하여 결제 요청이 정상적으로 처리되지 않은 경우 재시도할 수 있도록 구현했다.
  • 즉, 결제 성공 후 비즈니스 로직이 실패하면, 아웃박스 테이블에 저장된 데이터를 확인하여 자동으로 재처리할 수 있다.

중복 응답 처리 및 멱등성 보장

  • 외부 결제 API에서 같은 결제 성공 응답이 여러 번 들어오는 경우가 발생할 수 있다.
  • 이를 방지하기 위해 아웃박스 테이블의 상태 및 고유 키(예: 트랜잭션 ID)를 활용하여 중복 처리를 방지하였다.
  • 즉, 이미 처리된 결제라면 중복으로 로직이 실행되지 않도록 설계했다.

7. 결론

  1. MSA 환경에서의 분산 트랜잭션 문제를 해결하기 위해 SAGA 패턴을 적용하였다.
  2. 트랜잭션 흐름을 중앙에서 제어하고, 관리 용이성을 높이기 위해 Orchestration 방식을 선택하였다.
  3. 비즈니스 로직 실패 시 데이터 유실을 방지하고, 중복 처리를 방지하기 위해 아웃박스 패턴을 추가하였다.
  4. 아웃박스 패턴을 통해 트랜잭션 실패 시 자동 재시도를 지원하고, 멱등성을 보장하도록 설계하였다.

아웃박스 패턴을 통해 데이터 유실 및 중복 처리를 방지하면서, Orchestration 기반 SAGA 패턴을 활용하여 안정적인 분산 트랜잭션을 구현하였다.

댓글