개요

@Transactional 도큐먼트 읽어보기. 그간 궁금했던 것들 정리하기

 

궁금

  • @Transcational public/protected/private/default 설정 후 강제로 에러 발생 시, 롤백이 잘 작동하는가?
  • aop 기술을 사용함으로써 비즈니스 로직을 건드리지 않고 트랜잭션 처리가 가능한가? (혹은 이 문장이 올바른 표현인가?)
  • 트랜잭션 메커니즘이 어떻게 동작하는지

 

@Transactional

트랜잭션의 경계를 선언한다. 선언전 트랜잭션의 관리이다. 자바 표준의 javax.transaction.Transactional 은 spring 의 @Transactional 애노테이션으로 대체된다. (drop in replacement)

 

 

@Transcational 을 public/protected/private/default 메소드에 설정 후 강제로 에러 발생 시, 롤백이 작동여부

해당 동작의 조건은 아래와 같다.

  • JpaRepository 이용
  • save(E entity) 메소드 이용
  • 컨트롤러 단에서 서비스 레이어의 public 메소드를 호출

롤백 구문

Dish dish = new Dish("private-coffee".concat(number), false, 300, Dish.Type.OTHER);
dish = dishRepository.save(dish);

throw new RuntimeException("force runtime exception");

 

결과

  • public(@Transcactional) : 롤백 작동
  • public(X) : 롤백 미작동 
  • public method 에서 default method 호출하는 경우
    • public → default
      • public(@Transactional) + default(@Transactional) : 롤백 작동
      • public(@Transactional) + default(X) : 롤백 작동
      • public(X) + default(@Transactional) : 롤백 미작동
      • public(X) + default(X) : 롤백 미작동
  • public method 에서 protected method 호출하는 경우
    • public → protected
      • public(@Transactional) + protected(@Transactional) : 롤백 작동
      • public(@Transcational) + protected : 롤백 작동
      • public + protected(@Transactional) : 롤백 미작동
  • public method 에서 private method 호출하는 경우
    • public → private
      • private 에는 @Transactional 을 못 붙인다. 
      • public(@Transactional) + private(X) : 롤백 작동
      • public(X) + private(X) : 롤백 미작동

 

위의 결과를 살펴봤을때, @Transactionl 범위 내에 에러발생 시, 롤백이 일어난다. 반면에 @Transactional 을 붙이지 않은 상태에서 에러 발생 시, 롤백이 일어나지 않는다. 추가적으로 public 에 붙은 @Transactional 이 다른 메소드를 호출하면 해당 메소드도 같은 트랜잭션 범위 내에 포함되는 걸로 확인을 할 수 있었다.

 

https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction-declarative-annotations

스프링 공식 도큐먼트를 보면 프록시를 사용하는 경우에, @transcational 은 public 메소드에만 적용을 시켜야 한다고 한다. 만약 다른 접근 지정자 (protected, private, package-visible) 에도 충분하게 @Transactional 을 붙일 수 있고 에러가 발생하지는 않지만, 해당 애노테이션이 달린 메소드는 따로 구성이 되지 않고, 만약 public 이 아닌 메소드에 애노테이션을 달고싶다면 AspectJ 를 고려해달라고 한다.

 

여기서 한가지 프록시 의미는 클라이언트의 요청을 실제 대상이 처리하지 않고 프록시가 처리한다는 느낌의 대리자 느낌이 강하다. spring 에서는 @Transactional 을 클래스 또는 메소드에 붙이게 되었을 때, 비즈니스 코드를 포함하는 해당 클래스가 프록시 클래스로 래핑이 되어 동작한다.

 

그림으로 나타내면 아래와 같다.

  • 클라이언트는 target object 에 대한 business code 를 수행한다고 생각하겠지만, proxy 를 호출하여 target object 를 수행시키고 있는 것이다.
  • proxy 클래스는 특정한 보조 업무 (db connection) 이후에 business code 를 수행하고 다시 보조 업무를 수행한다. (db commit)

아까 롤백에 대한 적용 및 미적용 여부를 public/proctected/private 나뉘어 설명했는데, public 을 제외한 나머지는 접근지정자는 proxy 로 래핑되지 않기 때문에 트랜잭션 에러 발생 시 롤백을 수행하지 않는 것이다.

 

추가적으로 프록시모드에서 외부에서의 메소드 호출일 때여야 proxy 가 중간에 인터셉트를 할 수 있어 aop 처리가 가능하다. 결과적으로 외부 클래스에서 @Transactional 이 걸린 메소드를 호출하여야만 해당 메소드영역이 트랜잭션 단위로써 동작한다. 만약 프록시 클래스에서 내부 호출을 통한 @Transactional 처리를 하려고 한다면 올바르게 작동하지 않는다. 이와 관련된 타 블로그의 동일한 빈내 트랜잭션 동작방식 글이 있다. 

 

 

aop 를 사용함으로써 비즈니스 로직을 건드리지 않고 트랜잭션 처리가 가능여부

해당 글을 이해하기 위해선 aop 에 대한 개념이 필요하다. 결론부터 말하면 처리가 가능하다. 해당 내용은 여기글을 참고한다.

 

 

 

스프링 트랜잭션 메커니즘

스프링 프레임워크의 선언적 트랜잭션 관리는 spring aop 기술을 가능토록 해준다. 파악해야할 주요 핵심은 스프링 프레임워크가 지원하는 선언적 트랜잭션과 관련해서 aop proxies 통해 활성화된다는 점과 transactional advice 가 metadata 에 의해 구동된다는 의미이다.

 

aop 와 트랜잭션 메타데이터의 조합은 aop 프록시를 생성하는데, 해당 aop 프록시는 transactionInterceptor 를 사용하여 메소드 호출을 중심으로 동작한다.

 

https://docs.spring.io/spring-framework/docs/current/reference/html/images/tx.png

 

  • caller 는 aop 프록시를 실행시킨다. real target object 를 실행시키지 않는다.
  • aop proxy 에 의해서 transaction advisor 작동된다.
    • 이 때 db connection 을 맺는 동작이 이뤄진다.
  • 추가적인 custom advisor 가 있으면 그 부차적인 동작을 수행한다.
  • real target object 의 비즈니스 로직을 수행한다.
  • 그 이후에 custom advisor 에 대한 추가 후처리이후 db commit/rollback 여부를 수행한다.
  • 최종적으로 caller 에게 다시 리턴한다.

 

+) 추가로 알아볼 것

  • 트랜잭션 전파
  • isolation level 관련

 

참고자료

 

Posted by doubler
,