๋“ค์–ด๊ฐ€๊ธฐ์— ์•ž์„œ,

๊ธฐ์กด์— ์ง„ํ–‰ํ•˜๋˜ ๋น„๋Œ€๋ฉด ๋ชจ์ž„ ์„œ๋น„์Šค ํ”„๋กœ์ ํŠธ์— ๋ชจ์ž„ ๊ด€๋ จ ์•Œ๋ฆผ๋“ค์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋กœ ํ–ˆ์–ด์š”. ์•Œ๋ฆผ ๊ธฐ๋Šฅ์„ ๊ธฐํšํ•˜๋ฉด์„œ ์–ด๋–ค ๊ธฐ์ˆ ์„ ์ด์šฉํ• ์ง€ ๋…ผ์˜ํ•˜๊ณ  ๊ฒฐ์ •ํ•˜๋Š” ๊ณผ์ •์—์„œ ๊ณต๋ถ€ํ–ˆ๋˜ ๋‚ด์šฉ๋“ค์„ ๊ธฐ๋กํ•ด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

์–ด๋ ค์šด ๊ฒฐ์ •์€ ์•„๋‹ˆ์—ˆ์ง€๋งŒ, sse์™€ ๊ฐ™์ด ๋งŽ์ด ์–ธ๊ธ‰๋˜๋Š” web socket๊ณผ์˜ ์ฐจ์ด์ ๋„ ํ•จ๊ป˜ ์‚ดํŽด๋ณด๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์š”.


๐Ÿ“ Server-Sent-Events๋ž€ ?

์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ง€์†์ ์œผ๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” ํ†ต์‹  ๋ฐฉ์‹

      ยท ๋‹จ๋ฐฉํ–ฅ ํ†ต์‹  ์ง€์› : ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ๋งŒ ๋ฐ์ดํ„ฐ ์ „์†ก ๊ฐ€๋Šฅ
      ยท ์ž๋™ ์žฌ์—ฐ๊ฒฐ ๊ธฐ๋Šฅ : ์—ฐ๊ฒฐ์ด ๋Š์–ด์ ธ๋„ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์—ฐ๊ฒฐ
      ยท ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ : ํŠน์ • ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก ๊ฐ€๋Šฅ
      ยท ๊ธฐ์กด HTTP ํ”„๋กœํ† ์ฝœ ์‚ฌ์šฉ : WebSockets์™€ ๋‹ฌ๋ฆฌ ์ƒˆ๋กœ์šด ํ”„๋กœํ† ์ฝœ์„ ์š”๊ตฌํ•˜์ง€ ์•Š์Œ


๐Ÿ“ WebSocket ์ด๋ž€ ?

ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๊ฐ„์— ์–‘๋ฐฉํ–ฅ ํ†ต์‹ ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ํ”„๋กœํ† ์ฝœ

      ยท ์–‘๋ฐฉํ–ฅ ํ†ต์‹  ์ง€์› : ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ.
      ยท Full-Duplex : ๋™์‹œ์— ๋ฐ์ดํ„ฐ๋ฅผ ์†ก์ˆ˜์‹  ๊ฐ€๋Šฅ
      ยท Connection Persistence : ํ•œ ๋ฒˆ ์—ฐ๊ฒฐ๋˜๋ฉด ์ง€์†์ ์ธ ๋ฐ์ดํ„ฐ ๊ตํ™˜ ๊ฐ€๋Šฅ
      ยท ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ ์ง€์› : JSON, ํ…์ŠคํŠธ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ด๋ฏธ์ง€, ์˜ค๋””์˜ค, ๋น„๋””์˜ค ์ „์†ก ๊ฐ€๋Šฅ


SSE vs. WebSocket

๊ตฌ๋ถ„ SSE WebSocket
ํ†ต์‹  ๋ฐฉํ–ฅ ๋‹จ๋ฐฉํ–ฅ (์„œ๋ฒ„ โ†’ ํด๋ผ์ด์–ธํŠธ) ์–‘๋ฐฉํ–ฅ
ํ”„๋กœํ† ์ฝœ HTTP ๊ธฐ๋ฐ˜ ๋ณ„๋„ ํ”„๋กœํ† ์ฝœ
๊ตฌํ˜„ ๋‚œ์ด๋„ ๋น„๊ต์  ๋‹จ์ˆœ ์ƒ๋Œ€์ ์œผ๋กœ ๋ณต์žก
์žฌ์—ฐ๊ฒฐ ์ฒ˜๋ฆฌ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ์ง€์› ์ง์ ‘ ๊ตฌํ˜„ ํ•„์š”
์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€ ์•Œ๋ฆผ, ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ ์ฑ„ํŒ…, ์‹ค์‹œ๊ฐ„ ํ˜‘์—…


๊ธฐํšํ–ˆ๋˜ ์•Œ๋ฆผ ๊ธฐ๋Šฅ์˜ ๊ฒฝ์šฐ ๊ฐ ๊ณ„์ •๋งˆ๋‹ค ์•Œ๋ฆผ์ด ์Œ“์ด๋Š” ํ˜•์‹์œผ๋กœ, ๋‹จ๋ฐฉํ–ฅ ์ „์†ก๋งŒ ํ•„์š”ํ•œ ์ƒํ™ฉ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ตฌํ˜„ ๋‚œ์ด๋„๊ฐ€ ๋น„๊ต์  ๋‚ฎ์€ ํŽธ์ด๊ณ  ๋‹จ๋ฐฉํ–ฅ ์†ก์ˆ˜์‹ ์— ํŠนํ™”๋˜์–ด์žˆ๋Š” sse๊ฐ€ ๋” ์ ํ•ฉํ•˜๋‹ค๋Š” ๊ฒฐ๋ก ์„ ๋‚ด๋ฆฌ๊ฒŒ ๋˜์—ˆ์–ด์š”!


Short Polling

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ผ์ •ํ•œ ์ฃผ๊ธฐ๋กœ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ์„œ๋ฒ„๊ฐ€ ์ฆ‰์‹œ ์‘๋‹ตํ•˜๋Š” ๋ฐฉ์‹

short_polling

์š”์ฒญ์ด ์˜ฌ ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„๋Š” ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•œ ํ›„ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ด์š”. ๊ตฌํ˜„์€ ๋‹จ์ˆœํ•˜์ง€๋งŒ, ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์–ด๋„ ๋ฐ˜๋ณต์ ์œผ๋กœ ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜์—ฌ ์„œ๋ฒ„ ์š”์ฒญ์ˆ˜๊ฐ€ ํ•„์š”์ด์ƒ์œผ๋กœ ๋งŽ์•„์ง„๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, HTTP overhead๊ฐ€ ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋˜๋Š”๊ฑฐ์ฃ . ์ด๋ฅผ ๊ฐœ์„ ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ Long Polling ๊ฐœ๋…์ด ๋‚˜์˜ค๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


Long Polling

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์„œ๋ฒ„๊ฐ€ ์ฆ‰์‹œ ์‘๋‹ตํ•˜์ง€ ์•Š๊ณ  ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•˜๋Š” ๋ฐฉ์‹

long_polling

์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์„œ๋ฒ„๋Š” ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ๋Š” ๋‹ค์‹œ ์š”์ฒญ์„ ๋ณด๋‚ด ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•ด์š”. ์ด๋Ÿฐ ๋ฐฉ์‹์€ Short Polling ๋Œ€๋น„ ๋ถˆํ•„์š”ํ•œ ์š”์ฒญ ์ˆ˜๋ฅผ ์ค„์—ฌ HTTP overhead๊ฐ€ ๊ฐ์†Œํ•œ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋งค๋ฒˆ ์—ฐ๊ฒฐ์„ ์ƒˆ๋กœ ๋งบ์–ด์•ผ ํ•˜๋ฏ€๋กœ ์—ฌ์ „ํžˆ ๋น„ํšจ์œจ์ด ์กด์žฌํ–ˆ์–ด์š”..


SSE

๋ฐ˜๋ณต์ ์ธ polling ์š”์ฒญ ์—†์ด, ์„œ๋ฒ„๊ฐ€ ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•˜๋ฉฐ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹

sse

SSE๋Š” ์ด๋Ÿฌํ•œ Polling ๋ฐฉ์‹์˜ ํ•œ๊ณ„๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ•˜๊ฒŒ ๋์–ด์š”. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•œ ๋ฒˆ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์„œ๋ฒ„๋Š” ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•œ ์ฑ„ ์ง€์†์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๊ฒŒ ๋ผ์š”. ๋ถˆํ•„์š”ํ•œ ์žฌ์š”์ฒญ ์—†์ด ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์–ด ๋„คํŠธ์›Œํฌ ํšจ์œจ์ด ๋†’์Šต๋‹ˆ๋‹ค. HTTP ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ฐ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์กด ์ธํ”„๋ผ์™€๋„ ์ž˜ ์–ด์šธ๋ฆฐ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์–ด์š”.


SSE ๋กœ์ง

SSE๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ EventSource ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค!

๐Ÿ“ย EventSource ๋ž€ ?

SSE ์—ฐ๊ฒฐ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” Web API

        1. GET ์š”์ฒญ์ž…๋‹ˆ๋‹ค.
        2. ์—ฐ๊ฒฐ์ด ๋Š๊ธฐ๋ฉด ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
        3. SSE ์—ฐ๊ฒฐ ์š”์ฒญ์‹œ, header๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.


EventSource ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
1
2
3
const eventSource = new EventSource('/api/notifications', {
withCredentials: true, // ์ฟ ํ‚ค ๊ธฐ๋ฐ˜ ์ธ์ฆ
});

ํด๋ผ์ด์–ธํŠธ๋Š” SSE ์—”๋“œํฌ์ธํŠธ๋กœ EventSource๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐํ•ด์š”.
EventSource์„ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ๋ฐ›์€ ์‹œ์ ๋ถ€ํ„ฐ ์„œ๋ฒ„์™€์˜ ์ง€์†์ ์ธ ์—ฐ๊ฒฐ์ด ์œ ์ง€๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


๋ฉ”์„ธ์ง€ ์ด๋ฒคํŠธ ์ˆ˜์‹ 
1
2
3
4
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('์ƒˆ ์•Œ๋ฆผ ์ˆ˜์‹ :', data);
}

์„œ๋ฒ„์—์„œ ๊ธฐ๋ณธ ์ด๋ฒคํŠธ๋ฅผ ์ „์†กํ•˜๋ฉด onmessage ์ด๋ฒคํŠธ๊ฐ€ ํ˜ธ์ถœํ•ด์š”.
์ด๊ณณ์—์„œ ์ˆ˜์‹ ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์‹ฑํ•˜์—ฌ ์•Œ๋ฆผ ๋ชฉ๋ก ๊ฐฑ์‹  ๋“ฑ์˜ UI ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.


์—๋Ÿฌ ํ•ธ๋“ค๋ง
1
2
3
eventSource.onerror = (error) => {
console.error('SSE ์—ฐ๊ฒฐ ์˜ค๋ฅ˜ ๋ฐœ์ƒ', error);
};

๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋‚˜ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ํ˜ธ์ถœํ•ด์š”.
ํ•„์š”์— ๋”ฐ๋ผ ์žฌ์—ฐ๊ฒฐ ๋กœ์ง์ด๋‚˜ ์‚ฌ์šฉ์ž ์•ˆ๋‚ด ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


SSE ์—ฐ๊ฒฐ ์ข…๋ฃŒ
1
eventSource.close();

๋” ์ด์ƒ ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•  ํ•„์š”๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์—ฐ๊ฒฐ์„ ์ข…๋ฃŒํ•ด์š”.
์ปดํฌ๋„ŒํŠธ ์–ธ๋งˆ์šดํŠธ ์‹œ์ ์ด๋‚˜ ๋กœ๊ทธ์•„์›ƒ ์‹œ์ ์— ์ฃผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.


๊ทธ๋ ‡๋‹ค๋ฉด ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์–ด๋–ป๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”? ๐Ÿง

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// setMessage : SSE๋กœ ์ „๋‹ฌ๋ฐ›์€ ๋ฉ”์„ธ์ง€๋ฅผ ํ›… ์™ธ๋ถ€์—์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋กœ์ง
export function useNotificationSSE(setMessage) {

    useEffect(() => {
      const eventSource = new EventSource('/api/notifications/subscribe', {
        withCredentials: true,
    });
    eventSource.onmessage = (event) => {
        setMessage(JSON.parse(event.data));
    };
      return () => eventSource.close();
    }, [setMessage]);

}

EventSource ์—ฐ๊ฒฐํ•˜๋Š” ๋กœ์ง์„ Hook์œผ๋กœ ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์–ด์š”.

setMessage๋ฅผ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…ํ•˜๋„๋ก ์ž‘์„ฑํ•œ ์ด์œ ๋Š” ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ Hook์˜ ์ฑ…์ž„์„ ๋ช…ํ™•ํžˆ ํ•˜๊ธฐ ์œ„ํ•จ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
Hook์˜ ํ˜ธ์ถœ ์œ„์น˜์— ๋”ฐ๋ผ ๋ฉ”์„ธ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์— ์ฐจ์ด๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๊ณ , Hook์˜ ์ฑ…์ž„์„ SSE ์—ฐ๊ฒฐ์„ ์ œ์–ดํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ œํ•œํ•˜๊ฒŒ ๋˜๋ฉด ์ปดํฌ๋„ŒํŠธ์—์„œ ๋‚˜๋จธ์ง€ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ๋‹ด๋‹นํ•˜๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ฒ˜๋ฆฌ ๋กœ์ง์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ๋””๋ฒ„๊น…ํ•˜๊ธฐ ์œ„ํ•ด Hook์„ ๋“ค์—ฌ๋‹ค ๋ณผ ํ•„์š”๊ฐ€ ์—†๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์ด์ฃ ..

SSE์˜ ํ๋ฆ„๋งŒ ์ดํ•ดํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ์–ด๋ ต์ง€ ์•Š๊ฒŒ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ญ๋‹ˆ๋‹ค :)