저번 포스팅에는 @EventListener
를 통해 서로 다른 서비스 간의 의존성을 줄였습니다.
이번에는 트랜잭션을 분리하게 된 문제 상황과 방법을 정리해보겠습니다.
public class BoardService {
private final ApplicationEventPublisher publisher;
@Transactional
public void register() {
// 게시글 등록 로직(생략)
// 게시글 등록 이벤트 발행
publisher.publishEvent(new BoardRegisterEvent());
}
}
public class NotificationEventListener {
@EventListener
public void registerKeywordNotification(BoardRegisterEvent event) {
// 키워드 알림 생성 로직 중 예외 발생
}
}
위 코드에서 BoardRegisterEvent
가 발행되어 키워드 알림 생성 로직이 실행되는 도중 RuntimeException
이 발생한다고 가정하겠습니다.
스프링은 기본적으로 RuntimeException
이 발생한 경우 트랜잭션을 롤백하기 때문에 키워드 알림이 생성되지 않고 롤백이 될 것입니다.
하지만 여기서 게시글 등록과 키워드 알림 생성 로직이 동일한 트랜잭션에서 실행되기 때문에 게시글 등록이 되지 않습니다.
따라서 게시글 등록 로직이 키워드 알림 생성 로직에 대해 영향을 받는 문제가 발생합니다.
트랜잭션 전파라고 불리는 @Transactional
의 propation
옵션을 활용하여 문제 상황에서 나온 두 로직을 별도의 트랜잭션으로 분리할 수 있습니다.
트랜잭션 전파는 따로 설정하지 않는 경우 기본값으로 Propagation.REQUIRED
가 적용됩니다.
Propagation.REQUIRED
는 실행 시점에 이미 트랜잭션이 존재하는 경우 해당 트랜잭션에 참여하고 트랜잭션이 존재하지 않는 경우 새로운 트랜잭션을 시작합니다.
따라서 위 다이어그램과 같이 키워드 알림 생성 로직에서는 게시글 등록 메서드에서 생성된 트랜잭션 A에 참여하게 되어 게시글 등록과 키워드 알림 생성이 동일한 트랜잭션에서 실행됐습니다.
Propagation.REQUIRES_NEW
는 트랜잭션 전파 옵션 중 하나로 트랜잭션 존재 여부에 상관 없이 새로운 트랜잭션을 시작합니다.
따라서 해당 옵션을 사용하면 위 다이어그램과 같이 키워드 알림 생성 시점에 게시글 등록에서 실행됐던 트랜잭션에 참여하는 것이 아닌 별도의 트랜잭션을 시작하도록 구현할 수 있습니다.
public class NotificationEventListener {
@Transactional(propagation = Propagation.REQUIRES_NEW)
@EventListener
public void registerKeywordNotification(BoardRegisterEvent event) {
// 키워드 알림 생성 로직
}
}
위와 같이 트랜잭션 전파 옵션을 변경하여
게시글 등록과 키워드 알림 생성에 대한 트랜잭션을 분리할 수 있었습니다!