Q2. Transaction Propagation은 무엇인가요?

답변

**Propagation (전파)**은 트랜잭션 메서드가 다른 트랜잭션 메서드를 호출할 때의 동작 방식을 정의합니다.

7가지 Propagation

1. REQUIRED (기본값):

// ✅ REQUIRED: 기존 트랜잭션 사용, 없으면 새로 생성
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    methodB();  // methodB도 같은 트랜잭션 사용
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // methodA와 같은 트랜잭션
}

// 실행 흐름:
// methodA() 시작 → 트랜잭션 T1 생성
//   → methodB() 호출 → T1 재사용 (새로 생성 X)
//   → methodB() 완료 → T1 유지
// → methodA() 완료 → T1 커밋

문제: methodB()에서 예외 발생 시 methodA()도 롤백

@Transactional
public void methodA() {
    userRepository.save(user1);  // 저장됨
    try {
        methodB();  // 예외 발생
    } catch (Exception e) {
        // 예외 처리
    }
    userRepository.save(user2);  // 저장 시도
}

@Transactional
public void methodB() {
    throw new RuntimeException("Error");
}

// 결과:
// methodB()에서 예외 → 트랜잭션 rollback-only 마킹
// → methodA()의 user1, user2 모두 롤백! ⚠️

2. REQUIRES_NEW:

// ✅ REQUIRES_NEW: 항상 새 트랜잭션 생성
@Transactional
public void methodA() {
    userRepository.save(user1);
    try {
        methodB();  // 새 트랜잭션에서 실행
    } catch (Exception e) {
        // methodB 롤백되어도 methodA는 영향 없음
    }
    userRepository.save(user2);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    orderRepository.save(order);
    throw new RuntimeException("Error");
}

// 실행 흐름:
// methodA() 시작 → 트랜잭션 T1 생성
//   → user1 저장 (T1)
//   → methodB() 호출 → 트랜잭션 T2 생성 (T1 일시 중단)
//     → order 저장 (T2)
//     → 예외 발생 → T2 롤백 (order 롤백)
//   → T1 재개
//   → user2 저장 (T1)
// → methodA() 완료 → T1 커밋 (user1, user2 커밋)

// 결과:
// user1: 저장 ✅
// user2: 저장 ✅
// order: 롤백 ❌

사용 사례: 감사 로그 저장

@Transactional
public void processOrder(Order order) {
    orderRepository.save(order);

    try {
        auditService.saveLog(order);  // 별도 트랜잭션
    } catch (Exception e) {
        // 로그 저장 실패해도 주문은 저장
    }
}

@Service
public class AuditService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(Order order) {
        auditLogRepository.save(new AuditLog(order));
        // 별도 트랜잭션이므로 주문과 독립적
    }
}

3. NESTED:

// ✅ NESTED: 중첩 트랜잭션 (Savepoint 사용)
@Transactional
public void methodA() {
    userRepository.save(user1);  // 커밋됨

    try {
        methodB();  // Savepoint 생성
    } catch (Exception e) {
        // methodB만 롤백, methodA는 계속 진행
    }

    userRepository.save(user2);  // 커밋됨
}

@Transactional(propagation = Propagation.NESTED)
public void methodB() {
    orderRepository.save(order);
    throw new RuntimeException("Error");
}

// 실행 흐름:
// methodA() 시작 → 트랜잭션 T1 생성
//   → user1 저장 (T1)
//   → methodB() 호출 → Savepoint S1 생성
//     → order 저장 (T1)
//     → 예외 발생 → S1으로 롤백 (order만 롤백)
//   → user2 저장 (T1)
// → methodA() 완료 → T1 커밋 (user1, user2)

// 결과:
// user1: 저장 ✅
// user2: 저장 ✅
// order: 롤백 ❌

REQUIRES_NEW vs NESTED:

특징REQUIRES_NEWNESTED
트랜잭션완전히 새로운 트랜잭션외부 트랜잭션의 일부
커밋독립적으로 커밋외부 트랜잭션과 함께 커밋
롤백서로 영향 없음내부만 롤백 가능
DB 지원모든 DBSavepoint 지원 DB만

4. SUPPORTS:

// ✅ SUPPORTS: 트랜잭션 있으면 사용, 없어도 OK
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // 트랜잭션 있으면 사용, 없으면 트랜잭션 없이 실행
}

// Case 1: 트랜잭션 O
@Transactional
public void methodA() {
    methodB();  // methodA의 트랜잭션 사용
}

// Case 2: 트랜잭션 X
public void methodA() {
    methodB();  // 트랜잭션 없이 실행
}

5. NOT_SUPPORTED:

// ✅ NOT_SUPPORTED: 트랜잭션 없이 실행
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() {
    // 항상 트랜잭션 없이 실행
}

@Transactional
public void methodA() {
    methodB();  // methodA의 트랜잭션 일시 중단
}

6. MANDATORY:

// ✅ MANDATORY: 트랜잭션 필수 (없으면 예외)
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // 트랜잭션 없으면 IllegalTransactionStateException
}

// ❌ 예외 발생
public void methodA() {
    methodB();  // 트랜잭션 없음 → 예외!
}

// ✅ 정상
@Transactional
public void methodA() {
    methodB();  // 트랜잭션 있음 → 정상
}

7. NEVER:

// ✅ NEVER: 트랜잭션이 있으면 예외
@Transactional(propagation = Propagation.NEVER)
public void methodB() {
    // 트랜잭션 있으면 IllegalTransactionStateException
}

// ❌ 예외 발생
@Transactional
public void methodA() {
    methodB();  // 트랜잭션 있음 → 예외!
}

// ✅ 정상
public void methodA() {
    methodB();  // 트랜잭션 없음 → 정상
}

Propagation 요약

Propagation기존 트랜잭션 있음기존 트랜잭션 없음사용 사례
REQUIRED재사용새로 생성기본 (99%)
REQUIRES_NEW새로 생성새로 생성독립 트랜잭션
NESTEDSavepoint새로 생성부분 롤백
SUPPORTS재사용트랜잭션 X읽기 작업
NOT_SUPPORTED일시 중단트랜잭션 X성능 최적화
MANDATORY재사용예외트랜잭션 강제
NEVER예외트랜잭션 X트랜잭션 금지


📚 다음 편: 준비 중입니다.


👈 이전 편: Spring 트랜잭션 (Part 1: 기본 동작과 원리)