저번 포스팅에는 스프링의 트랜잭션 전파 기능을 활용하여 트랜잭션을 분리했습니다.
이번에는 @TransactionalEventListener
가 무엇인지 간단히 설명하고 프로젝트에서 발생한 문제를 해결한 과정을 정리해보겠습니다.
@TransactionalEventListener
스프링은 게시글 등록 트랜잭션처럼 이벤트를 발행한 트랜잭션의 처리 결과(커밋, 롤백 등)에 따라 실행 여부를 결정하는 @TransactionalEventListener
를 지원합니다.
아래 사진과 같이 phase 옵션을 통해 지정한 트랜잭션의 결과일때만 @TransactionalEventListener
가 실행되도록 구현할 수 있습니다.
phase 옵션에서 기본값으로 사용되는 TransactionPhase.AFTER_COMMIT
은 트랜잭션이 커밋된 경우에만 @TransactionalEventListener
가 실행되도록 동작합니다.
게시글 등록 이벤트를 발행한 이후 예외가 발생하여 게시글 등록에 실패하는 상황을 가정해보겠습니다.
여기서 키워드 알림은 당근마켓의 키워드 알림과 같이 등록한 게시글의 내용에 사용자가 지정한 키워드가 포함되는 경우 알림을 보내는 기능입니다. 따라서 키워드 알림은 특정 게시글을 기반으로 생성된 알림이기 때문에 게시글 생성에 실패한 경우 키워드 알림을 생성할 수 없습니다.
하지만 저번 포스팅에서 구현한 아래 코드를 보면 게시글 등록과 키워드 알림 생성은 서로 다른 트랜잭션으로 분리되었기 때문에 게시글 등록에 실패해도 게시글 등록 이벤트가 발행만 되면 키워드 알림이 생성되는 문제가 발생합니다.
public class BoardService {
private final ApplicationEventPublisher publisher;
@Transactional
public void register() {
// 게시글 등록 로직(생략)
// 게시글 등록 이벤트 발행
publisher.publishEvent(new BoardRegisterEvent());
}
}
public class NotificationEventListener {
@Transactional(propagation = Propagation.REQUIRES_NEW)
@EventListener
public void registerKeywordNotification(BoardRegisterEvent event) {
// 키워드 알림 생성 로직
}
}
게시글 등록 트랜잭션이 커밋된 경우에만 키워드 알림을 생성하도록 구현
하면 해당 문제를 해결할 수 있다고 생각했습니다. 저는 앞서 설명했던 @TransactionalEventListener
를 활용하여 아래와 같이 코드를 변경했습니다.
@Transactional(propagation = Propagation.REQUIRES_NEW)
@TransactionalEventListener // phase 옵션의 기본값 : TransactionPhase.AFTER_COMMIT
public void registerKeyword(ShareRegisterEvent event) {
notificationService.saveKeywordNotification(event.getShareId(), event.getMemberId());
}
phase 옵션을 TransactionPhase.AFTER_COMMIT
으로 지정하여 게시글 등록이 완료된 경우에만 키워드 알림을 생성할 수 있습니다!