SSE(Server Sent Event)์ EventSource
Server Sent Event๋ ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๋ ๊ธฐ๋ฒ ์ค ํ๋๋ค.
์น์์ผ๊ณผ๋ ์ด๋์ ๋ ๋์ฒด๊ฐ๋ฅํ ๊ด๊ณ์ ์๋ค.
๊ธฐ๋ณธ์ ์ธ ์๋ฆฌ๋ ๋จ์ํ๋ค.
๋ธ๋ผ์ฐ์ ์์ EventSource๋ผ๋ ํ์ค ๊ธฐ๋ฅ์ผ๋ก ์ฑ๋์ ์ด์ด๋๋ฉด, ์๋ฒ๊ฐ ๊ฑฐ๊ธฐ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์กํด์ค ์ ์๋ ๊ฒ์ด๋ค.
๋ธ๋ผ์ฐ์ ์ ์์ฒญ ์์ด๋ ์๋ฒ ํผ์์ ๋ฅ๋์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ค๋๊ฒ ํน์ง์ด๋ค.
๋ธ๋ผ์ฐ์ ํธํ์ฑ
์์ ์๋ SSE๊ฐ IE์์ ์ง์๋์ง ์๋๋ค๋ ๋ฌธ์ ๊ฐ ์์๋๋ฐ, ์์ฆ์ SSE๋ ์น์์ผ์ด๋ ๋๋ค ๋๋ถ๋ถ์ ๋ธ๋ผ์ฐ์ ์์ ๋ค ์ ์๋ํ๋ค.
๋งค์ฐ ํน์ํ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ๋ธ๋ผ์ฐ์ ํธํ์ฑ์ด ๋ฌธ์ ๋ ์ผ์ ์์ ๊ฒ์ด๋ค.
์ฅ์ (vs Websocket)
-
์ํธ์์ฉ์ด ํ์์๊ณ ์๋ฒ->ํด๋ผ์ด์ธํธ์ ๋จ๋ฐฉํฅ ํต์ ๋ง ํ๋ฉด ๋๋ ๊ฒฝ์ฐ์๋ ๋ ๊ฐ๋จํ ํด๊ฒฐ์ฑ ์ด๋ค. ๊ตฌํ๊ณผ ์ฌ์ฉ ์์ฒด๊ฐ ๋ ๋จ์ํ๋ค.
-
Websocket์ ๊ฒฝ์ฐ ํจํท ๊ฒ์ฌ๋ฅผ ์ํํ๋ ๋ฐฉํ๋ฒฝ ํ๊ฒฝ์์ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋๋ฐ, SSE๋ ๊ทธ๋ฌํ ๋ฌธ์ ๋ฅผ ํํผํ ์ ์๋ค.
๋จ์ (vs Websocket)
-
connection limit
HTML 1.1์ ์ฐ๋๊ฒฝ์ฐ ๋๋ถ๋ถ ๋์ ์ฐ๊ฒฐ ๊ฐ์์ ์ ํ์ด ์๋ค.
๋๋ถ๋ถ์ ๋ธ๋ผ์ฐ์ ๋ ๋๋ฉ์ธ๋น ์ต๋ ์ฐ๊ฒฐ ๊ฐ์๊ฐ 6๊ฐ์ฏค ๋๋ค.
์ฌ์ค ์ด๊ฑด ํฐ ๋ฌธ์ ๋ ์๋๊ธด ํ๋ค.
ํ ๋๋ฉ์ธ์ 6๊ฐ ์ด์ ์ธ์ผ์ด ๊ทธ๋ ๊ฒ ๋ง๋? ์ถ๊ธฐ๋ ํ๊ณ , HTTP2๋ถํฐ๋ ๊ฐ์ ์ ํ ๋ฌธ์ ๊ฐ ์๋ค. -
๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ๋ ์ ์ก ๋ถ๊ฐ (UTF-8 ํ ์คํธ๋ง ๊ฐ๋ฅ)
์ฌ์ฉ์ฒ
๊ทธ๋ผ ์ด๋จ๋ SSE๋ฅผ ์ฃผ๋ก ์ฌ์ฉํ ๊น?
์ผ๋ฐ์ ์ผ๋ก ์น์์ผ์ "์ฑํ " ๊ฐ์ ๋น ๋ฅด๊ณ ํ๋ ฅ์ ์ธ ์ค์๊ฐ ์ํธ์์ฉ์ด ํ์ํ ๊ณณ์ ์์ฉ๋๋ค.
๋ฐ๋ฉด SSE๋ ์ค์๊ฐ์ผ๋ก ์ ๋ฐ์ดํธ๋๋ "์ค์๊ฐ ์์ธ์ฐฝ" ๊ฐ์ ๊ธฐ๋ฅ์ด๋, "ํธ์ํฐ ํผ๋ ์ ๋ฐ์ดํธ ์๋ฆผ" ๋ฑ ํ์ฌ ์ฌ์ฉ์์ ์ก์ ๊ณผ ๊ด๊ณ์์ด ์ด๋ฃจ์ด์ง๋ ๋ณ๊ฒฝ์ฌํญ์ ๋ํ ์๋ฆผ์ ๊ตฌํํ๋๋ฐ ์ข๋ ์ ํฉํ ํธ์ด๋ค.
ํ์ง๋ง ์๋ก ๊ต์ฒด ๊ฐ๋ฅํ ๊ด๊ณ์ ์๊ณ , ์ฌ์ฉ์ค์ธ ๊ธฐ์ ์ ํ๊ฒฝ์ด ์ด๋ ํ์ง, ์ด๋ ํ ํํ์ ์๋น์ค๋ฅผ ๋ง๋๋์ง์ ๋ฐ๋ผ์ ์ ์ ํ ์ ํํ๋ฉด ๋๋ค.
์ฌ์ฉ๋ฒ (Express.js)
Express์์ SSE๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ ๋ฆฌํด๋ณด๊ฒ ๋ค.
๋ค๋ฅธ ํ๋ ์์ํฌ๋ค๋ ๋ค ๊ฐ๋ฅํ๋ค.
๋จผ์ ์ฐ๊ฒฐ์ฉ API๋ฅผ ์์ฑํด์ผ ํ๋ค.
SSE๋ ์์ฑ๋ response ๊ฐ์ฒด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ง์์ ์ผ๋ก ํต์ ์ ํ๋ ๊ตฌ์กฐ์ด๊ธฐ ๋๋ฌธ์ด๋ค.

๊ทธ๋ฆฌ๊ณ SSE ์ฌ์ฉ์ ํ์ํ ํค๋๋ค์ ๋ช๊ฐ์ง ์ค์ ํด์ค๋ค.
์ด๋์ผ์ง ๋ธ๋ผ์ฐ์ ๊ฐ SSE์ธ์ค ์๊ณ ๋ป์ง์ ํ์ง ์๋๋ค.
์ฌ๊ธฐ์๋ถํฐ SSE ๋์์ ๊ตฌํํ ์ฝ๋๋ค.
์ด 2๋ฒ๊น์ง๋ง ๋ฐ๋ณตํด์ ๋ณ๋ก ์๋ฏธ๋ ์๋ ํ
์คํธ๋ฅผ ์ ์กํ๋ค.

๊ทธ๋ฆฌ๊ณ ๋ธ๋ผ์ฐ์ ์์ ์ฐ๊ฒฐ์ ๋์ ๊ฒฝ์ฐ๋ฅผ ๊ณ ๋ คํด์ ๋ญ๋น๋ฅผ ์ค์ด๋๋ก ํ๋ค.

์ ์ฒด ์ฝ๋๋ค.
const Express = require("express");
const fs = require("fs");
const app = Express();
app.get("/", (req, res) => {
const html = fs.readFileSync("./index.html", "utf-8");
res.send(html);
});
app.get("/stream", (req, res) => {
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Content-Type", "text/event-stream"); // EventSource ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํด ํ์ํ ํค๋
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Connection", "keep-alive");
res.flushHeaders(); // flush the headers to establish SSE with client
let counter = 0;
let interValID = setInterval(() => {
counter++;
if (counter >= 3) {
clearInterval(interValID);
res.end(); // terminates SSE session
return;
}
// Server Sent Event๋ก ๋ฐ์ดํฐ ์ ์ก
res.write(`data: ${JSON.stringify({ num: counter })}\n\n`);
}, 1000);
// Client๊ฐ ์ฐ๊ฒฐ์ ๋์ ๊ฒฝ์ฐ
res.on("close", () => {
console.log("client dropped me");
clearInterval(interValID);
res.end();
});
});
app.listen(5500, () => {
console.log("Server started on port 5500");
});์ฌ์ฉ๋ฒ (๋ธ๋ผ์ฐ์ )
๋ธ๋ผ์ฐ์ ์ธก์ ์ฌ์ฉ๋ฒ์ ๋งค์ฐ ๊ฐ๋จํ ํธ์ด๋ค.
์ฐ๊ฒฐ์ฉ API๋ก EventStore ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ , ์ฝ๋ฐฑ์ผ๋ก ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌํ๋ฉด ๋๋ค.

๊ทธ๋ผ ์ด๋ ๊ฒ ์ ์ ํ ๋ ์์ฌ ๊ฒ์ด๋ค.

์ฐธ์กฐ
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
https://stackoverflow.com/questions/34657222/how-to-use-server-sent-events-in-express-js
https://stackoverflow.com/questions/5195452/websockets-vs-server-sent-events-eventsource