JVM/SpringMVC

스프링 이벤트 시스템

kyoulho 2024. 8. 10. 15:02

스프링 이벤트 시스템은 애플리케이션 내부에서 이벤트를 발행하고, 이를 처리하는 리스너에게 비동기 또는 동기적으로 전달함으로써 결합도를 낮추고 유연한 아키텍처를 제공하는 메커니즘이다. 특정 이벤트가 발생했을 때 이를 감지하고, 적절한 리스너에게 알림을 보내는 방식으로 동작한다. 이벤트를 발행하는 컴포넌트와 이를 처리하는 리스너는 서로 독립적으로 동작한다.

 

 

스프링 이벤트의 장점 및 주의점

  • 느슨한 결합: 이벤트 발행자와 리스너 간의 의존성이 없기 때문에 모듈 간 결합도를 낮출 수 있다.
  • 확장성: 새로운 이벤트 리스너를 추가하는 것이 매우 쉽다.
  • 유연성: 필요에 따라 동기 또는 비동기 방식으로 이벤트를 처리할 수 있다.
  • 비동기 처리 시 주의: 비동기 처리를 사용할 때는 스레드 풀 설정이나 예외 처리 등에 신경 써야 한다.
  • 복잡성 증가: 이벤트 기반 시스템이 지나치게 복잡해지면, 이벤트의 흐름을 추적하거나 디버깅이 어려울 수 있다.

 

스프링 이벤트의 사용 사례

  • 알림 시스템: 사용자가 특정 액션을 취했을 때 이메일 알림이나 푸시 알림을 보내는 경우
  • 로깅/감사: 애플리케이션의 특정 이벤트 발생 시 이를 로깅하거나 감사를 위해 기록하는 경우
  • 데이터 동기화: 여러 시스템 간의 데이터 동기화를 비동기적으로 처리할 때

 

스프링의 이벤트 처리 흐름 요약

  1. 이벤트 발행: ApplicationEventPublisherpublishEvent()를 호출하여 이벤트를 발행한다.
  2. 이벤트 브로드캐스트: 스프링 컨텍스트는 ApplicationEventMulticaster를 통해 이벤트를 모든 적합한 리스너에게 브로드캐스트 한다.
  3. 리스너 호출: ApplicationEventMulticaster는 등록된 리스너를 필터링하여 일치하는 리스너에게 이벤트를 전달한다.
  4. 이벤트 처리: 리스너는 이벤트를 동기적 또는 비동기적으로 처리한다.

 

스프링 이벤트의 주요 구성 요소

이벤트(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를 조회하여 관리한다. 이벤트 타입에 따라 일치하는 리스너를 선택하여 호출한다.

리스너 필터링

@EventListenercondition 속성을 사용하면 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