개발 기록
> [Spring Event] Event 개념과 Spring Event 사용법 본문

환경
java 17
spring version 6.0.11
spring boot version 3.1.3
■
1. 개념
이벤트는 트리거가 작동하면 시작되는 동작 혹은 사건을 말한다.
예를 들어서 영상을 업로드 시, 구독자에게 메일과 알림을 발송하는 서비스를 만든다고 생각해보자.
아래 코드를 보면 영상을 업로드 하는 기능에, 메일발송기능, 알람발송기능이 결합된 것을 볼 수 있다.
@Service
@Requiredargsconstructor
public class VideoService {
private final VideoRepository videoRepository;
private final EmailService emailService;
private final NotieService notieService;
@Transactional
public void upload(VideoDTO videoRequest) {
// 1. 영상 업로드
Video video = new Video(request);
videoRepository.savee(video);
// 2. 구독자에게 업로드 알림 메일 발송
emailService.sendMail(videoRequest.getChannelId());
// 3. 구독자에게 업로드 알람 발송
notieService.registerNotie(videoRequest.getChannelId());
}
}
기존 방식은 홍길동이 업무 완료 후 고길동과 둘리에게 작업이 끝났다고 직접 알려주고, 고길동과 둘리는 그때부터 작업을 시작하는 흐름이다.
즉 홍길동은 영상업로드 업무를 포함해서, 작업 완료 전달 업무까지 하는 것이다.
만일, 홍길동이 작업완료를 전달하는 것을 까먹는 최악의 상황이 생기면, 고길동과 둘리는 일을 시작하지 못할 것이다.

이벤트는 이 경우에 자신의 업무외에 다른 업무와의 관심사를 분리한다.
홍길동이 고길동과 둘리에게 직접 말하지 않아도, 고길동과 둘리는 영상업로드 이벤트를 수신받고 본인 일을 진행하는 것이다.

■
2. Spring 에서 Event를 사용하는 방법
Spring에는 ApplicationContext를 중심으로 이벤트를 발행하고 구독하는 기능을 제공하고 있는데 있는데, 서로 다른 Bean 간에 정보를 교환하는 데 사용할 수 있다.

Publisher는 이벤트 개체를 구성하고 이를 듣고 있는 모든 Listener 에게 발행한다.
(1) Event
1. 발생할 이벤트 객체 생성
이벤트 클래스는 ApplicationEvent 를 확장해서 사용한다.
★ 4.2 버전부터 이벤트 클래스는 더 이상 ApplicationEvent 클래스를 확장할 필요가 없다.
public class CustomEvent extends ApplicationEvent {
private String message;
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
◈ 1-1. 활용

(2) ApplicationEventPulisher - 이벤트 발행
이벤트를 발행하려면 ApplicationEventPublisher를 주입하고 publishEvent 메소드를 사용하면 된다.
@Component
public class CustomSpringEventPublisher {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void publishEvent(CustomEvent event) {
applicationEventPublisher.publishEvent(event);
}
}
◈ 2-1. 활용

비즈니스 로직 이전 또는 이후에 publishEvent() 메서드를 호출하여 이벤트를 발행한다.
@Service
@Requiredargsconstructor
public class EventService {
private final CustomSpringEventPublisher customSpringEventPublisher;
public void add() {
// 로직 진행
CustomEvent event = new CustomEvent(this, "new event");
// 이벤트 발행
customSpringEventPublisher.publishEvent(event);
}
}
◈ 2-2. 활용

(3) ApplicationEventListner - 이벤트 구독
이벤트 리스너는 ApplicationListener 인터페이스를 구현하여 이벤트를 받는다.
이벤트가 발행되면 ApplicationContext 는 해당 이벤트를 구독하는 빈들을 찾아서 notify 해주는데, 이러한 부분은 내부적으로는 옵저버 패턴을 사용해 구현되어 있다.
@Component
public class CustomSpringEventListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println("Received spring custom event - " + event.getMessage());
}
}
★ Spring 4.2부터 이벤트 리스너는 ApplicationListener 인터페이스를 구현하지 않아도, @EventListener 어노테이션을 이용하여 구독할 수 있다.
@Component
public class CustomSpringEventListener {
@EventListener
public void listen(CustomEvent event) {
}
}
◈ 3-1. 활용

◈ 3-2. 활용

(4) 결과

■
3. 스프링에서 이벤트의 발행과 구독
- 멀티 캐스팅( =다수의 수신자가 존재할 수 있는 통신 형태) 방식으로 동작
: 동일한 타입의 여러 리스너가 등록되었다면 모든 리스너가 이벤트를 받게 된다.
- 동기 방식으로 동작함
: 트랜잭션이 하나의 범위로 묶일 수있다. 만약 이벤트를 발행하는 곳에서 트랜잭션이 시작된 상태라면 이벤트를 구독하는 곳에서도 동일한 트랜잭션을 공유하게 된다.
- @TransacitionEventListener
: 이벤트 처리가 하나의 트랜잭션의 안에서 실행된다면 트랜잭션의 커밋 전/후, 완료 또는 롤백에 따라 동작을 고려해야할 수 있다.
스프링 4.2부터는 @TransacitionEventListener 어노테이션과 옵션을 통해 트랜잭션 단계에 관여할 수 있도록 도와준다.
■
4 이벤트를 사용하면 좋은 경우 및 사용 예시
☞ 이벤트 처리가 좋은 경우
☞ 여러 도메인에서 공통으로 사용되는 경우
☞ 두 도메인이 서로 다른 서비스로 분리될 가능성이 있는 경우
☞ 순서 보장 없는 멀티캐스팅 작업을 처리해야 하는 경우
☞ 구독자가 주체적으로 어떤 사건의 발생을 인지하고 처리해야 하는 경우
☞ ex. 메일 발송, 알림 발송
■
5. 이벤트의 사용과 장단점
☞ 장점
》 두 도메인간의 의존성을 완전히 분리할 수 있다.
》 클래스가 독립적이므로 재사용성을 높일 수 있다.
》 추후에 별도의 서비스로 분리하기 용이하다.
》 메세지 구독 모듈을 추가 또는 삭제할 때, 다른 모듈에 영향을 주지 않은 채로 수정할 수 있다
》 단위 테스트가 용이해진다.
☞ 단점
》 전반적인 작업량이 많아진다. => 이벤트 클래스 생성, 구독을 위한 커스텀 어노테이션 등
》 코드 흐름을 따라가기 어렵다.
》 메세지 구독 순서를 고려해야 하는 경우 복잡해진다.
》 전체적인 이벤트의 구독 및 발행 과정을 테스트하기 어렵다.
》 의존성 Decoupling과 비동기 동작으로 인해 트랜잭션 관리가 어렵다.
참고
https://www.nextree.io/spring-event/
https://mangkyu.tistory.com/292
'Spring' 카테고리의 다른 글
| > [Spring] Bean Scope - 2. 사용자 정의 Bean Scope (0) | 2024.01.18 |
|---|---|
| > [Spring] Bean Scope - 1. 개념과 사용 방법(singleton, prototype, request) (0) | 2024.01.18 |
| > [Spring Security]- 인증 정보는 어디에 저장되는 걸까? (0) | 2024.01.10 |
| >[cache] EHcache 3.x 개념을 알아보고 Spring Boot 에서 사용해보자! (0) | 2024.01.05 |
| > [Spring] Multi Thread - 선착순 티켓 예매로 알아보는 동시성 이슈 2. Pessimistic Lock, Optimisitc Lock, Named Lock (0) | 2023.12.15 |