์•Œ๋ฆผ ๊ธฐ๋Šฅ Polling ์—์„œ SSE ์ „ํ™˜ํ•˜๋ฉฐ ๊ฒช์—ˆ๋˜ ์‚ฝ์งˆ (์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ)

2025. 7. 11. 00:34ยท๐Ÿ“‚ backend/spring

๊ธฐ์กด MVP ๋ชจ๋ธ์—์„œ ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ์„ ์œ„ํ•ด ํด๋ง ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฉ”์‹œ์ง€ ์•Œ๋ฆผ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.

 

20์ดˆ ์ฃผ๊ธฐ๋กœ ํด๋ง(polling) ์„ค์ •ํ•˜์—ฌ ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€ ๊ฐฏ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ๊ณ ,

AWS ํ”„๋ฆฌํ‹ฐ์–ด ๊ธฐ๋ฐ˜ ์ตœ์†Œ ์ŠคํŽ™์œผ๋กœ ์„œ๋ฒ„๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ์žˆ์–ด ๋น„์šฉ๊ณผ ๋ถ€ํ•˜๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ธฐ ์ ‘๊ทผ ๋ฐฉ์‹์ด์—ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์•Œ๋ฆผ ๊ธฐ๋Šฅ์˜ ์ตœ์†Œํ•œ์˜ ์‹ค์‹œ๊ฐ„์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ๋ชปํ•˜๋Š” ํ”ผ๋“œ๋ฐฑ์ด ์žˆ์–ด ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ–ˆ๋‹ค.

 

์ตœ์†Œํ•œ์˜ ์‹ค์‹œ๊ฐ„์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ์งง์€ ํด๋ง ์ฃผ๊ธฐ๋„ ๊ฒ€ํ† ํ–ˆ์ง€๋งŒ,

๋‚ฎ์€ ์ŠคํŽ™์˜ ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ ๋ถ€ํ•˜๋ฅผ ๋‹ค์‹œ ์ฆ๊ฐ€ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด ํ•ด๋‹น ๋ฐฉ๋ฒ•์€ ์ ํ•ฉํ•˜์ง€ ์•Š์•„ ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์ด ํ•„์š”ํ–ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ๋Œ€์•ˆ ์ค‘ ํ•˜๋‚˜๋กœ SSE(server-sent-event) ๋ฅผ ๊ฒ€ํ† ํ•œ ๊ณผ์ •๊ณผ ์ด๋ฅผ ์ ์šฉํ•˜๋Š” ๊ณผ์ •์— ์ ์–ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

์••๋„์ ์œผ๋กœ ๋งŽ์€ ์ฝ์ง€ ์•Š๋Š” ์•Œ๋ฆผ ์ˆ˜ ์กฐํšŒ API...

 

 

1. ์‹ค์‹œ๊ฐ„์„ฑ์„ ์œ„ํ•œ ์„ ํƒ: WebSocket vs SSE ??

์ตœ์†Œํ•œ์˜ ์‹ค์‹œ๊ฐ„์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด WebSocket ๊ณผ SSE ์„ ๊ฒ€ํ† ํ•˜๊ธฐ ์ด์ „์— ๋น„์ฆˆ๋‹ˆ์Šค ํŠน์„ฑ์„ ๊ฒ€ํ† ํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

 

ํ˜„์žฌ ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€ ํŠน์„ฑ์€ ๊ฐœ์ธํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜ํ•œ ์ด๋ฒคํŠธ ํ‘ธ์‹œ๊ฐ€ ๋งŽ์•˜๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฒŒ์‹œ๊ธ€์— ๋ฆฌ๋ทฐ ์ƒ์„ฑ ๋˜๋Š” ์ฒดํฌ๋ฆฌ์ŠคํŠธ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด

์ž‘์„ฑ์ž์—๊ฒŒ ์•Œ๋ฆผ์ด ์ „๋‹ฌ๋˜๋Š” ํ˜•ํƒœ ๋ชจ๋‘ ๊ฐœ์ธํ™”๋œ ์•Œ๋ฆผ์„ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ๋‹ค.

 

๋‘ ๋ฐฉ์‹ ๋ชจ๋‘ ๋ถˆํ•„์š”ํ•œ ํด๋ง์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

WebSocket ์˜ ๊ฒฝ์šฐ ์–‘๋ฐฉํ–ฅ์„ฑ ํ†ต์‹ ์— ๊ด€ํ•œ ์š”๊ตฌ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜๊ธฐ ์œ„ํ•œ ์ผ๋ฐ˜์ ์ธ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์ด๋‹ค.

ํ•˜์ง€๋งŒ, ํ˜„์žฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ํŠน์„ฑ์„ ๊ณ ๋ คํ•˜๋ฉด ์„œ๋ฒ„์—์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋‹จ๋ฐฉํ–ฅ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์ด๋ฏ€๋กœ

์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ์˜ ๋‹จ๋ฑกํ–ฅ ํ†ต์‹ ์— ๊ด€ํ•œ ์š”๊ตฌ์‚ฌํ•ญ๋งŒ์„ ์ถฉ์กฑํ•˜๋Š” SSE ์‚ฌ์šฉ์ด ์ ํ•ฉํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

 

์ถ”๊ฐ€์ ์œผ๋กœ SSE๋Š” ์ถ”๊ฐ€์ ์ธ ํ”„๋กœํ† ์ฝœ์— ๊ด€ํ•œ ์ง€์‹์ด ํ•„์š”ํ•˜์ง€ ์•Š์•„ ๊ธฐ์ˆ  ์ ์šฉ์˜ ํ—ˆ๋“ค์ด ๋‚ฎ์€ ์žฅ์ ์ด ์žˆ์—ˆ๋‹ค.

๊ธฐ์กด HTTP protocol ์„ ๊ธฐ๋ฐ˜ํ•œ TCP Connection ์œ ์ง€ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์—ˆ๋‹ค.

(์ฐธ๊ณ ๋กœ Websocket ์€ ws protocol ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๋ฏ€๋กœ ์ถ”๊ฐ€์ ์ธ ์ง€์‹์ด ํ•„์š”ํ•˜๋‹ค.)

 

(์ฐธ๊ณ )

SSE ๋Š” Auto Reconnect ์ง€์›๊ณผ Cookie ์ฒจ๋ถ€ ์š”์ฒญ์ด ๊ฐ€๋Šฅํ•˜๊ณ  ์‘๋‹ต ํ˜•ํƒœ๋ฅผ text ๋กœ ์ „๋‹ฌํ•˜๋Š” ํŠน์„ฑ์„ ๊ฐ€์ง„๋‹ค.

๋ฐ˜๋ฉด, WebSocket ์˜ ๊ฒฝ์šฐ์—๋Š” Auto Reconnect ์ง€์›์„ ํ•˜์ง€ ์•Š๊ณ  text ๋˜๋Š” binary ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

 

 

2. SSE ๋™์ž‘ ๋ฐฉ์‹

SSE ๋˜ํ•œ HTTP/1.1 ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— TCP ๊ธฐ๋ฐ˜์˜ ์—ฐ๊ฒฐ ์ž‘์—…์„ ์ง„ํ–‰ํ•œ๋‹ค.  

  1. TCP 3-way handshake ๊ณผ์ •์„ ํ†ตํ•ด TCP Connection ์„ ์ˆ˜๋ฆฝํ•œ๋‹ค.
  2. ํด๋ผ์ด์–ธํŠธ๋Š” ์š”์ฒญ์œผ๋กœ HTTP ํ—ค๋”์— Accept: text/event-stream ๋กœ ์„ค์ •ํ•˜์—ฌ ์š”์ฒญํ•œ๋‹ค.
  3. ์„œ๋ฒ„๋Š” ์‘๋‹ต์œผ๋กœ HTTP ํ—ค๋”์— Content-Type: text/event-stream ์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค.
  4. ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ ์ชฝ์œผ๋กœ ์ง€์†์ ์œผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ํ‘ธ์‹œํ•œ๋‹ค.

 

 

3. SSE ์ฃผ์˜ ์‚ฌํ•ญ - ์—ฐ๊ฒฐ ์ˆ˜๋ฆฝ๋œ ์„œ๋ฒ„์—๊ฒŒ๋งŒ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

SSE ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ๋ฉ”์‹œ์ง€๋Š” Connection ์„ ์ˆ˜๋ฆฝํ•œ ์„œ๋ฒ„์—์„œ๋งŒ ๋ฉ”์‹œ์ง€ ์ŠคํŠธ๋ฆฌ๋ฐ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

 

TCP ๋Š” ์—ฐ๊ฒฐ์ง€ํ–ฅ ํ”„๋กœํ† ์ฝœ์ด๋‹ค.

TCP ์—ฐ๊ฒฐ์€ Client  IP, Client Port, Server IP, Server Port ์ •๋ณด๋ฅผ ํ† ๋Œ€๋กœ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„๋œ๋‹ค.

4๊ฐ€์ง€ ์ •๋ณด๋ฅผ ํ†ตํ•ด ํ•˜๋‚˜์˜ ์ปค๋„ฅ์…˜ ์†Œ์ผ“์„ ์ •์˜ํ•œ๋‹ค.

 

์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ์˜ TCP ์ปค๋„ฅ์…˜ ์ƒํƒœ ํ…Œ์ด๋ธ”(connection-state table) ์— ํ•ด๋‹น ์ •๋ณด๋ฅผ ์ €์žฅํ•œ๋‹ค.

์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ๋Š” OS ๋‹จ์œ„๋กœ ๊ด€๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ปค๋„ฅ์…˜์„ ์ˆ˜๋ฆฝํ•œ ์„œ๋ฒ„์—์„œ๋งŒ ์œ ์ง€๋˜๋ฉฐ ๋‹ค๋ฅธ ์„œ๋ฒ„์—์„œ ์ด ์—ฐ๊ฒฐ ์ƒํƒœ๋ฅผ ์•Œ ์ˆ˜ ์—†๋‹ค. (e.g. ๋ฒ„ํผ, ์ˆœ์„œ, ํŒจํ‚ท ๋ฒˆํ˜ธ ๋“ฑ)

State      Recv-Q  Send-Q      Local Address:Port         Peer Address:Port
------------------------------------------------------------------------
LISTEN     0       511         *:80                       *:*
SYN-RECV   0       0           192.0.2.145:80             203.0.113.5:35449
SYN-RECV   0       0           192.0.2.145:80             203.0.113.27:53599
ESTAB      0       0           192.0.2.145:80             203.0.113.27:33605
TIME-WAIT  0       0           192.0.2.145:80             203.0.113.47:50685

(1) State : TCP ์ƒํƒœ (LISTEN, SYN-RECV, ESTAB, ๋“ฑ)
(2) Recv-Q : ์ˆ˜์‹  ํ์— ์Œ“์ธ ๋ฐ์ดํ„ฐ (์ฝํžˆ์ง€ ์•Š์Œ)
(3) Send-Q : ์†ก์‹  ํ์— ์Œ“์ธ ๋ฐ์ดํ„ฐ (์•„์ง ์ „์†ก ์•ˆ ๋จ)
(4) Local Address:Port : ์„œ๋ฒ„ ์ธก IP์™€ ํฌํŠธ
(5) Peer Address:Port : ํด๋ผ์ด์–ธํŠธ ์ธก IP์™€ ํฌํŠธ

 

 

4. Scale-out ํ™˜๊ฒฝ์—์„œ SSE ๋Š” pub/sub ๊ตฌ์กฐ ๋„์ž…์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

ALB ์„ ํ†ตํ•ด ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑํ•˜๊ณ  ์žˆ๋‹ค๋ฉด  ๋ฉ”์‹œ์ง€๊ฐ€ SSE Connection ์ด ์ˆ˜๋ฆฝ๋œ ์„œ๋ฒ„๋กœ ์ „๋‹ฌ์„ ๋˜์ง€ ์•Š์„ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค.

 

ALB๋Š” ์ƒˆ HTTP ์š”์ฒญ์ด ๋„์ฐฉํ•  ๋•Œ๋งˆ๋‹ค L7 ๊ธฐ๋ฐ˜ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ๋”ฐ๋ผ ๋ผ์šฐํŒ… ํ•  ์„œ๋ฒ„๋ฅผ ์„ ํƒํ•œ๋‹ค.

(L7 ๊ธฐ๋ฐ˜ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜ : ๋ผ์šด๋“œ๋กœ๋นˆ, IP ํ•ด์‹œ, ๊ฐ€์ค‘์น˜ ๋“ฑ)

 

ALB ๋Š” ๋งค ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋ฅผ ์„ ํƒํ•˜๊ณ  ์ƒˆ ์†Œ์ผ“์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๊ธฐ์กด ์†Œ์ผ“ ํ’€์—์„œ ํ•ด๋‹น ์„œ๋ฒ„๋กœ ์—ฐ๊ฒฐ๋œ ์†Œ์ผ“์„ ์„ ํƒํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

์ฆ‰, ์†Œ์ผ“ ํ…Œ์ด๋ธ”์˜ ํ•ญ๋ชฉ์ด ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ALB ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์ปค๋„ฅ์…˜ ํ’€ ๋‚ด์—์„œ ๋‹ค๋ฅธ ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐ๋œ ์†Œ์ผ“์„ ์„ ํƒํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

์ด์™€ ๊ฐ™์€ ์ด์œ ๋กœ ์ธํ•ด ์Šค์ผ€์ผ ์•„์›ƒ ํ™˜๊ฒฝ์—์„œ ์ปค๋„ฅ์…˜ ์ˆ˜๋ฆฝ๋œ ์„œ๋ฒ„์—์„œ ๋ฉ”์‹œ์ง€ ์ŠคํŠธ๋ฆฌ๋ฐ์„ ์ง€์› ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•œ ๊ณ ๋ฏผ์ด ํ•„์š”ํ•˜๋‹ค.

์ด์™€ ๊ฐ™์ด ํด๋ผ์ด์–ธํŠธ ์ •๋ณด๋ฅผ ๊ฐ€์ง„ ์„œ๋ฒ„๊ฐ€ ๋ฉ”์‹œ์ง€ ์ ์ ˆํ•˜๊ฒŒ ๋ฉ”์‹œ์ง€ ํ‘ธ์‹œํ•˜๊ธฐ ์œ„ํ•ด pub/sub ๊ตฌ์กฐ๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์„œ๋ฒ„๊ฐ€ ์ŠคํŠธ๋ฆฌ๋ฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ํ•ด๋‹น ๋ฉ”์‹œ์ง€๊ฐ€ ์ŠคํŠธ๋ฆฌ๋ฐ ํ•ด์•ผํ•˜๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ํŒ๋ณ„ํ•˜์—ฌ ํ‘ธ์‹œํ•˜๋„๋ก ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šค์ผ€์ผ ์•„์›ƒ ํ™˜๊ฒฝ์—์„œ SSE ๊ธฐ๋ฐ˜ ๋ฉ”์‹œ์ง€ ํ‘ธ์‹œ ์ง€์› ๋ฐฉ๋ฒ• : pub/sub

 

 

5. Spring ์—์„œ SSE ๋„์ž… ์‹œ ๋™์‹œ์„ฑ์„ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ์ด์œ 

์Šคํ”„๋ง์—์„œ๋Š” SseEmitter ๋ฅผ ์ œ๊ณตํ•ด ์†์‰ฝ๊ฒŒ SSE ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•ด๋‹น SseEmitter ๋ฅผ ์ƒ์„ฑ ํ›„, ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

์‚ฌ์šฉ์ž๋ณ„ ๋˜๋Š” ๊ธฐ๊ธฐ๋ณ„๋กœ Emitter ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฉ”๋ชจ๋ฆฌ์— Emitter ๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

final SseEmitter sseEmitter = new SseEmitter(600 * 1000L);

 

ํ•œ๊ฐ€์ง€ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์€ ๋™์‹œ์„ฑ์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

 

SseEmitter ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜์ธ onCompletion, onTimeout, onError ๋ฉ”์„œ๋“œ๋Š” ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค

 

๊ทธ๋Ÿฌ๋ฏ€๋กœ ํ˜„์žฌ ์š”์ฒญ ์ค‘์„ ์ฒ˜๋ฆฌ ์ค‘์ธ ์Šค๋ ˆ๋“œ์™€ ๋™์‹œ์„ฑ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋ฏ€๋กœ ๋™์‹œ์„ฑ์„ ๊ณ ๋ คํ•œ ์„ค๊ณ„๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ๋กœ๋Š” Emitter ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ž๋ฃŒ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ์ง€์›ํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•œ๋‹ค.

// key : memberId
private final Map<UUID, SseEmitter> sseEmitters = new ConcurrentHashMap<>();

 

์•„๋ž˜ SseEmitter ๋ฅผ ๊ตฌ์„ฑํ–ˆ๋˜ ์˜ˆ์‹œ ์ฝ”๋“œ์ด๋‹ค.

@Slf4j
@Component
public class SseEmitters {

	// key : memberId
	private final Map<UUID, SseEmitter> sseEmitters = new ConcurrentHashMap<>();

	public SseEmitter add(UUID clientId) {
		final SseEmitter sseEmitter = new SseEmitter(600 * 1000L);

		if (sseEmitters.containsKey(clientId)) {
			return sseEmitters.get(clientId);
		} else {
			sseEmitters.put(clientId, sseEmitter);
		}

		sseEmitter.onCompletion(() -> {
			log.info("onCompletion sseEmitter : {}", sseEmitter);
			sseEmitters.remove(clientId);
		});
		sseEmitter.onTimeout(() -> {
			log.info("onTimeout sseEmitter : {}", sseEmitter);
			sseEmitters.remove(clientId);
		});
		sseEmitter.onError(e -> {
			log.info("onError sseEmitter : {}, error: {}", sseEmitter, e.getMessage());
			this.sseEmitters.remove(clientId);
		});

		log.info("added sseEmitter id : {}, sseEmitters size: {}", clientId, sseEmitters.size());
		return sseEmitter;
	}

	public SseEmitter remove(UUID memberId) {
		if (!sseEmitters.containsKey(memberId)) {
			throw new NotificationEmitterNotFoundException(NotificationErrorType.EMITTER_NOT_FOUND);
		}

		log.debug("removing sseEmitter for memberId: {}", memberId);

		final SseEmitter removedEmitter = sseEmitters.remove(memberId);
		removedEmitter.complete();

		return removedEmitter;
	}

	public <T> boolean send(UUID clientId, String eventName, T data) {
		if (!sseEmitters.containsKey(clientId)) {
			return false;
		}

		SseEmitter emitter = sseEmitters.get(clientId);

		try {
			emitter.send( // ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ
				SseEmitter.event()
					.name(eventName)
					.data(data)
					.build());

			return true;
		} catch (Exception exception) {
			log.info("send sseEmitter error : {}", exception.getMessage());
			sseEmitters.remove(clientId);
			emitter.completeWithError(exception);
		}

		return false;
	}
}

 

6. ํด๋ผ์ด์–ธํŠธ๋Š” ์ผ์ • ์‹œ๊ฐ„ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌ๋ฐ›์ง€ ๋ชปํ•˜๋ฉด ์—ฐ๊ฒฐ์ด ๋Š์–ด์ง„๋‹ค. (health check )

SSE ๋Š” ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•˜์—ฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ์†ก์ˆ˜์‹ ํ•˜๋Š” ๊ตฌ์กฐ์ด๋‹ค.

SSE ๋Š” ํšจ์œจ์ ์ธ ์—ฐ๊ฒฐ์„ ์œ„ํ•ด ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ ๋ฐฉํ–ฅ์œผ๋กœ health check ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค. 

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ผ์ • ์‹œ๊ฐ„๋™์•ˆ ๋ฉ”์‹œ์ง€๋ฅผ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ˆ˜์‹ ๋ฐ›์ง€ ๋ชปํ•˜๋ฉด ์—ฐ๊ฒฐ์„ ํ•ด์ œํ•˜๊ธฐ ๋•Œ๋ฌธ์— health check ๋ฅผ ์œ„ํ•œ ์ž„์˜์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋กœ์ง์„ ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค. (default : 45์ดˆ)

 

 

Reference

  • [naver d2] TCP/IP ๋„คํŠธ์›Œํฌ ์Šคํƒ ์ดํ•ดํ•˜๊ธฐ : https://d2.naver.com/helloworld/47667
  • [TOSS] SLASH 24 - SSE ์ด๋ฒคํŠธ ํ‘ธ์‰ฌ๋กœ ๋ถˆํ•„์š”ํ•œ Polling ์ œ๊ฑฐํ•˜๊ธฐ : https://www.youtube.com/watch?v=ovGgdPPUZ2I
  • TCP Connection States : https://maxnilz.com/docs/004-network/003-tcp-connection-state/
  • [tecoble] Spring์—์„œ Server-Sent-Events ๊ตฌํ˜„ํ•˜๊ธฐ : https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/

 

'๐Ÿ“‚ backend > spring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Spring Version 7.0 Relase Notes Summary  (0) 2025.09.02
Presigned URL ๋กœ ํŒŒ์ผ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ์„ ์™œ ์ ์šฉํ–ˆ๋ƒ๋ฉด์š”.  (1) 2025.06.01
Spring6.1 ๋ถ€ํ„ฐ ๋ณ€๊ฒฝ๋œ Parameter Name Retention  (0) 2025.03.11
์ปค์Šคํ…€ ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ AOP ๋ฅผ @Transactional ๋ณด๋‹ค ๋‚˜์ค‘์— ์‹คํ–‰์‹œํ‚ค๊ธฐ  (0) 2025.01.27
์ค‘๋ณต๋˜๋Š” ์Šค์ผ€์ค„๋ง ๋ถ„์‚ฐ๋ฝ์œผ๋กœ ํ•˜๋‚˜๋งŒ ์‹คํ–‰ํ•˜๊ธฐ(w.ShedLock)  (0) 2025.01.16
'๐Ÿ“‚ backend/spring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • Spring Version 7.0 Relase Notes Summary
  • Presigned URL ๋กœ ํŒŒ์ผ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ์„ ์™œ ์ ์šฉํ–ˆ๋ƒ๋ฉด์š”.
  • Spring6.1 ๋ถ€ํ„ฐ ๋ณ€๊ฒฝ๋œ Parameter Name Retention
  • ์ปค์Šคํ…€ ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ AOP ๋ฅผ @Transactional ๋ณด๋‹ค ๋‚˜์ค‘์— ์‹คํ–‰์‹œํ‚ค๊ธฐ
cooper25
cooper25
  • cooper25
    dev cooper
    cooper25
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (84)
      • ๐Ÿ“‚ backend (34)
        • spring (19)
        • architecture (10)
        • test (5)
      • ๐Ÿ“‚ computer-science (5)
      • ๐Ÿ“‚ programming-language (12)
        • java (12)
      • ๐Ÿ“‚ infra (9)
        • mysql (4)
        • redis (2)
        • message-queue (3)
      • ๐Ÿ“‚ cloud (2)
        • aws (2)
      • ๐Ÿ“‚ frontend (1)
        • react (0)
      • ๐Ÿ“‚ education & lecture (16)
        • ์ธํ”„๋ผ ๊ณต๋ฐฉ (11)
        • ํ•ญํ”Œ ๋ฐฑ์—”๋“œ 7๊ธฐ (5)
      • ๐Ÿ“‚ ai (1)
        • claude (1)
      • ๐Ÿ“‚ etc (2)
        • ํšŒ๊ณ  (1)
        • ์ปจํผ๋Ÿฐ์Šค (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ์ธํ”„๋ผ๊ณต๋ฐฉ
    kafka
    spring
    Redisson
    AWS
    ๋™์‹œ์„ฑ
    spring-batch
    nGrinder
    ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค๊ณ„
    UUID
    react
    spring AOP
    spring camp 2025
    gof
    ํ•ญํ•ดํ”Œ๋Ÿฌ์Šค
    ์Šคํ”„๋ง์บ ํ”„ ํ›„๊ธฐ
    ํšŒ๊ณ 
    mysql
    JPA
    ๊ฐ€์ƒ ๋ฉด์ ‘ ์‚ฌ๋ก€๋กœ ๋ฐฐ์šฐ๋Š” ๋Œ€๊ทœ๋ชจ ์„ค๊ณ„
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.6
cooper25
์•Œ๋ฆผ ๊ธฐ๋Šฅ Polling ์—์„œ SSE ์ „ํ™˜ํ•˜๋ฉฐ ๊ฒช์—ˆ๋˜ ์‚ฝ์งˆ (์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ)
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”