Spring Transaction Option

강정우·2024년 2월 12일
0

Spring-boot

목록 보기
68/73

트랜잭션 옵션

@Transactional
public @interface Transactional {
    String value() default "";
    String transactionManager() default "";
    Class<? extends Throwable>[] rollbackFor() default {};
    Class<? extends Throwable>[] noRollbackFor() default {};
    Propagation propagation() default Propagation.REQUIRED;
    Isolation isolation() default Isolation.DEFAULT;
    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
    boolean readOnly() default false;
    String[] label() default {};
}

옵션들을 요약을 하자면 위와 같다.

value, transactionManager

트랜잭션을 사용하려면 먼저 스프링 빈에 등록된 어떤 트랜잭션 매니저를 사용할지 알아야 한다.
생각해보면 코드로 직접 트랜잭션을 사용할 때 분명 트랜잭션 매니저를 주입 받아서 사용했다. @Transactional 에서도 트랜잭션 프록시가 사용할 트랜잭션 매니저를 지정해주어야 한다.
사용할 트랜잭션 매니저를 지정할 때는 value , transactionManager 둘 중 하나에 트랜잭션 매니저의 스프링 빈의 이름을 적어주면 된다.
이 값을 생략하면 기본으로 등록된 트랜잭션 매니저를 사용하기 때문에 대부분 생략한다. 그런데 사용하는 트랜잭션 매니저가 둘 이상이라면 다음과 같이 트랜잭션 매니저의 이름을 지정해서 구분하면 된다.

public class TxService {

	@Transactional("memberTxManager")
    public void member() {...}
    
    @Transactional("orderTxManager")
    public void order() {...}
}

참고로 애노테이션에서 속성이 하나인 경우 위 예처럼 value 는 생략하고 값을 바로 넣을 수 있다.

rollbackFor

예외 발생시 스프링 트랜잭션의 기본 정책은 다음과 같다.

  1. 언체크 예외인 RuntimeException , Error 와 그 하위 예외가 발생하면 롤백한다.
  2. 체크 예외인 Exception 과 그 하위 예외들은 커밋한다.

그런데 이 옵션을 사용하면 기본 정책에 추가로 어떤 예외가 발생할 때 롤백할 지 지정할 수 있다.

@Transactional(rollbackFor = Exception.class) 

예를 들어서 이렇게 지정하면 체크 예외인 Exception 이 발생해도 롤백하게 된다. (하위 예외들도 대상에 포함된다.)

참고로 rollbackForClassName 도 있는데, rollbackFor 는 예외 클래스를 직접 지정하고, rollbackForClassName 는 예외 이름을 문자로 넣으면 된다.

noRollbackFor

앞서 설명한 rollbackFor 와 반대이다. 기본 정책에 추가로 어떤 예외가 발생했을 때 롤백하면 안되는지 지정할 수 있다.
예외 이름을 문자로 넣을 수 있는 noRollbackForClassName 도 있다.

propagation

트랜잭션 전파에 대한 옵션이다.

isolation

트랜잭션 격리 수준을 지정할 수 있다. 기본 값은 데이터베이스에서 설정한 트랜잭션 격리 수준을 사용하는 DEFAULT 이다. 대부분 데이터베이스에서 설정한 기준을 따른다. 애플리케이션 개발자가 트랜잭션 격리 수준을 직접 지정하는 경우는 드물다.

  • DEFAULT : 데이터베이스에서 설정한 격리 수준을 따른다.
  • READ_UNCOMMITTED : 커밋되지 않은 읽기
  • READ_COMMITTED : 커밋된 읽기
  • REPEATABLE_READ : 반복 가능한 읽기
  • SERIALIZABLE : 직렬화 가능

참고: 강의에서는 일반적으로 많이 사용하는 READ COMMITTED(커밋된 읽기) 트랜잭션 격리 수준을 기준으로 설명한다.
트랜잭션 격리 수준은 데이터베이스에 자체에 관한 부분이어서 이 강의 내용을 넘어선다. 트랜잭션 격리 수준에 대한 더 자세한 내용은 데이터베이스 메뉴얼이나, JPA 책 16.1 트랜잭션과 락을 참고하자.

timeout

트랜잭션 수행 시간에 대한 타임아웃을 초 단위로 지정한다. 기본 값은 트랜잭션 시스템의 타임아웃을 사용한다. 운영 환경에 따라 동작하는 경우도 있고 그렇지 않은 경우도 있기 때문에 꼭 확인하고 사용해야 한다.
timeoutString 도 있는데, 숫자 대신 문자 값으로 지정할 수 있다.

label

트랜잭션 애노테이션에 있는 값을 직접 읽어서 어떤 동작을 하고 싶을 때 사용할 수 있다. 일반적으로 사용하지 않는다.

readOnly

트랜잭션은 기본적으로 읽기 쓰기가 모두 가능한 트랜잭션이 생성된다.
readOnly=true 옵션을 사용하면 읽기 전용 트랜잭션이 생성된다. 이 경우 등록, 수정, 삭제가 안되고 읽기 기능만 작동한다. (드라이버나 데이터베이스에 따라 정상 동작하지 않는 경우도 있다.) 그리고 readOnly 옵션을 사용하면 읽기에서 다양한 성능 최적화가 발생할 수 있다.
readOnly 옵션은 크게 3곳에서 적용된다.

프레임워크

JdbcTemplate은 읽기 전용 트랜잭션 안에서 변경 기능을 실행하면 예외를 던진다.
JPA(하이버네이트)는 읽기 전용 트랜잭션의 경우 커밋 시점에 플러시를 호출하지 않는다. 읽기 전용이니 변경에 사용되는 플러시를 호출할 필요가 없다. 추가로 변경이 필요 없으니 변경 감지를 위한 스냅샷 객체도 생성하지 않는다. 이렇게 JPA에서는 다양한 최적화가 발생한다.
JPA 관련 내용은 JPA를 더 학습해야 이해할 수 있으므로 지금은 이런 것이 있다 정도만 알아두자.

JDBC 드라이버

참고로 여기서 설명하는 내용들은 DB와 드라이버 버전에 따라서 다르게 동작하기 때문에 사전에 확인이 필요하다.
읽기 전용 트랜잭션에서 변경 쿼리가 발생하면 예외를 던진다.
읽기, 쓰기(마스터, 슬레이브) 데이터베이스를 구분해서 요청한다. 읽기 전용 트랜잭션의 경우 읽기(슬레이브) 데이터베이스의 커넥션을 획득해서 사용한다.
예시

데이터베이스

데이터베이스에 따라 읽기 전용 트랜잭션의 경우 읽기만 하면 되므로, 내부에서 성능 최적화가 발생한다.

그러나 readOnly를 주는 것이 항상 성능 최적화를 불러오는 것은 아니다.
성능에 따라 테스트를 해봐야한다. 왜냐하면
readOnly를 넣어주면 네트워크를 통해 DB에 readOnly Transaction이 한 번 전송이 되기 때문이다.

즉, 어쩌면 불필요한 네트워크 통신이 한 번 더 이루어지기 때문에 비용이 든다는 뜻이다.
하지만 이건 특수한 경우고 이게 DB, 로직 마다 모두 다르기 때문에 성능을 테스트 해보고 결정하는 것이 좋다.
하지만 일반적으로는 read-only를 쓰는 것이 더 낫다. 로 정리해볼 수 있겠다.

profile
智(지)! 德(덕)! 體(체)!

0개의 댓글