MSA 환경에서 분산 트랜잭션을 처리하는 **사가 패턴(Saga Pattern)**을 Java로 구현해 드립니다. Spring Boot 환경을 가정하며, 오케스트레이션(Orchestration) 방식을 사용하여 주문 취소 시 보상 트랜잭션을 실행하는 예시입니다.
💻 Java Saga Pattern 소스 코드 (Spring Boot 가정)
1. 서비스 인터페이스
각 마이크로서비스의 역할을 시뮬레이션합니다.
Java
// PaymentService.java
public interface PaymentService {
boolean processPayment(String orderId, int amount);
boolean refundPayment(String orderId); // 보상 트랜잭션
}
// InventoryService.java
public interface InventoryService {
boolean decreaseStock(String orderId, String item);
boolean increaseStock(String orderId, String item); // 보상 트랜잭션
}
// ShippingService.java
public interface ShippingService {
boolean createShipping(String orderId, String address);
boolean cancelShipping(String orderId); // 보상 트랜잭션
}
2. 서비스 구현체 (결제 실패 시뮬레이션 포함)
결제 서비스는 특정 확률로 실패를 시뮬레이션하도록 구현합니다.
Java
// PaymentServiceImpl.java
import java.util.Random;
public class PaymentServiceImpl implements PaymentService {
private final Random random = new Random();
@Override
public boolean processPayment(String orderId, int amount) {
System.out.printf("[PaymentService] %s: 결제 시도 - %d원\n", orderId, amount);
// 33% 확률로 실패 시뮬레이션
if (random.nextInt(3) == 0) {
System.out.printf("[PaymentService] %s: ❌ 결제 실패\n", orderId);
return false;
}
System.out.printf("[PaymentService] %s: ✅ 결제 성공\n", orderId);
return true;
}
@Override
public boolean refundPayment(String orderId) {
System.out.printf("[PaymentService] %s: ⏪ 보상 트랜잭션: 결제 환불 완료\n", orderId);
return true;
}
}
// InventoryServiceImpl.java
public class InventoryServiceImpl implements InventoryService {
@Override
public boolean decreaseStock(String orderId, String item) {
System.out.printf("[InventoryService] %s: 재고 감소 시도 - %s\n", orderId, item);
System.out.printf("[InventoryService] %s: ✅ 재고 감소 성공\n", orderId);
return true; // 성공 가정
}
@Override
public boolean increaseStock(String orderId, String item) {
System.out.printf("[InventoryService] %s: ⏪ 보상 트랜잭션: 재고 원상 복구 완료\n", orderId);
return true;
}
}
// ShippingServiceImpl.java
public class ShippingServiceImpl implements ShippingService {
@Override
public boolean createShipping(String orderId, String address) {
System.out.printf("[ShippingService] %s: 배송 정보 생성 시도 - %s\n", orderId, address);
System.out.printf("[ShippingService] %s: ✅ 배송 정보 생성 성공\n", orderId);
return true; // 성공 가정
}
@Override
public boolean cancelShipping(String orderId) {
System.out.printf("[ShippingService] %s: ⏪ 보상 트랜잭션: 배송 준비 취소 완료\n", orderId);
return true;
}
}
3. 오케스트레이터 (OrderService)

OrderService가 Saga의 흐름을 조정하며, 실패 시 try-catch 블록 내에서 보상 트랜잭션을 역순으로 실행합니다.
Java
// OrderService.java (Saga Orchestrator)
public class OrderService {
private final PaymentService paymentService;
private final InventoryService inventoryService;
private final ShippingService shippingService;
// 생성자 (Spring에서는 DI로 주입됨)
public OrderService() {
this.paymentService = new PaymentServiceImpl();
this.inventoryService = new InventoryServiceImpl();
this.shippingService = new ShippingServiceImpl();
}
public String createOrder(String orderId, int amount, String item, String address) {
System.out.println("\n--- 🛒 주문 프로세스 시작 (Saga) ---");
// 트랜잭션의 성공 여부를 추적 (보상 로직에서 활용)
boolean paymentProcessed = false;
boolean inventoryDecreased = false;
try {
// 1. 결제
if (!paymentService.processPayment(orderId, amount)) {
throw new RuntimeException("Payment Failed");
}
paymentProcessed = true;
// 2. 재고 감소
if (!inventoryService.decreaseStock(orderId, item)) {
throw new RuntimeException("Inventory Decrease Failed");
}
inventoryDecreased = true;
// 3. 배송 정보 생성
if (!shippingService.createShipping(orderId, address)) {
throw new RuntimeException("Shipping Creation Failed");
}
System.out.println("\n--- 🎉 주문 완료 (Saga 성공) ---");
return "SUCCESS";
} catch (RuntimeException e) {
// 🚨 실패 시 보상 트랜잭션 시작 (Saga의 핵심)
System.err.println("\n--- 🚨 주문 실패 감지: " + e.getMessage() + " ---");
System.out.println("--- 🔙 보상 트랜잭션 시작 ---");
// 실패한 단계 이전의 성공한 트랜잭션들을 역순으로 보상 실행
// (3단계의 보상) 배송 취소 (3단계가 실패했더라도 안전하게 호출)
shippingService.cancelShipping(orderId);
// (2단계의 보상) 재고 원상 복구
if (inventoryDecreased) {
inventoryService.increaseStock(orderId, item);
}
// (1단계의 보상) 결제 환불
if (paymentProcessed) {
paymentService.refundPayment(orderId);
}
System.out.println("--- 🛑 보상 트랜잭션 완료: 최종 일관성 유지 ---");
return "FAILURE";
}
}
}
4. 메인 클래스 (실행)
Java
// MainApplication.java
public class MainApplication {
public static void main(String[] args) {
OrderService orderService = new OrderService();
// 성공 또는 실패를 확인하기 위해 여러 번 실행
orderService.createOrder("ORD-001", 50000, "노트북", "서울시 강남구");
orderService.createOrder("ORD-002", 20000, "마우스", "경기도 수원시");
orderService.createOrder("ORD-003", 10000, "키보드", "부산시 해운대구");
}
}