@Transactional

szlee·2025년 1월 12일

@Transactional

스프링에서 제공하는 선언적 트랜잭션 관리를 위한 어노테이션
AOP를 기반으로 동작하며, 메서드 시작 전에 트랜잭션을 시작하고 메서드 종료 시 커밋 또는 롤백을 수행
클래스나 메서드 레벨에 적용할 수 있다.

@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.DEFAULT,
    timeout = -1,
    readOnly = false,
    rollbackFor = Exception.class,
    noRollbackFor = RuntimeException.class
)

주요 속성

  • propagation : 트랜잭션 전파 레벨
  • isolation : 트랜잭션 격리 수준
  • timeout: 트랜잭션 수행 시간 제한 (초 단위)
  • readOnly: 읽기 전용 트랜잭션 여부
  • rollbackFor: 롤백을 수행할 예외 클래스 지정
    • 모든 RuntimeException과 Error에 대해서는 자동으로 롤백
    • 체크예외는 자동으로 롤백하지 않기 때문에 추가로 롤백할 예외를 지정하는 것
// 사용자 정의 체크 예외
public class InsufficientBalanceException extends Exception {
    // ...
}

@Service
public class BankingService {
    @Transactional(rollbackFor = InsufficientBalanceException.class)
    public void withdraw(long accountId, BigDecimal amount) 
            throws InsufficientBalanceException {
        // 잔액 부족 시 체크 예외 발생 & 롤백
        if (balance.compareTo(amount) < 0) {
            throw new InsufficientBalanceException();
        }
        // ...
    }
}
  • noRollbackFor: 롤백을 수행하지 않을 예외 클래스 지정

주의사항

  • private 메서드에는 @Transactional이 동작하지 않는다.
  • 같은 클래스 내부 메서드 호출 시에는 프록시가 동작하지 않아 트랜잭션이 적용되지 않을 수 있다.
  • RuntimeException과 Error는 기본적으로 롤백을 수행하고, 체크 예외는 롤백하지 않는다.


트랜잭션 전파(Propagation)

propagation 속성을 통해 트랜잭션 동작 방식을 정의할 수 있다.

REQUIRED (기본값)

  • 현재 트랜잭션이 있으면 그것을 사용하고, 없으면 새로 생성
  • 가장 많이 사용되는 기본적인 전파 방식

REQUIRES_NEW

  • 항상 새로운 트랜잭션을 생성
  • 기존 트랜잭션이 있다면 잠시 중단시키고 새 트랜잭션을 실행
  • 다음과 같은 상황에 주로 사용
    • 실패해도 로그는 반드시 남겨야 하는 경우
    • 메인 트랜잭션과 별개로 항상 커밋되어야 하는 작업이 있는 경우

SUPPORTS

  • 현재 트랜잭션이 있으면 그것을 사용하고, 없어도 그냥 진행
  • 트랜잭션이 꼭 필요하지 않은 조회 작업 등에 사용

MANDATORY

  • 반드시 기존 트랜잭션이 있어야 한다. 없으면 예외가 발생한다.

NEVER

  • 트랜잭션을 사용하지 않는다. 있으면 예외가 발생한다.

NESTED

  • 기존 트랜잭션이 있으면 중첩 트랜잭션을 생성
  • 중첩 트랜잭션은 부모 트랜잭션의 커밋/롤백에 영향을 받지만, 자신의 롤백은 부모에게 영향을 주지 않는다.

트랜잭션 격리(Isolation) 수준

DEFAULT

  • DB의 기본 격리 수준을 사용
  • 대부분의 DB는 READ_COMMITTED를 기본값으로 사용. mysql은 repeatable

READ_UNCOMMITTED

  • 가장 낮은 격리 수준
  • Dirty Read, Non-Repeatable Read, Phantom Read 현상이 발생할 수 있음
  • 성능은 가장 좋지만 데이터 정합성이 깨질 수 있음

READ_COMMITTED

  • Dirty Read를 방지
  • Non-Repeatable Read와 Phantom Read는 여전히 발생 가능
  • 일반적으로 가장 많이 사용되는 격리 수준

REPEATABLE_READ

  • Dirty Read, Non-Repeatable Read를 방지
  • Phantom Read는 여전히 발생 가능
  • 같은 데이터를 여러 번 조회해도 동일한 결과를 보장

SERIALIZABLE

  • 가장 높은 격리 수준
  • 모든 트랜잭션을 순차적으로 실행
  • 완벽한 데이터 정합성을 보장하지만 성능이 가장 떨어진다.
profile
🌱

0개의 댓글