8월 30, 2023

[Spring] @Transactional 요소를 사용한 트랜잭션 정의

지난 글에서는

 

[Spring] Hibernate 객체 상태 구분 (transient, persistent, removed, detached)

 

HIbernate 세션으로 트랜잭션에 바인딩하는 과정, 그리고 그로 인해 변하는 객체 상태 구분에 대해 알아보았다. 

 

Spring의 트랜잭션 프레임워크가 이러한 과정을 대행해주기 때문에 개발자는 도메인 로직을 위주로 개발을 진행하게 된다. 그리고 프레임워크가 대부분의 일을 해주기 때문에 개발자는 트랜잭션의 경계가 어디까지이며 해당 트랜잭션 수행방식의 구체적인 옵션을 설정해주면 되는데, 오늘은 이와 같은 옵션들에 대해 알아보도록 하겠다. 

 

Spring의 @Transactional annotation을 사용하여 이러한 일을 해줄 수 있고 이러한 방식으로 트랜잭션을 지정하는 것을 선언적 트랜잭션 관리라고 한다. 

 

@Transactional 요소를 사용하여 트랜잭션을 정의할 수 있으며 사용할 수 있는 옵션에는 propagation, isolation, timeout, readOnly, rollbackFor, rollbackForClassName, noRollbackFor, noRollbackForClassName이 있다.

 

오늘은 이와 같은 옵션을 하나하나 세부적으로 알아보도록 하겠다.


 

1. propagation

propagation은 트랜잭션의 전파 동작을 정의하는 요소이다. 기본값은 REQUIRED 이며 기본값 이외에도 다양한 값을 지정할 수 있다.

  • REQUIRED: 기존에 이미 시작된 트랜잭션이 있으면 존재하는 트랜잭션에 참여하고, 기존에 시작된 트랜잭션이 없다면 새로운 트랜잭션을 시작하는 옵션. 특별히 언급이 없으면 REQUIRED 옵션이 기본값으로 사용된다.
  • MANDATORY: 기존 트랜잭션이 있다면 참여한다는 점에서 REQUIRED 와 유사하나, 기존에 시작된 트랜잭션이 없다면 새로운 트랜잭션을 시작하지 않고 예외를 발생시킨다. 독립적인 트랜잭션으로 수행되면 안되는 경우에 주로 사용한다.
  • NESTED: 이름이 의미하는 바와 같이 이미 수행되고 있는 트랜잭션이 있을 경우 중첩하여 트랜잭션을 진행한다. 
  • NOT_SUPPORTED: 이미 진행하고 있는 트랜잭션이 있다면 보류하고, 트랜잭션 없이 작업을 수행한다.
  • REQUIRES_NEW: 항상 새로운 트랜잭션을 시작하며, 기존 진행중인 트랜잭션은 보류한다.
  • SUPPORTS: 이미 진행중인 트랜잭션이 있다면 그 트랜잭션에 참여하고, 기존 진행중인 트랜잭션이 있다면 보류한다. 

2. isolation 

isolation은 트랜잭션의 격리 수준을 정의하는 옵션으로, 기본값으로는 DEFAULT를 가진다. 

이외에도 아래와 같은 다양한 옵션을 사용할 수 있다. 

  • DEFAULT: DB에서 기본 설정된 격리 수준을 따르는 옵션
  • READ_COMMITTED: Commit 된 데이터만 읽을 수 있는 옵션
  • READ_UNCOMMITED: 아직 Commit 되지 않은 데이터 또한 읽을 수 있는 옵션
  • REPEATABLE_READ: 트랜잭션이 종료될 때까지 그 영역에 해당하는 데이터를 다른 트랜잭션이 수정할 수 없는 옵션
  • SERIALIZABLE: 가장 높은 수준의 격리 수준을 가지는 옵션으로, 읽기 일관성 모드를 지원한다. 

DEFAULT 부터 아래로 내려갈 수록 격리의 수준이 점점 높아진다고 생각하면 된다. 


3. readOnly

readOnly는 트랜잭션이 읽기 전용인지를 판정한다. 기본적으로 readOnly의 기본값은 false이며, 설정을 통해 true로 설정할 수도 있다. 

메소드 위에 아래와 같이 annotation을 설정하면 readOnly의 값을 true로 설정할 수 있다.

@Transactional(readonly = true)

4. timeOut

timeOut은 트랜잭션의 제한시간을 초 단위로 정의하며 지정한 시간 내에 메소드가 수행완료되지 않으면 rollback된다. 

timeOut으로 정수의 값을 줄 수 있으며, 디폴트 값으로 timeOut의 값은 -1 이고 이 뜻은 시간 제한이 없다는 의미이다.


5. rollbackFor

특정 예외가 발생시 강제로 롤백을 시켜주는 옵션이다. 

@Transactional(rollbackFor=Exception.class)
public void addCompany(Company company) throws Exception {
	// 메소드 로직
}

이런식으로 사용할 수 있다. 


6. rollbackForClassName

rollbackForClassName 요소는 rollbackFor과 유사하지만 클래스 이름과 함께 사용된다는 점이 다른 점이다.


7. noRollbackFor

특정 예외가 발생시에 rollback 하지 않는다는 뜻이다. 위 rollbackFor과 동일하게

@Transactional(noRollbackFor=Exception.class)
public void addCompany(Company company) throws Exception {
	// 메소드 로직
}

위와 같이 사용할 수 있다.


8. noRollbackForClassName

noRollbackFor과 유사하지만 noRollbackForClassName은 클래스 이름과 함께 사용될 수 있다는 것이 다른 점이다.