본문으로 건너뛰기
Advertisement

2.11 Spring Events (애플리케이션 이벤트)

도메인 간 결합을 낮추고, “주문 완료 시 알림·정산·재고 반영”처럼 이벤트 기반으로 처리하려면 Spring Events를 사용할 수 있습니다.

작성 기준: Spring Boot 3.2.x

1. 이벤트 클래스

public class OrderCompletedEvent {

private final Long orderId;
private final String userId;

public OrderCompletedEvent(Long orderId, String userId) {
this.orderId = orderId;
this.userId = userId;
}
// getters
}
  • ApplicationEvent를 상속해도 되고, Spring 4.2+에서는 POJO도 가능합니다.

2. 이벤트 발행

@Service
@RequiredArgsConstructor
public class OrderService {

private final ApplicationEventPublisher publisher;

@Transactional
public void completeOrder(Long orderId) {
// 주문 완료 비즈니스 로직
Order order = orderRepository.findById(orderId).orElseThrow();
order.complete();
orderRepository.save(order);

publisher.publishEvent(new OrderCompletedEvent(orderId, order.getUserId()));
}
}
  • ApplicationEventPublisher를 주입해 publishEvent(...) 로 발행합니다.
  • 기본적으로 동기 실행: 리스너가 모두 끝난 뒤 completeOrder가 반환됩니다.

3. 이벤트 리스너

@Component
@Slf4j
public class OrderEventListener {

@EventListener
public void onOrderCompleted(OrderCompletedEvent event) {
log.info("주문 완료: orderId={}", event.getOrderId());
notificationService.sendOrderComplete(event.getUserId(), event.getOrderId());
}
}
  • @EventListener: 메서드 인자 타입에 맞는 이벤트가 발행되면 호출됩니다.

4. 비동기 이벤트

리스너를 별도 스레드에서 실행하려면 @Async를 함께 사용합니다.

@Async
@EventListener
public void onOrderCompletedAsync(OrderCompletedEvent event) {
// 비동기 처리
}
  • @EnableAsync 필요. 트랜잭션 경계는 발행 측과 다르므로, 리스너 안에서 DB 접근 시 트랜잭션을 새로 열어야 할 수 있습니다.

5. 조건부 리스너 (SpEL)

@EventListener(condition = "#event.orderId != null")
public void onOrderCompletedConditional(OrderCompletedEvent event) {
// orderId가 null이 아닐 때만 실행
}

복잡한 흐름은 트랜잭션 이벤트(@TransactionalEventListener)로 “커밋 후에만 실행”하도록 두면, 롤백 시 리스너가 실행되지 않아 데이터 정합성을 지키기 쉽습니다.

Advertisement