@TransactionalEventListener 내부 동작 보기

유수민·2025년 8월 29일
0

📌 개요

스프링을 사용하다 보면 이벤트 기반 아키텍처를 자주 접하게 된다. 그 중 많이 쓰는 것이 @TransactionalEventListener인데, 적용과 별개로 내부 동작을 파헤치는 곳이 없는 것 같아 아주 조금 확인해보는 시간을 가지려고 한다.

@TransactionalEventListener는 Spring의 트랜잭션 동기화(Transaction Synchronization) 메커니즘을 활용해 이벤트 실행 시점을 트랜잭션의 상태와 결합합니다. 핵심적으로, 이벤트가 발행될 때 즉시 실행되지 않고, 트랜잭션의 특정 단계(커밋, 롤백 등)에 맞춰 실행되도록 '예약'하는 방식으로 동작한다.

📌 기본 개념

기본 이벤트 리스너 vs 트랜잭셔널 이벤트 리스너

기본 이벤트 리스너

@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}")
    }
}
  • 트랜잭션 경계와 결합
  • phase에 따라 실행 시점 제어 가능

실행 시점 (TransactionPhase)

@TransactionalEventListener는 트랜잭션의 수명 주기에 맞춰 이벤트를 실행한다.

PhaseWhen
BEFORE_COMMIT커밋 직전에 실행
AFTER_COMMIT커밋 성공 직후 실행 (기본값)
AFTER_ROLLBACK롤백 직후 실행
AFTER_COMPLETION커밋/롤백 완료 후 실행 (성공/실패 무관)

📌 내부 동작

1. 이벤트 발행

eventPublisher.publishEvent(new PaidCompleteEvent(...));
  • 이 시점에서는 즉시 실행되지 않는다.
  • 대신, TransactionSynchronizationManager에 "트랜잭션 끝날 때 실행할 콜백"을 등록한다.

2. @TransactionalEventListener의 특별한 처리

스프링 부트가 실행될 때, TransactionalEventListenerFactory라는 BeanPostProcessor가 등록된다.

이 Factory는 @TransactionalEventListener가 붙은 메서드를 스캔한다. 해당 메서드를 TransactionalApplicationListener라는 특별한 리스너 객체로 감싼다. 즉, "이 메서드는 단순한 이벤트 리스너가 아니라, 트랜잭션 경계와 관련된 특별한 리스너"라고 스프링에 알려주는 장치이다.
역할: @TransactionalEventListener가 붙은 메서드를 스프링 이벤트 리스너로 등록하는 팩토리

💡 주요 메서드

  • supportsMethod : 특정 메서드가 이벤트 리스너로 등록될 수 있는지 판단
  • createApplicationListener : 실제 이벤트를 처리할 ApplicationListener 인스턴스를 생성
    1. 일반 이벤트 리스너는 ApplicationListenerMethodAdapter를 쓰지만,
    @TransactionalEventListener는 TransactionalApplicationListenerMethodAdapter를 쓴다.
    2. 이 어댑터가 트랜잭션과 연동되어 이벤트 실행 시점을 BEFORE_COMMIT, AFTER_COMMIT, AFTER_ROLLBACK, AFTER_COMPLETION으로 맞춰 준다.

3. 메서드와 이벤트를 연결하는 Adapter : TransactionalApplicationListenerMethodAdapter

  • 역할 : @TransactionalEventListener 메서드를 감싸서 트랜잭션 라이프사이클에 맞게 실행 시점을 조정하는 어댑터
  1. @TransactionalEventListener가 붙은 메서드를 감싸서 트랜잭션 동기화 매니저(TransactionSynchronizationManager)에 등록
  2. 지정된 트랜잭션 Phase(BEFORE_COMMIT, AFTER_COMMIT, AFTER_ROLLBACK, AFTER_COMPLETION)에 이벤트 실행 시점을 맞춤

내부에 여러 메서드가 있지만 내가 생각했을때 onApplicationEvent 메서드가 핵심인것 같다.

트랜잭션이 활성화되어 있을 때

  • TransactionalApplicationListenerSynchronization.register(...) 호출 시 이벤트를 즉시 실행하지 않고 예약한다.

  • 예약된 이벤트는 트랜잭션이 커밋되거나 롤백될 때 실행된다.

  • 내부적으로 TransactionSynchronizationManager에 등록되어 트랜잭션과 이벤트 실행을 동기화한다.

트랜잭션이 없지만 fallback 실행이 허용된 경우

  • isDefaultExecution()이 true이면, 트랜잭션 없이도 이벤트를 즉시 실행한다.
  • 단, Phase가 AFTER_ROLLBACK인 경우 경고 로그를 남긴다.

트랜잭션도 없고 fallback도 허용되지 않은 경우

  • 이벤트는 무시되고 실행되지 않는다.

이 메서드 덕분에 트랜잭션 커밋 이후에만 실행해야 하는 로직과, 트랜잭션과 상관없이 바로 실행해도 되는 로직을 명확히 구분할 수 있는 것 같다.

역할 정리

너무 이해하기 어렵다. 역할을 단순히 정리를 하자면

  • 트랜잭션 활성 여부 판단
  • 이벤트를 즉시 실행할지, 등록할지, 무시할지 결정
  • 등록이 성공하면 실제 이벤트 실행은 Synchronization에게 위임

4. 트랜잭션 시점에 맞춰 이벤트 실행을 담당 : TransactionalApplicationListenerSynchronization

이벤트 실행 자체와 콜백 관리를 책임지는 핵심 메서드: processEventWithCallbacks()

  • 역할: 트랜잭션 시점에 이벤트를 실제로 실행
  • 콜백 처리: 이벤트 실행 전(preProcessEvent), 후(postProcessEvent) 콜백 지원
  • 예외 처리: 이벤트 실행 중 예외 발생 시 postProcessEvent에 예외 전달

이벤트 실행 메서드 : beforeCommit, afterCompletion

  • 트랜잭션 커밋 전 : beforeCommit (TransactionPhase.BEFORE_COMMIT일 때만)
  • 트랜잭션이 완료된 후 : afterCompletion

클래스 역할 정리

  • 이벤트를 트랜잭션 Commit/Rollback 시점에 실행
  • BEFORE_COMMIT, AFTER_COMMIT, AFTER_ROLLBACK, AFTER_COMPLETION 모두 처리 가능
  • 이벤트 실행 전후로 콜백(pre/post) 처리 가능
  • 트랜잭션이 없으면 fallbackExecution 옵션에 따라 즉시 실행하거나 무시

📌 정리

  1. 이벤트 발행: 서비스 계층에서 ApplicationEventPublisher.publishEvent()가 호출

  2. 어댑터 실행: TransactionalApplicationListenerMethodAdapter의 onApplicationEvent 메서드가 호출.

  3. 트랜잭션 동기화 등록: 현재 활성화된 트랜잭션이 있으면, 이벤트와 그 실행 정보를 담은 TransactionalApplicationListenerSynchronization 객체를 TransactionSynchronizationManager에 등록.

  4. 트랜잭션 종료: 트랜잭션이 커밋되거나 롤백되면, TransactionSynchronizationManager는 등록된 TransactionalApplicationListenerSynchronization 객체의 콜백 메서드(beforeCommit 또는 afterCompletion)를 호출.

  5. 이벤트 실행: 콜백 메서드는 @TransactionalEventListener에 지정된 TransactionPhase에 따라 예약된 이벤트 핸들러 메서드를 실제로 호출.

profile
배우는 것이 즐겁다!

0개의 댓글