Spring/스프링 기본

[Spring] 스프링 이벤트 처리(Spring Event Processing) - 2탄 : 기본 사용법

공대키메라 2022. 7. 29. 14:59

지난 시간에 이벤트란 무엇인가 생각해보았고, 이벤트 처리를 위한 예시를 통해 필요성을 인지하였다.

(지난 내용이 궁금하면 여기 클릭!)

 

이번 시간에는 지난 시간의 예시 코드에 이어서 연습을 하도록 하겠다.

 

참고한 사이트는 다음과 같다. 

 

출처:

https://www.youtube.com/watch?v=xkWTO5M51FA&t=1564s&ab_channel=SeleniumExpress 

https://reflectoring.io/spring-boot-application-events-explained/

 

1. 문제 해결법 - ApplicatonEvent 사용하기

 

기존의 코드에서 ApplicationEvent 클래스를 상속해서 문제를 해결해보록 하겠다.

 

WooYoungWooEvent.java - ApplicationEvent 상속

package springevent.springevent.events;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

import java.time.Clock;

@Getter
public class WooYoungWooEvent extends ApplicationEvent {

    private String episodeNo;

    public WooYoungWooEvent(Object source) {
        super(source);
    }

    public WooYoungWooEvent(Object source,String episodeNo) {
        super(source);
        this.episodeNo = episodeNo;
    }

}

 

MsgListener.java - implements APplicationListener

package springevent.springevent.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import springevent.springevent.events.WooYoungWooEvent;

@Component
public class MsgListener implements ApplicationListener<WooYoungWooEvent> {
    public void watchWooYoungWoo(String epNo) {
        System.out.println("Msg : started watching Woo Young Woo");
        System.out.println("Msg : playing... : " + epNo);
    }

    // WooYoungWooEvent가 published 되면 이 메소드가 실행됨.
    @Override
    public void onApplicationEvent(WooYoungWooEvent event) {
        watchWooYoungWoo(event.getEpisodeNo());
    }
}

 

ShaneListener.java - implements ApplicationListener

package springevent.springevent.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import springevent.springevent.events.WooYoungWooEvent;

@Component
public class ShaneListener implements ApplicationListener<WooYoungWooEvent> {
    public void watchWooYoungWoo(String epNo) {
        System.out.println("Shane : started watching Woo Young Woo");
        System.out.println("Shane : playing... : " + epNo);
    }

    @Override
    public void onApplicationEvent(WooYoungWooEvent event) {
        watchWooYoungWoo(event.getEpisodeNo());
    }
}

 

NetFlixPublisher.java - 더이상 직접 listener를 DI 해서 사용하지 않음.

package springevent.springevent.publisher;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import springevent.springevent.events.WooYoungWooEvent;

@Component
public class NetFlixPublisher {

    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

    //publisher는 subscriber가 누구인지 알 필요가 없다.
    public void streamWooYoungWoo(String episodeNo){
        System.out.println("Woo Young Woo : starting episode : " + episodeNo);
        applicationEventPublisher.publishEvent(new WooYoungWooEvent(this, episodeNo));
    }

}

 

App.java

package springevent.springevent.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import springevent.springevent.config.AppConfig;
import springevent.springevent.publisher.NetFlixPublisher;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        NetFlixPublisher bean = context.getBean("netFlixPublisher", NetFlixPublisher.class);
        bean.streamWooYoungWoo("EP - 004");
    }
}

 

이제 실행을 하면, publish 하는 순간, 현재 WooYoungWoo를 구독중인 MsgListener와, ShaneListener는 streaming중인지 알아채서 언제든지 확인할 수 있다. (기러기토마토스위스인도인별똥별우영우)

 

 

2. 문제 해결법 - @EventListener 사용하기

 

사실 Listener가 ApplicationListener를 implements 하지 않아도, Event가 AppicationEvent 를 상속하지 않아도 기존 기능과 동일하게 작성이 가능하다. 

 

그리고, 새로운 버라이어티 쇼가 NetFlix에서 서비스한다고(publish)한다고 가정하고, 새로운 구독자도 생겨낫다고 생각하고 class를 더 늘려보겠다.

 

 

 

ShaneListener.java - @EventListener 사용

package springevent.springevent.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import springevent.springevent.events.WooYoungWooEvent;

@Component
public class ShaneListener {
    public void watchWooYoungWoo(String epNo) {
        System.out.println("Shane : started watching Woo Young Woo");
        System.out.println("Shane : playing... : " + epNo);
    }

    @EventListener
    public void listenToEvent(WooYoungWooEvent event) {
        watchWooYoungWoo(event.getEpisodeNo());
    }
}

 

MsgListener.java - @EventListener 사용

package springevent.springevent.listener;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import springevent.springevent.events.WooYoungWooEvent;

@Component
public class MsgListener {
    public void watchWooYoungWoo(String epNo) {
        System.out.println("Msg : started watching Woo Young Woo");
        System.out.println("Msg : playing... : " + epNo);
    }

    @EventListener
    public void listenToEvent(WooYoungWooEvent event) {
        watchWooYoungWoo(event.getEpisodeNo());
    }
}

 

NetFlixPublisher.java - streamVarietyShow() method added!

package springevent.springevent.publisher;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import springevent.springevent.events.VarietyShowEvent;
import springevent.springevent.events.WooYoungWooEvent;

@Component
public class NetFlixPublisher {

    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

    //publisher는 subscriber가 누구인지 알 필요가 없다.
    public void streamWooYoungWoo(String episodeNo){
        System.out.println("Woo Young Woo : starting episode : " + episodeNo);
        applicationEventPublisher.publishEvent(new WooYoungWooEvent(episodeNo));
    }

    public void streamVarietyShow(String episodeNo) {
        System.out.println("Variety show : starting episode : " + episodeNo);
        applicationEventPublisher.publishEvent(new VarietyShowEvent(episodeNo));

    }

}

 

 GillListener.java 

package springevent.springevent.listener;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import springevent.springevent.events.VarietyShowEvent;

@Component
public class GillListener {
    public void watchVarietyShow(String epNo) {
        System.out.println("Gill : started watching Variety show");
        System.out.println("Gill : playing... : " + epNo);
    }

    @EventListener
    public void listenToEvent(VarietyShowEvent event) {
        watchVarietyShow(event.getEpisodeNo());
    }
}

 

위 코드를 실행하면 다음과 같다.

 

 

 

3. 왜 Method 호출 대신에 Event를 사용해야 하나?

이벤트와 직접적인 메소드 호출 둘 다 다른 상황에 잘 맞는다. 

 

메소드 콜의 경우에는 보내는 것과 module을 받는 것의 상태와 관계없이 그들이 이 이벤트가 일어났다는 것을 확실히 알길 필요로 하는 것을 확고히 하는 것이다.

(이게 다시 정리하자면, Method를 호출한다는 것은 현재 상태가 정확히 어떤 상황읹인지, 이벤트인지 인지할 필요 없이 우리가 그냥 부르면 된다는 뜻인거 같다. 그래서 - 상태과 관계없이 -라고 표현한 것 같다.)

 

이벤트의 경우에는, 반면에, 우리는 이벤트가 발생했다는 것만 말하고 각각 모듈들이 우리의 관심사라 아니라는 것에 대해 알리기만 하면 된다. 

(우리가 발생한 이벤트에 대해서 선택적으로 접근해서 다룰 수 가 있다는 말인 것 같다.)

 

event는 다른 thread에서 처리를 넘기길 원할 때 사용하면 좋다. 또한, 테스트 기반 개발에 유용하다. 

 


이번 시간에는 지난 시간에 이어서 Spring 에서 지원하는 Event 지원 기능들을 이용해서

 

간단한 Pub-Sub 구조를 만들어 보았다.

 

다음 시간에는 좀 더 복잡한 Event 들을 다뤄보는 법을 배울 것이다.