6. 비동기 이벤트 처리

선종우·2023년 9월 1일
0

1. 비동기 처리의 필요성

  • 로그인한 사용자가 메시지를 보내거나, 매치가 성사되면 상대방에게 알람을 전송한다.
  • 기존에는 사용자 메시지 작성, 이벤트 발생 시 로직 내에서 알람 처리도 진행했다.
  • 그런데 로그인한 사용자 입장에서는 알람전송은 사용자의 관심사항이 아니다. 구현상으로도 사용자를 매치시키고 메시지를 저장하는 것과 알람은 연관관계가 적다. 그럼에도 기존 구조상 사용자는 알람 처리까지 끝난 후에야 서비스 처리 결과를 받을 수 있다.
  • 비동기 이벤트 처리방식을 활용하면 문제를 해결할 수 있을 듯 하여 적용해보았다.

2. 스프링 @EventListener, @Async를 이용한 이벤트 처리

  • 대부분의 이벤트 기반 로직은 Event를 발생시키는 곳과 이벤트를 소비하는 곳으로 구분된다. 스프링은 이러한 로직을 추상화 시켜 개발자가 쉽게 이벤트 기반으로 프로그램을 작성할 수 있게 해두었다.
  • 이벤트 발생은 ApplicationEventPublisher가 처리한다.
@RequiredArgsConstructor
@Service
public class MakeMatchAndMessageRoom {
    private final MatchService matchService;
    private final MessageRoomService messageRoomService;
    private final ApplicationEventPublisher eventPublisher;

    @Transactional
    public MessageRoomDto execute(Member fromMember, Member toMember) {
        Match newMatch = matchService.createMatch(fromMember, toMember);
        MessageRoom newMessageRoom = messageRoomService.createMessageRoom(fromMember, toMember, newMatch);
        MessageRoomDto messageRoomDto = MessageRoomDto.from(newMessageRoom);

        notifyMatch(messageRoomDto.matchDto(), messageRoomDto);
        return messageRoomDto;
    }


    private void notifyMatch(MatchDto matchDto, MessageRoomDto messageRoomDto) {
        eventPublisher.publishEvent(new MatchEvent(this, matchDto, messageRoomDto));
    }
}
  • 코드에서처럼 ApplicationEventPublisher를 주입받고, publishEvent만 호출하면 된다. 이때 publishEvent에는 발생시키고자 하는 ApplicationEvent 상속 객체를 전달하면 된다.

  • 프로젝트에서는 MatchEvent클래스를 작성했다.

@Getter
public class MatchEvent extends ApplicationEvent {

    private final MatchDto match;
    private final MessageRoomDto messageRoom;
    public MatchEvent(MakeMatchAndMessageRoom source, MatchDto newMatch, MessageRoomDto messageRoom) {
        super(source);
        this.match = newMatch;
        this.messageRoom = messageRoom;
    }


}
  • 해당 이벤트는 @EventListener를 이용해 처리한다.
@Component
@RequiredArgsConstructor
public class MatchEventListener {

    private final SseService sseService;
    private final MatchNotificationRepository matchNotificationRepository;

    @EventListener
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Async
    public void sendMatchNotification(MatchEvent matchEvent) {

 		(생략)

        sseService.send(matchEvent.getMatch().toMember().id(), notificationResponse);
        sseService.send(matchEvent.getMatch().fromMember().id(), notificationResponse);
    }


}
  • EventListener 애노테이션에 처리하고자 하는 Event를 명시할 수도 있다.
  • 이때 @Async를 이용해 비동기 처리를 해줌으로써 알람 로직이 기존 로직에 영향을 주지 않도록 처리하였다.

0개의 댓글