스프링은 애플리케이션 안에서 "이벤트를 발행(publish)하고, 그걸 구독(listen)하는" 구조를 지원. Observer패턴을 스프링이 대신 관리해주는 것.
// 1. 이벤트 정의
public class OrderCreatedEvent {
private final Long orderId;
// 생성자, getter...
}
// 2. 이벤트 발행
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
public void createOrder(Order order) {
// ... 주문 저장 로직
eventPublisher.publishEvent(new OrderCreatedEvent(order.getId()));
}
}
// 3. 이벤트 리스너
@Component
public class OrderEventHandler {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("주문 생성됨: " + event.getOrderId());
}
}
publishEvent() 가 호출되면, 해당 이벤트 타입을 파라미터로 받는 @EventListener메서드들이 동기적으로, 즉시 실행됨.
기본적으로 같은 스레드에서, 호출한 시점 그대로 실행 (트랜잭션 진행 중이면 그 안에서 실행됨)
여러 리스너가 같은 이벤트를 구독할 수 있음 -> 발행자는 누가 듣는지 몰라도 됨(느슨한 결합)
관심사 분리가 핵심, 예를 들어 주문 생성 시:
이런 걸 전부 OrderService.createOrder() 안에 몰아넣으면 메서드가 비대해지고 책임이 섞인다. 대신 OrderCreatedEvent만 발행하고, 각 관심사는 별도의 리스너로 분리하면 코드가 깔끔해지고 서로 독립적으로 테스트/수정 가능해진다.
| @EventListener | @TransactionalEventListener | |
|---|---|---|
| 실행 시점 | 이벤트 발행 즉시 | 트랜잭션 커밋/롤백 후 (지정한 phase) |
| 트랜잭션 없을 때 | 그냥 실행됨 | 기본적으로 실행 안 됨 (옵션으로 강제 가능) |
| 용도 | 일반적인 이벤트 처리 | DB 반영 확정 후에만 처리해야 하는 경우 |
Async를 추가로 붙여야 함