
@Transactional은 Spring Framework에서 트랜잭션을 관리하기 위해 사용되는 어노테이션으로, 데이터베이스 작업을 포함한 메서드 실행을 트랜잭션 단위로 관리할 수 있게 됩니다. 보통 Service 객체의 메서드에서 데이터베이스 작업을 하기 때문에 Service 객체의 메서드에 @Transactional 어노테이션을 붙입니다.
이전에 AOP를 "공통적인 부분을 스프링이 도와주어 처리하게 된다" 라는 표현으로 배웠는데, DB 작업 전/후에 트랜잭션과 관련된 처리가 필요하므로 AOP를 적용할 수 있습니다. 그럼 내부적으로 어떤 동작이 이루어질까요?
Spring AOP를 통해 트랜잭션이 어떻게 관리되는지 이해하려면 몇 가지 주요 개념을 알아야 합니다: MethodInvocation, Invocation, JoinPoint, Proxy, 그리고 TransactionManager.
Spring AOP는 dynamic proxy를 이용해 메서드 호출을 가로챕니다. 프록시 객체가 실제 객체를 대신하여 메서드를 호출하고, 트랜잭션 로직을 추가합니다. 메서드의 호출이 JoinPoint가 되어, 해당 시점에 추가적인 로직이 동작하는 것입니다.
MethodInvocation은 프록시 객체를 통해 가로채어진 메서드 호출을 나타냅니다. 개발자가 작성한 비즈니스 로직을 담은 메서드 호출을 포함하여, 추가적인 로직(예: 트랜잭션 관리)을 실행할 수 있는 기능을 제공합니다. Invocation은 MethodInvocation의 구체적인 구현으로, 실제 메서드를 호출하고 결과를 반환하는 역할을 합니다. MethodInvocation은 proceed() 메서드를 제공하여, proceed 호출 전 후 추가동작을 수행하게 하고, proceed를 호출하여 비즈니스 로직이 수행되게 합니다.
클라이언트가 @Transactional이 적용된 메서드를 호출하면, 실제 메서드가 아니라 프록시 객체의 메서드가 호출됩니다.
프록시 메서드는 TransactionInterceptor라는 AOP 어드바이스(advice)를 호출합니다.
어드바이스는 AOP가 동작하면서 실제로 동작하게 되는 기능을 뜻한다고 정리했었습니다(Transactional의 경우 트랜잭션과 관련된 기능이겠죠?).
이 어드바이스는 TransactionManager를 통해 createTransactionIfNecessary() 메서드를 호출해, 트랜잭션이 시작 상태인지 확인하고, 그렇지 않다면 새로운 트랜잭션을 생성합니다.
트랜잭션이 시작한 후, MethodInvocation.proceed()를 호출하여 실제 메서드 실행을 진행합니다.
메서드 실행이 성공적으로 완료되면, TransactionManager는 트랜잭션을 커밋합니다. 혹은, 메서드 실행 중 예외가 발생하면 트랜잭션을 롤백합니다.
트랜잭션이 커밋되거나 롤백된 후, 트랜잭션 리소스는 정리되고 연결이 반환됩니다.
@Transactional은 트랜잭션의 전파 방식과 격리 수준을 제어할 수 있는데, 관련된 개념은 새로운 포스팅으로 살펴보겠습니다.