Spring/Spring JPA

[JPA] @Transactional의 propagation 속성 자세히

개발자잡 2024. 12. 26. 15:34

@Transactional의 propagation 속성은 트랜잭션의 전파(Propagation) 동작을 정의합니다. 전파는 메서드가 실행될 때 기존 트랜잭션에 참여할지, 새로운 트랜잭션을 시작할지, 혹은 트랜잭션 없이 실행할지를 결정합니다.

 

Propagation의 주요 개념

Propagation은 트랜잭션 관리 중 다른 트랜잭션 환경과의 상호작용 방식을 정의하는 데 사용됩니다. 이는 주로 A 메서드가 트랜잭션 B 메서드를 호출할 때 트랜잭션 상태를 어떻게 처리할지를 결정합니다.

 

Propagation의 종류

스프링에서는 아래와 같은 전파 유형을 제공합니다:

 

Propagation Type 설명 예제 상황
REQUIRED 기본값. 기존 트랜잭션이 있으면 참여하고, 없으면 새 트랜잭션을 시작합니다. 서비스 계층에서 트랜잭션이 없으면 새 트랜잭션 시작. 이미 트랜잭션이 있으면 해당 트랜잭션에 참여.
REQUIRES_NEW 항상 새로운 트랜잭션을 생성하고, 기존 트랜잭션을 일시 중단합니다. 독립적인 트랜잭션이 필요할 때, 예를 들어 로그를 저장하는 작업.
SUPPORTS 트랜잭션이 있으면 참여하고, 없으면 트랜잭션 없이 실행됩니다. 메서드가 반드시 트랜잭션에 의존하지 않는 경우.
NOT_SUPPORTED 항상 트랜잭션 없이 실행하며, 기존 트랜잭션이 있으면 일시 중단됩니다. 배치 작업 등 트랜잭션이 필요 없는 작업.
MANDATORY 반드시 기존 트랜잭션이 있어야 하며, 없으면 예외를 발생시킵니다. 트랜잭션 없이 실행되면 안 되는 핵심 작업.
NEVER 트랜잭션 없이 실행되며, 트랜잭션이 이미 있으면 예외를 발생시킵니다. 트랜잭션에서 실행되면 안 되는 작업.
NESTED 기존 트랜잭션이 있으면 중첩 트랜잭션을 생성하고, 없으면 새로운 트랜잭션을 시작합니다. (Savepoint 사용) 트랜잭션 내부에서 독립적으로 롤백할 필요가 있는 경우.

Propagation 종류별 동작 방식

1. REQUIRED (기본값)

  • 기존 트랜잭션이 존재하면 참여합니다.
  • 기존 트랜잭션이 없으면 새 트랜잭션을 생성합니다.
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // A 메서드에서 새로운 트랜잭션 시작
    methodB(); // 이미 존재하는 트랜잭션에 참여
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // 트랜잭션이 생성되지 않음 (A의 트랜잭션에 참여)
}

2. REQUIRES_NEW

  • 항상 새로운 트랜잭션을 생성합니다.
  • 기존 트랜잭션은 일시 중단됩니다.
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    methodB(); // 새로운 트랜잭션 시작
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // methodB의 트랜잭션은 methodA와 독립적으로 동작
}
  • 사용 예: 로그 저장이나 비즈니스 로직이 독립적으로 처리되어야 하는 경우.

3. SUPPORTS

  • 트랜잭션이 존재하면 참여합니다.
  • 트랜잭션이 없으면 트랜잭션 없이 실행됩니다.
@Transactional(propagation = Propagation.SUPPORTS)
public void method() {
    // 트랜잭션이 존재하면 참여, 없으면 트랜잭션 없이 실행
}
  • 사용 예: 읽기 전용 작업 또는 트랜잭션이 필수적이지 않은 작업.

4. NOT_SUPPORTED

  • 트랜잭션이 있으면 일시 중단하고, 항상 트랜잭션 없이 실행됩니다.
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method() {
    // 항상 트랜잭션 없이 실행
}

 

  • 사용 예: 성능이 중요한 비트랜잭션 작업

5. MANDATORY

  • 반드시 기존 트랜잭션이 존재해야 합니다.
  • 트랜잭션이 없으면 예외를 던집니다.
@Transactional(propagation = Propagation.MANDATORY)
public void method() {
    // 트랜잭션이 없으면 IllegalTransactionStateException 발생
}
  • 사용 예: 트랜잭션 없이 실행되면 안 되는 중요한 작업.

6. NEVER

  • 트랜잭션 없이 실행됩니다.
  • 트랜잭션이 존재하면 예외를 던집니다.
@Transactional(propagation = Propagation.NEVER)
public void method() {
    // 트랜잭션이 존재하면 예외 발생
}
  • 사용 예: 트랜잭션 환경에서 실행되면 안 되는 작업.

7. NESTED

  • 기존 트랜잭션이 있으면 중첩 트랜잭션을 생성합니다.
  • 기존 트랜잭션이 없으면 새로운 트랜잭션을 생성합니다.
  • 중첩 트랜잭션은 부모 트랜잭션과 독립적으로 롤백할 수 있습니다(Savepoint 사용).
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    methodB(); // 중첩 트랜잭션 생성
}

@Transactional(propagation = Propagation.NESTED)
public void methodB() {
    // 부모 트랜잭션에 종속적이나 독립적으로 롤백 가능
}
  • 사용 예: 부모 트랜잭션 일부를 롤백해야 할 때.

 

 

Propagation 요약

  • REQUIRED: 기본값으로, 기존 트랜잭션이 있으면 참여하고, 없으면 새 트랜잭션 시작.
  • REQUIRES_NEW: 항상 새로운 트랜잭션 시작.
  • SUPPORTS: 트랜잭션이 있으면 참여, 없으면 비트랜잭션으로 실행.
  • NOT_SUPPORTED: 항상 트랜잭션 없이 실행.
  • MANDATORY: 반드시 기존 트랜잭션이 있어야 실행 가능.
  • NEVER: 트랜잭션이 있으면 예외 발생.
  • NESTED: 부모 트랜잭션에 종속적이나 독립적으로 롤백 가능.

 

Propagation 선택 기준

  1. 비즈니스 요구사항에 따라 독립적인 트랜잭션이 필요한지 여부를 판단.
  2. 성능 고려: 불필요한 새 트랜잭션 생성을 피하기 위해 REQUIRED를 우선적으로 사용.
  3. 특수 상황: 로그 저장, 비트랜잭션 처리 등은 REQUIRES_NEW, NOT_SUPPORTED 등을 고려.

Propagation은 비즈니스 요구사항과 데이터 무결성을 고려해 적절히 선택해야 합니다.