Event의 발행 과정

양성준·2025년 3월 10일

스프링

목록 보기
3/49

Event

: 사건의 발생을 알려주기 위한 메개체, 메시지
-> 유저의 생성과 삭제 사건이 일어났다는 걸 알려줌 + 추가적인 작업(EventListner) 실행
=> 클래스 및 기능 분리로 SRP와 OCP 원칙을 지킬 수 있게 해줌!

@Service
@RequiredArgsConstructor
public class MemberService {
 
    private final AlarmService alarmService;
    private final AlimTalkService alimTalkService;
 
    public void registerUser(String name) {
        // 회원가입 처리 로직
        System.out.println("회원 추가 완료");
 
        // 가입 축하 푸시 알림
        alarmService.send(name);
 
        // 카카오 알림톡 전송
        alimTalkService.send(name);
    }
}

위의 코드엔 총 세가지의 문제점이 있는데,

  • 강한결합 :
    • registerUser에 회원 가입 로직뿐만 아니라 가입 축하 푸시 알림을 보내는 로직, 카카오 알림톡 전송하는 로직이 모두 섞여있음
  • 성능 :
    • registerUser 메소드가 실행되고 끝날 때 까지, 모든 기능을 실행해야하기 때문에 오래 걸림
  • 트랜잭션 :
    • 한가지 기능만 오류가 나도 한 트랜잭션으로 묶여있어 전부 롤백됨

=> Event를 사용하면 registerUser가 회원가입만 담당할 수 있고, 각 EventListener도 기능별로 클래스로 분리할 수 있다. (SRP),
부가적인 기능이 생겨도 EventListener만 추가해주면 되므로 OCP도 지킬 수 있다.
(registerUSer 메소드 수정 필요 X)

이벤트의 발행 단계

  1. 특정 작업이 실행될 때 ApplicationEventPublisher를 사용하여 이벤트를 생성하고 발행
    -> Spring이 ApplicationEventPublisher를 통해 이벤트를 이벤트 큐에 저장
  2. Event Dispatcher가 어떤 Event Listener가 이벤트를 처리할지 찾음
  3. EventListner (이벤트 핸들러)가 전달된 이벤트를 사용해 실제 로직을 실행

Event 객체

public class UserEvent extends ApplicationEvent {

    private final UserEventType eventType;
    private final String message;

    public UserEvent(Object source, UserEventType eventType, String message) {
        super(source);
        this.eventType = eventType;
        this.message = message;
    }

    public UserEventType getEventType() {
        return eventType;
    }

    public String getMessage() {
        return message;
    }
}
  • 항상 ApplicationEvent를 상속해야함
  • 이벤트를 던진 스프링 Bean이 무엇인지를 알려주기 위해 Object source를 super(source)로 생성자에 전달해줘야함

EventListener

@Component
public class UserEventNotifier {

    @EventListener // userEvent가 발행됐을 때, 스프링이 이 메소드를 호출할 것
    public void handleUserEvent1(UserEvent userEvent) {
        System.out.println("User event received 1: " + userEvent.getEventType());
    }

    @EventListener // userEvent가 발행됐을 때, 스프링이 이 메소드를 호출할 것
    public void handleUserEvent2(UserEvent userEvent) {
        System.out.println("User event received 2: " + userEvent.getEventType());
    }
}
  • 메소드가 실행되어 관련 이벤트가 발행될 때, 구독하고 있는 작업(EventListener)들을 실행
  • 이벤트 발행 내역 로그만 모아서 DB에 저장하는 경우도 있음 - NoSqL인 timeseries DB에 많이 쌓음
    ex) 사용자 등록 기록을 DB에 남기기 위해
  • 같은 이벤트에 대해 여러개의 Listener 등록 가능
    Notifier - 사용자가 생성됐을 때 사람들에게 알림 ex) 카톡 가입 축하 메시지
    Logger - 사용자가 생성됐을 때 로그를 로그DB에 저장

Event Publisher

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;
    private final ApplicationEventPublisher eventPublisher; // User가 생성됐을 때 이벤트를 반환하기 위해 필요

    // 생성자 주입
    public UserServiceImpl(UserRepository userRepository, ApplicationEventPublisher eventPublisher) {
        this.userRepository = userRepository;
        this.eventPublisher = eventPublisher;
    }

    @Override
    public UUID registerUser(String userName) {
        User user = new User(UUID.randomUUID(), userName);
        userRepository.save(user);
        UserEvent event = new UserEvent(this, UserEventType.REGISTERED, "User registerd: " + userName);
        eventPublisher.publishEvent(event);
        return user.id();
    }
  • 특정 메소드가 실행될 때, 이벤트를 발생할 수 있는 객체
  • 이벤트 발행이 필요한 객체에 필드로 생성자 주입을 통해 주입되어야만 사용 가능!
  • EventPublisher.publishEvent(event);로 발행할 이벤트 전달
    -> 이벤트를 이벤트 큐에 저장 후, 어떤 Listener가 연결되어있는지 찾은 뒤 적절한 리스너를 실행

Spring Event 동작 과정 (정리)

1️⃣ IoC 컨테이너에 Bean 등록

  • Spring Boot 애플리케이션이 시작되면, IoC 컨테이너가 자동으로 Bean을 생성 및 등록함.
  • ApplicationEventPublisher (이벤트를 발행하는 Publisher) 먼저 등록됨
  • @Service / @Component가 붙은 UserServiceImpl, Notifier, Logger도 함께 등록됨

2️⃣ 유저 등록 요청 → registerUser() 실행

  • registerUser()가 실행되면서 이벤트를 발행함
  • 내부적으로 eventPublisher.publishEvent(event); 실행

3️⃣ Spring Event Publisher가 이벤트를 이벤트 큐에 저장

  • ApplicationEventPublisher는 이벤트를 즉시 처리하지 않고, Spring이 관리하는 이벤트 큐에 저장
  • Spring은 "이벤트 큐"를 확인하면서 어떤 리스너(@EventListener)가 연결되어 있는지 찾음

4️⃣ Spring이 적절한 EventListener 실행

  • Spring이 등록된 EventListener를 찾아 자동 실행!

참고 - https://dgjinsu.tistory.com/41

profile
백엔드 개발자를 꿈꿉니다.

0개의 댓글