SSE(Server Sent Event)์™€ EventSource

[์›๋ณธ ๋งํฌ]

Server Sent Event๋Š” ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๋Š” ๊ธฐ๋ฒ• ์ค‘ ํ•˜๋‚˜๋‹ค.
์›น์†Œ์ผ“๊ณผ๋Š” ์–ด๋А์ •๋„ ๋Œ€์ฒด๊ฐ€๋Šฅํ•œ ๊ด€๊ณ„์— ์žˆ๋‹ค.

๊ธฐ๋ณธ์ ์ธ ์›๋ฆฌ๋Š” ๋‹จ์ˆœํ•˜๋‹ค.

๋ธŒ๋ผ์šฐ์ €์—์„œ EventSource๋ผ๋Š” ํ‘œ์ค€ ๊ธฐ๋Šฅ์œผ๋กœ ์ฑ„๋„์„ ์—ด์–ด๋‘๋ฉด, ์„œ๋ฒ„๊ฐ€ ๊ฑฐ๊ธฐ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•ด์ค„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

๋ธŒ๋ผ์šฐ์ €์˜ ์š”์ฒญ ์—†์ด๋„ ์„œ๋ฒ„ ํ˜ผ์ž์„œ ๋Šฅ๋™์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š”๊ฒŒ ํŠน์ง•์ด๋‹ค.




๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ

์˜ˆ์ „์—๋Š” SSE๊ฐ€ IE์—์„œ ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ, ์š”์ฆ˜์€ SSE๋‚˜ ์›น์†Œ์ผ“์ด๋‚˜ ๋‘˜๋‹ค ๋Œ€๋ถ€๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋‹ค ์ž˜ ์ž‘๋™ํ•œ๋‹ค.
๋งค์šฐ ํŠน์ˆ˜ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ์ด ๋ฌธ์ œ๋  ์ผ์€ ์—†์„ ๊ฒƒ์ด๋‹ค.




์žฅ์  (vs Websocket)

  1. ์ƒํ˜ธ์ž‘์šฉ์ด ํ•„์š”์—†๊ณ  ์„œ๋ฒ„->ํด๋ผ์ด์–ธํŠธ์˜ ๋‹จ๋ฐฉํ–ฅ ํ†ต์‹ ๋งŒ ํ•˜๋ฉด ๋˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋” ๊ฐ„๋‹จํ•œ ํ•ด๊ฒฐ์ฑ…์ด๋‹ค. ๊ตฌํ˜„๊ณผ ์‚ฌ์šฉ ์ž์ฒด๊ฐ€ ๋” ๋‹จ์ˆœํ•˜๋‹ค.

  2. Websocket์˜ ๊ฒฝ์šฐ ํŒจํ‚ท ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉํ™”๋ฒฝ ํ™˜๊ฒฝ์—์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋Š”๋ฐ, SSE๋Š” ๊ทธ๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํšŒํ”ผํ•  ์ˆ˜ ์žˆ๋‹ค.





๋‹จ์  (vs Websocket)

  1. connection limit
    HTML 1.1์„ ์“ฐ๋Š”๊ฒฝ์šฐ ๋Œ€๋ถ€๋ถ„ ๋™์‹œ ์—ฐ๊ฒฐ ๊ฐœ์ˆ˜์— ์ œํ•œ์ด ์žˆ๋‹ค.
    ๋Œ€๋ถ€๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ €๋Š” ๋„๋ฉ”์ธ๋‹น ์ตœ๋Œ€ ์—ฐ๊ฒฐ ๊ฐœ์ˆ˜๊ฐ€ 6๊ฐœ์ฏค ๋œ๋‹ค.
    ์‚ฌ์‹ค ์ด๊ฑด ํฐ ๋ฌธ์ œ๋Š” ์•„๋‹ˆ๊ธด ํ•˜๋‹ค.
    ํ•œ ๋„๋ฉ”์ธ์— 6๊ฐœ ์ด์ƒ ์“ธ์ผ์ด ๊ทธ๋ ‡๊ฒŒ ๋งŽ๋‚˜? ์‹ถ๊ธฐ๋„ ํ•˜๊ณ , HTTP2๋ถ€ํ„ฐ๋Š” ๊ฐœ์ˆ˜ ์ œํ•œ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค.

  2. ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ๋Š” ์ „์†ก ๋ถˆ๊ฐ€ (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