티스토리 뷰

모든 소스는 Github에 있습니다.

 

 

안녕하세요.

오늘은 @EventListner 에 대해 공유해보도록 하겠습니다.

도메인 사이에 강한 의존성으로 인해 시스템이 복잡해지는 경우가 발생하는데

의존성을 줄이기 위한 방법으로 @EventListner를 소개하겠습니다.

Case 1.

우선 예시로 좋은 주문예시를 들도록 하겠습니다.

주문을 하게되면 발생하는 이벤트들이 있을겁니다.

예를 들면, 주문완료 카톡발송, 이메일 발송 등이 있을 수 있습니다.

아래의 코드를 보겠습니다.

@Service
public class OrderService {

    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

        @Transactional
    public void save(OrderRequestDto body) {
        System.out.println("ORDER PROCESSING...");

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("ORDER SUCCESS");

                sendKakao();

    }

    public void sendKakao() {
        System.out.println("SEND KAKAO MESSAGE");
    }

    public void sendEmail() {
        System.out.println("SEND EMAIL");
    }

}

save() 를 주문을 처리하는 기능이라고 생각해보겠습니다.

주문을 처리한 후 메시지를 보내는 메소드를 호출하고 있습니다.

 

이렇게 할 경우 주문을 처리하는 로직과 메시지를 발송(sendKakao())하는 로직이 섞이게 됩니다.

이와 같은 의존성을 줄이기 위해 ApplicationEvent, @EventListner를 활용해보도록 하겠습니다.


개선 1. ApplicationEvent

  • Order.java

      public class Order {
    
          private Long orderId;
    
          private String name;
    
          private String price;
    
          private String method;
    
      //        *** GETTER, SETTER,CONSTRUCTOR 생략 ***
      }

    주문 POJO객체이다. 간단한 getter, setter, toString()를 만들어줍니다. (Lombok을 사용하면 짧게 가능합니다)

  • OrderEvent.java

      import org.springframework.context.ApplicationEvent;
    
      public class OrderEvent extends ApplicationEvent {
    
          private Order data;
    
          public OrderEvent(Object source) {
              super(source);
          }
    
          public OrderEvent(Object source, Order data) {
              super(source);
              this.data = data;
          }
    
          public Order getData() {
              return data;
          }
      }

    ApplicationEvent 를 상속하는 클래스를 만들어줍니다.

  • MessageService.java

    메시지를 발송하는 Service입니다.

      @Service
      public class MessageService {
          public void sendKakao() {
              System.out.println("SEND KAKAO MESSAGE");
          }
    
          public void sendEmail() {
              System.out.println("SEND EMAIL");
          }
      }
  • OrderHandler.java

      @Component
      public class OrderHandler implements ApplicationListener<OrderEvent> {
    
              @Autowired
          MessageService messageService;
    
          @Override
          public void onApplicationEvent(OrderEvent order) {
              System.out.println("REQUEST SENDING KAKAO");
                      messageService.sendKakao();
          }
      }

    @Component Bean으로 등록해줘야합니다.

    ApplicationListner 를 구현하는 클래스를 만들어줍니다.

    ApplicationListner<T> T 에는 이벤트를 발행할 클래스로 등록해줍니다.

  • OrderService.java

      @Service
      public class OrderService {
    
          @Autowired
          ApplicationEventPublisher applicationEventPublisher;
    
          public void save() {
              System.out.println("ORDER PROCESSING...");
    
              Order order = new Order();
              order.setOrderId(1L);
              order.setName("ALXNDR");
              order.setPrice("10000");
              order.setMethod("CREDIT");
              try {
                  Thread.sleep(1000);
                  // 처리되는 느낌...
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
    
              System.out.println("ORDER SUCCESS");
    
              applicationEventPublisher.publishEvent(new OrderEvent(this, order));
          }
      }

    이제 주문을 처리하는 (척) 하는 Service를 만들어줍니다.

    ApplicationEventPublisher를 주입받습니다.

    그리고, Order 객체를 만들어주고 publishEvent()OrderEvent를 생성해서 이벤트를 발생시킨다.

    그리고 테스트를 도와줄 ApplicationRunner를 구현한 OrderRunner도 만들어줍니다.

  • OrderRunner.java

      @Component
      public class OrderRunner implements ApplicationRunner {
    
          @Autowired
          OrderService orderService;
    
          @Override
          public void run(ApplicationArguments args) throws Exception {
              // 주문 요청하는척...
              System.out.println("REQUEST ORDER");
    
              orderService.save();
    
              System.out.println("FINISHED");
          }
      }

실행.

정상적으로 Event를 발행, 실행된 것을 확인할 수 있습니다.

위에 코드는 Event를 발행하기 위한 많은 코드가 존재합니다.

Event마다 저러한 클래스들을 만들기엔 상당히 귀찮고 힘들죠.


개선 2. @EventListner

그래서 Spring 4.2 부터 사용 가능한 @EventListner 어노테이션을 사용하여 이벤트를 처리해보겠습니다.

@EventListner를 사용하면 ApplicationEvent, ApplicationListner 상속, 구현 할 필요가 없습니다.

위에서 만든 코드들을 조금 수정해보겠습니다.

  • OrderHandler.java

      @Component
      public class OrderHandler {
    
          @Autowired
          private MessageService messageService;
    
              @EventListener
              public void sendKakaoAfterOrder(Order order) {
              System.out.println("REQUEST SENDING KAKAO");
              messageService.sendKakao();
          }
      }

    받는 파라미터도 POJO객체 Order로 변경하였습니다.

    훨씬 간결해진것을 볼 수 있습니다.

    파라미터를 Order로 변경했으니 이벤트를 발행할 때도 변경해줘야합니다.

  • OrderService.java

    applicationEventPublisher.publishEvent(new OrderEvent(this, order)); 를 아래와 같이 변경해줍니다.

      applicationEventPublisher.publishEvent(order);

이제 프로젝트를 실행시키면,

똑같은 결과를 볼 수 있습니다.

만약 이메일 전송을 추가하고 싶다면,

@EventListener
public void sendEmailAfterOrder(Order order) {
    System.out.println("REQUEST SENDING EMAIL");
    messageService.sendEmail();
}

한가지 더 추가 해주면

 

이메일까지 전송할 수 있게됩니다~

개선사항..

아직까지 이벤트는 순차적으로 실행됩니다.

만약 메일 전송로직이 실패하면 주문처리 로직까지 실패하게 될 수 있습니다.

이러한 문제를 해결할 수 있는 방법은 다음 포스팅에서 다루도록 하겠습니다.

 

 

도움이 되었길 바라며..

감사합니다.


References

https://ivvve.github.io/2019/06/02/java/Spring/event-listener-2/

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함