[QnA 게시판] 알림기능 SSE(Server-Sent Events) 적용
Categories: Project
📌 개인적인 공간으로 공부를 기록하고 복습하기 위해 사용하는 블로그입니다.
정확하지 않은 정보가 있을 수 있으니 참고바랍니다 :😸
[틀린 내용은 댓글로 남겨주시면 복받으실거에요]
SSE(Server-Sent Events) 적용하기
게시판 만들다가 알림 기능은 어떻게 구현할까 하고 찾아봤는데 spring 에서 지원해주는 SseEmitter 클래스가 있어서 알아보았다.
SSE란?
- 서버가 클라이언트에게 실시간으로 메세지를 보내는 방법
- 실시간 알림기능을 구현하는데 유용한 기술
- 스프링부트 4.2 이상 버전부터 SseEmitter 클래스를 지원한다
- SseEmitter 클래스는 Spring MVC의 일부로 서버가 클라이언트에게 단방향으로 이벤트를 보낼 수 있게 해주며 클라이언트는 EventSource API를 사용하여 서버에서 보낸 이벤트를 받을 수 있다.
SseEmitter 클래스 주요 기능과 메서드
- 생성자
- SseEmitter() : 기본 생성자, 타임 아웃 설정 없이 인스턴스 생성
- SseEmitter(Long timeout) : 지정된 타임아웃을 가진 SseEmitter 인스턴스를 생성
- 주요 메서드
- send(Object object) : 단일 객체를 JSON 형식으로 클라이언트에게 전송한다. 클라이언트는 이 데이터를 기본 이벤트로 수신한다.
- send(SseEmitter.SseEventBuilder event) : 이벤트 빌더를 사용하면 이벤트를 세밀하게 설정하고 클라이언트에게 전송한다. 이벤트 이름/ 데이터 / ID 등을 설정 할 수 있다.
- complete( ) : SSE 스트림을 정상적으로 완료한다, 클라이언트에게 EOF(End Of File)를 전송하여 스트림을 닫는다.
- completeWithError(Throwable ex) : 지정된 에러와 함께 SSE 스트림을 완료한다. 클라이언트에게 에러를 전송하고 스트림을 닫는다.
- onCompletion(Runnable callback) : 스트림이 타임아웃 되었을 때 실행할 콜백을 설정한다.
- onTimeout(Runnable callback) : 스트림이 타임아웃되었을 때 실행할 콜백을 설정한다.
- setTimeout(Long timeout) : 타임아웃을 설정한다.
-
활용 방법
SseEmitter 클래스는 실시간으로 데이터 업데이트를 클라이언트에게 푸시해야 하는 다양한 애플리케이션에 유용하다. 예를 들어, 채팅 애플리케이션, 실시간 알림 시스템, 라이브 피드 업데이트 등을 구현할 때 사용할 수 있다. SseEmitter를 사용하면 서버가 클라이언트에게 비동기적으로 데이터를 전송할 수 있어, 클라이언트는 새로운 데이터를 즉시 받아볼 수 있다.
적용 방법
-
클라이언트 → 서버 : 알림요청
1
const eventSource = new EventSource('/notifications');
-
서버 → 클라이언트 : 연결 설정 및 메세지 보내기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// 연결 설정 @GetMapping("/notifications") public SseEmitter subscribe() { SseEmitter sseEmitter = new SseEmitter(3600000L); // 1시간 동안 연결 유지 // 연결이 종료되면 제거 sseEmitter.onCompletion(() -> emitterRepository.delete(userId)); sseEmitter.onTimeout(() -> emitterRepository.delete(userId)); return sseEmitter; } // 메세지 보내기 private void sendNotification(String message, String username) { notificationService.getEmittersForUser(username).forEach(emitter -> { try { emitter.send(SseEmitter.event().name("notification").data(message)); } catch (IOException e) { emitter.completeWithError(e); } }); }
-
클라이언트가 메세지 받기
1 2 3 4 5
eventSource.addEventListener('notification', function(event) { const newElement = document.createElement("li"); newElement.textContent = event.data; document.getElementById("notifications").appendChild(newElement); });
SseEmitter 외 다른 알림 기능
WebSocket
- 장점: 양방향 통신 가능, 실시간 데이터 전송에 적합
- 단점: 서버와 클라이언트 모두 WebSocket을 지원해야 함
- 채팅 애플리케이션, 실시간 게임, 주식 시세 등 실시간 데이터 전송이 필요한 애플리케이션에 적합
적용방법
-
의존성 추가
1 2 3
dependencies { implementation 'org.springframework.boot:spring-boot-starter-websocket' }
-
WebSocket 설정
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new NotificationHandler(), "/notifications").setAllowedOrigins("*"); } }
-
WebSocket 핸들러
1 2 3 4 5 6 7 8 9 10 11 12
import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; public class NotificationHandler extends TextWebSocketHandler { @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 메시지 처리 로직 session.sendMessage(new TextMessage("New notification!")); } }
Long Polling
- 클라이언트가 서버에 요청을 보내고, 서버는 새로운 데이터가 있을 때까지 응답을 지연시키는 방식
- 데이터가 준비되면 서버는 응답을 보내고, 클라이언트는 다시 요청을 보낸다
- WebSocket이나 SSE를 사용할 수 없는 환경에서 사용될 수 있습니다.
- 장점: 비교적 구현이 간단, 모든 브라우저에서 지원
- 단점: 서버 리소스 소모가 큼, 지연 시간이 있을 수 있음
적용방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class NotificationController {
@GetMapping("/notifications")
public DeferredResult<String> getNotifications() {
DeferredResult<String> deferredResult = new DeferredResult<>(30000L);
new Thread(() -> {
try {
Thread.sleep(10000); // 예시로 10초 대기
deferredResult.setResult("New notification!");
} catch (InterruptedException e) {
deferredResult.setErrorResult(e);
}
}).start();
return deferredResult;
}
}
Push Notifications
모바일 애플리케이션과 웹 애플리케이션에서 많이 사용
서버는 푸시 서비스(APNs, FCM 등)를 통해 클라이언트에게 푸시 메시지를 보낸다.
- 장점: 모바일 및 웹 애플리케이션 모두에 적합, 사용자가 앱을 사용 중이지 않을 때도 알림 전송 가능
- 단점: 설정이 복잡할 수 있음, 브라우저 푸시 지원 필요
적용 방법
-
의존성 추가
1 2 3
dependencies { implementation 'nl.martijndwars:web-push:5.1.0' }
-
Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import nl.martijndwars.webpush.Notification; import nl.martijndwars.webpush.PushService; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class NotificationController { private PushService pushService = new PushService(); @PostMapping("/sendNotification") public void sendNotification(@RequestBody Subscription subscription) throws Exception { Notification notification = new Notification(subscription, "New notification!"); pushService.send(notification); } }
Leave a comment