Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- Python
- 기타 연주
- 오류
- pandas
- Visual Studio
- ubuntu
- 프로그래머스
- C
- mysql
- Linux
- Numpy
- label
- nvidia-smi
- 핑거스타일
- JSON
- 채보
- OpenCV
- paramiko
- SSH
- YOLO
- error
- windows forms
- pip
- C#
- VS Code
- 컨테이너
- Selenium
- C++
- Docker
- pytorch
Archives
- Today
- Total
기계는 거짓말하지 않는다
Spring Boot Server-Sent Events(SSE) 실시간 이벤트 본문
서버에서 클라이언트로 단방향 통신이 필요할 때가 있다.
일반적으로 풀링 방식을 사용하게 되면 비효율적일 수 있는데,
실시간 알람, 이벤트 등을 위해 Server-Sent Events(SSE)를 사용할 수 있다.
주의점은 SSE에서 DB 관련 작업을 하지 않아야 하고,
타임아웃 또는 complete 후 재 연결 시 데이터 유실이 가능하다.
Controller
connect 후 emitter를 반환해야 한다.
또한 connect 후 더미 데이터를 한 번 전송해야 503 에러를 방지할 수 있다.
connect 외에 함수에서 반환할 경우 타임아웃까지 계속해서 대기 상태가 된다.
connect의 id는 고유해야 한다. id 값으로 중복 요청을 방지할 수 있다.
아래 코드는 샘플이며, 재 연결 유실 처리, 중복 요청 처리가 되어있지 않다.
package com.temp.springboottemp; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @RestController @RequestMapping("/sse") public class SSEController { // thread safe private final ConcurrentHashMap<String, SseEmitter> clients = new ConcurrentHashMap<>(); @GetMapping("/connect") public SseEmitter connect(@RequestParam("id") String id) { SseEmitter emitter = new SseEmitter(180000L); // millisecond 단위, connection 180초 유지 후 재연결 시도 try { // 저장된 클라이언트에 연결을 추가 clients.put(id, emitter); // 더미 데이터 전송. 503 에러 방지 emitter.send(SseEmitter.event() .name("connect") .data("connected! emitter: " + id.toString())); // SSE 연결 타임아웃 및 완료 핸들링 emitter.onTimeout(() -> { clients.remove(id); emitter.complete(); }); emitter.onCompletion(() -> clients.remove(id)); } catch (IOException e) { e.printStackTrace(); } return emitter; } @GetMapping(value = "/send-json", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public void sendJson(@RequestBody String jsonString) { Map<String, Object> jsonMap = null; SseEmitter emitter = null; try { if (jsonString != null) { jsonMap = GeneralFunction.parseJson(jsonString); System.out.println(jsonMap); if (jsonMap != null) { emitter = clients.get(jsonMap.get("id").toString()); } } if (emitter != null) { emitter.send(SseEmitter.event() .name("send-json") .data(jsonMap)); } } catch (Exception e) { e.printStackTrace(); } } }
JavaScript
EventSource를 사용한다. EventSource 선언 시 URL과 Controller의 요청 URL이 일치해야 한다.
그리고 Controller emitter send event의 name과 addEventListener 선언 시 이름도 일치해야 한다.
// id는 가변적으로 요청 const sse = new EventSource("/sse/connect?id=1", headers = { 'Content-Type': 'text/event-stream; charset=utf-8', 'Cache-Control': 'no-cache', }); sse.addEventListener('connect', (e) => { const { data: receivedConnectData } = e; console.log('connect event data: ', receivedConnectData); // "connected!" }); sse.addEventListener('send-json', (e) => { const { data: receivedConnectData } = e; json_data = JSON.parse(receivedConnectData) console.log('connect json event data: ', json_data); // json data // TextareaElement.value += JSON.stringify(json_data); // TextareaElement.scrollTop = TextareaElement.scrollHeight; });
Controller에서 사용한 General Function
package com.temp.springboottemp; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; public class GeneralFunction { public static Map<String, Object> parseJson(String jsonString) { try { ObjectMapper objectMapper = new ObjectMapper(); TypeReference<Map<String, Object>> typeReference = new TypeReference<Map<String, Object>>() {}; return objectMapper.readValue(jsonString, typeReference); } catch (Exception e) { return null; } } }
'Web > Spring Boot' 카테고리의 다른 글
jsch 오류 com.jcraft.jsch.JSchException: Algorithm negotiation fail (0) | 2023.10.20 |
---|---|
JPA 데이터베이스 초기화 전략 (0) | 2021.07.02 |
Ajax 데이터 Controller 전달 (0) | 2020.12.03 |
Controller Get, Post 오류 (0) | 2020.12.02 |
Schema-validation Exception (0) | 2020.12.02 |