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 | 31 |
Tags
- pytorch
- pandas
- pip
- Docker
- ubuntu
- SSH
- 기타 연주
- paramiko
- error
- 명령어
- C
- label
- JSON
- Visual Studio
- 채보
- Python
- windows forms
- mysql
- C++
- LIST
- 오류
- Numpy
- Selenium
- YOLO
- VS Code
- C#
- Linux
- OpenCV
- 프로그래머스
- 핑거스타일
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 |
Comments