스프링을 사용하다 보면 이벤트 기반 아키텍처를 자주 접하게 된다. 그 중 많이 쓰는 것이 @TransactionalEventListener인데, 적용과 별개로 내부 동작을 파헤치는 곳이 없는 것 같아 아주 조금 확인해보는 시간을 가지려고 한다.
@TransactionalEventListener는 Spring의 트랜잭션 동기화(Transaction Synchronization) 메커니즘을 활용해 이벤트 실행 시점을 트랜잭션의 상태와 결합합니다. 핵심적으로, 이벤트가 발행될 때 즉시 실행되지 않고, 트랜잭션의 특정 단계(커밋, 롤백 등)에 맞춰 실행되도록 '예약'하는 방식으로 동작한다.
@Component
class OrderEventHandler {
@EventListener
fun handle(event: PaidCompleteEvent) {
log.info("일반 이벤트 리스너 호출: ${event.orderUUId}")
}
}
ApplicationEventPublisher.publishEvent(event) 호출 시 즉시 실행
트랜잭션과 무관하게 동작
@Component
class OrderEventHandler {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
fun handle(event: PaidCompleteEvent) {
log.info("트랜잭션 커밋 이후 실행: ${event.orderUUId}")
}
}
@TransactionalEventListener는 트랜잭션의 수명 주기에 맞춰 이벤트를 실행한다.
Phase | When |
---|---|
BEFORE_COMMIT | 커밋 직전에 실행 |
AFTER_COMMIT | 커밋 성공 직후 실행 (기본값) |
AFTER_ROLLBACK | 롤백 직후 실행 |
AFTER_COMPLETION | 커밋/롤백 완료 후 실행 (성공/실패 무관) |
eventPublisher.publishEvent(new PaidCompleteEvent(...));
스프링 부트가 실행될 때, TransactionalEventListenerFactory라는 BeanPostProcessor가 등록된다.
이 Factory는 @TransactionalEventListener가 붙은 메서드를 스캔한다. 해당 메서드를 TransactionalApplicationListener라는 특별한 리스너 객체로 감싼다. 즉, "이 메서드는 단순한 이벤트 리스너가 아니라, 트랜잭션 경계와 관련된 특별한 리스너"라고 스프링에 알려주는 장치이다.
역할: @TransactionalEventListener가 붙은 메서드를 스프링 이벤트 리스너로 등록하는 팩토리
💡 주요 메서드
- supportsMethod : 특정 메서드가 이벤트 리스너로 등록될 수 있는지 판단
- createApplicationListener : 실제 이벤트를 처리할 ApplicationListener 인스턴스를 생성
1. 일반 이벤트 리스너는 ApplicationListenerMethodAdapter를 쓰지만,
@TransactionalEventListener는 TransactionalApplicationListenerMethodAdapter를 쓴다.
2. 이 어댑터가 트랜잭션과 연동되어 이벤트 실행 시점을 BEFORE_COMMIT, AFTER_COMMIT, AFTER_ROLLBACK, AFTER_COMPLETION으로 맞춰 준다.
내부에 여러 메서드가 있지만 내가 생각했을때 onApplicationEvent 메서드가 핵심인것 같다.
TransactionalApplicationListenerSynchronization.register(...) 호출 시 이벤트를 즉시 실행하지 않고 예약한다.
예약된 이벤트는 트랜잭션이 커밋되거나 롤백될 때 실행된다.
내부적으로 TransactionSynchronizationManager에 등록되어 트랜잭션과 이벤트 실행을 동기화한다.
이 메서드 덕분에 트랜잭션 커밋 이후에만 실행해야 하는 로직과, 트랜잭션과 상관없이 바로 실행해도 되는 로직을 명확히 구분할 수 있는 것 같다.
너무 이해하기 어렵다. 역할을 단순히 정리를 하자면
이벤트 발행: 서비스 계층에서 ApplicationEventPublisher.publishEvent()가 호출
어댑터 실행: TransactionalApplicationListenerMethodAdapter의 onApplicationEvent 메서드가 호출.
트랜잭션 동기화 등록: 현재 활성화된 트랜잭션이 있으면, 이벤트와 그 실행 정보를 담은 TransactionalApplicationListenerSynchronization 객체를 TransactionSynchronizationManager에 등록.
트랜잭션 종료: 트랜잭션이 커밋되거나 롤백되면, TransactionSynchronizationManager는 등록된 TransactionalApplicationListenerSynchronization 객체의 콜백 메서드(beforeCommit 또는 afterCompletion)를 호출.
이벤트 실행: 콜백 메서드는 @TransactionalEventListener에 지정된 TransactionPhase에 따라 예약된 이벤트 핸들러 메서드를 실제로 호출.