MSA 환경에서 분산 트랜잭션을 처리하는 사가 패턴(Saga Pattern)을 Java로 구현

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)

MSA 환경에서 분산 트랜잭션을 처리하는 사가 패턴(Saga Pattern)을 Java로 구현

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, "키보드", "부산시 해운대구");
    }
}