Spring

@Transactional 동작 원리

꾸준함. 2023. 3. 29. 00:46

용어 정리

트랜잭션 관리에는 크게 두 가지 방법이 있습니다.

  • 선언 방식의 트랜잭션 관리
  • 프로그래밍 방식의 트랜잭션 관리

 

선언적 트랜잭션 관리

 

  • @Transactional 애노테이션 하나만 선언해서 편리하게 트랜잭션을 적용하는 방법
  • "트랜잭션을 적용하겠다"라고 메서드나 서비스에 선언하기만 하면 AOP를 통해 트랜잭션이 적용되는 방식

 

프로그래밍 방식의 트랜잭션 관리

 

  • PlatformTransactionManager 또는 TransactionTemplate 등을 통해 트랜잭션 관련 코드를 직접 작성하는 방법

 

이번 게시글에서는 선언적 트랜잭션 관리의 동작 원리에 대해 알아보겠습니다.

 

Transaction AOP 전체 흐름

@Transactional 애노테이션이 선언되면 AOP를 통해 트랜잭션이 적용되므로 Transaction AOP 전체 흐름을 알아야 내부 동작 원리를 파악할 수 있습니다.

 

 

  • 사용자가 @Transactional이 붙은 메서드를 호출하면 AOP가 적용된 CGLIB proxy를 호출
  • 트랜잭션이 시작되고 Springboot가 자동으로 등록시킨 PlatformTransactionManager를 Spring Container로부터 획득
  • PlatformTransactionManager의 getTransaction() 메서드를 호출
  • 데이터소스와 커넥션을 생성하고 TransactionSynchronizationManager에 커넥션을 보관
  • 프록시가 실제 비즈니스 로직을 호출
  • 비즈니스 로직 내 데이터소스와 연동할 때 TransactionSynchronizationManager로부터 보관한 커넥션 획득

 

1. 사용자가 @Transactional이 붙은 메서드를 호출하면 AOP가 적용된 프록시를 호출

 

실제로 메서드에 @Transactional 애노테이션이 붙은 서비스의 class를 보면 아래와 같이 CGLIB Proxy인 것을 확인할 수 있습니다.

 

트랜잭션 AOP는 TransactionAutoConfiguration 클래스에서 확인할 수 있습니다.

 

TransactionAutoConfiguration > EnableTransactionManagementConfiguration

 

AOP 관련 참고할만한 클래스

  • Advisor: BeanFactoryTransactionAttributeSourceAdvisor
  • Pointcut: TransactionAttributeSourcePointcut
  • Advice: TransactionInterceptor

 

 

2 ~ 6. 스프링 컨테이너 통해 트랜잭션 매니저 획득 -> 트랜잭션 동기화 커넥션 획득

 

스프링 부트는 자동으로 PlatformTransactionManager를 빈으로 등록하는데 트랜잭션 매니저는 크게 두 가지 역할을 수행합니다.

  • 트랜잭션 추상화
  • 리소스 추상화

 

트랜잭션 추상화

 

트랜잭션을 사용하는 코드는 데이터 접근 기술마다 다릅니다.

JDBC 기술을 사용할 경우 JDBC 트랜잭션 코드에 의존하고 JPA 기술을 사용할 경우 JPA 트랜잭션 코드에 의존합니다.

기술을 변경할 때마다 대규모 코드 수정을 진행할 수 없으므로 스프링의 꽃인 DI와 OCP 원칙을 지키도록 트랜잭션 기능을 추상화한 것이 PlatformTransactionManager 인터페이스입니다.

스프링은 데이터 접근 기술마다 트랜잭션 구현체도 대부분 만들어두었기 때문에 개발자는 가져다 쓰기만 하면 됩니다.


 

 

 

getTransaction() 메서드는 트랜잭션을 시작하거나 기존에 이미 진행 중인 트랜잭션이 있는 경우 해당 트랜잭션에 참여합니다.

commit과 rollback 메서드는 수행하는 역할이 자명하므로 설명을 생략하겠습니다.

 

리소스 동기화

 

트랜잭션을 유지하기 위해서는 트랜잭션의 시작부터 끝까지 같은 데이터소스와 커넥션을 유지해야 합니다.

이를 직접 구현할 경우 매개변수로 계속 Connection을 넘겨줘야 하기 때문에 코드가 지저분해지는 단점이 생기고 트랜잭션의 유무에 따라 같은 역할을 수행하는 메서드를 Connection 매개변수가 있는 버전과 없는 버전으로 오버로딩을 해줘야 하는 단점이 생깁니다.

따라서 스프링은 커넥션을 보관해 주는 TransactionSynchronizationManager를 제공하며 ThreadLocal을 사용해서 커넥션을 동기화해 주기 때문에 멀티 쓰레드 환경에서도 안전하게 필요할 때 커넥션을 획득할 수 있습니다.


 

정리하자면 트랜잭션이 시작되면 트랜잭션 매니저를 통해 트랜잭션을 불러오고 데이터소스 커넥션을 생성한 뒤 트랜잭션 동기화 매니저에 커넥션을 보관합니다.

이후 프록시에서 실제 비즈니스 로직을 호출했을 때 데이터베이스와 연동하는 과정에서 트랜잭션 동기화 매니저로부터 동일 커넥션을 획득하여 성공적으로 트랜잭션이 이루어집니다.

 

참고

스프링 DB 1편 - 데이터 접근 핵심 원리 (김영한 강사님)

반응형