상황: 판매자 승격 이벤트를 AFTER_COMMIT에서 처리하는데, DB에는 Role 변경이 반영되지 않거나(마이페이지에 계속 USER), 토큰도 갱신되지 않는 문제가 발생.
@Component
@RequiredArgsConstructor
public class AuthEventListener {
private final AuthPromoteSellerUseCase authPromoteSellerUseCase;
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleSellerPromotedEvent(SellerPromotedEvent event) {
// 1) 원래 트랜잭션이 커밋된 이후에 실행됨
// 2) 여기서 DB 변경이 필요하면 별도의 트랜잭션이 필요함
authPromoteSellerUseCase.promoteSeller(event.memberPublicId());
}
}
@Service
@RequiredArgsConstructor
public class AuthPromoteSellerUseCase {
private final AccountRepository accountRepository;
@Transactional // (문제) 기본 REQUIRED
public void promoteSeller(String memberPublicId) {
Account account = accountRepository.findByMemberPublicId(memberPublicId)
.orElseThrow(() -> new CustomException(ErrorType.MEMBER_NOT_FOUND));
// Role 변경
account.changeRole(AuthRole.SELLER);
// 저장/커밋이 보장되지 않아 실제 DB 반영이 누락될 수 있음
// (AFTER_COMMIT 시점에는 기존 트랜잭션이 이미 종료)
}
}
AFTER_COMMIT 이벤트 리스너에서 실행되는 DB 변경 로직은 기존 트랜잭션이 이미 종료된 상태라 커밋이 보장되지 않음
@TransactionalEventListener(phase = AFTER_COMMIT)는 원래 트랜잭션이 커밋된 후 이벤트 핸들러를 실행한다. 즉, 이벤트 핸들러가 실행될 때는 기존 트랜잭션이 이미 끝난 상태다.account.changeRole(...))은 보통 트랜잭션 커밋 시 flush → DB 반영이 일어난다.
그런데 AFTER_COMMIT에서 호출되는 로직이 독립 트랜잭션을 확실히 열지 않으면, 변경이 커밋되지 않아 DB에 반영이 누락될 수 있다.Role 변경 로직을 반드시 독립 트랜잭션에서 실행하도록 보장
핵심은 이벤트 리스너(AFTER_COMMIT)에서 호출되더라도 무조건 새 트랜잭션을 열어 커밋까지 가게 만드는 것.
REQUIRES_NEW 적용@Service
@RequiredArgsConstructor
public class AuthPromoteSellerUseCase {
private final AccountRepository accountRepository;
// 변경 전: @Transactional (REQUIRED)
// 변경 후:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void promoteSeller(String memberPublicId) {
Account account = accountRepository.findByMemberPublicId(memberPublicId)
.orElseThrow(() -> new CustomException(ErrorType.MEMBER_NOT_FOUND));
account.changeRole(AuthRole.SELLER);
// 이 메서드 종료 시점에 새 트랜잭션이 커밋되며 DB에 확정 반영됨
}
}
REQUIRES_NEW 효과