SSE 적용기

도입 계기

현재 진행 중인 관제 플랫폼 서비스에서는 특정 차량의 총 주행거리가 5,000km 이상일 경우 차량 점검 대상임을 판단하고, 해당 차량의 점검 필요 알림을 사용자에게 전달하는 기능을 구현이 필요 했다. 이 알림 기능의 경우 서버에서 클라이언트로의 일방향 전송만 필요하기 때문에, 웹소켓(양방향 통신) 대신 SSE(Server-Sent Events)를 사용하는 방안으로 진행하기로 했다.

SSE 장점과 단점

SSE의 장점

SSE 적용 코드

컨트롤러 설정

// AlarmController.java
@GetMapping("/subscribe")
@PreAuthorize("hasRole('ROLE_USER')")
public SseEmitter subscribe(@AuthenticationPrincipal CustomUserDetails user) {
    return alarmService.subscribe(user.getId());
}

서비스 로직

// AlarmService.java
public SseEmitter subscribe(String userId) {

    SseEmitter sseEmitter = new SseEmitter(0L);

    sseEmitters.put(userId, sseEmitter);

    sseEmitter.onCompletion(() -> sseEmitters.remove(userId));

    sseEmitter.onTimeout(() -> sseEmitters.remove(userId));

    sseEmitter.onError(event -> sseEmitters.remove(userId));

    return sseEmitter;
}

알림 전송

public void sendAll(SendAlarm sendAlarm) {
    sseEmitters.forEach((userId, emitter) -> {
        try {
            emitter.send(
                SseEmitter.event()
                    .name(String.valueOf(sendAlarm.getStatus()))
                    .data(sendAlarm, MediaType.APPLICATION_JSON)
            );
        } catch (IOException e) {
            logger.error(userId + " 메세지 전송 중  문제 발생", e);
            sseEmitters.remove(userId);
        }
    });
}

알림 상태를 이벤트 이름으로 설정하여, 클라이언트에서는 해당 상태에 따라 다른 방식으로 알림을 표시할 수 있다. 모든 연결된 사용자에게 알림을 전송하며, 전송 중 오류가 발생하면 해당 사용자의 SseEmitter를 제거할 수 있다.

적용 결과

알림 전송 후, 클라이언트에서 아래 이미지와 같이 정상적으로 알림을 확인이 가능했다! Image