본문 바로가기
Spring/Core

[Spring] @Transactional 속성

by 기몬식 2023. 12. 24.

@Transactional

스프링은 @Transactional 어노테이션을 활용하여 트랜잭션 관리를 단순화합니다. 이는 트랜잭션의 관리를 설정이나 어노테이션과 같은 외부 수단을 통해 선언하는 방식인 선언적 트랜잭션(Declarative Transaction) 방식 중 하나입니다. 스프링은 선언적 트랜잭션을 처리하기 위해 Spring AOP를 활용하여 적용 대상 로직 전후에 트랜잭션 관련 기능을 삽입함으로써 핵심 비즈니스 로직과 트랜잭션 관리를 분리할 수 있습니다.

동작

스프링에서는 트랜잭션을 관리하기 위해 PlatformTransactionManager 이라는 추상화된 인터페이스를 제공합니다. @Transactional 이 적용된 메소드가 호출될 경우 트랜잭션을 시작하고 실행 결과에 따라 Commit 또는 Rollback 동작을 수행합니다.

PlatformTransactionManager 는 데이터베이스 연동 기술에 따라 각각의 구현 클래스가 제공됩니다. 이는 AbstractPlatformTransactionManager 이라는 추상 클래스를 통해 다음과 같은 워크플로 처리를 제공합니다.


  • 기존 트랜잭션 존재 여부 확인
  • 적절한 전파(propagation) 동작 적용
  • 필요한 경우 트랜잭션 일시 중지 및 재개
  • 커밋 시 롤백 전용 플래그를 확인
  • 롤백 시 실제 롤백 또는 롤백 전용 설정 적용
  • 등록된 동기화 콜백 트리거

기본 구현은 JtaTransactionManager, DataSourceTransactionManager 로 다른 트랜잭션 전략에 대한 가이드 역할을 제공하고 있습니다.


  • DataSourceTransactionManager: J데이터베이스C 기반의 트랜잭션 관리자로 DataSource를 통해 전달받은 프로퍼티를 통해 트랜잭션을 관리합니다.
  • JpaTransactionManager: JPA 기반의 트랜잭션 관리자로 EntityManagerFactory 를 통해 트랜잭션을 관리합니다.
  • JtaTransactionManager: 여러 리소스에 걸쳐 있는 트랜잭션을 처리하는 분산 트랜잭션 기반의 트랜잭션 관리자로 JNDI에서 사용 가능한 J데이터베이스C DataSource와 같이 애플리케이션 서버 리소스를 관리합니다.

속성

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    String[] label() default {};

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    String timeoutString() default "";

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};

}

@Transactional 어노테이션이 제공하는 여러 속성을 통해서 트랜잭션의 동작을 세부적으로 제어할 수 있습니다.
다음은 주요 속성에 대한 설명입니다.


  • value (또는 transactionManager): 특정 트랜잭션 매니저를 사용하기 위한 해당 트랜잭션 매니저 빈의 이름을 지정하기 위한 속성입니다.
  • label: 트랜잭션의 이름 또는 레이블을 지정하는 속성으로 주로 트랜잭션 로그 등에서 구분하기 위해 사용됩니다.
  • propagation: 트랜잭션 전파 방식을 지정하는 속성으로 다른 트랜잭션과의 상호작용을 제어합니다. 보다 자세한 내용은 아래에 작성하겠습니다.
  • isolations: 트랜잭션 격리 레벨을 지정하는 속성으로 여러 트랜잭션간의 데이터 격리 수준을 조절합니다.
  • timeout: 트랜잭션 타임아웃을 정수로 지정하는 속성으로 초단위를 사용합니다. 기본 값은 -1로 시간 제한이 없다는 것을 의미합니다.
  • timeoutString: 트랜잭션 타임아웃을 문자열로 지정하는 속성으로 초단위를 사용합니다. 기본 값은 -1로 시간 제한이 없다는 것을 의미합니다.
  • readOnly
    • 트랜잭션을 읽기 전용으로 설정하는 속성으로 데이터를 읽기만 하고 쓰기 작업은 하지 않을 때 사용합니다.
    • 다수의 트랜잭션이 병행하여 읽기 작업을 수행함으로써 락 충돌이 최소화되어 여러 트랜잭션이 동시에 데이터를 읽을 수 있습니다.
    • 읽기 작업이 많은 상황에서는 데이터베이스나 미들웨어에서 쿼리 결과를 캐시에 저장하고 재사용할 수 있습니다.
    • 변경된 데이터를 고려하지 않고 단순히 읽기 작업만 하는 경우 데이터베이스는 수행 되는 쿼리를 더 효과적인 실행 계획을 선택할 수 있습니다.
  • rollbackFor, rollbackForClassName, noRollbackFor, noRollbackForClassName:
    • 롤백이 수행되어야 하는 예외와 롤백이 수행되지 않아야 하는 예외를 지정하는 속성들입니다.
    • rollbackFor와 noRollbackFor는 예외의 클래스를 직접 지정하는 방식이며 rollbackForClassName과 noRollbackForClassName은 예외의 클래스 이름을 문자열로 지정하는 방식입니다.

Propagation

트랜잭션 전파(Transaction Propagation)란 스프링이 제공하는 기능 중 하나로 @Transactional 이 적용된 여러 메소드 호출 간에 트랜잭션 동작을 어떻게 전파할지 설정하는 것을 지원해줍니다. 즉 이미 진행 중인 트랜잭션에서 추가로 진행되는 트랜잭션을 어떻게 동작할건지에 대한 정책을 결정하는 것입니다.

 

위와 같은 속성이 필요한 것은 사실 트랜잭션은 데이터베이스에서 제공하는 기술이므로 트랜잭션 처리한다는 것은 실제 데이터베이스의 커넥션을 맺는다는 것을 의미합니다. 따라서 스프링에서는 실제 데이터베이스의 트랜잭션을 사용하는 트랜잭션을 물리 트랜잭션이라고 부르고 내부적으로 비즈니스 로직을 처리하는데 있어 물리 트랜잭션과 같이 ACID를 지원하기 위해 필요한 트랜잭션을 위해 논리 트랜잭션이라는 개념을 추가하였습니다.

 

즉 스프링은 내부적으로 트랜잭션을 물리와 논리라는 속성으로 나누었으며 이를 하나의 트랜잭션 매니저로 관리하기 위해 트랜잭션 전파 속성이 필요한 것입니다. 


다음은 물리 트랙잭션과 논리 트랜잭션에 대한 설명입니다.


  • 물리 트랜잭션: 물리 트랜잭션은 데이터베이스 엔진에서 제공하는 트랜잭션 제어 메커니즘에 의해 관리되는 트랜잭션을 나타냅니다.
  • 논리 트랜잭션: 스프링 트랜잭션 매니저가 관리하는 트랜잭션으로 비즈니스 로직이나 응용 프로그램에서의 트랜잭션을 의미하며 비즈니스 규칙과 로직을 기반으로 작업의 일관성을 유지하는 논리적인 단위를 나타냅니다.

다음은 전파 속성에 대한 설명입니다.


  • Propagation.REQUIRED: 스프링 트랜잭션의 기본 속성으로 현재 물리 트랜잭션이 존재하면 해당 트랜잭션에 참여(매핑)하고 물리 트랜잭션이 존재하지 않다면 새로운 물리 트랜잭션을 시작합니다.
  • Propagation.REQUIRES_NEW: 요청에 따라 항상 새로운 트랜잭션을 시작하며 N 개의 서로 다른 물리 트랜잭션을 가집니다. 따라서 내부에서 두번째로 호출한 트랜잭션에서 롤백이 일어나도 첫번째 내부 트랜잭션에는 영향을 미치지 않습니다.
  • Propagation.SUPPORTS: 현재 물리 트랜잭션이 존재하면 해당 트랜잭션에 참여하고 없으면 트랜잭션 없이 메소드를 실행합니다.
  • Propagation.MANDATORY: 현재 물리적 트랜잭션이 반드시 존재해야 합니다. 물리적 트랜잭션이 없으면 예외가 발생합니다.
  • Propagation.NOT_SUPPORTED: 트랜잭션 없이 논리적 트랜잭션을 시작하며 현재 물리적 트랜잭션이 존재하면 일시 중단합니다.
  • Propagation.NEVER: 트랜잭션 없이 메소드를 실행하며 트랜잭션이 존재하면 예외가 발생합니다.
  • Propagation.NESTED: 현재 트랜잭션이 존재하면 중첩된 트랜잭션을 시작합니다. 중첩된 트랜잭션은 부모 트랜잭션과 독립적으로 커밋 또는 롤백될 수 있습니다.

오탈자 및 오류 내용을 댓글 또는 메일로 알려주시면, 검토 후 조치하겠습니다.

'Spring > Core' 카테고리의 다른 글

[Spring] Spring AOP 동작 방식  (0) 2023.08.30
[Spring] Spring AOP JDK Dynamic & CGLIB Proxy 생성 방식  (0) 2023.08.28
[Spring] Spring AOP란?  (0) 2023.08.27
[Spring] DL(Dependency Lookup)  (0) 2023.07.31
[Spring] Spring Container  (0) 2023.07.27