Skip to content

Instantly share code, notes, and snippets.

@leedc0101
Last active March 13, 2026 01:29
Show Gist options
  • Select an option

  • Save leedc0101/99604f550fa95252d7d266ddc35a753c to your computer and use it in GitHub Desktop.

Select an option

Save leedc0101/99604f550fa95252d7d266ddc35a753c to your computer and use it in GitHub Desktop.
frontend-briefing-2026-03-13-KST

์›๋ฌธ ์ œ๋ชฉ: Temporal: The 9-Year Journey to Fix Time in JavaScript ์›๋ฌธ ๋งํฌ: https://bloomberg.github.io/js-blog/post/temporal/ ๋ฒˆ์—ญ์ผ: 2026-03-13 KST

Temporal: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์‹œ๊ฐ„์„ ๋ฐ”๋กœ์žก๊ธฐ๊นŒ์ง€ 9๋…„

Bloomberg์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ธํ”„๋ผ ํŒ€์€ ์˜ค๋žซ๋™์•ˆ ๊ธˆ์œต ๋‹จ๋ง๊ธฐ, ์„œ๋ฒ„, UI ์ „๋ฐ˜์—์„œ ์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ค„ ์™”๋‹ค. ๋‹ค์–‘ํ•œ ์‹œ๊ฐ„๋Œ€, ์žฆ์€ DST ๋ณ€๊ฒฝ, ๊ณ ์ •๋ฐ€ ํƒ€์ž„์Šคํƒฌํ”„ ์š”๊ตฌ๊ฐ€ ๊ฒน์น˜๋ฉด์„œ ๊ธฐ์กด Date API์˜ ํ•œ๊ณ„๊ฐ€ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋“œ๋Ÿฌ๋‚ฌ๊ณ , ์ด ๋ฌธ์ œ๋ฅผ ๊ทผ๋ณธ์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ํ‘œ์ค€ํ™” ๋…ธ๋ ฅ์ด ๋ฐ”๋กœ Temporal์ด์—ˆ๋‹ค.

์™œ Date๋Š” ๋Š˜ ๋ฌธ์ œ์˜€๋‚˜

์ดˆ๊ธฐ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ Date๋Š” 1995๋…„์˜ ์ œ์•ฝ ์†์—์„œ ์ž๋ฐ” ๊ตฌํ˜„์„ ๋น ๋ฅด๊ฒŒ ์ด์‹ํ•œ ๊ฒฐ๊ณผ๋ฌผ์ด์—ˆ๋‹ค. ๋‹น์‹œ์—๋Š” ์‹ค์šฉ์ ์ธ ์„ ํƒ์ด์—ˆ์ง€๋งŒ, ์˜ค๋Š˜๋‚ ์˜ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์š”๊ตฌํ•˜๋Š” ์ •ํ™•ํ•œ ์‹œ๊ฐ„ ๋ชจ๋ธ์—๋Š” ๋งž์ง€ ์•Š๋Š”๋‹ค.

๋Œ€ํ‘œ์ ์ธ ๋ฌธ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ๊ฐ€๋ณ€์„ฑ(mutability): ํ—ฌํผ ํ•จ์ˆ˜๊ฐ€ ์›๋ณธ Date๋ฅผ ์˜๋„์น˜ ์•Š๊ฒŒ ๋ณ€๊ฒฝํ•˜๊ธฐ ์‰ฝ๋‹ค.
  • ์›” ๊ณ„์‚ฐ์˜ ์ง๊ด€์„ฑ ๋ถ€์กฑ: 1์›” 31์ผ์— ํ•œ ๋‹ฌ์„ ๋”ํ–ˆ๋”๋‹ˆ 2์›” ๋ง์ด ์•„๋‹ˆ๋ผ 3์›” ์ดˆ๋กœ ๋„˜์–ด๊ฐ€๋Š” ์‹์˜ ํ•จ์ •์ด ์ƒ๊ธด๋‹ค.
  • ์• ๋งคํ•œ ๋ฌธ์ž์—ด ํŒŒ์‹ฑ: ๊ฑฐ์˜ ISO์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ์™„์ „ํžˆ ๊ฐ™์ง€ ์•Š์€ ์ž…๋ ฅ์€ ๋ธŒ๋ผ์šฐ์ €๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ํ•ด์„๋  ์ˆ˜ ์žˆ๋‹ค.
  • ์‹œ๊ฐ„๋Œ€ ์ฒ˜๋ฆฌ ํ•œ๊ณ„: ๋จธ์‹  ๋กœ์ปฌ ์‹œ๊ฐ„๋Œ€์™€ ์‚ฌ์šฉ์ž ์„ค์ • ์‹œ๊ฐ„๋Œ€๊ฐ€ ๋‹ค๋ฅผ ๋•Œ ์ผ๊ด€๋œ ๋ชจ๋ธ์„ ์ œ๊ณตํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

์ด๋Ÿฐ ๋ฌธ์ œ ๋•Œ๋ฌธ์— ์›น ์ƒํƒœ๊ณ„๋Š” Moment.js ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ณต๋ฐฑ์„ ๋ฉ”์›Œ ์™”๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ฐฉ์‹์€ ๋ฒˆ๋“ค ํฌ๊ธฐ ์ฆ๊ฐ€, locale/timezone ๋ฐ์ดํ„ฐ ๋™๋ด‰, tree-shaking ํ•œ๊ณ„ ๊ฐ™์€ ์ƒˆ๋กœ์šด ๋น„์šฉ์„ ๋งŒ๋“ค์—ˆ๋‹ค.

Temporal์€ ๋ฌด์—‡์„ ๋ฐ”๊พธ๋ ค ํ–ˆ๋‚˜

Temporal์€ ๊ธฐ์กด Date๋ฅผ ์กฐ๊ธˆ ์†๋ณด๋Š” ์ˆ˜์ค€์ด ์•„๋‹ˆ๋ผ, ์‹œ๊ฐ„ ๋ฌธ์ œ๋ฅผ ์ œ๋Œ€๋กœ ๋ชจ๋ธ๋งํ•˜๋Š” ์ƒˆ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋„์ž…ํ•˜๋ ค๋Š” ์‹œ๋„์˜€๋‹ค.

ํ•ต์‹ฌ ๋ฐฉํ–ฅ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • Date์˜ ์‹ค์งˆ์  ๋Œ€์ฒด์žฌ ์ œ๊ณต
  • ๋‹จ์ผ API ๋Œ€์‹  ๋ชฉ์ ๋ณ„ ์‹œ๊ฐ„ ํƒ€์ž… ์ œ๊ณต
  • ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ถˆ๋ณ€(immutable)
  • ์‹œ๊ฐ„๋Œ€์™€ ๋‹ฌ๋ ฅ(calendar)์„ 1๊ธ‰ ๊ฐœ๋…์œผ๋กœ ์ง€์›
  • ๋” ๋†’์€ ์ •๋ฐ€๋„(์˜ˆ: ๋‚˜๋…ธ์ดˆ) ์ฒ˜๋ฆฌ

์ฆ‰, โ€œํ•˜๋‚˜์˜ ๋А์Šจํ•œ ํƒ€์ž…์œผ๋กœ ๋ชจ๋“  ์‹œ๊ฐ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐโ€ํ•˜๋ ค๋Š” ๊ณผ๊ฑฐ ๋ชจ๋ธ์„ ๋ฒ„๋ฆฌ๊ณ , ์šฉ๋„๋ณ„ ํƒ€์ž…๊ณผ ๋ช…์‹œ์ ์ธ ์˜๋ฏธ๋ฅผ ๋„์ž…ํ•œ ๊ฒƒ์ด๋‹ค.

ํ‘œ์ค€ํ™”๋Š” ์™œ 9๋…„์ด๋‚˜ ๊ฑธ๋ ธ๋‚˜

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋Œ์•„๊ฐ€๋Š” ๊ณต์šฉ ์–ธ์–ด์ด๊ธฐ ๋•Œ๋ฌธ์—, ์–ด๋А ํ•œ ์กฐ์ง์ด ๋‹จ๋…์œผ๋กœ API๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋‹ค. ๋ณ€๊ฒฝ์€ TC39 ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ฑฐ์น˜๋ฉฐ, ์•„์ด๋””์–ด ํ•ฉ์˜๋ถ€ํ„ฐ ๊ตฌํ˜„ ํ”ผ๋“œ๋ฐฑ, ์ตœ์ข… ํ‘œ์ค€ํ™”๊นŒ์ง€ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ํ†ต๊ณผํ•ด์•ผ ํ•œ๋‹ค.

Temporal์€ 2017~2018๋…„ ๋ฌด๋ ต ๋ฌธ์ œ ์ธ์‹๊ณผ ์ดˆ๊ธฐ ์„ค๊ณ„๋ฅผ ์ธ์ •๋ฐ›์•˜์ง€๋งŒ, ์ดํ›„ ์‹ค์ œ๋กœ ์“ธ ์ˆ˜ ์žˆ๋Š” API๋กœ ๋‹ค๋“ฌ๊ธฐ๊นŒ์ง€ ๊ธด ์‹œ๊ฐ„์ด ํ•„์š”ํ–ˆ๋‹ค. ๊ทธ ๊ณผ์ •์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜„์‹ค์  ์ด์œ ๊ฐ€ ์žˆ์—ˆ๋‹ค.

  • ์‹œ๊ฐ„๊ณผ ๋‚ ์งœ๋Š” ํ‘œ๋ฉด์  ๋‹จ์ˆœ์„ฑ์— ๋น„ํ•ด ์˜ˆ์™ธ ์ผ€์ด์Šค๊ฐ€ ๋งค์šฐ ๋งŽ๋‹ค.
  • ์‹œ๊ฐ„๋Œ€, DST, ๋‹ฌ๋ ฅ, ์ •๋ฐ€๋„, ํŒŒ์‹ฑ/ํฌ๋งทํŒ… ์š”๊ตฌ๋ฅผ ๋ชจ๋‘ ํ•จ๊ป˜ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ธŒ๋ผ์šฐ์ €/์—”์ง„/๋Ÿฐํƒ€์ž„/๊ตญ์ œํ™” API์™€ ์กฐํ™”๋ฅผ ๋งž์ถฐ์•ผ ํ•œ๋‹ค.
  • ์‹ค์ œ ํ˜„์—…์—์„œ ๋ฐ˜๋ณต๋˜๋Š” ์‹ค์ˆ˜๊นŒ์ง€ API ์„ค๊ณ„์— ๋ฐ˜์˜ํ•ด์•ผ ํ•œ๋‹ค.

Bloomberg, Igalia, Google ๊ตญ์ œํ™” ํŒ€, Microsoft ์ถœ์‹  ์ฑ”ํ”ผ์–ธ ๋“ฑ ์—ฌ๋Ÿฌ ์ฃผ์ฒด๊ฐ€ ์žฅ๊ธฐ๊ฐ„ ํ˜‘์—…ํ•œ ์ด์œ ๋„ ์—ฌ๊ธฐ์— ์žˆ๋‹ค.

์‹ค๋ฌด ๊ด€์ ์—์„œ ์ค‘์š”ํ•œ ํฌ์ธํŠธ

์ด ๊ธ€์ด FE ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ค‘์š”ํ•œ ์ด์œ ๋Š” Temporal์ด ๋‹จ์ˆœํžˆ โ€œ์ƒˆ ๋‚ ์งœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌโ€๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ์ œ์•ˆ์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‹œ๊ฐ„ ์ฒ˜๋ฆฌ์˜ ๊ธฐ๋ณธ ์ฒ ํ•™์„ ๋ฐ”๊พผ๋‹ค.

1) ๋ช…์‹œ์„ฑ์ด ๋†’์•„์ง„๋‹ค

์‹œ๊ฐ„๋Œ€๊ฐ€ ์žˆ๋Š” ๊ฐ’๊ณผ ์—†๋Š” ๊ฐ’์„ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š์€ ์ฑ„ ์„ž์–ด ์“ฐ๋Š” ์Šต๊ด€์ด ์˜ค๋ฅ˜๋ฅผ ๋งŒ๋“ ๋‹ค. Temporal์€ ๊ฐ’์˜ ์˜๋ฏธ๋ฅผ ํƒ€์ž… ์ˆ˜์ค€์—์„œ ๋” ๋ถ„๋ช…ํ•˜๊ฒŒ ๋“œ๋Ÿฌ๋‚ด๋Š” ๋ฐฉํ–ฅ์„ ํƒํ•œ๋‹ค.

2) ๋ถˆ๋ณ€์„ฑ์ด ๊ธฐ๋ณธ์ด๋‹ค

React ๊ฐ™์€ ํ™˜๊ฒฝ์—์„œ ์ƒํƒœ ์ถ”์ , ๋ฉ”๋ชจ์ด์ œ์ด์…˜, ๋น„๊ต ๋กœ์ง์„ ๋‹ค๋ฃฐ ๋•Œ ๋ถˆ๋ณ€ ๊ฐ’์€ ํ›จ์”ฌ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ณผ๊ฑฐ Moment.js ์ƒํƒœ๊ณ„๊ฐ€ ๊ฒช์—ˆ๋˜ โ€œ๊ฐ€๋ณ€ ๊ฐ์ฒด๋กœ ์ธํ•œ ํ˜ผ๋ž€โ€์„ ์ค„์ด๋Š” ๋ฐ ์œ ๋ฆฌํ•˜๋‹ค.

3) ์‹œ๊ฐ„๋Œ€/์บ˜๋ฆฐ๋”๋ฅผ ์šฐํšŒํ•˜์ง€ ์•Š๋Š”๋‹ค

๊ธ€๋กœ๋ฒŒ ์„œ๋น„์Šค์—์„œ๋Š” โ€œ์„œ๋ฒ„ ์‹œ๊ฐ„๋Œ€โ€, โ€œ์‚ฌ์šฉ์ž ์„ค์ • ์‹œ๊ฐ„๋Œ€โ€, โ€œ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ง€์—ญ ์‹œ๊ฐ„๋Œ€โ€๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค. Temporal์€ ์ด ๋ณต์žก์„ฑ์„ ์ˆจ๊ธฐ๊ธฐ๋ณด๋‹ค ๋ชจ๋ธ ์•ˆ์œผ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.

4) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด๋„๋ฅผ ์ค„์ผ ์—ฌ์ง€๊ฐ€ ์ƒ๊ธด๋‹ค

ํ‘œ์ค€ API๊ฐ€ ์ถฉ๋ถ„ํžˆ ๊ฐ•๋ ฅํ•ด์ง€๋ฉด ๋‚ ์งœ ์ฒ˜๋ฆฌ ๋•Œ๋ฌธ์— ๋ฌด๊ฑฐ์šด ์„œ๋“œํŒŒํ‹ฐ ํŒจํ‚ค์ง€๋ฅผ ๋“ค์ด๋Š” ๋ถ€๋‹ด์ด ์ค„์–ด๋“ค ์ˆ˜ ์žˆ๋‹ค. ๋ฒˆ๋“ค ์ตœ์ ํ™”์™€ ์žฅ๊ธฐ ์œ ์ง€๋ณด์ˆ˜ ๊ด€์ ์—์„œ๋„ ์˜๋ฏธ๊ฐ€ ํฌ๋‹ค.

๊ธ€์˜ ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€

Temporal์€ ์–ด๋А ๋‚  ๊ฐ‘์ž๊ธฐ ๋“ฑ์žฅํ•œ ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋ผ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ƒํƒœ๊ณ„๊ฐ€ ์ˆ˜์‹ญ ๋…„ ๋™์•ˆ ๋ˆ„์ ํ•ด ์˜จ ์‹œ๊ฐ„ ์ฒ˜๋ฆฌ์˜ ๋ถˆ๋งŒ์„ ์ •๋ฆฌํ•ด ํ‘œ์ค€์œผ๋กœ ์˜ฎ๊ธฐ๋Š” ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋‹ค. ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์„œ๋น„์Šค, ๊ธˆ์œต ์‹œ์Šคํ…œ, ํ˜‘์—… ๋„๊ตฌ์ฒ˜๋Ÿผ ์‹œ๊ฐ„ ์ •ํ™•๋„๊ฐ€ ์ œํ’ˆ ํ’ˆ์งˆ๊ณผ ์ง์ ‘ ์—ฐ๊ฒฐ๋˜๋Š” ํ™˜๊ฒฝ์—์„œ ๊ทธ ๊ฐ€์น˜๊ฐ€ ํฌ๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ ์‹ค๋ฌด์ž ์ž…์žฅ์—์„œ๋Š” ์ง€๊ธˆ ๋‹น์žฅ ์ „์ฒด ์•ฑ์„ Temporal๋กœ ๋ฐ”๊พธ๋Š” ๋ฌธ์ œ๋ณด๋‹ค,

  • ๊ธฐ์กด Date ์‚ฌ์šฉ์ด ์–ด๋–ค ์‹์œผ๋กœ ๋ฒ„๊ทธ๋ฅผ ๋งŒ๋“œ๋Š”์ง€ ์ดํ•ดํ•˜๊ณ ,
  • ์‹œ๊ฐ„ ๊ด€๋ จ ๋„๋ฉ”์ธ์„ ๋” ๋ช…์‹œ์ ์œผ๋กœ ๋ชจ๋ธ๋งํ•˜๋ฉฐ,
  • ์žฅ๊ธฐ์ ์œผ๋กœ Temporal ๋„์ž… ๊ฐ€๋Šฅ์„ฑ์„ ์—ผ๋‘์— ๋‘” ์„ค๊ณ„๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ

์ด ๋” ์ค‘์š”ํ•˜๋‹ค.

Temporal์€ โ€œ๋‚ ์งœ API ๊ฐœ์„ โ€์ด ์•„๋‹ˆ๋ผ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์‹œ๊ฐ„์„ ๋‹ค๋ฃจ๋Š” ๋ฐฉ์‹ ์ž์ฒด๋ฅผ ๋‹ค์‹œ ์ •์˜ํ•˜๋Š” ์ž‘์—…์— ๊ฐ€๊น๋‹ค.

์›๋ฌธ ์ œ๋ชฉ: Evolving the Node.js Release Schedule ์›๋ฌธ ๋งํฌ: https://nodejs.org/en/blog/announcements/evolving-the-nodejs-release-schedule ๋ฒˆ์—ญ์ผ: 2026-03-13 KST

Node.js ๋ฆด๋ฆฌ์Šค ์ผ์ • ๊ฐœํŽธ: ์—ฐ 2ํšŒ ๋ฉ”์ด์ €์—์„œ ์—ฐ 1ํšŒ ๋ฉ”์ด์ €๋กœ

Node.js ๋ฆด๋ฆฌ์Šค ํŒ€์€ 27.x๋ถ€ํ„ฐ ๋ฉ”์ด์ € ๋ฆด๋ฆฌ์Šค๋ฅผ ์—ฐ 2ํšŒ์—์„œ ์—ฐ 1ํšŒ๋กœ ์ค„์ด๊ฒ ๋‹ค๊ณ  ๋ฐœํ‘œํ–ˆ๋‹ค. ํ•ต์‹ฌ์€ ๋ฒ„์ „ ์ˆ˜๋ฅผ ์ค„์ด๋Š” ๋Œ€์‹ , ๋งค๋…„ ๋‚˜์˜ค๋Š” ๋ฉ”์ด์ € ๋ฆด๋ฆฌ์Šค๋ฅผ ๋ชจ๋‘ LTS๋กœ ์Šน๊ฒฉํ•˜๋Š” ๊ตฌ์กฐ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด๋‹ค.

๋ฌด์—‡์ด ๋ฐ”๋€Œ๋‚˜

2026๋…„ 10์›”๋ถ€ํ„ฐ ์ ์šฉ๋˜๋Š” ์ƒˆ ๋ชจ๋ธ์˜ ํ•ต์‹ฌ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ๋ฉ”์ด์ € ๋ฆด๋ฆฌ์Šค๋Š” ๋งค๋…„ 4์›” 1ํšŒ
  • ํ•ด๋‹น ๋ฆด๋ฆฌ์Šค๋Š” ๊ฐ™์€ ํ•ด 10์›”์— LTS๋กœ ์ „ํ™˜
  • ๋” ์ด์ƒ ํ™€์ˆ˜/์ง์ˆ˜ ๊ตฌ๋ถ„์ด ์—†๋‹ค
  • ๋ชจ๋“  ๋ฉ”์ด์ € ๋ฆด๋ฆฌ์Šค๊ฐ€ ๊ฒฐ๊ตญ LTS๊ฐ€ ๋œ๋‹ค
  • ์ •์‹ Current ์ „ ๋‹จ๊ณ„๋กœ Alpha ์ฑ„๋„์ด ๋„์ž…๋œ๋‹ค

์˜ˆ๋ฅผ ๋“ค์–ด Node.js 27์€ 2026๋…„ 10์›” Alpha๋ฅผ ์‹œ์ž‘ํ•˜๊ณ , 2027๋…„ 4์›” ์ •์‹ Current, 2027๋…„ 10์›” LTS๊ฐ€ ๋œ๋‹ค.

์™œ ๋ฐ”๊พธ๋Š”๊ฐ€

Node.js ํŒ€์€ ์ง€๋‚œ 10๋…„์˜ ์‚ฌ์šฉ ํŒจํ„ด์„ ๋ณด๋ฉฐ ํ˜„์žฌ ์ฒด๊ณ„๊ฐ€ ํ˜„์‹ค๊ณผ ๋งž์ง€ ์•Š๋Š”๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

์ฃผ์š” ์ด์œ ๋Š” ์ด๋ ‡๋‹ค.

  1. ํ™€์ˆ˜ ๋ฒ„์ „์€ ์‹ค์ œ ์ฑ„ํƒ๋ฅ ์ด ๋‚ฎ๋‹ค ๋งŽ์€ ์กฐ์ง์€ ์ด๋ฏธ LTS๋งŒ ์‚ฌ์šฉํ•˜๊ณ , ํ™€์ˆ˜ ๋ฆด๋ฆฌ์Šค๋Š” ๊ฑฐ์˜ ๊ฑด๋„ˆ๋›ด๋‹ค.

  2. ์ดˆ๋ณด์ž์—๊ฒŒ odd/even ๊ทœ์น™์ด ํ—ท๊ฐˆ๋ฆฐ๋‹ค โ€œ์–ด๋–ค ๋ฒ„์ „์ด ์•ˆ์ • ๋ฒ„์ „์ธ๊ฐ€?โ€๋ฅผ ๋ณ„๋„ ์„ค๋ช… ์—†์ด ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

  3. ์œ ์ง€๋ณด์ˆ˜ ๋ถ€๋‹ด์ด ๋„ˆ๋ฌด ํฌ๋‹ค ๋ณด์•ˆ ์ˆ˜์ •, ๋ฐฑํฌํŠธ, ๋ฆด๋ฆฌ์Šค ๊ด€๋ฆฌ๊ฐ€ ์—ฌ๋Ÿฌ ํ™œ์„ฑ ๋ธŒ๋žœ์น˜์— ๋ถ„์‚ฐ๋˜๋ฉด์„œ ์œ ์ง€๊ฐ€ ์–ด๋ ค์›Œ์กŒ๋‹ค. Node.js๋Š” ์ƒ๋‹น ๋ถ€๋ถ„ ์ž์›๋ด‰์‚ฌ์ž ์ค‘์‹ฌ์œผ๋กœ ์šด์˜๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์‹ค์ œ ์‚ฌ์šฉ๋˜๋Š” ๋ฆด๋ฆฌ์Šค์— ์ง‘์ค‘ํ•˜๋Š” ํŽธ์ด ์ง€์† ๊ฐ€๋Šฅํ•˜๋‹ค.

Alpha ์ฑ„๋„์€ ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋‚˜

์ƒˆ ๊ตฌ์กฐ์—์„œ ์ค‘์š”ํ•œ ๋ณ€ํ™”๋Š” Alpha ์ฑ„๋„์ด๋‹ค. ์˜ˆ์ „์—๋Š” ํ™€์ˆ˜ ๋ฉ”์ด์ €๊ฐ€ ์‚ฌ์‹ค์ƒ ์‹คํ—˜/์กฐ๊ธฐ ๊ฒ€์ฆ ์—ญํ• ์„ ํ–ˆ๋Š”๋ฐ, ์•ž์œผ๋กœ๋Š” ๊ทธ ์—ญํ• ์„ Alpha๊ฐ€ ๋งก๋Š”๋‹ค.

Alpha์˜ ํŠน์ง•:

  • semver-major ๋ณ€๊ฒฝ ํ—ˆ์šฉ
  • ์„œ๋ช…๋˜๊ณ  ํƒœ๊ทธ๋œ ๋ฆด๋ฆฌ์Šค ์ œ๊ณต
  • CITGM์œผ๋กœ ์ฃผ์š” ์˜คํ”ˆ์†Œ์Šค ํŒจํ‚ค์ง€ ํ˜ธํ™˜์„ฑ ๊ฒ€์ฆ ์ˆ˜ํ–‰
  • Nightly๋ณด๋‹ค ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ๊ฐ€ ๋†’์Œ
  • ํ”„๋กœ๋•์…˜์šฉ์ด ์•„๋‹ˆ๋ผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ/๋„๊ตฌ ์ž‘์„ฑ์ž์˜ ์กฐ๊ธฐ ํ…Œ์ŠคํŠธ์šฉ

์ฆ‰, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž‘์„ฑ์ž๋Š” LTS๋งŒ ๋ณด์ง€ ๋ง๊ณ  Alpha์—์„œ ๋ฏธ๋ฆฌ ๊นจ์ง€๋Š” ๋ถ€๋ถ„์„ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

๋ฐ”๋€Œ์ง€ ์•Š๋Š” ๊ฒƒ

Node.js ํŒ€์€ โ€œ๋ฒ„์ „ ์ „๋žต์€ ๋ฐ”๋€Œ์ง€๋งŒ, ์‹ค๋ฌด์ž๊ฐ€ ์ฒด๊ฐํ•˜๋Š” ์•ˆ์ •์„ฑ์€ ์œ ์ง€๋œ๋‹คโ€๊ณ  ๊ฐ•์กฐํ•œ๋‹ค.

์œ ์ง€๋˜๋Š” ํ•ญ๋ชฉ:

  • LTS ์ง€์› ๊ธฐ๊ฐ„์€ ์—ฌ์ „ํžˆ ๋น„์Šทํ•จ(์•ฝ 30๊ฐœ์›”)
  • LTS ๊ฐ„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฒน์นจ ๊ตฌ๊ฐ„ ์œ ์ง€
  • ํ…Œ์ŠคํŠธ/๋ณด์•ˆ/ํ’ˆ์งˆ ๊ธฐ์ค€ ์œ ์ง€
  • V8 ๋„์ž… ์ฃผ๊ธฐ ์—ญ์‹œ ํฌ๊ฒŒ ๋ฒ—์–ด๋‚˜์ง€ ์•Š์Œ

์ฆ‰, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒ€์ด ์›๋ž˜ LTS๋งŒ ๋”ฐ๋ผ๊ฐ€๊ณ  ์žˆ์—ˆ๋‹ค๋ฉด ํฐ ๋ณ€ํ™”๋Š” โ€œ๋ฒ„์ „ ๋ฒˆํ˜ธ ์ฒด๊ณ„๊ฐ€ ๋‹จ์ˆœํ•ด์ง„๋‹คโ€๋Š” ์ •๋„์— ๊ฐ€๊น๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ ์‹ค๋ฌด์ž์—๊ฒŒ ์™œ ์ค‘์š”ํ•œ๊ฐ€

๋ธŒ๋ผ์šฐ์ € ์ฝ”๋“œ๋งŒ ์ž‘์„ฑํ•˜๋”๋ผ๋„, ์‹ค์ œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์€ Node.js ์œ„์— ์˜ฌ๋ผ๊ฐ„๋‹ค. Vite, Next.js, ESLint, TypeScript, Vitest, Storybook, Playwright, ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €์™€ CI ํŒŒ์ดํ”„๋ผ์ธ๊นŒ์ง€ ๋ชจ๋‘ Node ์ƒํƒœ๊ณ„์— ๊ฐ•ํ•˜๊ฒŒ ์—ฐ๊ฒฐ๋ผ ์žˆ๋‹ค.

์ด ๋ฐœํ‘œ๊ฐ€ ์ค‘์š”ํ•œ ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

1) ์—…๊ทธ๋ ˆ์ด๋“œ ์บ˜๋ฆฐ๋”๊ฐ€ ๋” ์˜ˆ์ธก ๊ฐ€๋Šฅํ•ด์ง„๋‹ค

๋งค๋…„ 4์›” Current, 10์›” LTS๋ผ๋Š” ๋ฆฌ๋“ฌ์ด ๊ณ ์ •๋˜๋ฉด CI ์ด๋ฏธ์ง€, ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ, ๋ฐฐํฌ ๋Ÿฐํƒ€์ž„ ์—…๊ทธ๋ ˆ์ด๋“œ ๊ณ„ํš์„ ์„ธ์šฐ๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค.

2) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํŒ€์€ ๋” ์ผ์ฐ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•œ๋‹ค

๋„๊ตฌ/๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“œ๋Š” ํŒ€์ด๋ผ๋ฉด LTS ์ถœ์‹œ ํ›„ ๋Œ€์‘ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ๋Š” ๋Šฆ๋‹ค. Alpha ๋‹จ๊ณ„์—์„œ ๊นจ์ง€๋Š” ๋ณ€๊ฒฝ์„ ์žก์•„์•ผ ์‚ฌ์šฉ์ž ํ”ผํ•ด๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

3) โ€œํ˜„์žฌ ์ง€์› ๋ฒ”์œ„โ€ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜์ด ๋‹จ์ˆœํ•ด์ง„๋‹ค

๋ฌธ์„œ, engines ํ•„๋“œ, CI matrix, README ์ง€์› ๋ฒ”์œ„๋ฅผ ์„ค๋ช…ํ•  ๋•Œ odd/even ๊ตฌ๋ถ„์ด ์‚ฌ๋ผ์ง€๋ฉด ์„ค๋ช… ๋น„์šฉ์ด ์ค„์–ด๋“ ๋‹ค.

์š”์•ฝ

Node.js๋Š” โ€œ๋งŽ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒฐ๊ตญ LTS๋งŒ ์“ด๋‹คโ€๋Š” ํ˜„์‹ค์„ ๋ฐ˜์˜ํ•ด ๋ฆด๋ฆฌ์Šค ์ „๋žต์„ ๋‹จ์ˆœํ™”ํ–ˆ๋‹ค. ์•ž์œผ๋กœ๋Š” ๋งค๋…„ ํ•˜๋‚˜์˜ ๋ฉ”์ด์ €๊ฐ€ ๋‚˜์˜ค๊ณ , ๊ทธ ๋ฒ„์ „์€ ๋ชจ๋‘ LTS๊ฐ€ ๋œ๋‹ค. ๋Œ€์‹  ์ƒํƒœ๊ณ„ ์กฐ๊ธฐ ๊ฒ€์ฆ์€ Alpha ์ฑ„๋„์ด ๋งก๋Š”๋‹ค.

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

์›๋ฌธ ์ œ๋ชฉ: Frontend Memory Leaks: A 500-Repository Static Analysis and Five-Scenario Benchmark Study ์›๋ฌธ ๋งํฌ: https://stackinsight.dev/blog/memory-leak-empirical-study/ ๋ฒˆ์—ญ์ผ: 2026-03-13 KST

ํ”„๋ก ํŠธ์—”๋“œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜: 500๊ฐœ ์ €์žฅ์†Œ ์ •์  ๋ถ„์„๊ณผ 5๊ฐ€์ง€ ๋ฒค์น˜๋งˆํฌ ์—ฐ๊ตฌ

์ด ๊ธ€์€ React, Vue, Angular ๊ธฐ๋ฐ˜ ์˜คํ”ˆ์†Œ์Šค ์ €์žฅ์†Œ 500๊ฐœ๋ฅผ ์ •์  ๋ถ„์„ํ•ด, ํ”„๋ก ํŠธ์—”๋“œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์‹ค์ œ๋กœ ์–ผ๋งˆ๋‚˜ ํ”ํ•œ์ง€ ์ˆ˜์น˜๋กœ ๋ณด์—ฌ์ค€๋‹ค. ์ €์ž๋Š” AST ๊ธฐ๋ฐ˜ ํƒ์ง€๊ธฐ๋ฅผ ๋งŒ๋“ค์–ด 714,217๊ฐœ ํŒŒ์ผ์„ ์Šค์บ”ํ–ˆ๊ณ , 55,864๊ฐœ์˜ ์ž ์žฌ์  ๋ˆ„์ˆ˜ ํŒจํ„ด์„ ์ฐพ์•˜๋‹ค.

๊ฐ€์žฅ ๋จผ์ € ๊ธฐ์–ตํ•  ํ•ต์‹ฌ ์ˆซ์ž

  • ๋ถ„์„ ๋Œ€์ƒ ์ €์žฅ์†Œ: 500๊ฐœ
  • ์Šค์บ” ํŒŒ์ผ ์ˆ˜: 714,217๊ฐœ
  • ๋ฐœ๊ฒฌ๋œ ๋ˆ„์ˆ˜ ํŒจํ„ด: 55,864๊ฑด
  • ์ตœ์†Œ 1๊ฑด ์ด์ƒ ๋ˆ„์ˆ˜ ํŒจํ„ด์ด ์žˆ๋˜ ์ €์žฅ์†Œ ๋น„์œจ: 86%

์ฆ‰, ํ”„๋ก ํŠธ์—”๋“œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” ์˜ˆ์™ธ์ ์ธ ์‚ฌ๊ณ ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋Œ€ํ˜• ์˜คํ”ˆ์†Œ์Šค ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ๋„ ์•„์ฃผ ํ”ํ•œ ํ’ˆ์งˆ ๋ฌธ์ œ๋ผ๋Š” ๊ฒฐ๋ก ์ด๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” ์™œ GC๊ฐ€ ์žˆ์–ด๋„ ์ƒ๊ธฐ๋‚˜

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์ด ์žˆ์œผ๋‹ˆ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ์‰ฝ๋‹ค. ํ•˜์ง€๋งŒ GC๋Š” โ€œ๊ฐœ๋ฐœ์ž๊ฐ€ ๋” ์ด์ƒ ํ•„์š” ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฐ์ฒดโ€๋ฅผ ์ง€์šฐ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋„๋‹ฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋งŒ ํšŒ์ˆ˜ํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋œ ๋’ค์—๋„ window.addEventListener์— ๋“ฑ๋กํ•œ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋‚จ์•„ ์žˆ์œผ๋ฉด,

window -> listener list -> handler closure -> component state -> data

๊ฐ™์€ ์ฐธ์กฐ ์‚ฌ์Šฌ์ด ์‚ด์•„ ์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ GC ์ž…์žฅ์—์„œ๋Š” ์—ฌ์ „ํžˆ reachable ์ƒํƒœ์ด๋ฏ€๋กœ ์ˆ˜๊ฑฐํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰, ๋ˆ„์ˆ˜๋Š” ๋Ÿฐํƒ€์ž„ ๊ฒฐํ•จ์ด ์•„๋‹ˆ๋ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๊ฐ€ ๋งŒ๋“  ์ฐธ์กฐ ์œ ์ง€ ๋ฒ„๊ทธ๋‹ค.

์–ด๋–ค ํŒจํ„ด์ด ๊ฐ€์žฅ ํ”ํ–ˆ๋‚˜

๋ฐœ๊ฒฌ ๋น„์ค‘ ์ƒ์œ„ ํ•ญ๋ชฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•˜๋‹ค.

  • ํƒ€์ด๋จธ ์ •๋ฆฌ ๋ˆ„๋ฝ: 43.9%
  • ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ํ•ด์ œ ๋ˆ„๋ฝ: 19.0%
  • subscription ์ •๋ฆฌ ๋ˆ„๋ฝ: 13.9%
  • effect cleanup ๋ˆ„๋ฝ: 9.3%
  • watch stop handle ๋ˆ„๋ฝ: 7.1%

๊ฐ€์žฅ ํ”ํ•œ ๋‹จ์ผ ํŒจํ„ด์€ setTimeout์ด์—ˆ๋‹ค. ๋ˆˆ์— ์ž˜ ๋ณด์ด์ง€ ์•Š๊ณ , ๊ธฐ๋Šฅ์€ ๋‹น์žฅ ๋™์ž‘ํ•˜๋ฏ€๋กœ cleanup์„ ๋†“์น˜๊ธฐ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

ํ”„๋ ˆ์ž„์›Œํฌ๋ณ„ ๊ด€์ฐฐ

  • React: 34,787๊ฑด (62.3%)
  • Vue: 15,750๊ฑด (28.2%)
  • Angular: 5,327๊ฑด (9.5%)

React๊ฐ€ ๊ฐ€์žฅ ๋งŽ์€ ์ด์œ ๋Š” ๋‹จ์ˆœ ์ ์œ ์œจ๋ฟ ์•„๋‹ˆ๋ผ, useEffect์˜ cleanup์„ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋Š” ์„ค๊ณ„ ํŠน์„ฑ๋„ ํฌ๋‹ค. โ€œ์˜ฌ๋ฐ”๋ฅธ ํ›…์„ ๊ณจ๋ž์ง€๋งŒ cleanup return์„ ๋นผ๋จน๋Š”โ€ ์‹ค์ˆ˜๊ฐ€ ํŠนํžˆ ๋งŽ์•˜๋‹ค.

Vue์—์„œ๋Š” watch, watchEffect, onMounted ๊ณ„์—ด์˜ ์ •๋ฆฌ ๋ˆ„๋ฝ์ด, Angular์—์„œ๋Š” subscribe ํ›„ unsubscribe ๋˜๋Š” takeUntil ํŒจํ„ด ๋ฏธ์ ์šฉ์ด ์ฃผ์š” ์›์ธ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ๋‹ค.

์‹ค์ œ ๋น„์šฉ์€ ์–ด๋А ์ •๋„์ธ๊ฐ€

์ €์ž๋Š” ๋ˆ„์ˆ˜ ํŒจํ„ด์„ ์ธ์œ„์ ์œผ๋กœ ์‹ฌ์€ 5๊ฐ€์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€ํ•ด, 100ํšŒ mount/unmount์™€ 50ํšŒ ๋ฐ˜๋ณต ์‹คํ—˜์„ ์ˆ˜ํ–‰ํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  GC๋ฅผ ๊ฐ•์ œ๋กœ ๋Œ๋ฆฐ ๋’ค retained heap์„ ์ธก์ •ํ–ˆ๋‹ค.

๊ฒฐ๊ณผ๋Š” ์ผ๊ด€๋๋‹ค.

  • cleanup์ด ์—†์„ ๋•Œ: ์‚ฌ์ดํด๋‹น ์•ฝ 8KB retained heap ์ฆ๊ฐ€
  • cleanup์ด ์žˆ์„ ๋•Œ: ๊ฑฐ์˜ 0์— ๊ฐ€๊นŒ์šด ์ฆ๊ฐ€

8KB๋Š” ์ž‘์•„ ๋ณด์ด์ง€๋งŒ, SPA์—์„œ ํŽ˜์ด์ง€ ์ด๋™ยทํƒญ ์ „ํ™˜ยทํŒจ๋„ ํ† ๊ธ€์ด ์ˆ˜๋ฐฑ ๋ฒˆ ์Œ“์ด๋ฉด ๊ธˆ๋ฐฉ ์ฒด๊ฐ ์„ฑ๋Šฅ ์ €ํ•˜๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

์‹ค๋ฌด์šฉ 30๋ถ„ ์•ก์…˜ ํ”Œ๋žœ

๊ธ€์ด ์ œ์‹œํ•œ ์šฐ์„  ์ ๊ฒ€ ํฌ์ธํŠธ๋Š” ๋งค์šฐ ํ˜„์‹ค์ ์ด๋‹ค.

React

๋‹ค์Œ ํ˜ธ์ถœ์ด ์žˆ๋Š” useEffect๋ฅผ ๋จผ์ € ํ›‘์–ด๋ณธ๋‹ค.

  • addEventListener
  • setInterval
  • setTimeout
  • .subscribe()

์ด๋•Œ return () => ... cleanup์ด ์—†์œผ๋ฉด ์šฐ์„  ์˜์‹ฌํ•ด์•ผ ํ•œ๋‹ค.

Vue

  • onMounted์— ๋Œ€์‘ํ•˜๋Š” onUnmounted๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
  • watch, watchEffect์˜ stop handle์„ ์žก๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ
  • ์ˆ˜๋™ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ œ๊ฑฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ

Angular

  • .subscribe()์— ๋Œ€ํ•ด unsubscribe() ๋˜๋Š” takeUntil ํŒจํ„ด์ด ์žˆ๋Š”์ง€ ํ™•์ธ
  • ngOnDestroy ๊ตฌํ˜„ ์—ฌ๋ถ€ ํ™•์ธ
  • Renderer2.listen() ๋ฐ˜ํ™˜ ํ•ธ๋“ค ์ •๋ฆฌ ์—ฌ๋ถ€ ํ™•์ธ

FE ํŒ€์— ์ฃผ๋Š” ์‹œ์‚ฌ์ 

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

ํŠนํžˆ ์˜๋ฏธ ์žˆ๋Š” ํฌ์ธํŠธ๋Š” ๋‹ค์Œ ์…‹์ด๋‹ค.

  1. ํƒ€์ด๋จธ์™€ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์••๋„์ ์ด๋‹ค
    ๋ณต์žกํ•œ ์˜ต์ €๋ฒ„๋ณด๋‹ค ๊ธฐ๋ณธ API cleanup๋ถ€ํ„ฐ ์žก๋Š” ํŽธ์ด ROI๊ฐ€ ๋†’๋‹ค.

  2. ์žฅ์‹œ๊ฐ„ ์„ธ์…˜ ์ œํ’ˆ์ผ์ˆ˜๋ก ๋” ์น˜๋ช…์ ์ด๋‹ค
    ๋Œ€์‹œ๋ณด๋“œ, ํ˜‘์—…๋„๊ตฌ, ํ™”์ƒํšŒ์˜, admin panel์ฒ˜๋Ÿผ ์˜ค๋žซ๋™์•ˆ ์—ด์–ด๋‘๋Š” ์•ฑ์€ ๋ˆ„์ˆ˜๊ฐ€ ์„ฑ๋Šฅ ๋ฌธ์ œ๋กœ ๋ฐ”๋กœ ๋‚˜ํƒ€๋‚œ๋‹ค.

  3. ์ •์  ๋ถ„์„ ๊ทœ์น™ ์ž๋™ํ™” ๊ฐ€์น˜๊ฐ€ ํฌ๋‹ค
    ESLint custom rule, codemod, PR ์ฒดํฌ๋กœ ์ƒ๋‹น์ˆ˜ ๋ฌธ์ œ๋ฅผ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฒฐ๋ก 

ํ”„๋ก ํŠธ์—”๋“œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” ๋“œ๋ฌธ ์˜ˆ์™ธ๊ฐ€ ์•„๋‹ˆ๋ผ, ์ž˜ ๊ด€๋ฆฌ๋˜๋Š” ๋Œ€ํ˜• ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ๋„ ๋งค์šฐ ํ”ํ•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์›์ธ์€ ๋Œ€๊ฐœ ๊ฑฐ์ฐฝํ•˜์ง€ ์•Š๋‹ค. useEffect์˜ cleanup ํ•œ ์ค„, unsubscribe ํ•œ ์ค„, removeEventListener ํ•œ ์ค„์ด ๋น ์ง„ ๊ฒฝ์šฐ๊ฐ€ ๋Œ€๋ถ€๋ถ„์ด๋‹ค.

์‹ค๋ฌด์—์„œ๋Š” โ€œ์–ด๋–ค ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์“ฐ๋А๋ƒโ€๋ณด๋‹ค๋„, ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŒ๋“  ์ฝ”๋“œ๊ฐ€ ๋ฆฌ์†Œ์Šค๋ฅผ ์ •๋ฆฌํ•˜๋Š”์ง€๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ๊ฒ€ํ† ํ•˜๋Š” ๋ฌธํ™”๊ฐ€ ๋” ์ค‘์š”ํ•˜๋‹ค. ์ด ๊ธ€์€ ๊ทธ ์‚ฌ์‹ค์„ ์ˆซ์ž๋กœ ์„ค๋“ํ•ด ์ค€๋‹ค.

์›๋ฌธ ์ œ๋ชฉ: CSP Failure in Rails And What It Taught Us About Security ์›๋ฌธ ๋งํฌ: https://syndicode.com/blog/csp-failure-rails/ ๋ฒˆ์—ญ์ผ: 2026-03-13 KST

Rails์˜ CSP ์‹คํŒจ๊ฐ€ ์•Œ๋ ค์ค€ ๊ฒƒ: ๋ณด์•ˆ ์„ค์ •์€ UI ๋™์ž‘๊นŒ์ง€ ํฌํ•จํ•œ๋‹ค

์ด ๊ธ€์€ Ruby on Rails + ActiveAdmin ๊ธฐ๋ฐ˜ ๋‚ด๋ถ€ ๋„๊ตฌ์—์„œ, ์—„๊ฒฉํ•œ Content Security Policy(CSP) ์„ค์ • ๋•Œ๋ฌธ์— ์ธ๋ผ์ธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์กฐ์šฉํžˆ ๋ง‰ํ˜€ ๊ด€๋ฆฌ์ž ํผ์ด ๋™์ž‘ํ•˜์ง€ ์•Š๊ฒŒ ๋œ ์‚ฌ๋ก€๋ฅผ ๋‹ค๋ฃฌ๋‹ค. ํ™”๋ฉด์€ ๋ฉ€์ฉกํ•ด ๋ณด์˜€์ง€๋งŒ ์‹ค์ œ ์—…๋ฌด ํ”Œ๋กœ์šฐ๋Š” ๋ง๊ฐ€์กŒ๊ณ , ์›์ธ์€ ๋ˆ„๋ฝ๋œ CSP nonce์˜€๋‹ค.

์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‚˜

๊ฒ‰์œผ๋กœ๋Š” ๋ฐฐํฌ๋„ ์ •์ƒ, UI๋„ ์ •์ƒ์ฒ˜๋Ÿผ ๋ณด์˜€๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๋‹ค์Œ ๊ธฐ๋Šฅ๋“ค์ด ๋ฉˆ์ท„๋‹ค.

  • ์กฐ๊ฑด์— ๋”ฐ๋ผ ํ•„๋“œ show/hide
  • ํผ validation ํŠธ๋ฆฌ๊ฑฐ
  • ์„ ํƒ ์ƒํƒœ์— ๋”ฐ๋ผ ํ™œ์„ฑํ™”๋˜๋Š” ๋ฒ„ํŠผ ๋กœ์ง

๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†”์—๋Š” ๋‹ค์Œ ๋ฉ”์‹œ์ง€๋งŒ ๋‚จ์•˜๋‹ค.

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'"

์ฆ‰, script-src 'self'๋งŒ ํ—ˆ์šฉํ•œ ์ƒํƒœ์—์„œ nonce๋‚˜ hash๊ฐ€ ์—†์œผ๋‹ˆ, ๋ธŒ๋ผ์šฐ์ €๋Š” ๋ชจ๋“  ์ธ๋ผ์ธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ฐจ๋‹จํ–ˆ๋‹ค.

์™œ ์ด๋Ÿฐ ์ผ์ด ์ƒ๊ธฐ๋‚˜

CSP๋Š” XSS ๋ฐฉ์–ด๋ฅผ ์œ„ํ•ด ์ธ๋ผ์ธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์œ„ํ—˜ํ•œ ๊ฒƒ์œผ๋กœ ๋ณธ๋‹ค. ๋”ฐ๋ผ์„œ ์„œ๋ฒ„๊ฐ€ โ€œ์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ๋‚ด๊ฐ€ ์˜๋„์ ์œผ๋กœ ๋ Œ๋”๋งํ•œ ์ •์ƒ ์ฝ”๋“œโ€๋ผ๋Š” ์ฆ๊ฑฐ๋ฅผ ์ฃผ์ง€ ์•Š์œผ๋ฉด ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค.

๊ทธ ์ฆ๊ฑฐ๊ฐ€ ๋ฐ”๋กœ nonce๋‹ค.

nonce๋Š” ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๋Š” ๋žœ๋ค ๊ฐ’์œผ๋กœ,

  • ์‘๋‹ต ํ—ค๋”์˜ CSP script-src ์ง€์‹œ๋ฌธ
  • ์‹ค์ œ <script> ํƒœ๊ทธ ์†์„ฑ

์–‘์ชฝ์— ๋™์ผํ•˜๊ฒŒ ๋“ค์–ด๊ฐ€์•ผ ํ•œ๋‹ค. ์ด ๊ฐ’์ด ์ผ์น˜ํ•  ๋•Œ๋งŒ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•ด๋‹น ์ธ๋ผ์ธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

์ด ์‚ฌ๋ก€์—์„œ๋Š” Rails ์•ฑ์ด ์—„๊ฒฉํ•œ CSP๋Š” ์ผœ ๋‘์—ˆ์ง€๋งŒ, nonce ์ƒ์„ฑ๊ณผ ์ฃผ์ž…์€ ๋น ์ ธ ์žˆ์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ณด์•ˆ ์ •์ฑ…์€ ๊ฐ•ํ–ˆ์ง€๋งŒ, ๊ตฌํ˜„์€ ๋ถˆ์™„์ „ํ–ˆ๊ณ  ๊ฒฐ๊ณผ์ ์œผ๋กœ ํ•ฉ๋ฒ•์ ์ธ ํ”„๋ŸฐํŠธ์—”๋“œ ๋™์ž‘๊นŒ์ง€ ๋ง‰์•„ ๋ฒ„๋ ธ๋‹ค.

๊ฒ€ํ† ํ•œ ํ•ด๊ฒฐ์ฑ…๋“ค

๊ธ€์€ ๋„ค ๊ฐ€์ง€ ์„ ํƒ์ง€๋ฅผ ๋น„๊ตํ•œ๋‹ค.

A. ์ธ๋ผ์ธ JS๋ฅผ ์ „๋ถ€ ์™ธ๋ถ€ ํŒŒ์ผ๋กœ ์ด๋™

  • ์žฅ์ : ๊ฐ€์žฅ ๊น”๋”ํ•˜๊ณ  ๋ณด์•ˆ์ ์œผ๋กœ ๊ฐ•ํ•จ
  • ๋‹จ์ : ActiveAdmin ๊ตฌ์กฐ์ƒ ๋ฆฌํŒฉํ„ฐ๋ง ๋น„์šฉ์ด ํผ

B. unsafe-inline ํ—ˆ์šฉ

  • ์žฅ์ : ์ฆ‰์‹œ ๋™์ž‘ ๋ณต๊ตฌ
  • ๋‹จ์ : CSP์˜ ํ•ต์‹ฌ ๋ชฉ์ ์„ ์‚ฌ์‹ค์ƒ ํฌ๊ธฐ

C. ์ธ๋ผ์ธ ์Šคํฌ๋ฆฝํŠธ๋งˆ๋‹ค hash ๋“ฑ๋ก

  • ์žฅ์ : ๋ณด์•ˆ์ ์œผ๋กœ ํ—ˆ์šฉ ๊ฐ€๋Šฅ
  • ๋‹จ์ : ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์กฐ๊ธˆ๋งŒ ๋ฐ”๋€Œ์–ด๋„ hash๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด์•ผ ํ•ด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์ทจ์•ฝํ•จ

D. nonce ๋ฐฉ์‹ ์ฑ„ํƒ

  • ์žฅ์ : ์•ˆ์ „์„ฑ๊ณผ ๊ฐœ๋ฐœ ํŽธ์˜์„ฑ์˜ ๊ท ํ˜•์ด ๊ฐ€์žฅ ์ข‹์Œ
  • ๋‹จ์ : ์š”์ฒญ๋ณ„ nonce ์ƒ์„ฑ/์ „๋‹ฌ ๊ตฌํ˜„ ํ•„์š”

์ตœ์ข… ์„ ํƒ์€ D์˜€๋‹ค.

์‹ค์ œ ๊ตฌํ˜„ ์š”์•ฝ

Rails์—์„œ ํ•œ ์ผ์€ ํฌ๊ฒŒ ์„ธ ๋‹จ๊ณ„๋‹ค.

  1. ์š”์ฒญ๋งˆ๋‹ค nonce ์ƒ์„ฑ
  2. CSP ํ—ค๋”์˜ script-src์— nonce ๋ฐ˜์˜
  3. ActiveAdmin์ด ๋ Œ๋”๋งํ•˜๋Š” ์ธ๋ผ์ธ <script> ํƒœ๊ทธ์—๋„ ๋™์ผ nonce ๋ถ€์—ฌ

์˜ˆ๋ฅผ ๋“ค์–ด content_security_policy_nonce_generator๋กœ nonce๋ฅผ ๋งŒ๋“ค๊ณ , ๋ทฐ์—์„œ script nonce: csp_nonce do ... end ํ˜•ํƒœ๋กœ ์—ฐ๊ฒฐํ•œ๋‹ค.

์ดํ›„ ํ™•์ธํ•œ ๊ฒฐ๊ณผ:

  • ์‘๋‹ต ํ—ค๋”์— nonce๊ฐ€ ํฌํ•จ๋˜๊ณ 
  • ์ธ๋ผ์ธ ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ์—๋„ ๊ฐ™์€ nonce๊ฐ€ ๋“ค์–ด๊ฐ€๋ฉฐ
  • ๋™์  ํผ ๋™์ž‘์ด ๋ณต๊ตฌ๋˜๊ณ 
  • unsafe-inline ์—†์ด๋„ ์—„๊ฒฉํ•œ CSP๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค

FE ์‹ค๋ฌด์— ์™œ ์ค‘์š”ํ•œ๊ฐ€

์ด ์‚ฌ๋ก€๋Š” Rails ์ „์šฉ ํŒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ, ํ”„๋ก ํŠธ์—”๋“œ ์ „๋ฐ˜์— ์ ์šฉ๋˜๋Š” ๊ตํ›ˆ์ด ์žˆ๋‹ค.

1) ๋ณด์•ˆ ์„ค์ •์€ ๊ธฐ๋Šฅ ๋™์ž‘์˜ ์ผ๋ถ€๋‹ค

CSP๋Š” ์ธํ”„๋ผ ์„ค์ •์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ๋ธŒ๋ผ์šฐ์ € ๋Ÿฐํƒ€์ž„ ๊ทœ์น™์ด๋‹ค. ์ฆ‰, UI๊ฐ€ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ ๊ฒฝ๋กœ๋ฅผ ์ง์ ‘ ๋ฐ”๊พผ๋‹ค.

2) โ€œํ™”๋ฉด์ด ๋ณด์ธ๋‹คโ€๋Š” ๊ฒƒ์€ โ€œ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•œ๋‹คโ€์™€ ๋‹ค๋ฅด๋‹ค

์ด ์‚ฌ๋ก€์ฒ˜๋Ÿผ UI๋Š” ๋ฉ€์ฉกํ•œ๋ฐ JS๋งŒ ์กฐ์šฉํžˆ ์ฐจ๋‹จ๋  ์ˆ˜ ์žˆ๋‹ค. ์‹œ๊ฐ์  ์Šค๋ƒ…์ƒท ํ…Œ์ŠคํŠธ๋งŒ์œผ๋กœ๋Š” ๋†“์น˜๊ธฐ ์‰ฝ๋‹ค.

3) E2E ํ…Œ์ŠคํŠธ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค

๋™์  ํผ, ์ธ๋ผ์ธ hydration, ์„œ๋“œํŒŒํ‹ฐ ์‚ฝ์ž… ์Šคํฌ๋ฆฝํŠธ, CMS ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋Š” CSP ๋ณ€ํ™”์— ์ทจ์•ฝํ•˜๋‹ค. ๊ธฐ๋Šฅ ์ค‘์‹ฌ์˜ ๋ธŒ๋ผ์šฐ์ € ํ…Œ์ŠคํŠธ๋ฅผ ๋‘์ง€ ์•Š์œผ๋ฉด ํšŒ๊ท€๋ฅผ ์žก๊ธฐ ์–ด๋ ต๋‹ค.

4) nonce/hash/์™ธ๋ถ€ ์Šคํฌ๋ฆฝํŠธ ์ „๋žต์„ ์ผ์ฐ ์ •ํ•ด์•ผ ํ•œ๋‹ค

์•ฑ์ด ์ปค์ง„ ๋’ค CSP๋ฅผ ๋’ค๋Šฆ๊ฒŒ ๊ฐ•ํ™”ํ•˜๋ฉด ์ด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ๋Œ€๋Ÿ‰์œผ๋กœ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์ดˆ๊ธฐ์— ์–ด๋А ํŒจํ„ด์„ ํ‘œ์ค€์œผ๋กœ ์‚ผ์„์ง€ ์ •ํ•ด ๋‘๋Š” ํŽธ์ด ๋‚ซ๋‹ค.

๊ฒฐ๋ก 

๋ณด์•ˆ์€ ์ œํ’ˆ ๋ฐ”๊นฅ์˜ ๋ณ„๋„ ๋ ˆ์ด์–ด๊ฐ€ ์•„๋‹ˆ๋‹ค. ์ž˜๋ชป๋œ CSP๋Š” XSS๋ฅผ ๋ง‰๋Š” ๋™์‹œ์— ํ•ฉ๋ฒ•์ ์ธ UI ๋™์ž‘๋„ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ธ€์˜ ํ•ต์‹ฌ์€ โ€œCSP๋ฅผ ์•ฝํ•˜๊ฒŒ ํ’€์žโ€๊ฐ€ ์•„๋‹ˆ๋ผ, ์—„๊ฒฉํ•œ ๋ณด์•ˆ ๊ทœ์น™๋„ ์ œํ’ˆ ๋™์ž‘๊ณผ ํ•จ๊ป˜ ์„ค๊ณ„ยทํ…Œ์ŠคํŠธํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ฐ ์žˆ๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ ํŒ€์ด๋ผ๋ฉด CSP ๋ณ€๊ฒฝ ์‹œ ๋‹ค์Œ ์„ธ ๊ฐ€์ง€๋ฅผ ๊ฐ™์ด ๋ด์•ผ ํ•œ๋‹ค.

  • ์–ด๋–ค ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹ค์ œ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š”๊ฐ€
  • ๊ทธ ์‹คํ–‰ ๊ฒฝ๋กœ๊ฐ€ nonce/hash ์ •์ฑ…๊ณผ ๋งž๋Š”๊ฐ€
  • ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ทธ ํšŒ๊ท€๋ฅผ ์žก์„ ์ˆ˜ ์žˆ๋Š”๊ฐ€

์ด ์…‹์„ ํ•จ๊ป˜ ๋‹ค๋ฃจ์ง€ ์•Š์œผ๋ฉด, ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ํ–ˆ๋Š”๋ฐ๋„ ์ œํ’ˆ ํ’ˆ์งˆ์€ ์˜คํžˆ๋ ค ๋‚˜๋น ์งˆ ์ˆ˜ ์žˆ๋‹ค.

์›๋ฌธ ์ œ๋ชฉ: Nexus KB Announcement ์›๋ฌธ ๋งํฌ: https://nexus-kb.com/blog/nexus-kb-announcement/ ๋ฒˆ์—ญ์ผ: 2026-03-13 KST

๋ฆฌ๋ˆ…์Šค ์ปค๋„ ๋ฉ”์ผ๋ง๋ฆฌ์ŠคํŠธ ํ”„๋ŸฐํŠธ์—”๋“œ๋ฅผ ๋‹ค์‹œ ๋งŒ๋“  ์ด์œ 

Nexus KB ํŒ€์€ ๋ฆฌ๋ˆ…์Šค ์ปค๋„ ๋ฉ”์ผ๋ง๋ฆฌ์ŠคํŠธ ์•„์นด์ด๋ธŒ๋ฅผ ๋” ์ž˜ ํƒ์ƒ‰ํ•˜๊ธฐ ์œ„ํ•ด ์ž์ฒด ํ”„๋ŸฐํŠธ์—”๋“œ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ๊ธฐ์กด ๋„๊ตฌ์ธ LWN๊ณผ lore.kernel.org๋„ ์œ ์šฉํ–ˆ์ง€๋งŒ, ์Šค๋ ˆ๋“œ ํƒ์ƒ‰๊ณผ ํŒจ์น˜ ์‹œ๋ฆฌ์ฆˆ ๋น„๊ต์— ํด๋ฆญ ์ˆ˜๊ฐ€ ๋งŽ๊ณ  ์ •๋ณด ๊ตฌ์กฐ๊ฐ€ ์‚ฌ์šฉ์ž ์‚ฌ๊ณ  ํ๋ฆ„๊ณผ ์™„์ „ํžˆ ๋งž์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ฌธ์ œ๋ฅผ ๋А๊ผˆ๋‹ค๊ณ  ํ•œ๋‹ค.

์ถœ๋ฐœ์ : โ€œ์ด๊ฑด ๊ฒ€์ƒ‰์ด ์•„๋‹ˆ๋ผ ํƒ์ƒ‰ ๋ฌธ์ œ๋‹คโ€

์ปค๋„ ๋ฉ”์ผ๋ง๋ฆฌ์ŠคํŠธ๋Š” ๋‹จ์ˆœํ•œ ๊ฒŒ์‹œํŒ์ด ์•„๋‹ˆ๋ผ,

  • ๊ธด ์Šค๋ ˆ๋“œ ๊ตฌ์กฐ
  • ์—ฌ๋Ÿฌ ๋ฒ„์ „์œผ๋กœ ์ด์–ด์ง€๋Š” ํŒจ์น˜ ์‹œ๋ฆฌ์ฆˆ
  • ์ปค๋ฒ„๋ ˆํ„ฐ, base commit, diff ๋น„๊ต
  • ์ˆ˜๋งŽ์€ ํ•˜์œ„ ๋ฆฌ์ŠคํŠธ์™€ ์•„์นด์ด๋ธŒ

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

๊ทธ๋ž˜์„œ ์ด ํŒ€์€ ๋‹จ์ˆœํžˆ โ€œ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅํ•œ ์•„์นด์ด๋ธŒโ€๊ฐ€ ์•„๋‹ˆ๋ผ, ์Šค๋ ˆ๋“œ์™€ ํŒจ์น˜ ๊ณ„๋ณด(lineage)๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ์ง€์‹ ๋ฒ ์ด์Šคํ˜• UI๋ฅผ ๋งŒ๋“ค๊ฒ ๋‹ค๋Š” ๋ชฉํ‘œ๋ฅผ ์„ธ์› ๋‹ค.

์š”๊ตฌ์‚ฌํ•ญ์€ ์˜์™ธ๋กœ ํ”„๋ก ํŠธ์—”๋“œ์ ์ด๋‹ค

์ด ๊ธ€์—์„œ ํŠนํžˆ ํฅ๋ฏธ๋กœ์šด ๋ถ€๋ถ„์€, ๋ฌธ์ œ ์ •์˜๊ฐ€ ์•„์ฃผ FE์Šค๋Ÿฝ๋‹ค๋Š” ์ ์ด๋‹ค.

ํ•ต์‹ฌ ์š”๊ตฌ์‚ฌํ•ญ:

  • ์Šค๋ ˆ๋“œ๋ฅผ ์ค‘์ฒฉ ๊ตฌ์กฐ๋กœ ์‹œ๊ฐํ™”ํ•˜๊ณ  ์ ‘๊ณ  ํŽผ์น  ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ
  • ํŒจ์น˜ ์‹œ๋ฆฌ์ฆˆ์˜ ๋ฒ„์ „ ๊ด€๊ณ„๋ฅผ ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ
  • ๊ฒ€์ƒ‰์€ ๋น ๋ฅด๋˜, ๋‹จ์ˆœ ํ‚ค์›Œ๋“œ ๊ฒฐ๊ณผ๊ฐ€ ์•„๋‹ˆ๋ผ ์ฝ๊ธฐ ํ๋ฆ„์„ ์ง€์›ํ•ด์•ผ ํ•จ
  • ์ •๋ณด ๋ฐ€๋„๊ฐ€ ๋†’์•„๋„ ์‚ฌ์šฉ์ž๊ฐ€ ๋งฅ๋ฝ์„ ์žƒ์ง€ ์•Š์•„์•ผ ํ•จ
  • ์–ด๋‘์šด ํ…Œ๋งˆ, ๋น„๊ต ๋ทฐ ๋“ฑ ์‹ค์ œ ํƒ์ƒ‰ ๋„๊ตฌ๋‹ค์šด UI๊ฐ€ ํ•„์š”ํ•จ

์ฆ‰, ์ด๊ฒƒ์€ ์˜ˆ์œ ์›น์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋ณต์žกํ•œ ์ „๋ฌธ ์ž‘์—…์„ ๋•๋Š” ์ •๋ณด ์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„ ๋ฌธ์ œ๋‹ค.

๊ตฌํ˜„ ๊ณผ์ •์—์„œ ๋ฐฐ์šด ๊ฒƒ

์ดˆ๊ธฐ ํ”„๋กœํ† ํƒ€์ž…์€ Tauri ๊ธฐ๋ฐ˜์œผ๋กœ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค์—ˆ์ง€๋งŒ, ๋ฉ”์ผ ํŒŒ์‹ฑยท์Šค๋ ˆ๋”ฉยทํŒจ์น˜ ๊ณ„๋ณด ์—ฐ๊ฒฐ์ด ์ƒ๊ฐ๋ณด๋‹ค ํ›จ์”ฌ ๊นŒ๋‹ค๋กœ์› ๋‹ค. ํŠนํžˆ ๋ฉ”์ผ ํฌ๋งท์€ ์ž…๋ ฅ์ด ๊ณ ๋ฅด์ง€ ์•Š๊ณ , ์‹ค์ œ ์ปค๋„ ์›Œํฌํ”Œ๋กœ๋Š” ์ผ๋ฐ˜์ ์ธ ํฌ๋Ÿผ ๊ตฌ์กฐ๋ณด๋‹ค ํ›จ์”ฌ ๋ณต์žกํ•˜๋‹ค.

ํ”„๋กœ์ ํŠธ๋Š” ๊ฒฐ๊ตญ ์„ธ ๋ฒˆ์งธ ์žฌ์ž‘์„ฑ ๋์— ๊ตฌ์กฐ๋ฅผ ์ •๋ฆฌํ–ˆ๋‹ค.

์ฃผ์š” ์•„ํ‚คํ…์ฒ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • Postgres: ์ง„์‹ค์˜ ์›์ฒœ ์ €์žฅ์†Œ
  • Meilisearch: ๋น ๋ฅธ ๊ฒ€์ƒ‰
  • Axum API ์„œ๋ฒ„: ์ฝ๊ธฐ ์ค‘์‹ฌ API
  • Rust ์›Œ์ปค: ์ˆ˜์ง‘, ์Šค๋ ˆ๋”ฉ, ๊ณ„๋ณด ์ถ”์ถœ, ์ธ๋ฑ์‹ฑ

์ค‘์š”ํ•œ ์ ์€ โ€œ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋“  ๊ฑธ ๊ณ„์‚ฐํ•˜๋Š” ๊ฑฐ๋Œ€ํ•œ ์‹œ์Šคํ…œโ€์ด ์•„๋‹ˆ๋ผ, ์š”์ฒญ ๊ฒฝ๋กœ์—์„œ ๋ฌด๊ฑฐ์šด ์ฒ˜๋ฆฌ๋ฅผ ์ตœ๋Œ€ํ•œ ๋นผ๋Š” ๊ตฌ์กฐ๋ฅผ ํƒํ–ˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ’€์—ˆ๋‚˜

์ฒ˜์Œ LKML ์ „์ฒด ingest๋Š” 2์ผ ์ด์ƒ ๊ฑธ๋ ธ๋‹ค๊ณ  ํ•œ๋‹ค. ์›์ธ์€ ๊ฐ™์€ ๋ฉ”์‹œ์ง€ ๋ณธ๋ฌธ์„ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์—์„œ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ฝ๊ณ  DB์— ์••๋ฐ•์„ ์ค€ ๊ฒƒ์ด์—ˆ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํŒ€์€ ๋‹ค์Œ์ฒ˜๋Ÿผ ๋ฐ”๊ฟจ๋‹ค.

  • ๋ณธ๋ฌธ์ด ํ•„์š”ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ingest ๋‹จ๊ณ„์—์„œ ์ตœ๋Œ€ํ•œ ๋๋ƒ„
  • ์ดํ›„ threading/lineage ๋‹จ๊ณ„๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์™€ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ฒ˜๋ฆฌ
  • ์š”์ฒญ ํ•ธ๋“ค๋Ÿฌ๋Š” ์ฝ๊ธฐ ์ค‘์‹ฌ์œผ๋กœ ๋‹จ์ˆœํ™”
  • lexical search๊ฐ€ ์ค€๋น„๋˜๋ฉด ๋จผ์ € ๋ณด์—ฌ์ฃผ๊ณ , embedding ๊ฐ™์€ ๋ฌด๊ฑฐ์šด enrichment๋Š” ๋‚˜์ค‘์— ๋”ฐ๋ผ์˜ค๊ฒŒ ํ•จ

์ด ์ „๋žต์€ ํ”„๋ก ํŠธ์—”๋“œ ์ œํ’ˆ์—์„œ๋„ ์ต์ˆ™ํ•˜๋‹ค. ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ฐ€์น˜ ์žˆ๋Š” ์ฒซ ๊ฒฐ๊ณผ๋ฅผ ๋นจ๋ฆฌ ์ œ๊ณตํ•˜๊ณ , ๊ณ ๋น„์šฉ ํ›„์ฒ˜๋ฆฌ๋Š” ์ง€์—ฐ์‹œํ‚ค๋Š” ๋ฐฉ์‹์ด๋‹ค.

UI๊ฐ€ ์ง„์งœ ์ œํ’ˆ์ด ๋˜๋Š” ์ง€์ 

Nexus KB์˜ ํ•ต์‹ฌ ํ™”๋ฉด์€ โ€œseries workspaceโ€๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ํŒจ์น˜ ์‹œ๋ฆฌ์ฆˆ๋ฅผ ๋‹จ์ˆœํ•œ ์ด๋ฉ”์ผ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ๋‹ค์Œ ์š”์†Œ๋“ค์˜ ์กฐํ•ฉ์œผ๋กœ ๋ณด์—ฌ์ค€๋‹ค.

  • ์ž‘์„ฑ์ž, ๋ฆฌ์ŠคํŠธ, base commit ๊ฐ™์€ fact row
  • ๊ฐ€๋กœ revision ํƒญ ์ŠคํŠธ๋ฆฝ
  • ํŒจ์น˜์…‹ ์ฝ๊ธฐ ๋ทฐ
  • diff ๋ทฐ
  • ๋ฒ„์ „ ๊ฐ„ compare ๋ทฐ
  • ์ปค๋ฒ„๋ ˆํ„ฐ ๊ณ ์ • ๋…ธ์ถœ

์ด ๊ตฌ์„ฑ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ œ๋กœ ๊ถ๊ธˆํ•ดํ•˜๋Š” ์งˆ๋ฌธโ€”โ€œ์ด๋ฒˆ v7์€ v6๊ณผ ๋ญ๊ฐ€ ๋‹ฌ๋ผ์กŒ๋‚˜?โ€, โ€œ์ด ์Šค๋ ˆ๋“œ์˜ ์–ด๋А ๋‹ต๊ธ€์ด ํ•ต์‹ฌ์ธ๊ฐ€?โ€โ€”์— ๋งž์ถฐ์ง„๋‹ค. ์ •๋ณด ์„ค๊ณ„๊ฐ€ ๋„๋ฉ”์ธ ์›Œํฌํ”Œ๋กœ์™€ ์ •๋ ฌ๋  ๋•Œ UI๋Š” ๊ฒ€์ƒ‰ ํ™”๋ฉด์ด ์•„๋‹ˆ๋ผ ์ž‘์—… ๋„๊ตฌ๊ฐ€ ๋œ๋‹ค.

FE ์‹ค๋ฌด์— ์ฃผ๋Š” ๊ตํ›ˆ

์ด ๊ธ€์€ ์ „ํ˜•์ ์ธ ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ ํŒ ๊ธ€์€ ์•„๋‹ˆ์ง€๋งŒ, ์‹ค๋ฌด์ ์œผ๋กœ ๋ฐฐ์šธ ์ ์ด ๋งŽ๋‹ค.

1) UI ๋ฌธ์ œ๋Š” ์ข…์ข… ์ •๋ณด ๊ตฌ์กฐ ๋ฌธ์ œ๋‹ค

ํƒ์ƒ‰์ด ๋ถˆํŽธํ•œ ์ด์œ ๊ฐ€ ์Šคํƒ€์ผ ๋ถ€์กฑ์ด ์•„๋‹ˆ๋ผ, ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ์šฉ์ž์˜ ์‚ฌ๊ณ  ํ๋ฆ„๊ณผ ๋‹ค๋ฅด๊ฒŒ ๊ตฌ์กฐํ™”๋ผ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ผ ์ˆ˜ ์žˆ๋‹ค.

2) ์ „๋ฌธ ๋„๊ตฌ UI๋Š” โ€œ๋ฐ€๋„โ€๋ฅผ ๋‘๋ ค์›Œํ•˜๋ฉด ์•ˆ ๋œ๋‹ค

์ผ๋ฐ˜ ์†Œ๋น„์ž ์•ฑ๊ณผ ๋‹ฌ๋ฆฌ, ์ „๋ฌธ๊ฐ€์šฉ ๋„๊ตฌ๋Š” ์ •๋ณด๋ฅผ ๋œ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ๋ณด๋‹ค ๋งฅ๋ฝ ์žˆ๊ฒŒ ๋งŽ์ด ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ด ๋” ์ค‘์š”ํ•  ์ˆ˜ ์žˆ๋‹ค.

3) ๋ฐฑ์—”๋“œ ํŒŒ์ดํ”„๋ผ์ธ ์„ค๊ณ„๊ฐ€ FE ๊ฒฝํ—˜์„ ๊ฒฐ์ •ํ•œ๋‹ค

์Šค๋ ˆ๋”ฉ, ๊ณ„๋ณด ์ถ”์ถœ, ๊ฒ€์ƒ‰ ์ธ๋ฑ์‹ฑ ์ˆœ์„œ ๊ฐ™์€ ์„œ๋ฒ„ ์„ค๊ณ„๊ฐ€ ๊ณง ํƒ์ƒ‰ ์†๋„์™€ ์ธํ„ฐ๋ž™์…˜ ํ’ˆ์งˆ๋กœ ์ด์–ด์ง„๋‹ค. ์ข‹์€ FE๋Š” ์ข…์ข… ์ข‹์€ ๋ฐ์ดํ„ฐ ์ค€๋น„ ํŒŒ์ดํ”„๋ผ์ธ ์œ„์—์„œ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.

4) โ€œํ•œ ๋ฒˆ ๋œ ํด๋ฆญํ•˜๊ฒŒ ํ•˜๊ธฐโ€๋Š” ํฐ ๊ฐ€์น˜๋‹ค

๊ธ€์˜ ์ถœ๋ฐœ์ ๋„ ๊ฒฐ๊ตญ ์ด๊ฒƒ์ด๋‹ค. ๋งํฌ ๋ช‡ ๋ฒˆ์„ ๋œ ๋ˆ„๋ฅด๊ฒŒ ๋งŒ๋“œ๋Š” ์ผ์€ ์‚ฌ์†Œํ•ด ๋ณด์—ฌ๋„, ๊ณ ๋นˆ๋„ ๋ถ„์„ ์ž‘์—…์—์„œ๋Š” ์ƒ์‚ฐ์„ฑ์„ ํฌ๊ฒŒ ๋ฐ”๊พผ๋‹ค.

๊ฒฐ๋ก 

Nexus KB๋Š” ๋ฉ”์ผ๋ง๋ฆฌ์ŠคํŠธ ์œ„์— ์›น ํ”„๋ŸฐํŠธ์—”๋“œ๋ฅผ ์–น์€ ํ”„๋กœ์ ํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ, ํŠน์ • ๋„๋ฉ”์ธ์˜ ํƒ์ƒ‰ ํ–‰์œ„๋ฅผ ๋‹ค์‹œ ์„ค๊ณ„ํ•œ ์‚ฌ๋ก€๋‹ค. FE ๊ด€์ ์—์„œ ๋ณด๋ฉด ์ด ๊ธ€์€ โ€œ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ์™€ ์›Œํฌํ”Œ๋กœ๋ฅผ ์–ด๋–ป๊ฒŒ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ฒˆ์—ญํ•  ๊ฒƒ์ธ๊ฐ€โ€์— ๋Œ€ํ•œ ์ข‹์€ ์ฐธ๊ณ  ์ž๋ฃŒ๋‹ค.

์‹ค๋ฌด ์ œํ’ˆ์—์„œ๋„ ๊ฐ™์€ ์งˆ๋ฌธ์„ ๋˜์งˆ ์ˆ˜ ์žˆ๋‹ค.

  • ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ œ๋กœ ์ถ”์ ํ•˜๋Š” ๊ฐ์ฒด๋Š” ๋ฌด์—‡์ธ๊ฐ€?
  • ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ณด๋‹ค ๋” ์ค‘์š”ํ•œ ๋งฅ๋ฝ ๊ตฌ์กฐ๋Š” ๋ฌด์—‡์ธ๊ฐ€?
  • ํด๋ฆญ ์ˆ˜๋ฅผ ์ค„์ด๋Š” ๋Œ€์‹  ์–ด๋–ค ๋น„๊ตยทํƒ์ƒ‰ affordance๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•˜๋Š”๊ฐ€?

๊ทธ ๋‹ต์„ ์ž˜ ์ฐพ์œผ๋ฉด, ํ‰๋ฒ”ํ•œ CRUD ํ™”๋ฉด๋„ ํ›จ์”ฌ ๊ฐ•ํ•œ ๋„๊ตฌ๋กœ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋‹ค.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment