문제 상황
회원탈퇴 기능을 구현하면서 아웃박스 패턴을 사용하던 중, 다음과 같은 오류가 발생했습니다.
TransactionRequiredException: Executing an update/delete query
오류 발생 원인
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)를 사용하여 이벤트를 처리할 때, 기존 트랜잭션이 이미 커밋되어 종료된 상태에서 새로운 데이터베이스 업데이트 작업을 시도했기 때문입니다.
이벤트 처리 플로우
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleUserWithdrawn(UserEvents.Withdrawn event) {
// 1. RabbitMQ로 메시지 발행
userEventPublisher.publishUserWithdrawn(event.userId(), event.email(), event.nickname());
// 2. 아웃박스 상태 업데이트 시도 - 이 시점에서 트랜잭션이 없음!
userOutboxService.markEventAsPublished(event.userId(), "USER_WITHDRAWN");
}
해결 방법: REQUIRES_NEW 전파 속성 사용
markEventAsPublished 메서드에 @Transactional(propagation = Propagation.REQUIRES_NEW)를 적용하여 새로운 독립적인 트랜잭션을 시작하도록 했습니다.

트랜잭션 전파 속성 선택 기준
각 메서드별 적절한 트랜잭션 설정
- markEventAsPublished: REQUIRES_NEW
- 이유: @TransactionalEventListener(AFTER_COMMIT) 후 호출되므로 기존 트랜잭션이 이미 종료됨
- 새로운 독립적인 트랜잭션이 필요
- saveUserWithdrawnEvent: 일반 @Transactional
- 이유: 회원탈퇴 비즈니스 로직과 같은 트랜잭션 내에서 실행
- 회원 삭제와 아웃박스 저장이 하나의 단위로 처리되어야 함
- retryEvent: 일반 @Transactional
- 이유: 스케줄러에서 독립적으로 호출
- 재시도와 상태 업데이트가 하나의 트랜잭션으로 처리
- cleanupOldPublishedEvents: 일반 @Transactional
- 이유: 스케줄러에서 독립적으로 호출되는 배치 작업
- getRetryableEvents: readOnly = true (기본값)
- 이유: 조회만 하는 메서드
핵심 포인트
@TransactionalEventListener의 AFTER_COMMIT 단계에서 호출되는 메서드가 데이터베이스 변경 작업을 수행해야 한다면, 반드시 REQUIRES_NEW 전파 속성을 사용하여 새로운 트랜잭션을 시작해야 합니다.
이는 이벤트 리스너가 실행되는 시점에서 기존 트랜잭션이 이미 커밋되어 종료된 상태이기 때문입니다.
결과
이 수정을 통해 아웃박스 패턴이 정상적으로 작동하게 되었고, 메시지 발행과 상태 업데이트가 올바른 순서로 처리되었습니다.


'Spring 7기 프로젝트 > 모임 플렛폼 프로젝트' 카테고리의 다른 글
| Spring Boot MySQL 데이터베이스 설정 완벽 가이드 (3) | 2025.08.09 |
|---|---|
| Spring Boot에서 RabbitMQ Publisher Confirm 도메인별 설정하기 (1) | 2025.08.06 |
| 아웃박스 패턴과 RabbitMQ를 활용한 안전한 이벤트 발행 (0) | 2025.08.05 |
| Spring Event에서 RabbitMQ로: MSA 전환을 위한 메시징 아키텍처 마이그레이션 (2) | 2025.08.01 |
| Docker Compose 아키텍처 선택 가이드: 단일 vs 다중 인스턴스 구성 (1) | 2025.08.01 |