๐Ÿ’ป PROJECT/[Spring Boot, React] ๋…์„œ ์Šต๊ด€ ๊ด€๋ฆฌ ์„œ๋น„์Šค

[ํ”„๋กœ์ ํŠธ] ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ ๊ตฌํ˜„ ๋ฐฉ์‹ ์„ ํƒ ๊ณผ์ •

devCloud 2026. 5. 1. 14:11
728x90
PROJECT

๋…์„œ ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ ๊ตฌํ˜„ ๋ฐฉ์‹ ์„ ํƒ ๊ณผ์ •

1. ๋ฐฐ๊ฒฝ

๋…์„œ ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ ๊ธฐ๋Šฅ์„ ์ฒ˜์Œ ๊ตฌํ˜„ํ•ด๋ณด๋Š” ์ƒํ™ฉ์—์„œ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ• ์ง€ ๊ณ ๋ฏผ์ด ์žˆ์—ˆ๋‹ค. ์•Œ๋ฆผ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์ด ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์žˆ๋‹ค๋Š” ๊ฒƒ์€ ์•Œ๊ณ  ์žˆ์—ˆ์ง€๋งŒ, SSE ์™ธ์—๋Š” ์ต์ˆ™ํ•˜์ง€ ์•Š์€ ๋ฐฉ์‹๋“ค์ด์—ˆ๋‹ค.

๋ฆฌ๋งˆ์ธ๋” ์˜ˆ์‹œ

 

๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ ์‹œ๊ฐ„์— ๋งž์ถฐ ์„œ๋ฒ„๊ฐ€ ์•Œ๋ฆผ์„ ๋ณด๋‚ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” API์™€ ๋‹ฌ๋ฆฌ, ์„œ๋ฒ„๊ฐ€ ํŠน์ • ์‹œ์ ์— ์‚ฌ์šฉ์ž๋ฅผ ํ™•์ธํ•˜๊ณ  ์•Œ๋ฆผ์„ ์ „๋‹ฌํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ํ•„์š”ํ–ˆ๋‹ค.

ํ•ต์‹ฌ ๊ธฐ์ค€์€ ๊ตฌํ˜„ ๋‚œ์ด๋„, ํ˜„์žฌ ์„œ๋น„์Šค ๊ทœ๋ชจ, ๊ทธ๋ฆฌ๊ณ  ์•Œ๋ฆผ ๋ฐฉํ–ฅ์ด์—ˆ๋‹ค. Booktine์˜ ๋ฆฌ๋งˆ์ธ๋”๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„๋กœ ์‹ค์‹œ๊ฐ„ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋ผ, ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ๋กœ ์•Œ๋ฆผ์„ ์ „๋‹ฌํ•˜๋Š” ๋‹จ๋ฐฉํ–ฅ ๊ตฌ์กฐ์— ๊ฐ€๊น๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

2. ์•Œ๋ฆผ ๊ตฌํ˜„ ๋ฐฉ์‹ ๋น„๊ต

๋ฐฉ์‹ ์„ค๋ช… ๋‚œ์ด๋„
@Scheduled + SSE ์„œ๋ฒ„๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ DB๋ฅผ ํ™•์ธํ•œ ๋’ค SSE๋กœ ๋ธŒ๋ผ์šฐ์ €์— ์•Œ๋ฆผ์„ ํ‘ธ์‹œํ•œ๋‹ค. ๋‚ฎ์Œ
@Scheduled + ์ด๋ฉ”์ผ ์„œ๋ฒ„๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ DB๋ฅผ ํ™•์ธํ•œ ๋’ค ์ด๋ฉ”์ผ์„ ๋ฐœ์†กํ•œ๋‹ค. ๋‚ฎ์Œ
WebSocket ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์‹ค์‹œ๊ฐ„ ํ†ต์‹ ํ•œ๋‹ค. ๋†’์Œ
FCM / APNs ๋ชจ๋ฐ”์ผ ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ‘ธ์‹œ ์•Œ๋ฆผ ๋ฐฉ์‹์ด๋‹ค. ์ค‘๊ฐ„

3. WebSocket์„ ์„ ํƒํ•˜์ง€ ์•Š์€ ์ด์œ 

WebSocket์€ ์ฒ˜์Œ์— SSE์™€ ํ•จ๊ป˜ ๊ฐ€์žฅ ํ˜„์‹ค์ ์ธ ์„ ํƒ์ง€๋กœ ๊ณ ๋ คํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ ๊ตฌํ˜„์„ ์ƒ๊ฐํ•ด๋ณด๋ฉด ํ˜„์žฌ ์š”๊ตฌ์‚ฌํ•ญ์— ๋น„ํ•ด ๋ณต์žก๋„๊ฐ€ ๋†’๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

  • spring-boot-starter-websocket ์˜์กด์„ฑ ์ถ”๊ฐ€ ๋ฐ STOMP ๋ธŒ๋กœ์ปค ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค.
  • ์—ฐ๊ฒฐ๊ณผ ํ•ด์ œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ ๊ตฌํ˜„์ด ํ•„์š”ํ•˜๋‹ค.
  • ์ ‘์† ์ค‘์ธ ์‚ฌ์šฉ์ž ์„ธ์…˜์„ ๋ณ„๋„๋กœ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
  • Spring Security ์ ์šฉ ์‹œ WebSocket ์ธ์ฆ ์ฒ˜๋ฆฌ๋ฅผ ๋ณ„๋„๋กœ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

๋ฌด์—‡๋ณด๋‹ค ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ์€ ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ๋‹จ๋ฐฉํ–ฅ ํ†ต์‹ ๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค. WebSocket์€ ์ฑ„ํŒ…์ฒ˜๋Ÿผ ํด๋ผ์ด์–ธํŠธ๋„ ์„œ๋ฒ„๋กœ ์‹ค์‹œ๊ฐ„ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ๋” ์ ํ•ฉํ•˜๋‹ค.

Booktine์˜ ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ์€ ์–‘๋ฐฉํ–ฅ ์‹ค์‹œ๊ฐ„ ํ†ต์‹ ์ด ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋ฏ€๋กœ, WebSocket์€ ํ˜„์žฌ ์š”๊ตฌ์‚ฌํ•ญ์— ๋น„ํ•ด ๊ณผํ•œ ์„ ํƒ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

4. ๊ฒฐ์ • — @Scheduled + SSE

@Scheduled๋ฅผ ์‚ฌ์šฉํ•œ ์ฃผ๊ธฐ์  ์•Œ๋ฆผ ํ™•์ธ

@Scheduled๋Š” ์„œ๋ฒ„๊ฐ€ ์ •ํ•ด์ง„ ์ฃผ๊ธฐ๋งˆ๋‹ค ํŠน์ • ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ํ•˜๋Š” Spring์˜ ์Šค์ผ€์ค„๋ง ๊ธฐ๋Šฅ์ด๋‹ค. ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ์—์„œ๋Š” ์„œ๋ฒ„๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ DB๋ฅผ ์กฐํšŒํ•ด์„œ ํ˜„์žฌ ์‹œ๊ฐ„์— ์•Œ๋ฆผ์„

๋ณด๋‚ผ ์‚ฌ์šฉ์ž๋ฅผ ํ™•์ธํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค.

@Scheduled๋ฅผ ์‚ฌ์šฉํ•ด ๋งค๋ถ„ ๋ฆฌ๋งˆ์ธ๋” ๋Œ€์ƒ ์‚ฌ์šฉ์ž๋ฅผ ์กฐํšŒํ•˜๊ณ  SSE๋กœ ์•Œ๋ฆผ์„ ์ „์†กํ•˜๋Š” ์ฝ”๋“œ๋‹ค.

cron ํ‘œํ˜„์‹ 0 * * * * *๋Š” ๋งค ๋ถ„ 0์ดˆ์— ์‹คํ–‰๋œ๋‹ค๋Š” ์˜๋ฏธ๋‹ค. ์ดˆ ๋‹จ์œ„๋กœ ์ •๋ฐ€ํ•˜๊ฒŒ ์•Œ๋ฆผ์„ ๋ณด๋‚ผ ํ•„์š”๋Š” ์—†๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— 1๋ถ„ ์ฃผ๊ธฐ๋กœ ์„ค์ •ํ–ˆ๋‹ค.

 

SSE๋ฅผ ์‚ฌ์šฉํ•œ ์„œ๋ฒ„ ๋‹จ๋ฐฉํ–ฅ ์•Œ๋ฆผ ์ „์†ก

SSE(Server-Sent Events)๋Š” ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์— ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ํ‘ธ์‹œํ•˜๋Š” ๋‹จ๋ฐฉํ–ฅ ํ†ต์‹  ๋ฐฉ์‹์ด๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•ด๋‘๋ฉด, ์„œ๋ฒ„๋Š” ํ•„์š”ํ•œ ์‹œ์ ์— ์ด๋ฒคํŠธ๋ฅผ ๋‚ด๋ ค๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

Spring MVC์—์„œ SseEmitter๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด ๋ณ„๋„์˜ ๋ณต์žกํ•œ ์‹ค์‹œ๊ฐ„ ํ†ต์‹  ์ธํ”„๋ผ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

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

 

SSE ์—ฐ๊ฒฐ ๊ด€๋ฆฌ ๋ฐฉ์‹

SSE ์—ฐ๊ฒฐ์€ ์‚ฌ์šฉ์ž๋ณ„๋กœ SseEmitter๋ฅผ ConcurrentHashMap์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ด€๋ฆฌํ–ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ /reminders/connect๋กœ ์—ฐ๊ฒฐํ•˜๋ฉด ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ emitter๋ฅผ ์ €์žฅํ•˜๊ณ , ์—ฐ๊ฒฐ์ด ๋Š๊ธฐ๋ฉด ์ž๋™์œผ๋กœ ์ œ๊ฑฐ๋˜๋„๋ก ์ฒ˜๋ฆฌํ–ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ˜„์žฌ ์ ‘์† ์ค‘์ธ ์‚ฌ์šฉ์ž์—๊ฒŒ๋งŒ ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ณ , ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋Š์–ด์ง„ ์—ฐ๊ฒฐ์„ ๊ณ„์† ๋ณด๊ด€ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

 

์„ ํƒ ์ด์œ 

  • ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ์€ ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ณด๋‚ด๋Š” ๋‹จ๋ฐฉํ–ฅ ํ†ต์‹ ๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค.
  • ๋ณ„๋„ ์ธํ”„๋ผ ์—†์ด Spring ๊ธฐ๋ณธ ๊ธฐ๋Šฅ๋งŒ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • WebSocket๋ณด๋‹ค ๊ตฌํ˜„ ๋ณต์žก๋„๊ฐ€ ๋‚ฎ๋‹ค.
  • Spring Security ์—ฐ๋™ ์‹œ ์ถ”๊ฐ€ ์„ค์ • ๋ถ€๋‹ด์ด ์ƒ๋Œ€์ ์œผ๋กœ ์ ๋‹ค.
  • ํ˜„์žฌ Booktine ์„œ๋น„์Šค ๊ทœ๋ชจ์— ์ ํ•ฉํ•œ ๋ฐฉ์‹์ด๋‹ค.

SSE์˜ ํ•œ๊ณ„

SSE์—๋„ ํ•œ๊ณ„๋Š” ์žˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ ์žฌ์‹œ์ž‘๋˜๋ฉด ๊ธฐ์กด ์—ฐ๊ฒฐ์€ ๋Š์–ด์ง€๋ฏ€๋กœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋‹ค์‹œ ์—ฐ๊ฒฐํ•ด์•ผ ํ•œ๋‹ค. ๋˜ํ•œ ๋ธŒ๋ผ์šฐ์ € ํƒญ์ด ๋‹ซํžˆ๋ฉด SSE ์—ฐ๊ฒฐ๋„ ํ•จ๊ป˜ ๋Š๊ธด๋‹ค.

๋‹ค๋งŒ ํ˜„์žฌ ์„œ๋น„์Šค ๊ทœ๋ชจ์—์„œ๋Š” ์ด๋Ÿฌํ•œ ํ•œ๊ณ„๊ฐ€ ์น˜๋ช…์ ์ด์ง€ ์•Š๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค. Booktine์˜ ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ์€ ๊ณ ๋นˆ๋„ ์‹ค์‹œ๊ฐ„ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ๋ณด๋‹ค, ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ ์‹œ๊ฐ„์— ์•Œ๋ฆผ์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ •๋ฆฌํ•˜๋ฉด, Booktine์˜ ๋…์„œ ๋ฆฌ๋งˆ์ธ๋” ์•Œ๋ฆผ์€ ์–‘๋ฐฉํ–ฅ ํ†ต์‹ ์ด ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋ฏ€๋กœ WebSocket๋ณด๋‹ค @Scheduled์™€ SSE ์กฐํ•ฉ์ด ๋” ๋‹จ์ˆœํ•˜๊ณ  ์ ํ•ฉํ•œ ์„ ํƒ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.
728x90