본문 바로가기
Spring&SpringBoot/<토비의 스프링 3.1 Vol1.1>, 이일민

애노테이션 트랜잭션과 테스트

by 민휘 2023. 4. 20.

트랜잭션 애노테이션

지금까지 포인트컷 패턴이나 트랜잭션 속성을 이용해 트랜잭션을 일괄적으로 적용하는 방법을 알아보았다. 이번에는 클래스나 메소드에 세밀하게 튜닝된 트랜잭션 속성을 적용할 때 사용하면 좋은 트랜잭션 애노테이션을 사용해본다.

 

일괄적으로 속성을 부여하는 대신 직접 타깃에 트랜잭션 속성정보를 가진 애노테이션을 지정하는 방법을 사용한다. 포인트컷은 따로 지정할 필요가 없다. 애노테이션이 붙은 메소드나 클래스가 곧 포인트컷으로 선정한 결과가 되기 때문이다. 트랜잭션 속성을 애노테이션에 지정하면 된다.

 

애노테이션으로 트랜잭션을 지정하는 방법은 유연하면서도 직관적이고 간단해서 애용되는 방법이다. 하지만 일괄적인 패턴에 트랜잭션을 적용할 때는 포인트컷을 사용하는 것이 더 경제적이고 빠뜨릴 위험도 적을 것이다.

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
	Propagation propagation() default Propagation.REQUIRED;
	Isolation isolation() default Isolation.DEFAULT;
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
	boolean readOnly() default false;
	Class<? extends Throwable>[] rollbackFor() default {};
	// ..
}

Transactional 애노테이션을 살펴보자. 메소드나 클래스, 인터페이스 타입에 사용 가능하다. 런타임까지 메모리가 살아있으므로 리플렉션으로 참조가 가능하다. 애노테이션의 타깃을 상속한 클래스에도 접근 가능하다. 그리고 애노테이션 필드로 트랜잭션 속성을 지정할 수 있다.

 

트랜잭션 애노테이션 적용하기

트랜잭션을 적용하고 싶은 메소드나 클래스, 인터페이스에 @Transactional을 붙이면 된다. 트랜잭션 속성을 지정하고 싶다면 값을 주면 된다.

 

트랜잭션 애노테이션을 적용할 때는 4단계의 대체 정책을 이용해 적용될 위치를 결정한다. 탐색 우선순위가 높은 순서로 타깃 메소드, 타깃 클래스, 선언(인터페이스) 메소드, 선언 클래스 순이다. 우선순위대로 탐색해서 트랜잭션 애노테이션이 붙어있는 것을 확인하면 적용한다.

@Transactional
public interface UserService {
    void add(User user);
    void upgradeLevels();
    
    @Transactional(readOnly = true)
    User get(String id);
}

위의 코드에서 위임 메소드에 적용된 읽기 전용 트랜잭션이 get에 적용되고, 나머지 메소드는 클래스에 달린 기본 속성의 트랜잭션이 적용된다.

 

선언적 트랜잭션과 트랜잭션 전파 속성

선언적 트랜잭션은 AOP를 이용해 코드 외부에서 트랜잭션 기술과 속성을 지정하는 기술을 말한다. 선언적 트랜잭션을 사용하기 때문에 트랜잭션 전파 속성을 사용해 기존 트랜잭션에 참여가 가능하도록 만들었다. 만약 트랜잭션 참여 속성을 사용할 수 없었다면 코드를 복붙해서 강제로 트랜잭션에 포함되도록 만들어야했다.

 

테스트를 위한 트랜잭션 애노테이션

 

@Transactional

테스트 메소드 실행 전에 새로운 트랜잭션을 만들고 메소드가 종료되면 트랜잭션을 종료한다. 테스트 안에서 실행되는 테스트 메소드는 하나의 트랜잭션으로 실행된다. 테스트용 트랜잭션은 테스트가 끝나면 자동으로 롤백된다.

 

@Rollback

테스트용 트랜잭션에서 강제 롤백을 원하지 않는 경우 혹은 커밋을 DB에 반영하고 싶은 경우 @Rollback(false)를 사용한다.

 

@TransactionConfiguration

테스트 클래스의 모든 테스트에 공통 트랜잭션 속성을 지정하고 싶다면 이 애노테이션을 사용한다. 우선순위는 개별 테스트 메소드보다 낮다.

 

@NonTransactional

@Transactional을 무시하고 트랜잭션을 실행하지 않는다. 스프링 3.0에서 제거됐으므로, 트랜잭션 테스트와 비 트랜잭션 테스트를 분리하는 것이 더 좋은 방법이다.

 

@Transactional(propagation=Propagation.NEVER)

다음과 같이 설정해도 트랜잭션이 시작되지 않는다.