fragile and resilient

Spring

[Spring] @Transactional을 통해 어떻게 트랜잭션이 보장될까?

Green Lawn 2024. 2. 21. 22:02

스프링 기반 프로젝트에서 매우 익숙하게 보실 수 있는 코드일 텐데요.
스프링에서는 @Transactional 만 붙여주면 메서드 로직의 트랜잭션이 보장됩니다.
 
디버그를 실행하여 JpaRepository를 상속한 MemberRepository 를 살펴보면 프록시라는 것을 알 수 있습니다. 

 
적용된 어드바이스들을 살펴보면 TransactionInterceptor 가 눈에 띕니다.

 
TransactionInterceptor.invoke를 보면 TransactionAspectSupport.invokeWithinTransaction를 호출하고 있는 것을 알 수 있는데요. 이 메서드가 핵심 로직입니다.
 
중요한 몇 가지 부분만 보겠습니다.

341번 라인의 getTransactionAttribute 메서드를 타고 들어가보면,
메소드에 @Transactional이 붙은지 확인하고 트랜잭션의 전파 레벨이나 격리 레벨, 타임 아웃, readOnly 등의 속성들을 설정합니다.

전문은 SpringTransactionAnnotationParser.parseTransactionAnnotation 에서 보실 수 있습니다.
 
다시 TransactionAspectSupport.invokeWithinTransaction 로 돌아와서 핵심 로직을 보겠습니다.

 
377번 라인에서 PlatformTransactionManager를 가지고 와서 382번 라인에서 트랜잭션을 생성하고 실행합니다.
388 번 라인에서 @Transactional을 선언해 둔 우리의 구현 로직을 실행하고,
392번 라인에서 예외가 발생하면 롤백하거나, 문제가 없을 경우 407번에서 커밋하는 것을 알 수 있습니다.
 
스프링에서 프록시를 통해 위와 같은 과정을 제공해주고 있기 때문에 @Transactional만 선언하면 트랜잭션을 보장할 수 있었던 것입니다.
 

결론

  • 스프링은 위와 같은 프록시 방식의 AOP를 통해 트랜잭션을 보장하고 있습니다.
  • 주의사항
    • 위 과정의 흐름을 정리해 보면 클라이언트 > 프록시 > 타깃(구현 객체) 순서인데요. 여기서 트랜잭션 적용은 중간의 프록시에서 해주고 있는 것입니다. 따라서 구현 객체 내부에서 다른 메서드를 호출하는 경우에는, 프록시를 거쳐서 오는 것이 아니기 때문에 다른 메서드에 적용된 트랜잭션 속성들이 적용되지 않기 때문에 주의해야 합니다.
    • 예를 들어 클라이언트에서 A 라는 메서드(@Transactional(readOnly = true)로 선언되어 있었음) 에서 B 메서드(@Transactional로 선언되어 있었음)를 호출하는 경우에는 readOnly false로 적용되지 않는다는 것입니다.

 
References
https://github.com/spring-projects/spring-framework