Spring

@Transactional 사용 시 주의해야하는 8가지

Flambee 2021. 3. 21. 11:39

@Transactional 주의점

지난번에 @Transactional에 대해서 알아보았다. 오늘은 @Transactional을 사용시 주의점에 대해서 8가지정도 알아보려고 합니다.

@Transactional이 무엇인지 궁금하다면, 아래의 링크를 통하여 한번 보고 오시길 바랍니다.

 

@Transactional 이란?

 

@Transactional

Spring을 공부하다보면 AOP(Aspect oriented programming) 라는 주제가 항상 나온다. 그 AOP라는 주제에 대해서 공부하다보면 가장 먼저 나오는 AOP의 주제는 바로 @Transactional을 구현하는 것이다. 해당 @Tran..

flambeeyoga.tistory.com

@Transactional 사용 시 주의해야하는 8가지

1. @Transactional의 우선순위.

클래스 메소드 -> 클래스 -> 인터페이스 메소드 -> 인터페이스 의 우선순위를 가지고있다.

2. @Transactional의 Mode는 Proxy Mode와 Aspect Mode가 있다. Default는 Proxy Mode이다.

ProxyMode (Default로 사용되는 Mode)

- Public Method에 적용되어야 합니다.(Protected, Private Method에서 선언되지만 작동하지 않아요.)

- @Trnasactional은 Proxy 외부에서 접근해야 AOP가 적용된다.

- @Transactional이 적용되지 않은 Public Method에서 @Trnasactional이 적용된 Public Method를 호출할 경우, 트랜잭션이 동작하지 않는다.

- self-invocation 상황(내부 호출)에서는 적용되지 않는다.(Spring AOP CGLIB이 동작하지 않음.)

     ->  내부 메소드에 대해서 Proxy 객체가 감싸지지 않았기 때문이다.

 

CGLIB(Code Generator Library) :
1. Enhancer 클래스로 Proxy 구현. JDK Dynamic Proxy와 다르게 Reflection을 사용하지 않고, 
Extends을 이용해서 Proxy화 할 메서드를 오버라이딩 하는 방식.
2. 기본적으로 Byte 코드를 조작해서, 바이너르가 만들어지기 때문에 JDK Dynamic Proxy보다 성능적으로 좋다.
3. final 객체 혹은 private 접근자로 된 메서드는 상속이 지원하지 않는다.
-> 해당 상속이 지원되지 않기 때문에 Proxy Method내에서 작동을 안하는 것.
4. Spring Boot는 기본적으로 CGLIB 프록시를 사용하도록 설정해놓았다. 
-> 즉, 인터페이스가 있든 없든 상관없이 무조건 CGLIB를 사용해서 AOP가 가능하다.
5. Spring은 proxy 객체를 사용하기 때문에 interface가 필요하다.

 

Aspect Mode

- Non-Public 메서드에 적용할 때 고려.

- 직접 바이트 코드를 주입하는 형식이기 때문에, 자신의 클래스 내부 메소드를 호출해도, 트랜잭션이 코드가 포함된 메서드를 호출한다.

- @EnableTransactionManagement(proxyTargetClass = true, mode = AdviceMode.ASPECTJ)

3. @Transactional은 unchecked Exception(Runtime Exception)만 관리, checked Exception(IOException, SQLException)은 관리하지 않는다.

unchecked Exception -> Rollback 가능

checkedException -> Rollback 불가능

 

@Transactional(rollbackFor = Exception.class) 사용시 checkedExcpetion도 롤백 할 수 있음.

하지만, 체크드 예외가 발생하는 부분이 있다면, 위에 명령어보다, try .. catch로 명시적으로 Exception을 발생시키는 것이 좋다.

4. 단일 작업에 대해서 JPA를 사용 시 @Transactional을 직접 선언할 필요가 없다.

- JPA구현체에 모든 메서드에 @Transactional이 선언되어 있다.

JPA SaveMethod

5.  @Transactional을 선언한 Method가 부모 Method가 되며, 단일 작업이 아닌 여러 작업이 필요한 경우 부모 Method에 @Transcational을 선언하면 된다.

- 전체 트랜잭션이 부모 Method에 대한 트랜잭션으로 묶이기 때문

6.  @Transactional을 선언한 Method가 과연 다른 Class의 @Transactioanl Method에서도 Proxy가 잘 통할까?

- 각각 Porxy로 감싸여있어서, 같은 트랜잭션내에서 사용이 가능하다고 한다.

7.  같은 Class내에서 여러 작업을 해야하는 경우 메서드에 @Transactional을 선언한 뒤 같은 Class내의 메서드에서도 @Transactional이 같은 트랜잭션을 바라볼까?

class testClass { 
    
    public test1() {
    	save();
    	test2();
    }
    
    @Transactional
    public test2() {
    	save2();
    }

}

해당 방법은 동일한 Bean(Class)에서 @Transactional이 발생하기 때문에 결론적으로는 바라보지 않는다고 한다.

->  Spring의 AOP 프록시가 확장되지 않고 호출을 가로 채기 위해 서비스 인스턴스를 래핑하기 때문이다. 즉, 해당 test2()는 프록시로 감싸여있지 않고, 내부에서 호출하기 때문이다.

 

바라보기 위한 방법은 세가지가 있다.

1. 같은 트랜잭션에서 실행되야하며, 각각의 메소드에서 실행이 되는 @Transactional 메소드 중 부모가 아닌 Method를 다른 클래스로 빼내는 방법.

 

2. Self Autowired 방법을 사용하는 방법. (Spring4 부터는 Self Autowried 기능을 사용할 수 있다.)

 -> 생성자 주입을 하지말고, @Autowired를 사용하길 바란다.

 -> this.xxx로 순수한 method가 아닌 proxy로 한번 감싸진 method(@Autowired)를 사용할 수 있게 하자.

@Transactional 메서드는 클래스 내부적으로 사용하지말고, 밖에서 사용하고, 굳이 내부적으로 사용하려면, 자기 자신의 Proxy(Self Autowried)하여 사용하자.

 

3. TransactionTemplate 방법도 있다.

8.  JPA 사용시에 오직 SELECT를 위한 @Transactional이라면 (ReadOnly = true)를 사용하자.

Hibernate의 Session Flush가 MANUAL 설정이 되어 트랜잭션 커밋 시 자동으로 Flush하지 않기 때문에 SELECT에 대해서 좀 더 성능최적화를 기대할 수 있다.


이정도만 알아도 @Transactional을 사용하는데에 어렵지 않다고 보고, side effect를 피할 수 있다고 봅니다.

 

이상 @Trnsactional의 8가지 주의사항 입니다.

 

읽어주셔서 감사해요!

 

저는 요가하러 가보겠습니다~