반응형
Server Sent Event(SSE)란?
- HTTP 스트리밍을 통해 서버에서 클라이언트로 단방향의 Push Notification을 전송할 수 있는 HTML5 표준 기술.
- HTTP 프로토콜을 기반으로 서버에서 클라이언트로 Real-Time Push Notification을 전송할 수 있다.
- EventStream의 최대 개수는 HTTP/1.1 사용시 6개, HTTP/2 사용시 최대 100개까지 유지 가능하다.
- JavaScript의 EventSource를 이용하여 사용 가능하고, 접속에 문제가 있으면 자동으로 연결을 재시도하는 특징을 갖고 있다.
- IE에서는 EventSource를 기본 제공하지 않지만 polyfill을 통해 사용 가능하다.
- Server To Client로의 단방향 연결만 지원하기 때문에 서버에서 데이터를 프론트로 일방적으로 내려주는 케이스에서 적절하다.
- 양방향 연결을 사용해야할 경우 WebSocket이 적절하다.
Server
@RestController
class ChattingController {
private val log = LoggerFactory.getLogger(this::class.java)
private val clients = ConcurrentHashMap<String, SseEmitter>()
@GetMapping("/chat/connect/{clientId}")
fun connect(@PathVariable clientId: String): SseEmitter {
val emitter = SseEmitter(60_000)
clients[clientId] = emitter
log.info("connected: {}", clientId)
emitter.onCompletion {
log.info("disconnected: {}", clientId)
clients.remove(clientId)
}
emitter.onTimeout {
log.info("timeout: {}", clientId)
clients.remove(clientId)
}
emitter.onError {
log.info("error: {}", clientId, it)
clients.remove(clientId)
}
return emitter
}
@PostMapping("/chat/send")
fun send(@RequestBody message: Message) {
if (message.receiverId == null) {
clients.values.forEach { it.send(message, MediaType.APPLICATION_JSON) }
} else {
clients[message.receiverId]?.send(message, MediaType.APPLICATION_JSON)
}
}
data class Message(
val senderId: String,
val receiverId: String?,
val message: String,
)
}
Client
경로 : ~/src/main/resources/static/index.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE Chat Example</title>
</head>
<body>
<h1>Chat Example</h1>
<ul id="messages"></ul>
<div>
<input id="receiverId" placeholder="receiverId"/>
<input id="message" placeholder="message"/>
<button id="send">전송</button>
</div>
<script>
const clientId = "client_" + Math.random().toString(36).substring(7);
const eventSource = new EventSource(`/chat/connect/${clientId}`);
eventSource.onopen = function (event) {
console.log(`open - ${clientId}`, event);
};
eventSource.onerror = function (event) {
console.error(`error - ${clientId}`, event);
};
eventSource.onmessage = function (event) {
const messages = document.querySelector("#messages");
const newMessage = document.createElement("li");
const json = JSON.parse(event.data);
newMessage.textContent = json.message;
messages.appendChild(newMessage);
};
document.querySelector('#send').addEventListener("click", function () {
const receiverId = document.querySelector('#receiverId').value;
const message = document.querySelector('#message').value
fetch('/chat/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
senderId: clientId,
receiverId: receiverId || null,
message,
})
});
});
</script>
</body>
</html>
참고
반응형
'Development > Spring' 카테고리의 다른 글
[Spring] DataSource (0) | 2021.05.19 |
---|---|
[Spring] Testcontainers (0) | 2021.04.15 |
[Spring] H2 Database (0) | 2021.02.27 |
[Spring] Spring Cloud Feign (0) | 2020.12.27 |
[Spring] Spring Cloud Gateway (0) | 2020.12.27 |