본문 바로가기

개발일지

20240125

SSE를 통해 알림 기능 구현

 

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/notification")
public class NotificationController {

  private final NotificationService notificationService;
  public static Map<Long, SseEmitter> sseEmitters = new ConcurrentHashMap<>();

  @GetMapping( "/subscribe")
  public SseEmitter notification(@AuthMember Member member) {
    SseEmitter sseEmitter = notificationService.subscribe(member.getId());

    return sseEmitter;
  }
}

 

@Service
@RequiredArgsConstructor
public class NotificationService {

  public SseEmitter subscribe(Long memberId) {
    SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE);

    try{
      sseEmitter.send(SseEmitter.event().name("connect"));
    } catch (IOException e) {
      throw new BisException(ErrorCode.INTERNAL_SERVER_ERROR);
    }

    NotificationController.sseEmitters.put(memberId, sseEmitter);

    sseEmitter.onCompletion(()-> NotificationController.sseEmitters.remove(memberId));
    sseEmitter.onTimeout(()-> NotificationController.sseEmitters.remove(memberId));
    sseEmitter.onError((e)-> NotificationController.sseEmitters.remove(memberId));

    return sseEmitter;
  }

  public void notifyInviting(Long memberId, String clubTitle){

    if(NotificationController.sseEmitters.containsKey(memberId)){
      SseEmitter sseEmitterReceiver = NotificationController.sseEmitters.get(memberId);

      try{
        sseEmitterReceiver.send(SseEmitter
            .event()
            .name("invitedClub")
            .data(clubTitle+"에 초대되었습니다."));
      } catch (IOException e){
        NotificationController.sseEmitters.remove(memberId);
      }
    }
  }

  public void notifyMessage(Long memberId){

    if(NotificationController.sseEmitters.containsKey(memberId)){
      SseEmitter sseEmitterReceiver = NotificationController.sseEmitters.get(memberId);

      try{
        sseEmitterReceiver.send(SseEmitter
            .event()
            .name("message")
            .data("채팅메세지가 도착했습니다."));
      } catch (IOException e){
        NotificationController.sseEmitters.remove(memberId);
      }
    }
  }
}

 

알림 기능은 두 군데에 사용되고 있다. 첫 번째는 클럽에 초대되었을 때와, 채팅 메시지가 도착했을 때다.

MemberService의 클럽초대 메서드 끝에 notifyInviting 메서드를 추가해 주면 SseEmitter에 이벤트를 발생시키고 데이터로 해당 알림 내용을 보낸다.

 

function connectSse(){
  const eventSource = new EventSource(`/api/notification/subscribe`);
  eventSource.addEventListener("invitedClub", function (event) {
    console.log(event.data);
    const data = event.data;
    (async () => {
      //브라우저 알림
      const showNotification = () => {
        const notification = new Notification('메세지', {
          body: data
        });

        setTimeout(() => {
          notification.close();
        }, 10 * 1000);

        notification.addEventListener('click', () => {
          window.open(data.url, 'blank');
        });
      }

      let granted = false;
      if (Notification.permission === 'granted') {
        granted = true;
      } else if (Notification.permission !== 'denied') {
        let permission = await Notification.requestPermission();
        granted = permission === 'granted';
      }

      if (granted) {
        showNotification();
      }
    })();
  })

 

프런트에서는 해당 이벤트가 발생할 시 알림을 띄워주는 코드를 작성해 준다.

'개발일지' 카테고리의 다른 글

MySQL 성능 개선 프로젝트 2  (0) 2024.09.03
MySQL 성능 개선 프로젝트 1  (0) 2024.09.02
20240118  (0) 2024.01.18
20240116  (0) 2024.01.17
20240115  (0) 2024.01.15