스프링 이벤트 시스템은 애플리케이션 내부에서 이벤트를 발행하고, 이를 처리하는 리스너에게 비동기 또는 동기적으로 전달함으로써 결합도를 낮추고 유연한 아키텍처를 제공하는 메커니즘이다. 특정 이벤트가 발생했을 때 이를 감지하고, 적절한 리스너에게 알림을 보내는 방식으로 동작한다. 이벤트를 발행하는 컴포넌트와 이를 처리하는 리스너는 서로 독립적으로 동작한다.
스프링 이벤트의 장점 및 주의점
- 느슨한 결합: 이벤트 발행자와 리스너 간의 의존성이 없기 때문에 모듈 간 결합도를 낮출 수 있다.
- 확장성: 새로운 이벤트 리스너를 추가하는 것이 매우 쉽다.
- 유연성: 필요에 따라 동기 또는 비동기 방식으로 이벤트를 처리할 수 있다.
- 비동기 처리 시 주의: 비동기 처리를 사용할 때는 스레드 풀 설정이나 예외 처리 등에 신경 써야 한다.
- 복잡성 증가: 이벤트 기반 시스템이 지나치게 복잡해지면, 이벤트의 흐름을 추적하거나 디버깅이 어려울 수 있다.
스프링 이벤트의 사용 사례
- 알림 시스템: 사용자가 특정 액션을 취했을 때 이메일 알림이나 푸시 알림을 보내는 경우
- 로깅/감사: 애플리케이션의 특정 이벤트 발생 시 이를 로깅하거나 감사를 위해 기록하는 경우
- 데이터 동기화: 여러 시스템 간의 데이터 동기화를 비동기적으로 처리할 때
스프링의 이벤트 처리 흐름 요약
- 이벤트 발행:
ApplicationEventPublisher
가publishEvent()
를 호출하여 이벤트를 발행한다. - 이벤트 브로드캐스트: 스프링 컨텍스트는
ApplicationEventMulticaster
를 통해 이벤트를 모든 적합한 리스너에게 브로드캐스트 한다. - 리스너 호출:
ApplicationEventMulticaster
는 등록된 리스너를 필터링하여 일치하는 리스너에게 이벤트를 전달한다. - 이벤트 처리: 리스너는 이벤트를 동기적 또는 비동기적으로 처리한다.
스프링 이벤트의 주요 구성 요소
이벤트(Event)
ApplicationEvent
클래스를 상속하여 구현할 수 있으며, 스프링 4.2 이후에는 POJO를 이벤트로 사용할 수 있다.
// 사용자가 등록될 때 이를 알리는 UserRegisteredEvent 클래스
public class UserRegisteredEvent {
private final String username;
public UserRegisteredEvent(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
이벤트 퍼블리셔(Event Publisher)
이벤트를 발행하기 위해 스프링의 ApplicationEventPublisher
인터페이스를 사용한다. 이를 통해 발생한 이벤트는 스프링 컨텍스트에 등록된 리스너에게 전달된다.
@Component
public class UserService {
private final ApplicationEventPublisher eventPublisher;
public UserService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
// 사용자가 등록된 후 이벤트를 발행한다.
public void registerUser(String username) {
// 사용자 등록 로직
eventPublisher.publishEvent(new UserRegisteredEvent(username));
}
}
이벤트 리스너(Event Listener)
이벤트가 발생했을 때 이를 처리하는 컴포넌트이다. @EventListener
어노테이션을 사용하여 특정 이벤트를 처리하는 메서드를 정의할 수 있다.
@Component
public class UserRegistrationListener {
@EventListener
public void handleUserRegisteredEvent(UserRegisteredEvent event) {
// 이벤트 처리 로직
System.out.println("User registered: " + event.getUsername());
}
}
스프링 이벤트의 동작 원리
스프링 이벤트 시스템의 동작은 다음과 같은 주요 단계를 통해 이루어진다.
- 이벤트 발행: 애플리케이션의 서비스 계층이나 다른 컴포넌트에서 ApplicationEventPublisher를 통해 이벤트를 발행한다.
- 이벤트 전달 메커니즘: ApplicationEventPublisher는 이벤트를 스프링 컨텍스트에 등록된 모든 ApplicationListner에게 전달한다. 이 과정에서 ApplicationEventMulticaster가 사용되어 이벤트를 브로드캐스트한다.
- 이벤트 리스닝: 발행된 이벤트는 @EventListener 어노테이션으로 등록된 리스너에 의해 동기적 또는 비동기적으로 처리된다.
- 동기적 처리: 이벤트를 발행한 메소드와 이벤트를 처리하는 리스너 메소드가 같은 스레드에서 실행되며, 이벤트 리스너의 실행이 완료될 때까지 publishEvent 메소드가 반환되지 않는다.
- 비동기적 처리: @EventListener 어노테이션과 함께 @Async어노테이션을 사용하거나 ApplicationEventMulticaster를 커스터마이징하여 비동기 이벤트 처리가 가능하다. 비동기 처리에서는 리스너가 별도의 스레드에서 실행되어 이벤트 발행자는 즉시 반환된다.
ApplicationEventMulticaster의 역할과 커스터마이징
ApplicationEventMulticaster는 스프링 이벤트 시스템에서 중요한 역할을 하는 컴포넌트이다. 이 컴포넌트는 발행된 이벤트를 스프링 컨텍스트에 등록된 모든 적합한 리스너에게 전달한다. 기본 구현체는 SimpleApplicationEventMulticaster이며, 이벤트를 동기적으로 처리한다.
- 동기 처리: 기본적으로 SimpleApplicationEventMulticaster는 이벤트를 처리할 때, 등록된 리스너들을 순차적으로 호출한다. 이 경우, 이벤트가 처리되는 동안 현재 스레드가 차단된다.
- 비동기 처리: SimpleApplicationEventMulticaster는 비동기 처리를 위해 TaskExecutor를 설정할 수 있다. 이 설정을 통해 이벤트를 비동기적으로 처리할 수 있으며, 이벤트 발행 후 즉시 반환할 수 있다. 이로 인해 이벤트 처리 작업이 별도의 스레드에서 이루어지므로, 애플리케이션의 성능과 응답성을 향상시킬 수 있다.
멀티캐스터 커스터마이징
멀티캐스터를 커스터마이징하여 비동기 처리나 특정 조건에 따른 이벤트 필터링 등의 기능을 구현할 수 있다.
@Bean
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return eventMulticaster;
}
리스너 검색과 필터링
리스너 검색
ApplicationEventMulticaster
는 스프링 컨텍스트에서 ApplicationListener
를 조회하여 관리한다. 이벤트 타입에 따라 일치하는 리스너를 선택하여 호출한다.
리스너 필터링
@EventListener의 condition 속성을 사용하면 SpEL (Spring Expression Language)을 활용하여 이벤트 리스너가 특정 조건에서만 동작하도록 할 수 있다.
1. 이벤트 객체의 멤버를 통한 조건 처리
@EventListener(condition = "#event.success")
public void handleSuccessEvent(MyEvent event) {
// 성공적인 이벤트만 처리
}
2. 이벤트 객체의 복잡한 조건 처리
@EventListener(condition = "#event.type == 'SUCCESS' and #event.priority > 5")
public void handleConditionalEvent(MyEvent event) {
// 특정 조건에서만 이벤트 처리
}
3. 외부 클래스의 메소드를 통한 조건 처리
@EventListener(condition = "@eventConditionChecker.isHighPriority(#event)")
public void handleHighPriorityEvent(MyEvent event) {
// 우선순위가 높은 이벤트만 처리
}
4. 외부 서비스의 상태를 통한 조건 처리
@EventListener(condition = "@someService.isReady()")
public void handleReadyEvent(MyEvent event) {
// someService가 준비되었을 때만 처리
}
'JVM > SpringMVC' 카테고리의 다른 글
빈 스코프: 빈의 생명 주기와 활용 (0) | 2024.03.09 |
---|---|
CORS와 Preflight Request (0) | 2024.03.09 |
요청 처리와 스레드풀 (0) | 2023.12.20 |
Spring WebSocket & STOMP (0) | 2023.12.15 |