Skip to content

Instantly share code, notes, and snippets.

@leedc0101
Created April 20, 2026 01:31
Show Gist options
  • Select an option

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

Select an option

Save leedc0101/ecf2027e9246d541714cf7eae30c8df7 to your computer and use it in GitHub Desktop.
frontend-briefing-2026-04-20-KST

React Server Components Your Way, ํ•œ๊ตญ์–ด ๋ฒˆ์—ญ

  • ์›๋ฌธ ์ œ๋ชฉ: React Server Components Your Way
  • ์›๋ฌธ ๋งํฌ: https://tanstack.com/blog/react-server-components
  • ๋ฒˆ์—ญ์ผ: 2026-04-20 KST
  • ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ฐœ๊ฒฌ: Hacker News, This Week in React #277

๋ฒˆ์—ญ

TanStack ํŒ€์€ ๋Š˜ ๊ฐ™์€ ์ฒ ํ•™์„ ๊ฐ•์กฐํ•ด ์™”๋‹ค. 90%์˜ ์ผ๋ฐ˜์ ์ธ ์š”๊ตฌ๋Š” ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•˜๋˜, ๋‚˜๋จธ์ง€ 10%์˜ ๊ณ ๊ธ‰ ์š”๊ตฌ์—์„œ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๊ฐœ๋ฐœ์ž๋ฅผ ๊ฐ€๋‘์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. React Server Components(RSC)๋„ ๊ฐ™์€ ๊ด€์ ์œผ๋กœ ์ ‘๊ทผํ–ˆ๋‹ค.

๊ธ€์€ ๋จผ์ € RSC ์ž์ฒด๋ฅผ ๋ถ€์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง ๋กœ์ง์„ ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ ์˜ฎ๊ธฐ๊ณ , ์ž์ฃผ ๋ฐ”๋€Œ์ง€ ์•Š๋Š” ์ฝ˜ํ…์ธ ๋ฅผ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์บ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ RSC๋Š” ๋ถ„๋ช… ๊ฐ•๋ ฅํ•œ ์›์‹œ ๋„๊ตฌ๋ผ๊ณ  ๋ณธ๋‹ค. Markdown ํŒŒ์‹ฑ, ์ฝ”๋“œ ํ•˜์ด๋ผ์ดํŒ…, ๋‚ ์งœ ํฌ๋งคํŒ…, ๊ฒ€์ƒ‰ ์ธ๋ฑ์‹ฑ, ์ฝ˜ํ…์ธ  ๋ณ€ํ™˜ ๊ฐ™์€ ์ผ์€ ํŠนํžˆ RSC์™€ ๊ถํ•ฉ์ด ์ข‹๋‹ค.

๋‹ค๋งŒ ํ˜„์žฌ ๋งŽ์€ RSC ํ”„๋ ˆ์ž„์›Œํฌ๋Š” "์„œ๋ฒ„๊ฐ€ ํŠธ๋ฆฌ๋ฅผ ์†Œ์œ ํ•˜๋Š” ๊ตฌ์กฐ"๋ฅผ ๊ธฐ๋ณธ ์ „์ œ๋กœ ๋‘”๋‹ค. ์„œ๋ฒ„๊ฐ€ ์ „์ฒด ํŠธ๋ฆฌ๋ฅผ ์žก๊ณ , use client ๋กœ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ ๋ถ€๋ถ„์„ ํ‘œ์‹œํ•˜๊ณ , ํ”„๋ ˆ์ž„์›Œํฌ ๊ด€๋ก€๊ฐ€ ๋ Œ๋”๋ง๊ณผ ์žฌ์กฐํ•ฉ ๋ฐฉ์‹์„ ๊ฑฐ์˜ ์ „๋ถ€ ๊ฒฐ์ •ํ•œ๋‹ค. TanStack๋Š” ๋ฐ”๋กœ ์ด ์ง€์ ์„ ๋ฌธ์ œ๋กœ ๋ณธ๋‹ค. ๋‹จ์ง€ RSC์˜ ์ด์ ์„ ์–ป๊ธฐ ์œ„ํ•ด ์•ฑ ์ „์ฒด๊ฐ€ ์„œ๋ฒ„ ์šฐ์„  ๊ตฌ์กฐ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์žฌํŽธ๋˜์–ด์•ผ ํ•˜๋Š”๊ฐ€, ๋ผ๋Š” ์งˆ๋ฌธ์ด๋‹ค.

TanStack Start์˜ ํ•ต์‹ฌ ์•„์ด๋””์–ด๋Š” ๊ฐ„๋‹จํ•˜๋‹ค. RSC๋ฅผ ํŠน์ˆ˜ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ ์ „์šฉ ๊ฐœ๋…์ด ์•„๋‹ˆ๋ผ, "ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ฐ€์ ธ์˜ค๊ณ (fetch), ์บ์‹œํ•˜๊ณ (cache), ๋ Œ๋”๋ง(render)ํ•  ์ˆ˜ ์žˆ๋Š” React Flight ์ŠคํŠธ๋ฆผ"์œผ๋กœ ์ทจ๊ธ‰ํ•˜์ž๋Š” ๊ฒƒ์ด๋‹ค. ์ฆ‰ RSC๋ฅผ JSON ๋ฐ์ดํ„ฐ์ฒ˜๋Ÿผ ๋” ์ž˜๊ฒŒ, ๋” ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค๊ฒ ๋‹ค๋Š” ์ ‘๊ทผ์ด๋‹ค.

์ด ์ ‘๊ทผ์„ ์œ„ํ•ด ๊ธ€์€ ์„ธ ๊ฐ€์ง€ ์›์‹œ API๋ฅผ ์ œ์‹œํ•œ๋‹ค.

  • renderToReadableStream: ์„œ๋ฒ„์—์„œ React ์—˜๋ฆฌ๋จผํŠธ๋ฅผ Flight ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ Œ๋”๋งํ•œ๋‹ค.
  • createFromReadableStream: ํด๋ผ์ด์–ธํŠธ๋‚˜ SSR ํ™˜๊ฒฝ์—์„œ Flight ์ŠคํŠธ๋ฆผ์„ ๋‹ค์‹œ React ์—˜๋ฆฌ๋จผํŠธ ํŠธ๋ฆฌ๋กœ ๋ณต์›ํ•œ๋‹ค.
  • createFromFetch: fetch ์‘๋‹ต์œผ๋กœ๋ถ€ํ„ฐ ์ง์ ‘ RSC๋ฅผ ๋””์ฝ”๋”ฉํ•œ๋‹ค.

์ค‘์š”ํ•œ ํฌ์ธํŠธ๋Š” ์ด ๊ณผ์ •์— "์ˆจ๊ฒจ์ง„ ํŠน๋ณ„ ํ”„๋กœํ† ์ฝœ"์ด ์—†๋‹ค๋Š” ์ ์ด๋‹ค. ํ‘œ์ค€ Flight ์ŠคํŠธ๋ฆผ์ด ๋“ค์–ด์˜ค๊ณ , React ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ๋‚˜์˜จ๋‹ค. ๊ทธ๋ž˜์„œ RSC๋ฅผ ๊ธฐ์กด ๋ฐ์ดํ„ฐ ํ๋ฆ„๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์บ์‹œ ์ „๋žต๋„ ์ƒˆ๋กœ ๋ฐฐ์šฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค

๊ธ€์€ RSC์˜ ์บ์‹œ ์ „๋žต์„ ์ƒˆ๋กœ ๋ฐœ๋ช…ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ๊ฐ•์กฐํ•œ๋‹ค. RSC๊ฐ€ ๊ทธ๋ƒฅ ๋ฐ์ดํ„ฐ๊ฐ€ ๋˜๋ฉด, ์บ์‹œ๋„ ๊ธฐ์กด ๋„๊ตฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

  • TanStack Query์—์„œ๋Š” ์ผ๋ฐ˜ async query์ฒ˜๋Ÿผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค. queryKey, staleTime, background refetch ๊ฐ™์€ ๊ธฐ์กด ๊ธฐ๋Šฅ์„ ๊ทธ๋Œ€๋กœ ํ™œ์šฉํ•œ๋‹ค.
  • TanStack Router์—์„œ๋Š” loader ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œํ•˜๋“ฏ RSC๋„ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ €์žฅํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • CDN ๋ ˆ๋ฒจ์—์„œ๋„ GET ์„œ๋ฒ„ ํ•จ์ˆ˜ ์‘๋‹ต์„ ์ผ๋ฐ˜ HTTP ์‘๋‹ต์ฒ˜๋Ÿผ ์บ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฆ‰ RSC๋ฅผ ๋„์ž…ํ–ˆ๋‹ค๊ณ  ํ•ด์„œ ์บ์‹œ ์ „๋žต ์ „์ฒด๋ฅผ ๋‹ค์‹œ ํ•™์Šตํ•˜๊ฑฐ๋‚˜, ํ”„๋ ˆ์ž„์›Œํฌ ๊ณ ์œ ์˜ ๋ธ”๋ž™๋ฐ•์Šค๋ฅผ ๋ฐ›์•„๋“ค์ผ ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ์ฃผ์žฅ์ด๋‹ค.

๋ณด์•ˆ ๊ด€์ ์—์„œ use server ์•ก์…˜์„ ์˜๋„์ ์œผ๋กœ ๋ฐฐ์ œ

TanStack Start๋Š” use server ์•ก์…˜์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ตœ๊ทผ RSC ์ƒํƒœ๊ณ„์—์„œ ๋‚˜์˜จ ์—ฌ๋Ÿฌ CVE๊ฐ€ ์„œ๋ฒ„ ํ•จ์ˆ˜์™€ Flight payload ์ฒ˜๋ฆฌ ๊ฒฝ๊ณ„์—์„œ ๋ฐœ์ƒํ–ˆ๋‹ค๋Š” ์ ์„ ์˜์‹ํ•œ ์„ค๊ณ„๋‹ค. ๋Œ€์‹  createServerFn ๊ธฐ๋ฐ˜์˜ ๋ช…์‹œ์  RPC ํ˜ธ์ถœ์„ ์‚ฌ์šฉํ•œ๋‹ค. ๊ธ€์€ ์ด๊ฒƒ์ด ๋„คํŠธ์›Œํฌ ๊ฒฝ๊ณ„๋ฅผ ๋” ๋ถ„๋ช…ํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ , ์ง๋ ฌํ™”, ๊ฒ€์ฆ, ๋ฏธ๋“ค์›จ์–ด ์ฒ˜๋ฆฌ๋ฅผ ๋” ์˜์‹์ ์œผ๋กœ ์„ค๊ณ„ํ•˜๊ฒŒ ํ•ด ๊ณต๊ฒฉ๋ฉด์„ ์ค„์ธ๋‹ค๊ณ  ์„ค๋ช…ํ•œ๋‹ค.

RSC๊ฐ€ ํŠนํžˆ ์ž˜ ๋งž๋Š” ๊ฒฝ์šฐ์™€ ์•„๋‹Œ ๊ฒฝ์šฐ

TanStack ํŒ€์€ tanstack.com์˜ ์ฝ˜ํ…์ธ  ์ค‘์‹ฌ ์˜์—ญ์„ ์‹ค์ œ๋กœ RSC ๊ธฐ๋ฐ˜์œผ๋กœ ์˜ฎ๊ธฐ๊ณ  ์ˆ˜์น˜๋ฅผ ๊ณต์œ ํ–ˆ๋‹ค.

  • ๋ธ”๋กœ๊ทธ ๊ธ€ ํŽ˜์ด์ง€๋Š” gzipped client JS๊ฐ€ ์•ฝ 153KB ๊ฐ์†Œํ–ˆ๋‹ค.
  • ๋ฌธ์„œ ํŽ˜์ด์ง€๋„ ์•ฝ 153KB ๊ฐ์†Œํ–ˆ๋‹ค.
  • ์˜ˆ์ œ ๋ฌธ์„œ ํŽ˜์ด์ง€๋Š” ์•ฝ 40KB ๊ฐ์†Œํ–ˆ๋‹ค.
  • ํŠน์ • ํŽ˜์ด์ง€์˜ Lighthouse ์ ์ˆ˜๋Š” 52์—์„œ 74๋กœ ์ƒ์Šนํ–ˆ๊ณ , Total Blocking Time์€ 1200ms์—์„œ 260ms๋กœ ์ค„์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๊ธ€์€ RSC๊ฐ€ ๋งŒ๋Šฅ ์„ฑ๋Šฅ ์ฟ ํฐ์€ ์•„๋‹ˆ๋ผ๊ณ  ์„ ์„ ๊ธ‹๋Š”๋‹ค. ์ด๋ฏธ ์ธํ„ฐ๋ž™์…˜ ์ค‘์‹ฌ์ธ ๋Œ€์‹œ๋ณด๋“œ, ๋นŒ๋”, ์žฅ์‹œ๊ฐ„ ์œ ์ง€๋˜๋Š” ์•ฑ ์„ธ์…˜ ๊ฐ™์€ ํ™”๋ฉด์€ RSC๋ฅผ ๋„ฃ๋Š”๋‹ค๊ณ  ์ž๋™์œผ๋กœ ๋นจ๋ผ์ง€์ง€ ์•Š๋Š”๋‹ค. ์‹ค์ œ๋กœ ํด๋ผ์ด์–ธํŠธ์—์„œ ํ•˜๋˜ ๋ฌด๊ฑฐ์šด ์ผ์„ ์ œ๊ฑฐํ•  ๋•Œ๋งŒ ํฐ ํšจ๊ณผ๊ฐ€ ๋‚œ๋‹ค.

๊ทธ๋ž˜์„œ ์ด ๊ธ€์˜ ๊ฒฐ๋ก ์€ "๋ชจ๋“  ๋ผ์šฐํŠธ๋ฅผ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ”๊ฟ”๋ผ"๊ฐ€ ์•„๋‹ˆ๋‹ค. ์ฝ˜ํ…์ธ ๊ฐ€ ๋ฌด๊ฒ๊ณ , ์ •์ ์ด๊ฑฐ๋‚˜, ์˜์กด์„ฑ์ด ํฐ ์˜์—ญ์—์„œ๋Š” ๊ฐ•๋ ฅํ•˜๊ณ , ์ƒํƒœ์™€ ์ƒํ˜ธ์ž‘์šฉ์ด ์ค‘์‹ฌ์ธ ์•ฑ ํ™”๋ฉด์—์„œ๋Š” ์ด๋“์ด ์ œํ•œ์ ์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ํ˜„์‹ค์ ์ธ ์ •๋ฆฌ๋‹ค.

Composite Components๋ผ๋Š” ์ƒˆ๋กœ์šด ์กฐํ•ฉ ๋ฐฉ์‹

๊ธ€์˜ ํ›„๋ฐ˜๋ถ€์—์„œ ๊ฐ€์žฅ ํฅ๋ฏธ๋กœ์šด ๋ถ€๋ถ„์€ Composite Components๋‹ค. ๊ธฐ์กด use client ๋ชจ๋ธ์€ ์„œ๋ฒ„๊ฐ€ ๋ Œ๋”๋งํ•  ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์˜ ์œ„์น˜๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค. ๋ฐ˜๋ฉด Composite Components๋Š” ์„œ๋ฒ„๊ฐ€ ์Šฌ๋กฏ๋งŒ ๋‚จ๊ธฐ๊ณ , ์‹ค์ œ๋กœ ์–ด๋–ค ํด๋ผ์ด์–ธํŠธ UI๋ฅผ ๊ฝ‚์•„ ๋„ฃ์„์ง€๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ฒฐ์ •ํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์„œ๋ฒ„๋Š” ๊ฒŒ์‹œ๊ธ€ ๋ณธ๋ฌธ๊ณผ ๋‹ค์Œ ๊ธ€ ๋งํฌ๋ฅผ ๋ Œ๋”๋งํ•˜๋ฉด์„œ,

  • renderPostActions ๊ฐ™์€ ์Šฌ๋กฏ์œผ๋กœ ํด๋ผ์ด์–ธํŠธ ์•ก์…˜ ์˜์—ญ์„ ๋น„์›Œ๋‘˜ ์ˆ˜ ์žˆ๊ณ 
  • children ์Šฌ๋กฏ์œผ๋กœ ๋Œ“๊ธ€ ์ปดํฌ๋„ŒํŠธ ๊ฐ™์€ ๊ฒƒ์„ ๋ฐ›๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

์ •๋ฆฌ

์ด ๊ธ€์ด ์ฃผ๋Š” ์‹ค๋ฌด์  ๋ฉ”์‹œ์ง€๋Š” ๋ช…ํ™•ํ•˜๋‹ค.

  1. RSC๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์ „์ฒด๋ฅผ ์„œ๋ฒ„ ์šฐ์„ ์œผ๋กœ ์žฌํŽธํ•ด์•ผ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ผ ํ•„์š”๊ฐ€ ์—†๋‹ค.
  2. RSC๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐ ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ๋กœ ๋ณด๋ฉด Query, Router, CDN ๋“ฑ ๊ธฐ์กด ๋„๊ตฌ์™€ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ†ตํ•ฉ๋œ๋‹ค.
  3. ์„ฑ๋Šฅ ์ด์ ์€ ์ฝ˜ํ…์ธ  ์ค‘์‹ฌ ํ™”๋ฉด์—์„œ ํŠนํžˆ ํฌ๋ฉฐ, ์ธํ„ฐ๋ž™์…˜ ์ค‘์‹ฌ ํ™”๋ฉด์—์„œ๋Š” ๋ƒ‰์ •ํ•˜๊ฒŒ ๋น„์šฉ ๋Œ€๋น„ ํšจ๊ณผ๋ฅผ ๋ด์•ผ ํ•œ๋‹ค.
  4. Composite Components์ฒ˜๋Ÿผ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ๊ฒฝ๊ณ„๋ฅผ ๋” ๋ช…์‹œ์ ์œผ๋กœ ๋งŒ๋“œ๋Š” ์ ‘๊ทผ์€ ์•ž์œผ๋กœ React ์•„ํ‚คํ…์ฒ˜ ๋…ผ์˜๋ฅผ ๊ฝค ํ”๋“ค ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค.

ํ˜„์žฌ ์‹œ์ ์—์„œ TanStack Start์˜ RSC ์ง€์›์€ ์‹คํ—˜์ ์ด์ง€๋งŒ, "RSC๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ์‹ ์•™์ด ์•„๋‹ˆ๋ผ ๋„๊ตฌ๋กœ ๋˜๋Œ๋ฆฌ์ž"๋Š” ์ œ์•ˆ ์ž์ฒด๋Š” ์•„์ฃผ ๊ฐ•ํ•˜๋‹ค.

The Uphill Climb of Making Diff Lines Performant, ํ•œ๊ตญ์–ด ๋ฒˆ์—ญ

๋ฒˆ์—ญ

GitHub๋Š” PR ๋ฆฌ๋ทฐ๊ฐ€ ์„œ๋น„์Šค์˜ ์‹ฌ์žฅ์ด๋ผ๊ณ  ๋งํ•œ๋‹ค. ๋ฌธ์ œ๋Š” GitHub ๊ทœ๋ชจ์˜ PR์€ ํ•œ๋‘ ์ค„ ์ˆ˜์ •๋ถ€ํ„ฐ ์ˆ˜์ฒœ ๊ฐœ ํŒŒ์ผ, ์ˆ˜๋ฐฑ๋งŒ ์ค„ ๋ณ€๊ฒฝ๊นŒ์ง€ ํญ์ด ๋„ˆ๋ฌด ํฌ๋‹ค๋Š” ์ ์ด๋‹ค. ๊ทธ๋ž˜์„œ Files changed ํƒญ์€ ์ž‘์€ PR์—์„œ๋Š” ๋‹น์—ฐํžˆ ๋นจ๋ผ์•ผ ํ•˜๊ณ , ๊ฑฐ๋Œ€ํ•œ PR์—์„œ๋„ ์™„์ „ํžˆ ๋ฌด๋„ˆ์ง€๋ฉด ์•ˆ ๋œ๋‹ค.

์ด ๊ธ€์€ React ๊ธฐ๋ฐ˜ ์ƒˆ Files changed ๊ฒฝํ—˜์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ „ํ™˜ํ•˜๋ฉด์„œ ์–ด๋–ค ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ๋Š”์ง€๋ฅผ ์„ค๋ช…ํ•œ๋‹ค. ์ตœ์ ํ™” ์ „์—๋Š” ๊ทน๋‹จ์ ์ธ PR์—์„œ JavaScript heap์ด 1GB๋ฅผ ๋„˜๊ณ , DOM ๋…ธ๋“œ ์ˆ˜๊ฐ€ 40๋งŒ ๊ฐœ๋ฅผ ๋„˜์–ด๊ฐ€๋ฉฐ, ์ƒํ˜ธ์ž‘์šฉ์ด ์ฒด๊ฐ๋  ์ •๋„๋กœ ๋А๋ ค์กŒ๋‹ค๊ณ  ํ•œ๋‹ค. INP๋„ ํ—ˆ์šฉ ๋ฒ”์œ„๋ฅผ ๋„˜์–ด์„œ ์ž…๋ ฅ ์ง€์—ฐ์ด ๋ถ„๋ช…ํ•˜๊ฒŒ ๋А๊ปด์กŒ๋‹ค.

GitHub๋Š” ์—ฌ๊ธฐ์„œ "์€ํƒ„ํ™˜ ํ•˜๋‚˜"๋ฅผ ์ฐพ์ง€ ์•Š์•˜๋‹ค. ๋Œ€์‹  PR ํฌ๊ธฐ์™€ ๋ณต์žก๋„๋ณ„๋กœ ๋‹ค๋ฅธ ์ „๋žต์„ ์กฐํ•ฉํ–ˆ๋‹ค.

  • ์ผ๋ฐ˜์ ์ธ ์ค‘๋Œ€ํ˜• PR์—์„œ๋Š” diff line ์ž์ฒด๋ฅผ ๊ฐ€๋ณ๊ฒŒ ๋งŒ๋“œ๋Š” ์ตœ์ ํ™”
  • ์ดˆ๋Œ€ํ˜• PR์—์„œ๋Š” virtualization์œผ๋กœ ์šฐ์•„ํ•˜๊ฒŒ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ œ์–ดํ•˜๋Š” ์ „๋žต
  • ์–ด๋–ค ํฌ๊ธฐ์˜ PR์ด๋“  ํšจ๊ณผ๊ฐ€ ๋ˆ„์ ๋˜๋Š” ๊ธฐ๋ฐ˜ ์ปดํฌ๋„ŒํŠธ์™€ ๋ Œ๋”๋ง ๊ฐœ์„ 

v1์˜ ๋ฌธ์ œ: ํ•œ ์ค„์ด ๋„ˆ๋ฌด ๋น„์ŒŒ๋‹ค

์ดˆ๊ธฐ React ๋ฒ„์ „(v1)์—์„œ๋Š” diff ํ•œ ์ค„์„ ๋ Œ๋”๋งํ•˜๋Š” ๋น„์šฉ์ด ์ปธ๋‹ค.

  • unified view ํ•œ ์ค„๋‹น DOM ์š”์†Œ๊ฐ€ ๋Œ€๋žต 10๊ฐœ
  • split view ํ•œ ์ค„๋‹น DOM ์š”์†Œ๊ฐ€ ๋Œ€๋žต 15๊ฐœ
  • syntax highlighting์ด ๋“ค์–ด๊ฐ€๋ฉด <span> ์ด ๋” ๋Š˜์–ด๋‚˜ DOM์ด ๋” ์ปค์ง
  • React ์ปดํฌ๋„ŒํŠธ๋„ unified์—์„œ ์ตœ์†Œ 8๊ฐœ, split์—์„œ ์ตœ์†Œ 13๊ฐœ
  • ํ•œ ์ค„์— 20๊ฐœ ์ด์ƒ์˜ event handler๊ฐ€ ๋ถ™๊ธฐ๋„ ํ•จ

์ฒ˜์Œ์—๋Š” ์ž‘์€ ์žฌ์‚ฌ์šฉ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŽ์ด ์ชผ๊ฐœ๋Š” ๋ฐฉ์‹์ด ์ข‹์•„ ๋ณด์˜€์ง€๋งŒ, ๋Œ€๊ทœ๋ชจ PR์—์„œ๋Š” ์ด ๋น„์šฉ์ด ๋ˆ„์ ๋˜์–ด ํฌ๊ฒŒ ํ„ฐ์กŒ๋‹ค. ์ž‘์€ ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋‹ฌ๋ฆฌ๊ณ , split/unified ์–‘์ชฝ์„ ์œ„ํ•œ ๊ณตํ†ต ๋ž˜ํผ๊ฐ€ ๋ถˆํ•„์š”ํ•œ ๋กœ์ง์„ ํ•จ๊ป˜ ๋“ค๊ณ  ๋‹ค๋‹ˆ๋ฉด์„œ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์ปค์กŒ๋‹ค.

v2์˜ ๋ฐฉํ–ฅ: ๋‹จ์ˆœํ™”

GitHub๋Š” ์„ฑ๋Šฅ ๊ฐœ์„  ๋ชฉํ‘œ๋ฅผ ์„ธ ๊ฐ€์ง€๋กœ ์žก์•˜๋‹ค.

  • ๋ฉ”๋ชจ๋ฆฌ์™€ JS heap ์ค„์ด๊ธฐ
  • DOM node ์ˆ˜ ์ค„์ด๊ธฐ
  • ํ‰๊ท  INP์™€ p95/p99 ๊ฐœ์„ ํ•˜๊ธฐ

ํ•ต์‹ฌ ์ „๋žต์€ ๋‹จ์ˆœํ™”์˜€๋‹ค. ์ƒํƒœ๋ฅผ ์ค„์ด๊ณ , ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ค„์ด๊ณ , JavaScript๋ฅผ ์ค„์ด๊ณ , React ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ค„์ด๋Š” ๊ฒƒ.

์ž‘์€ ๋ณ€๊ฒฝ๋„ ๋ฌด์‹œํ•˜์ง€ ์•Š์•˜๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด line number ์…€์—์„œ ๋ถˆํ•„์š”ํ•œ <code> ํƒœ๊ทธ ๋‘ ๊ฐœ๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด, 1๋งŒ ์ค„ PR์—์„œ๋Š” DOM ๋…ธ๋“œ 2๋งŒ ๊ฐœ๊ฐ€ ์ค„์–ด๋“ ๋‹ค. ์ด๋Ÿฐ ์‹์˜ ์‚ฌ์†Œํ•œ ๊ฐ์ถ•์ด ์‹ค์ œ๋กœ๋Š” ํฐ ์ฐจ์ด๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

v2์—์„œ๋Š” ํ•œ ์ค„๋‹น ์ปดํฌ๋„ŒํŠธ ์ˆ˜๋ฅผ 8๊ฐœ์—์„œ 2๊ฐœ ์ˆ˜์ค€์œผ๋กœ ๋‚ฎ์ท„๋‹ค. split/unified ์ „์šฉ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ๊ฐ ๋”ฐ๋กœ ๋‘์–ด ์ผ๋ถ€ ์ค‘๋ณต์„ ๊ฐ์ˆ˜ํ–ˆ์ง€๋งŒ, ๋Œ€์‹  ๋ฐ์ดํ„ฐ ์ ‘๊ทผ๊ณผ ๋ Œ๋”๋ง ๊ตฌ์กฐ๊ฐ€ ํ›จ์”ฌ ๋‹จ์ˆœํ•ด์กŒ๋‹ค.

์ด๋ฒคํŠธ์™€ ์ƒํƒœ๋„ ์œ„๋กœ ์˜ฌ๋ฆฌ๊ณ , ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ๋‘ 

์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๋„ ๊ฐ ์ค„๋งˆ๋‹ค ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ถ™์ด์ง€ ์•Š๊ณ , ์ƒ์œ„ ๋ ˆ๋ฒจ ํ•ธ๋“ค๋Ÿฌ ํ•˜๋‚˜๊ฐ€ data-* ์†์„ฑ์„ ๋ณด๊ณ  ๋™์ž‘ํ•˜๊ฒŒ ๋ฐ”๊ฟจ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋“œ๋ž˜๊ทธ๋กœ ์—ฌ๋Ÿฌ ์ค„์„ ์„ ํƒํ•  ๋•Œ๋„ ๊ฐ ๋ผ์ธ์ด ์ž๊ธฐ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๋Š” ๋Œ€์‹ , ์ƒ์œ„ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์–ด๋–ค ์ค„์ด ์„ ํƒ ์ค‘์ธ์ง€ ํŒ๋ณ„ํ•œ๋‹ค.

๋˜ ๋Œ“๊ธ€ ์ž‘์„ฑ ์ƒํƒœ๋‚˜ ์ปจํ…์ŠคํŠธ ๋ฉ”๋‰ด ์ƒํƒœ์ฒ˜๋Ÿผ ๋ณต์žกํ•œ ์ƒํƒœ๋ฅผ diff line ๋ณธ์ฒด์— ๋‹ค ์–น์–ด ๋‘์ง€ ์•Š์•˜๋‹ค. ์‹ค์ œ๋กœ ๋Œ“๊ธ€์ด๋‚˜ ๋ฉ”๋‰ด๊ฐ€ ์—ด๋ฆฌ๋Š” ์ค„์€ ์ผ๋ถ€๋ฟ์ธ๋ฐ, ๋ชจ๋“  ์ค„์ด ๊ทธ ์ƒํƒœ๋ฅผ ๋“ค๊ณ  ์žˆ์„ ์ด์œ ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋Ÿฐ ์ƒํƒœ๋Š” ์กฐ๊ฑด๋ถ€๋กœ ๋ Œ๋”๋ง๋˜๋Š” ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ€์–ด ๋„ฃ์—ˆ๋‹ค. ๊ธ€์€ ์ด๊ฒƒ์ด Single Responsibility Principle์—๋„ ๋” ๋งž๋Š” ๊ตฌ์กฐ๋ผ๊ณ  ์„ค๋ช…ํ•œ๋‹ค.

useEffect ์ถ•์†Œ์™€ O(1) ์กฐํšŒ

์‹ค๋ฌด์ ์œผ๋กœ ํŠนํžˆ ๋ˆˆ์— ๋„๋Š” ๋ถ€๋ถ„์€ ์—ฌ๊ธฐ๋‹ค. GitHub๋Š” v1์—์„œ ํฉ์–ด์ง„ useEffect ์™€ O(n) ์กฐํšŒ๊ฐ€ ๋ Œ๋”๋ง ๋น„์šฉ๊ณผ ์žฌ๋ Œ๋”๋ฅผ ํ‚ค์šด๋‹ค๊ณ  ๋ดค๋‹ค.

๊ทธ๋ž˜์„œ v2์—์„œ๋Š”:

  • useEffect ์‚ฌ์šฉ์„ diff ํŒŒ์ผ ์ตœ์ƒ๋‹จ์œผ๋กœ ์—„๊ฒฉํžˆ ์ œํ•œํ•˜๊ณ 
  • line wrapper ๊ฐ™์€ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—๋Š” effect๋ฅผ ๋„ฃ์ง€ ๋ชปํ•˜๊ฒŒ lint ๊ทœ์น™๊นŒ์ง€ ์ถ”๊ฐ€ํ–ˆ์œผ๋ฉฐ
  • ๊ณต์šฉ ์ƒํƒœ์™€ diff ์ƒํƒœ ๋จธ์‹ ์„ Map ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ”๊ฟ” O(1) ์กฐํšŒ๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ํŠน์ • ํŒŒ์ผ์˜ ํŠน์ • ์ค„์— ๋Œ“๊ธ€์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•  ๋•Œ, ์„ ํ˜• ํƒ์ƒ‰ ๋Œ€์‹  commentsMap[path][line] ํ˜•ํƒœ์˜ ์ฆ‰์‹œ ์กฐํšŒ๋ฅผ ์“ฐ๊ฒŒ ๋งŒ๋“  ๊ฒƒ์ด๋‹ค. ์ด๊ฑด ์„ฑ๋Šฅ๋ฟ ์•„๋‹ˆ๋ผ ์ฝ”๋“œ์˜ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ๋„ ๋†’์ธ๋‹ค.

์ˆ˜์น˜๋กœ ๋ณธ ๊ฐœ์„  ๊ฒฐ๊ณผ

10,000์ค„ diff๋ฅผ split view๋กœ ๋น„๊ตํ•œ ์ธก์ •์—์„œ:

  • ์ „์ฒด ์ฝ”๋“œ ๋ผ์ธ ์ˆ˜: 2,800 โ†’ 2,000, ์•ฝ 27% ๊ฐ์†Œ
  • ๊ณ ์œ  ์ปดํฌ๋„ŒํŠธ ํƒ€์ž… ์ˆ˜: 19 โ†’ 10, ์•ฝ 47% ๊ฐ์†Œ
  • ๋ Œ๋”๋œ ์ „์ฒด ์ปดํฌ๋„ŒํŠธ ์ˆ˜: ์•ฝ 183,504 โ†’ 50,004, ์•ฝ 74% ๊ฐ์†Œ
  • DOM ๋…ธ๋“œ ์ˆ˜: ์•ฝ 200,000 โ†’ 180,000, ์•ฝ 10% ๊ฐ์†Œ
  • ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰: ์•ฝ 150250MB โ†’ 80120MB, ๋Œ€๋žต ์ ˆ๋ฐ˜ ์ˆ˜์ค€
  • INP: ์•ฝ 450ms โ†’ 100ms, ์•ฝ 78% ๊ฐœ์„ 

์ดˆ๋Œ€ํ˜• PR์€ virtualization์œผ๋กœ ๋Œ€์‘

p95 ์ด์ƒ ๊ทœ๋ชจ, ์ฆ‰ diff line์ด 1๋งŒ ์ค„์„ ๋„˜๋Š” ์ดˆ๋Œ€ํ˜• PR์—์„œ๋Š” ์•„๋ฌด๋ฆฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ตœ์ ํ™”ํ•ด๋„ ํ•œ ๋ฒˆ์— ๋‹ค ๋ Œ๋”๋งํ•˜๋ฉด ํ•œ๊ณ„๊ฐ€ ์žˆ์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ GitHub๋Š” TanStack Virtual์„ ๋„์ž…ํ•ด ํ™”๋ฉด์— ๋ณด์ด๋Š” ๋ถ€๋ถ„๋งŒ DOM์— ์œ ์ง€ํ–ˆ๋‹ค.

๊ทธ ๊ฒฐ๊ณผ p95+ PR์—์„œ๋Š”:

  • JS heap๊ณผ DOM ๋…ธ๋“œ๊ฐ€ 10๋ฐฐ ์ˆ˜์ค€์œผ๋กœ ๊ฐ์†Œํ–ˆ๊ณ 
  • INP๊ฐ€ 275700ms ์ด์ƒ์—์„œ 4080ms ์ˆ˜์ค€์œผ๋กœ ๋‚ด๋ ค๊ฐ”๋‹ค.

์ฆ‰ ํ‰์ƒ์‹œ์—๋Š” ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘๊ณผ find-in-page ๊ฐ™์€ ๊ฒฝํ—˜์„ ์œ ์ง€ํ•˜๊ณ , ์ดˆ๋Œ€ํ˜• PR์—์„œ๋Š” ๊ฐ€์ƒํ™”๋กœ ์‚ด์•„๋‚จ๋Š” ์ด์ค‘ ์ „๋žต์„ ์ทจํ•œ ์…ˆ์ด๋‹ค.

๊ธฐํƒ€ ์ตœ์ ํ™”๋„ ์‹ค๋ฌด ๊ฐ๊ฐ์ด ์ข‹๋‹ค

๊ธ€์€ ์ถ”๊ฐ€๋กœ ๋‹ค์Œ๋„ ์–ธ๊ธ‰ํ•œ๋‹ค.

  • :has(...) ๊ฐ™์€ ๋ฌด๊ฑฐ์šด CSS selector ์ œ๊ฑฐ
  • ๋“œ๋ž˜๊ทธ/๋ฆฌ์‚ฌ์ด์ฆˆ๋ฅผ GPU transform ๊ธฐ๋ฐ˜์œผ๋กœ ์žฌ์„ค๊ณ„
  • interaction ๋‹จ์œ„ INP ์ถ”์ , diff ํฌ๊ธฐ๋ณ„ ์„ธ๋ถ„ํ™”, ๋ฉ”๋ชจ๋ฆฌ tagging์„ Datadog ๋Œ€์‹œ๋ณด๋“œ๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง
  • ์„œ๋ฒ„์—์„œ ๋ณด์ด๋Š” diff line๋งŒ hydrate
  • progressive diff loading๊ณผ background fetch๋กœ ์ดˆ๊ธฐ ์ฒด๊ฐ ์†๋„ ๊ฐœ์„ 

์ •๋ฆฌ

์ด ๊ธ€์˜ ํ•ต์‹ฌ์€ ๊ฒฐ๊ตญ "๋Œ€๊ทœ๋ชจ UI ์„ฑ๋Šฅ ๊ฐœ์„ ์€ ํ™”๋ คํ•œ ๋งˆ๋ฒ•๋ณด๋‹ค ๋‹จ์ˆœํ™”์˜ ๋ˆ„์ "์ด๋ผ๋Š” ์ ์ด๋‹ค.

  • ์ปดํฌ๋„ŒํŠธ ์ˆ˜๋ฅผ ์ค„์ด๊ณ 
  • DOM์„ ์ค„์ด๊ณ 
  • ์ƒํƒœ๋ฅผ ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ๋‘๊ณ 
  • ์ด๋ฒคํŠธ๋ฅผ ์œ„์ž„ํ•˜๊ณ 
  • effect๋ฅผ ์ œํ•œํ•˜๊ณ 
  • ์กฐํšŒ๋ฅผ O(1)๋กœ ๋ฐ”๊พธ๊ณ 
  • ์ •๋ง ํฐ ๊ฒฝ์šฐ์—๋Š” ๊ฐ€์ƒํ™”๋ฅผ ์“ฐ๋Š” ๊ฒƒ

์ด ์กฐํ•ฉ์ด ๊ฒฐ๊ณผ์ ์œผ๋กœ React ๊ธฐ๋ฐ˜ ๋Œ€ํ˜• UI๋ฅผ ๋‹ค์‹œ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค. FE ์‹ค๋ฌด์—์„œ๋Š” ํŠนํžˆ useEffect ์–ต์ œ, ์ƒํƒœ ๊ตญ์†Œํ™”, ์ด๋ฒคํŠธ ์œ„์ž„, Map ๊ธฐ๋ฐ˜ ์ƒ์ˆ˜ ์‹œ๊ฐ„ ์กฐํšŒ, ๊ทธ๋ฆฌ๊ณ  "์ค‘๋ณต์„ ์กฐ๊ธˆ ํ—ˆ์šฉํ•ด๋„ ๋” ๋‹จ์ˆœํ•œ ๊ตฌ์กฐ๊ฐ€ ๋‚ซ๋‹ค"๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ๊ฝค ์ง์ ‘์ ์œผ๋กœ ์™€๋‹ฟ๋Š”๋‹ค.

The Vertical Codebase, ํ•œ๊ตญ์–ด ๋ฒˆ์—ญ

  • ์›๋ฌธ ์ œ๋ชฉ: The Vertical Codebase
  • ์›๋ฌธ ๋งํฌ: https://tkdodo.eu/blog/the-vertical-codebase
  • ๋ฒˆ์—ญ์ผ: 2026-04-20 KST
  • ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ฐœ๊ฒฌ: This Week in React #277

๋ฒˆ์—ญ

์ด ๊ธ€์€ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ components / hooks / types / utils / constants ๊ฐ™์€ ์‹์œผ๋กœ ๋‚˜๋ˆ„๋Š” ์ „ํ†ต์ ์ธ ์ˆ˜ํ‰ ๋ถ„ํ• (horizontal split)์„ ๊ฐ•ํ•˜๊ฒŒ ๋น„ํŒํ•œ๋‹ค. ์ด์œ ๋Š” ๊ฐ„๋‹จํ•˜๋‹ค. ์ด ๊ตฌ์กฐ๋Š” "๋ฌด์Šจ ์ผ์„ ํ•˜๋Š” ์ฝ”๋“œ์ธ๊ฐ€"๊ฐ€ ์•„๋‹ˆ๋ผ "์–ด๋–ค ์ข…๋ฅ˜์˜ ํŒŒ์ผ์ธ๊ฐ€"๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฌถ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด useTheme ์™€ useTodo ๊ฐ€ ๊ฐ™์€ hooks ํด๋”์— ์žˆ์ง€๋งŒ, ThemeProvider ๋‚˜ todo ํ™”๋ฉด ๊ทผ์ฒ˜์—๋Š” ์—†๊ฒŒ ๋œ๋‹ค. ์ฒ˜์Œ ํ”„๋กœ์ ํŠธ๋ฅผ ์—ด ๋•Œ๋Š” ์ด๋Ÿฐ ๊ตฌ์กฐ๊ฐ€ ํŽธํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ํŒŒ์ผ์ด ์ปดํฌ๋„ŒํŠธ๊ณ , ๋‚˜๋จธ์ง€ ํด๋”๋Š” ๋ช‡ ๊ฐœ ์•ˆ ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ•˜์ง€๋งŒ ์‹œ๊ฐ„์ด ์ง€๋‚˜๊ณ  ์ฝ”๋“œ๊ฐ€ ์ปค์ง€๋ฉด ๋ฐ”๋กœ ํƒ์ƒ‰์„ฑ๊ณผ ๋ณ€๊ฒฝ ๋น„์šฉ์ด ๋ฌด๋„ˆ์ง„๋‹ค.

๊ธ€์€ Sentry ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ์˜ˆ๋กœ ๋“ ๋‹ค. 10๋…„ ๋„˜๊ฒŒ ์ˆ˜ํ‰ ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•œ ๊ฒฐ๊ณผ top-level components ๋””๋ ‰ํ„ฐ๋ฆฌ์— 200๊ฐœ๊ฐ€ ๋„˜๋Š” ํŒŒ์ผ์ด ์Œ“์˜€๋‹ค. ๊ทธ ํŒŒ์ผ๋“ค์˜ ๊ณตํ†ต์ ์€ "์ปดํฌ๋„ŒํŠธ๋‹ค"๋ฟ์ด๋‹ค. ๊ธฐ๋Šฅ์  ๊ด€๋ จ์„ฑ์€ ๊ฑฐ์˜ ์—†๋‹ค. ๊ฒฐ๊ตญ ๋ฌด์–ธ๊ฐ€๋ฅผ ์ฐพ์œผ๋ ค๋ฉด ์–ด๋””์„œ๋ถ€ํ„ฐ ์ฐพ์•„์•ผ ํ• ์ง€์กฐ์ฐจ ๋ชจ๋ฅธ๋‹ค.

์ธ๊ฐ„๋ฟ ์•„๋‹ˆ๋ผ AI ์—์ด์ „ํŠธ์—๊ฒŒ๋„ ๊ตฌ์กฐ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค

๊ธ€์“ด์ด๋Š” ์š”์ฆ˜ ์—์ด์ „ํŠธ ์‹œ๋Œ€๋ฅผ ์–ธ๊ธ‰ํ•˜๋ฉด์„œ๋„, ์˜คํžˆ๋ ค ๊ทธ๋Ÿด์ˆ˜๋ก ๊ตฌ์กฐ๊ฐ€ ๋” ์ค‘์š”ํ•˜๋‹ค๊ณ  ๋งํ•œ๋‹ค. ์‚ฌ๋žŒ๊ณผ ์—์ด์ „ํŠธ ๋ชจ๋‘ ํšจ์œจ์ ์œผ๋กœ ์ผํ•˜๋ ค๋ฉด ๊ฒฝ๊ณ„, ์ œ์•ฝ, ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ํƒ์ƒ‰ํ•˜๊ธฐ ์‰ฌ์šด ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ, ์ข‹์€ lint ๊ทœ์น™, ๊ฐ•ํ•œ TypeScript ์„ค์ •, ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์ธ ํ…Œ์ŠคํŠธ๋Š” ์—์ด์ „ํŠธ์—๊ฒŒ๋„ ๊ทธ๋Œ€๋กœ ๋„์›€์ด ๋œ๋‹ค.

์ฆ‰ "AI๊ฐ€ ๋‹ค ์•Œ์•„์„œ ๊ณ ์ณ์ค„ ํ…Œ๋‹ˆ ๊ตฌ์กฐ๋Š” ์ค‘์š”ํ•˜์ง€ ์•Š๋‹ค"๋Š” ํƒœ๋„๋Š” ํ˜„์‹ค์ ์ด์ง€ ์•Š๋‹ค๋Š” ์ฃผ์žฅ์ด๋‹ค. ์œ ๊ธฐ์ ์œผ๋กœ ์˜ค๋ž˜ ์ž๋ž€ ์ฝ”๋“œ๋ฒ ์ด์Šค์ผ์ˆ˜๋ก ๊ตฌ์กฐ๊ฐ€ ์ƒ์‚ฐ์„ฑ์„ ๊ฒฐ์ •ํ•œ๋‹ค.

ํ•ต์‹ฌ ์›์น™: ๊ฐ™์ด ๋ฐ”๋€Œ๋Š” ์ฝ”๋“œ๋Š” ๊ฐ™์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค

๊ธ€์˜ ์ค‘์‹ฌ ๋ฌธ์žฅ์€ ์ด๊ฑฐ๋‹ค.

Code that changes together should live together.

์˜ˆ๋ฅผ ๋“ค์–ด Widget ์ปดํฌ๋„ŒํŠธ์˜ props ํƒ€์ž…์€ ๋Œ€๋ถ€๋ถ„ ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ ์•ˆ์— ๊ฐ™์ด ๋‘”๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ฝ์„ ๋•Œ ํ•จ๊ป˜ ๋ณด๊ณ  ์‹ถ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์— ๋ฐ์ดํ„ฐ ํŒจ์นญ์ด ์ถ”๊ฐ€๋˜๋ฉด, ์ˆ˜ํ‰ ๊ตฌ์กฐ์—์„œ๋Š” widgetQueryOptions ๊ฐ™์€ ๊ฒƒ์ด ๊ฐ‘์ž๊ธฐ utils/widget.ts ๋กœ ๊ฐ€๋ฒ„๋ฆฐ๋‹ค. ๊ธฐ์ˆ ์ ์œผ๋กœ๋Š” util์ด์ง€๋งŒ, ๋…ผ๋ฆฌ์ ์œผ๋กœ๋Š” Widget์˜ ์ผ๋ถ€๋‹ค.

๊ฒฐ๊ตญ ์ˆ˜ํ‰ ๊ตฌ์กฐ๋Š” ์‘์ง‘๋„(cohesion)๋ฅผ ๋‚ฎ์ถ”๊ณ , ๊ด€๋ จ ์—†๋Š” ์ฝ”๋“œ๋ผ๋ฆฌ ๋ฌถ์–ด ๋ฒ„๋ฆฐ๋‹ค. utils ํด๋”๋Š” ํŠนํžˆ "์ปดํฌ๋„ŒํŠธ๋„ ํ›…๋„ ์•„๋‹Œ ๋ชจ๋“  ๊ฒƒ"์ด ์Œ“์ด๋Š” ์žก๋™์‚ฌ๋‹ˆ ์ฐฝ๊ณ ๊ฐ€ ๋˜๊ธฐ ์‰ฝ๋‹ค.

๋Œ€์•ˆ: ์ˆ˜์ง ๋ถ„ํ• (vertical structure)

์ด ๊ธ€์ด ์ œ์•ˆํ•˜๋Š” ๋ฐฉ์‹์€ ๋„๋ฉ”์ธ ์ค‘์‹ฌ ๊ตฌ์กฐ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด widget ๊ด€๋ จ ์ฝ”๋“œ๋Š” ์ „๋ถ€ src/widgets/ ์•„๋ž˜์— ๋‘”๋‹ค.

  • ์ปดํฌ๋„ŒํŠธ
  • ํ›…
  • ํƒ€์ž…
  • ์ฟผ๋ฆฌ ์˜ต์…˜
  • ์œ ํ‹ธ
  • ์ƒ์ˆ˜

๋ฌด์—‡์ด๋“  ์ƒ๊ด€์—†๋‹ค. ์ค‘์š”ํ•œ ๊ฒƒ์€ ๊ธฐ์ˆ ์  ์ข…๋ฅ˜๊ฐ€ ์•„๋‹ˆ๋ผ ๊ธฐ๋Šฅ์  ๋ชฉ์ ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด "์ด ์ฝ”๋“œ๊ฐ€ ๋ฌด์—‡์„ ํ•˜๋Š”๊ฐ€"๋ฅผ ์•Œ ๋•Œ ์ฐพ๊ธฐ๊ฐ€ ํ›จ์”ฌ ์‰ฌ์›Œ์ง€๊ณ , ๋ณ€๊ฒฝ๋„ ํ•œ ์˜์—ญ ์•ˆ์—์„œ ๋๋‚˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„์ง„๋‹ค.

์ด ๋ฐฉ์‹์€ ํŒ€ ๊ตฌ์กฐ์™€๋„ ์ž˜ ๋งž๋Š”๋‹ค. ์‹ค์ œ ์กฐ์ง๋„ billing ํŒ€, dashboard ํŒ€, profiling ํŒ€์ฒ˜๋Ÿผ ๋„๋ฉ”์ธ ๋‹จ์œ„๋กœ ์ผํ•˜๋Š”๋ฐ, ์ฝ”๋“œ๋งŒ types/util/components ๊ธฐ์ค€์œผ๋กœ ๊ฐˆ๋ผ๋†“๋Š” ๊ฒƒ์€ ์˜คํžˆ๋ ค ํ˜„์‹ค๊ณผ ์–ด๊ธ‹๋‚œ๋‹ค๋Š” ์ง€์ ์ด๋‹ค.

๊ณต์œ  ์˜์—ญ๋„ ๋„๋ฉ”์ธ์œผ๋กœ ๋‹ค๋ฃฌ๋‹ค

์ˆ˜์ง ๊ตฌ์กฐ๋ฅผ ์ œ์•ˆํ•˜๋ฉด ํ”ํžˆ "์—ฌ๋Ÿฌ ํ™”๋ฉด์—์„œ ๊ฐ™์ด ์“ฐ๋Š” ์ฝ”๋“œ๋Š” ์–ด๋””์— ๋‘๋‚˜"๋ผ๋Š” ์งˆ๋ฌธ์ด ๋‚˜์˜จ๋‹ค. ๊ธ€์˜ ๋Œ€๋‹ต์€ ๊ทธ ์ž์ฒด๋ฅผ ํ•˜๋‚˜์˜ ์ˆ˜์ง(vertical)๋กœ ๋ณด๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์—ฌ๋Ÿฌ ํ™”๋ฉด์ด ํ•จ๊ป˜ ์“ฐ๋Š” PageFilters ๋Š” ๊ทธ๋ƒฅ ๊ณต์šฉ ์œ ํ‹ธ์ด ์•„๋‹ˆ๋ผ, pageFilters ๋ผ๋Š” ๋…๋ฆฝ๋œ ๋„๋ฉ”์ธ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” components ํด๋”, types ํด๋”, utils ํด๋”๋กœ ํฉ์–ด์ ธ ์žˆ์„ ๋•Œ๋ณด๋‹ค, src/pageFilters/ ์•ˆ์— ๋ชจ์—ฌ ์žˆ์„ ๋•Œ ํ›จ์”ฌ ๋ฐœ๊ฒฌํ•˜๊ธฐ ์‰ฝ๊ณ  ownership๋„ ๋ช…ํ™•ํ•ด์ง„๋‹ค.

์‘์ง‘๋„๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•˜๊ณ , ๊ฒฝ๊ณ„๋„ ํ•„์š”ํ•˜๋‹ค

๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ๋ชจ์œผ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ์ถฉ๋ถ„ํ•˜์ง€ ์•Š๋‹ค. ์–ด๋–ค ์ฝ”๋“œ๋Š” ์™ธ๋ถ€์—์„œ ์จ๋„ ๋˜๊ณ , ์–ด๋–ค ์ฝ”๋“œ๋Š” ๋‚ด๋ถ€ ์ „์šฉ์ธ์ง€ ๊ฒฝ๊ณ„๋ฅผ ์ •ํ•ด์•ผ ํ•œ๋‹ค. ๊ธ€์€ ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ์‹์œผ๋กœ monorepo๋ฅผ ์ œ์•ˆํ•œ๋‹ค.

  • ๊ฐ vertical์„ ํ•˜๋‚˜์˜ ํŒจํ‚ค์ง€์ฒ˜๋Ÿผ ๋‘๊ณ 
  • package.json ์˜ exports ๋กœ public interface๋ฅผ ์ •์˜ํ•˜๊ณ 
  • pnpm workspace ๊ฐ™์€ ๋„๊ตฌ๋กœ ์˜์กด์„ฑ์„ ๋ช…์‹œ์ ์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์–ด๋–ค vertical์ด ์–ด๋–ค vertical์— ์˜์กดํ•˜๋Š”์ง€ ๋“œ๋Ÿฌ๋‚œ๋‹ค. Nx์ฒ˜๋Ÿผ ํ”„๋กœ์ ํŠธ ๊ฐ„ ์˜์กด์„ฑ ๊ทœ์น™์„ ๊ฐ•์ œํ•˜๋Š” ๋„๊ตฌ๋„ ์“ธ ์ˆ˜ ์žˆ๋‹ค. monorepo๊ฐ€ ์•„๋‹ˆ์–ด๋„ eslint-plugin-boundaries ๊ฐ™์€ ๋„๊ตฌ๋กœ deep import๋ฅผ ๋ง‰๊ณ  ๊ฒฝ๊ณ„๋ฅผ ์„ธ์šธ ์ˆ˜ ์žˆ๋‹ค.

ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋„ ์žˆ๋‹ค

๊ธ€์€ ๊ณต์งœ ์ ์‹ฌ์€ ์—†๋‹ค๊ณ  ์ธ์ •ํ•œ๋‹ค.

  • ์–ด๋–ค ์ฝ”๋“œ๋ฅผ ์–ด๋А vertical์— ๋‘˜์ง€ ํŒ๋‹จํ•˜๋Š” ์ผ์€ ์–ด๋ ต๋‹ค.
  • private ์ฝ”๋“œ๋ฅผ ๊ฐ•์กฐํ•˜๋ฉด ํŒ€๋ณ„๋กœ ๋น„์Šทํ•œ ๊ฒƒ์„ ์ค‘๋ณต ๊ตฌํ˜„ํ•  ์œ„ํ—˜๋„ ์žˆ๋‹ค.

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

์ •๋ฆฌ

์ด ๊ธ€์˜ ์‹ค๋ฌด์  ์š”์ง€๋Š” ๋ถ„๋ช…ํ•˜๋‹ค.

  1. ํŒŒ์ผ ์ข…๋ฅ˜ ๊ธฐ์ค€ ๋ถ„ํ• ์€ ์ž‘์„ ๋•Œ๋งŒ ํŽธํ•˜๊ณ , ์ปค์งˆ์ˆ˜๋ก ํƒ์ƒ‰์„ฑ๊ณผ ์‘์ง‘๋„๋ฅผ ํ•ด์นœ๋‹ค.
  2. ๊ธฐ๋Šฅ์ ์œผ๋กœ ๊ฐ™์ด ๋ฐ”๋€Œ๋Š” ์ฝ”๋“œ๋Š” ๊ฐ™์€ ํด๋”, ๊ฐ™์€ ์˜์—ญ์— ๋‘๋Š” ๊ฒƒ์ด ๋งž๋‹ค.
  3. ๊ณต์šฉ ์ฝ”๋“œ๋„ "shared" ๋ผ๋Š” ์“ฐ๋ ˆ๊ธฐํ†ต์— ๋˜์ง€์ง€ ๋ง๊ณ , ๋…๋ฆฝ๋œ ๋„๋ฉ”์ธ์œผ๋กœ ๊ฒฉ์ƒํ•ด ๋‹ค๋ฃจ๋Š” ๊ฒŒ ๋‚ซ๋‹ค.
  4. ์ˆ˜์ง ๊ตฌ์กฐ๋Š” ๋ชจ๋†€๋ฆฌ์‹ ํด๋” ์ •๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ ownership, ๊ฒฝ๊ณ„, ์˜์กด์„ฑ ์„ค๊ณ„๊นŒ์ง€ ์ด์–ด์ ธ์•ผ ํ•œ๋‹ค.

React๋‚˜ ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก, ํŠนํžˆ Query ์˜ต์…˜, types, hooks, UI๊ฐ€ ํ•œ ๊ธฐ๋Šฅ์„ ์ค‘์‹ฌ์œผ๋กœ ๊ฐ™์ด ์›€์ง์ด๋Š” ํŒ€์ด๋ผ๋ฉด ์ด ๊ธ€์˜ ์ œ์•ˆ์ด ๊ฝค ๊ฐ•ํ•˜๊ฒŒ ๋จนํžŒ๋‹ค.

Now More Than Ever, You Need to Master Custom ESLint Rules, ํ•œ๊ตญ์–ด ๋ฒˆ์—ญ

  • ์›๋ฌธ ์ œ๋ชฉ: Now more then ever, you need to master custom ESLint rules
  • ์›๋ฌธ ๋งํฌ: https://neciudan.dev/master-eslint-rules
  • ๋ฒˆ์—ญ์ผ: 2026-04-20 KST
  • ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ฐœ๊ฒฌ: This Week in React #277

๋ฒˆ์—ญ

์ด ๊ธ€์€ "๋ถˆํ•„์š”ํ•œ useEffect๋ฅผ ์ฝ”๋“œ ๋ฆฌ๋ทฐ์—์„œ ๋งค๋ฒˆ ๋ง๋กœ ์‹ธ์šฐ์ง€ ๋ง๊ณ , ๋ฃฐ๋กœ ์˜ฎ๊ฒจ๋ผ"๋ผ๋Š” ์•„์ฃผ ์‹ค์ „์ ์ธ ๋ฌธ์ œ์˜์‹์—์„œ ์‹œ์ž‘ํ•œ๋‹ค.

๊ธ€์“ด์ด๋Š” ํŒ€ ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ๋ฐ˜๋ณต์ ์œผ๋กœ ๊ฐ™์€ PR ์ฝ”๋ฉ˜ํŠธ๋ฅผ ๋ณด๊ฒŒ ๋๋‹ค๊ณ  ๋งํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

useEffect(() => setFiltered(data.filter(...)), [data])

์ฒ˜๋Ÿผ derived state๋ฅผ effect๋กœ ๋™๊ธฐํ™”ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์˜ฌ๋ผ์˜ค๋ฉด, ๋ฆฌ๋ทฐ์–ด ์—ฌ๋Ÿฌ ๋ช…์ด "์ด๊ฑด effect๊ฐ€ ํ•„์š” ์—†๋‹ค"๊ณ  ์ง€์ ํ•˜๊ณ , ์ž‘์„ฑ์ž๋Š” ๋ฐ˜๋ฐ•ํ•˜๊ณ , ์งง์€ ์ฝ”๋“œ ํ•œ ์ค„ ๋•Œ๋ฌธ์— ๊ธด ๋…ผ์Ÿ์ด ๋ฐ˜๋ณต๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์•„์˜ˆ custom ESLint rule์„ ๋งŒ๋“ค์–ด ์ด ํŒจํ„ด์„ ์ž๋™์œผ๋กœ ์žก๊ธฐ๋กœ ํ–ˆ๋‹ค.

์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ํŠธ๋ฆฌ๋ฅผ ๋ณธ๋‹ค๋Š” ๊ด€์ 

๊ธ€์˜ ํ•ต์‹ฌ ๊ต์œก ํฌ์ธํŠธ๋Š” AST(Abstract Syntax Tree)๋‹ค. ์‚ฌ๋žŒ์ด JavaScript๋ฅผ ๋ณด๋ฉด ํ…์ŠคํŠธ๋ฅผ ๋ณด์ง€๋งŒ, ESLint๋Š” ์ฝ”๋“œ๋ฅผ ํŠธ๋ฆฌ๋กœ ๋ณธ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด const name = "Dan"; ๋Š” ๋ณ€์ˆ˜ ์„ ์–ธ ํ…์ŠคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ,

  • VariableDeclaration
  • VariableDeclarator
  • Identifier
  • Literal

๊ฐ™์€ ๋…ธ๋“œ๊ฐ€ ์ค‘์ฒฉ๋œ ๊ตฌ์กฐ๋กœ ํ•ด์„๋œ๋‹ค. ๊ณต๋ฐฑ, ์„ธ๋ฏธ์ฝœ๋ก , ํฌ๋งคํŒ…์€ ์ค‘์š”ํ•˜์ง€ ์•Š๊ณ , ์ฝ”๋“œ์˜ ์˜๋ฏธ ๊ตฌ์กฐ๋งŒ ๋‚จ๋Š”๋‹ค.

์ด ์‹œ๊ฐ์ด ์ค‘์š”ํ•œ ์ด์œ ๋Š” ESLint, Babel, Prettier, TypeScript ๋ชจ๋‘ ๊ฒฐ๊ตญ AST๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์›€์ง์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ฆ‰ custom rule ์ž‘์„ฑ์€ ๋ฌธ์ž์—ด ๋น„๊ต๊ฐ€ ์•„๋‹ˆ๋ผ "์ด๋Ÿฐ ํ˜•ํƒœ์˜ ํŠธ๋ฆฌ๋ฅผ ์ฐพ๋Š” ํŒจํ„ด ๋งค์นญ" ๋ฌธ์ œ๋‹ค.

ESLint๋Š” ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€

๊ธ€์€ ESLint ๋™์ž‘์„ ์„ธ ๋‹จ๊ณ„๋กœ ์„ค๋ช…ํ•œ๋‹ค.

  1. ์†Œ์Šค ์ฝ”๋“œ๋ฅผ AST๋กœ ํŒŒ์‹ฑํ•œ๋‹ค.
  2. ํŠธ๋ฆฌ๋ฅผ ๊นŠ์ด ์šฐ์„ ์œผ๋กœ ์ˆœํšŒํ•œ๋‹ค.
  3. ๊ฐ ๋…ธ๋“œ์— ๋Œ€ํ•ด ํ•ด๋‹น ํƒ€์ž…์„ ๋“ฃ๊ณ  ์žˆ๋Š” rule listener๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

๋ฃฐ์€ ๊ฒฐ๊ตญ create() ํ•จ์ˆ˜๊ฐ€ ์žˆ๊ณ , ๊ทธ ์•ˆ์—์„œ CallExpression, VariableDeclarator ๊ฐ™์€ ๋…ธ๋“œ ํƒ€์ž…๋ณ„ ์ฝœ๋ฐฑ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ์ฒด๋‹ค. context.report() ๋ฅผ ๋ถ€๋ฅด๋ฉด ์—๋””ํ„ฐ๋‚˜ CI์— ๊ฒฝ๊ณ ๊ฐ€ ๋œฌ๋‹ค.

์ฆ‰ no-console ๊ฐ™์€ ๊ธฐ๋ณธ ๋ฃฐ๋„ ๋ณธ์งˆ์ ์œผ๋กœ๋Š” "CallExpression์„ ๋ณผ ๋•Œ, callee๊ฐ€ console.log ์ธ์ง€ ๊ฒ€์‚ฌํ•˜๋ผ"๋Š” ๋‹จ์ˆœํ•œ ํ•จ์ˆ˜๋‹ค.

AST Explorer๊ฐ€ ์‚ฌ๊ณ ๋ฐฉ์‹์„ ๋ฐ”๊พผ๋‹ค

๊ธ€์“ด์ด๋Š” AST Explorer๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์ถ”์ฒœํ•œ๋‹ค. ์ฝ”๋“œ๋ฅผ ๋ถ™์—ฌ ๋„ฃ๊ณ , ๋Œ€์‘ํ•˜๋Š” AST ๋…ธ๋“œ๋ฅผ ํด๋ฆญํ•ด ๊ฐ€๋ฉฐ ๊ตฌ์กฐ๋ฅผ ๋ณด๋Š” ์ˆœ๊ฐ„ custom rule ์ž‘์„ฑ์ด ํ›จ์”ฌ ์‰ฌ์›Œ์กŒ๋‹ค๊ณ  ๋งํ•œ๋‹ค.

  • ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋Š” ArrowFunctionExpression
  • ํ•จ์ˆ˜ ํ˜ธ์ถœ์€ CallExpression
  • ๊ฐ์ฒด ์†์„ฑ ์ ‘๊ทผ์€ MemberExpression
  • ์‹๋ณ„์ž๋Š” Identifier

์ฒ˜๋Ÿผ ๋ณด์ด๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด, ์ด์ œ "๋‚˜์œ ์ฝ”๋“œ ํŒจํ„ด"์„ AST ํ˜•ํƒœ๋กœ ์ฐพ์•„๋‚ด๊ณ  ๊ทธ ํŒจํ„ด์„ ๊ฒ€์‚ฌํ•˜๋Š” ๋ฃฐ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

์‹ค์ œ ๋ฃฐ ์˜ˆ์‹œ: derived state๋ฅผ effect์—์„œ ์„ธํŒ…ํ•˜์ง€ ๋ง๊ธฐ

๊ธ€์˜ ๋Œ€ํ‘œ ์˜ˆ์‹œ๋Š” useEffect ์•ˆ์—์„œ state setter๋งŒ ํ˜ธ์ถœํ•˜๋Š” ํŒจํ„ด์„ ์žก๋Š” ๋ฃฐ์ด๋‹ค.

์ ‘๊ทผ ๋ฐฉ์‹์€ ์ด๋ ‡๋‹ค.

  • ๋จผ์ € useState ํ˜ธ์ถœ์„ ์ฐพ์•„ setter ์ด๋ฆ„๋“ค(setFiltered, setValue ๋“ฑ)์„ ์ˆ˜์ง‘ํ•œ๋‹ค.
  • ๋‹ค์Œ์œผ๋กœ useEffect ํ˜ธ์ถœ์„ ์ฐพ๋Š”๋‹ค.
  • effect body ์•ˆ์˜ ๋ชจ๋“  statement๊ฐ€ setter ํ˜ธ์ถœ๋ฟ์ธ์ง€ ๋ณธ๋‹ค.
  • ๊ทธ๋ ‡๋‹ค๋ฉด ์ด effect๋Š” ์™ธ๋ถ€ ์‹œ์Šคํ…œ ๋™๊ธฐํ™”๊ฐ€ ์•„๋‹ˆ๋ผ derived state ๊ณ„์‚ฐ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์œผ๋‹ˆ ๊ฒฝ๊ณ ํ•œ๋‹ค.

์ด ๋ฃฐ์€ ์˜๋„์ ์œผ๋กœ ์ข๊ฒŒ ๋งŒ๋“ค์–ด์ ธ ์žˆ๋‹ค. if ๋ฌธ ์•ˆ์— setter๊ฐ€ ๋“ค์–ด๊ฐ„ ๊ฒฝ์šฐ๋‚˜, .then() ์•ˆ์—์„œ state๋ฅผ ๋ฐ”๊พธ๋Š” ๊ฒฝ์šฐ์ฒ˜๋Ÿผ ๋” ๋ณต์žกํ•œ ์ผ€์ด์Šค๋Š” ๋†“์น  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ false positive๋ฅผ ์ค„์ด๊ณ  "๋ช…๋ฐฑํ•œ ์•ˆํ‹ฐํŒจํ„ด"๋งŒ ์ •ํ™•ํžˆ ์žก๋Š” ๋ฐ ์ง‘์ค‘ํ•œ ์„ค๊ณ„๋‹ค.

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

๋กœ์ปฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์‰ฝ๊ฒŒ ๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค

ESLint flat config๋ฅผ ์“ฐ๋ฉด ์ปค์Šคํ…€ ๋ฃฐ ๋ฐฐํฌ๋„ ๋ถ€๋‹ด์ด ์ ๋‹ค. npm์— ๊ณต๊ฐœ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋งŒ๋“ค ํ•„์š” ์—†์ด, ํ”„๋กœ์ ํŠธ ๋‚ด๋ถ€ ํŒŒ์ผ ํ•˜๋‚˜๋ฅผ eslint.config.js ์—์„œ import ํ•ด local/no-derived-state-in-effect ๊ฐ™์€ ๋ฃฐ๋กœ ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

์ฆ‰ ๋„์ž… ๋น„์šฉ์€ ์ƒ๊ฐ๋ณด๋‹ค ๋‚ฎ๋‹ค.

  • eslint-rules/no-derived-state-in-effect.js ํŒŒ์ผ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ 
  • config์—์„œ plugins.local.rules ๋กœ ๋“ฑ๋กํ•˜๊ณ 
  • ์›ํ•˜๋Š” severity๋กœ ์ผœ๋ฉด ๋์ด๋‹ค.

๋ฃฐ์ด ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ๋Š˜์–ด๋‚˜๋ฉด eslint-rules/index.js ๋กœ ๋ชจ์•„๋„ ๋œ๋‹ค.

ํ…Œ์ŠคํŠธ์™€ auto-fix

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

auto-fix๋„ ์„ค๋ช…ํ•˜์ง€๋งŒ, ๋ชจ๋“  ๋ฃฐ์— auto-fix๊ฐ€ ํ•„์š”ํ•œ ๊ฒƒ์€ ์•„๋‹ˆ๋ผ๊ณ  ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด derived state effect๋ฅผ ์ž๋™ ์ˆ˜์ •ํ•˜๋ ค๋ฉด useState ์ œ๊ฑฐ, useEffect ์ œ๊ฑฐ, ์ƒˆ๋กœ์šด const ์„ ์–ธ ์‚ฝ์ž…๊นŒ์ง€ ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฐ ๋ณ€ํ™˜์€ AST ๋ถ„์„๋ณด๋‹ค ์•ˆ์ „์„ฑ ๋ณด์žฅ์ด ๋” ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์—, ๊ฒฝ๊ณ ๋งŒ ์ฃผ๋Š” ํŽธ์ด ๋” ๋‚˜์„ ์ˆ˜ ์žˆ๋‹ค.

๊ธ€์˜ ๋” ํฐ ๋ฉ”์‹œ์ง€

์ด ๊ธ€์€ ESLint๋ฅผ ๋‹จ์ˆœ ์Šคํƒ€์ผ ๊ฒ€์‚ฌ๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ "ํŒ€์˜ ๋ฐ˜๋ณต ๋…ผ์Ÿ์„ ํˆด๋กœ ๋‚ด๋ฆฌ๋Š” ์žฅ์น˜"๋กœ ๋ณด๊ฒŒ ๋งŒ๋“ ๋‹ค.

  • ์ฝ”๋“œ ๋ฆฌ๋ทฐ์—์„œ ๊ณ„์† ๊ฐ™์€ ๋ง์„ ํ•˜๊ฒŒ ๋˜๋Š”๊ฐ€?
  • ํŒ€ ๊ทœ์น™์ด ์žˆ๋Š”๋ฐ ์‚ฌ๋žŒ๋งˆ๋‹ค ํ•ด์„์ด ๋‹ฌ๋ผ ๋…ผ์Ÿ์ด ๊ธธ์–ด์ง€๋Š”๊ฐ€?
  • ํŠน์ • ์•ˆํ‹ฐํŒจํ„ด์„ ๋ฏธ๋ฆฌ ๋ง‰๊ณ  ์‹ถ์€๊ฐ€?

๊ทธ๋ ‡๋‹ค๋ฉด custom rule์ด ๋‹ต์ด ๋  ์ˆ˜ ์žˆ๋‹ค. ํŠนํžˆ React ์‹ค๋ฌด์—์„œ๋Š” useEffect, state setter, ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ, ํผ submit, ๋„ค์ด๋ฐ ๊ทœ์น™์ฒ˜๋Ÿผ ํŒ€๋งˆ๋‹ค ์ค‘์š”ํ•˜๊ฒŒ ๋ณด๋Š” ํŒจํ„ด์ด ๋ถ„๋ช…ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, custom ESLint rule์€ ์ƒ๊ฐ๋ณด๋‹ค ROI๊ฐ€ ํฌ๋‹ค.

์ •๋ฆฌ

  1. AST๋ฅผ ์ดํ•ดํ•˜๋ฉด ESLint ๋ฃฐ์€ ๋ฌด์„ญ์ง€ ์•Š๋‹ค.
  2. ์ปค์Šคํ…€ ๋ฃฐ์€ ๋ฐ˜๋ณต๋˜๋Š” PR ๋ฆฌ๋ทฐ ์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ž๋™ํ™”ํ•˜๋Š” ๊ฐ€์žฅ ํ˜„์‹ค์ ์ธ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋‹ค.
  3. ์ฒซ ๋ฒ„์ „์€ ์ข๊ณ  ์ •ํ™•ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ํŽธ์ด ๋‚ซ๋‹ค.
  4. auto-fix๋Š” ๋ฉ‹์žˆ์ง€๋งŒ, ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐ”๊พธ๊ธฐ ์–ด๋ ค์šฐ๋ฉด ๊ฒฝ๊ณ ๋งŒ ์ค˜๋„ ์ถฉ๋ถ„ํ•˜๋‹ค.

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

Frontend Framework Bundle Size Benchmark, ํ•œ๊ตญ์–ด ๋ฒˆ์—ญ

๋ฒˆ์—ญ

ํ”„๋ ˆ์ž„์›Œํฌ ๋น„๊ต๋Š” ๋ณดํ†ต DX, ์ƒํƒœ๊ณ„, ๋Ÿฐํƒ€์ž„ ์„ฑ๋Šฅ ์ด์•ผ๊ธฐ์— ์ง‘์ค‘๋œ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์žฅ ๋จผ์ € ๊ฒช๋Š” ๊ฒƒ์€ ๊ทธ๋ณด๋‹ค ์•ž๋‹จ์ด๋‹ค. ๋‚ด๋ ค๋ฐ›๊ณ , ์••์ถ•์„ ํ’€๊ณ , ํŒŒ์‹ฑํ•˜๊ณ , ์‹คํ–‰ํ•˜๋Š” ๋น„์šฉ์ด๋‹ค. ์ด ๊ธ€์€ ๋ฐ”๋กœ ๊ทธ ๊ด€์ ์—์„œ ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ๋น„๊ตํ•œ๋‹ค.

๊ธ€์“ด์ด๋Š” ๊ฐ™์€ TodoMVC ๊ธฐ๋Šฅ ์„ธํŠธ๋ฅผ ์—ฌ๋Ÿฌ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๊ฐ๊ฐ ๊ตฌํ˜„ํ•œ ๋’ค, ๋™์ผํ•œ ๊ธฐ์ค€์œผ๋กœ ๋ฒˆ๋“ค ๋ฆฌํฌํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ๋น„๊ต ์ง€ํ‘œ๋Š” ๋‹ค์Œ ์„ธ ๊ฐ€์ง€๋‹ค.

  • raw size
  • minified size
  • minified + gzip size

๋˜ ์ด ํฌ๊ธฐ๋ฅผ runtime, template, script, style ๋กœ ๋‚˜๋ˆ ์„œ ๋ณธ๋‹ค. ์ฆ‰ ๋‹จ์ˆœ ์ดํ•ฉ๋งŒ ๋ณด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋ฌด์—‡์ด ๋น„์šฉ์„ ๋ฐ€์–ด ์˜ฌ๋ฆฌ๋Š”์ง€๊นŒ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“  ๋ฒค์น˜๋งˆํฌ๋‹ค.

๊ณต์ •์„ฑ ์ œ์•ฝ

๊ตฌํ˜„ ์ฐจ์ด๋กœ ์ธํ•œ ์žก์Œ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋ช‡ ๊ฐ€์ง€ ์ œ์•ฝ์„ ๋‘”๋‹ค.

  • ๋ชจ๋“  ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋™์ผํ•œ TodoMVC ๋™์ž‘์„ ๊ตฌํ˜„ํ•œ๋‹ค.
  • template / script / style ์‚ฐ์ถœ๋ฌผ์„ ๋ถ„๋ฆฌํ•ด์„œ ๋น„๊ตํ•œ๋‹ค.
  • ์Šคํƒ€์ผ์€ ์ „๋ถ€ scope ์ฒ˜๋ฆฌํ•œ๋‹ค. TSX ๊ธฐ๋ฐ˜ ๊ตฌํ˜„์€ CSS Modules๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ฐจํŠธ ๊ธฐ๋ณธ ์„ ํƒ์—์„œ๋Š” style ๋น„์ค‘์„ ๊ฐ•์กฐํ•˜์ง€ ์•Š์ง€๋งŒ, ํ†ต๊ณ„์—๋Š” ํฌํ•จํ•œ๋‹ค.

๊ธ€์€ style ์ฐจ์ด๊ฐ€ ์ด๋ฒˆ ์‹คํ—˜์—์„œ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ๊ตฌ์กฐ๋ณด๋‹ค scope metadata ์˜ํ–ฅ์ด ๋” ํฐ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„์„œ, ํ•ต์‹ฌ ๋น„๊ต ์ถ•์€ runtime๊ณผ script ์ชฝ์— ์žˆ๋‹ค๊ณ  ๋ณธ๋‹ค.

๋ฉ”์ด์ € ํ”„๋ ˆ์ž„์›Œํฌ ๊ทธ๋ฃน: React, Angular, Vue 3

1๊ฐœ ์ปดํฌ๋„ŒํŠธ ๊ธฐ์ค€ minified ๋ฒˆ๋“ค ํฌ๊ธฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•˜๋‹ค.

  • Angular: ์•ฝ 201.69KB
  • React: ์•ฝ 189.44KB
  • Vue 3: ์•ฝ 65.64KB

์™œ ์ด๋Ÿฐ ์ฐจ์ด๊ฐ€ ๋‚˜๋Š”์ง€ ๋ณด๋ฉด runtime ๋น„์šฉ์ด ๊ฑฐ์˜ ๊ทธ๋Œ€๋กœ ๋“œ๋Ÿฌ๋‚œ๋‹ค.

  • Angular runtime: ์•ฝ 195.39KB
  • React runtime: ์•ฝ 185.66KB
  • Vue 3 runtime: ์•ฝ 61.83KB

์ด ๋ฒค์น˜๋งˆํฌ์—์„œ๋Š” ์•ฑ ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก Angular๊ฐ€ ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ์ปค์ง€๊ณ , React๋„ ๋†’์€ ์ˆ˜์ค€์„ ์œ ์ง€ํ•˜๋ฉฐ, Vue 3๊ฐ€ ์ด ๊ทธ๋ฃน์—์„œ๋Š” ์ƒ๋Œ€์ ์œผ๋กœ ๋‚ฎ์€ ํŽธ์„ ์œ ์ง€ํ•œ๋‹ค. ์ฆ‰ ์ž‘์€ ๋ฐ๋ชจ ์ˆซ์ž๋งŒ์ด ์•„๋‹ˆ๋ผ runtime ๊ธฐ๋ฐ˜ ๋น„์šฉ ๊ตฌ์กฐ๊ฐ€ ์ „์ฒด ์„ฑ์žฅ ๊ณก์„ ์„ ์ขŒ์šฐํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

Fine-grained ๊ทธ๋ฃน: Solid, Vue Vapor, Svelte 5, QingKuai

์ตœ๊ทผ ํ”„๋ ˆ์ž„์›Œํฌ ํ๋ฆ„์€ virtual DOM์˜ ์ผ๋ฐ˜์ ์ธ diffing๋ณด๋‹ค, ๋” ์„ธ๋ฐ€ํ•œ dependency ๊ธฐ๋ฐ˜ ์—…๋ฐ์ดํŠธ๋กœ ์ด๋™ํ•˜๋Š” ๋ฐฉํ–ฅ์ด๋ผ๋Š” ์„ค๋ช…์ด ๋‚˜์˜จ๋‹ค. ํ•˜์ง€๋งŒ "fine-grained๋‹ˆ๊นŒ ๋ฌด์กฐ๊ฑด ์ž‘๊ณ  ์ข‹๋‹ค"๊ณ  ๋งํ•˜๊ธฐ๋Š” ์–ด๋ ต๋‹ค.

1๊ฐœ ์ปดํฌ๋„ŒํŠธ ๊ธฐ์ค€ minified ์ˆ˜์น˜๋Š”:

  • Solid: ์•ฝ 19.04KB
  • QingKuai: ์•ฝ 25.42KB
  • Svelte 5: ์•ฝ 39.25KB
  • Vue Vapor: ์•ฝ 47.27KB

์ฒ˜์Œ ์ถœ๋ฐœ์ ๋งŒ ๋ณด๋ฉด Solid๊ฐ€ ๊ฐ€์žฅ ์ž‘๋‹ค. ํ•˜์ง€๋งŒ ์„ฑ์žฅ ๊ณก์„ ์„ ๋ณด๋ฉด ์ด์•ผ๊ธฐ๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค.

  • QingKuai๋Š” ์ฆ๊ฐ€ ๊ณก์„ ์ด ๋” ์™„๋งŒํ•ด์„œ, ๋†’์€ ์ปดํฌ๋„ŒํŠธ ์ˆ˜ ๊ตฌ๊ฐ„์—์„œ๋Š” ๊ฐ€์žฅ ๋‚ฎ๊ฒŒ ๋๋‚œ๋‹ค.
  • Solid๋Š” ์ถœ๋ฐœ์€ ๊ฐ€์žฅ ์ž‘์ง€๋งŒ ์ฆ๊ฐ€ ์†๋„๊ฐ€ ๋” ๋น ๋ฅด๋‹ค.
  • Svelte 5์™€ Vue Vapor๋Š” ๋†’์€ ์ปดํฌ๋„ŒํŠธ ์ˆ˜ ๊ตฌ๊ฐ„์—์„œ QingKuai๋ณด๋‹ค ๋†’๋‹ค.
  • Vue Vapor๋Š” ํฐ ๊ทœ๋ชจ์—์„œ Solid, Svelte 5๋ณด๋‹ค๋Š” ๋‚ฎ๊ฒŒ ์œ ์ง€๋œ๋‹ค.

๊ทธ๋ž˜์„œ ์ด ๊ธ€์˜ ๊ตํ›ˆ์€ "fine-grained๊ฐ€ ์ข‹๋‹ค"๊ฐ€ ์•„๋‹ˆ๋ผ, "๋Ÿฐํƒ€์ž„์ด ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๊ณ  ์ปดํฌ๋„ŒํŠธ ์ˆ˜ ์ฆ๊ฐ€์— ๋”ฐ๋ผ ๋น„์šฉ์ด ์–ด๋–ป๊ฒŒ ๋Š˜์–ด๋‚˜๋Š”๊ฐ€"๊ฐ€ ๋” ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

Svelte 4๋Š” ๋ณ„๋„ ๋ฉ”๋ชจ๊ฐ€ ํ•„์š”ํ•˜๋‹ค

Svelte 4๋Š” ์ž‘์€ ์•ฑ์—์„œ ๋งค์šฐ ๋งค๋ ฅ์ ์ธ ์ถœ๋ฐœ์ ์„ ๋ณด์—ฌ ์ค€๋‹ค.

  • 1๊ฐœ ์ปดํฌ๋„ŒํŠธ ๊ธฐ์ค€ minified: ์•ฝ 11.08KB
  • runtime: ์•ฝ 0.71KB

ํ•˜์ง€๋งŒ ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ์ฆ๊ฐ€ ๊ณก์„ ์ด ๊ฝค ๊ฐ€ํŒŒ๋ฅด๋‹ค. ๊ธ€์€ ์ด๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•ด์„ํ•œ๋‹ค.

  • ์ค‘์•™ ์ง‘์ค‘ ๋Ÿฐํƒ€์ž„ ๋น„์šฉ์€ ๋งค์šฐ ์ž‘๋‹ค.
  • ๋Œ€์‹  ๊ฐ ์ปดํฌ๋„ŒํŠธ ์‚ฐ์ถœ๋ฌผ ์•ˆ์— ๋ฐ˜๋ณต๋˜๋Š” ๊ตฌํ˜„ ์กฐ๊ฐ์ด ๋” ๋งŽ์ด ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.
  • ๊ทธ๋ž˜์„œ ์•ฑ์ด ์ปค์งˆ์ˆ˜๋ก ์ „์ฒด ๋ฒˆ๋“ค ์ฆ๊ฐ€ ์†๋„๊ฐ€ ๋นจ๋ผ์ง„๋‹ค.

์‹ค๋ฌด์ ์œผ๋กœ๋Š” ์ž‘์€ ์•ฑ์ด๋‚˜ ๊ทน์†Œํ˜• ์œ„์ ฏ ์ˆ˜์ค€์—์„œ๋Š” Svelte 4๊ฐ€ ์—ฌ์ „ํžˆ ๋งค์šฐ ์ข‹์€ ์„ ํƒ์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ค‘๋Œ€ํ˜• ์•ฑ๊นŒ์ง€ ๊ฐˆ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋ฉด "์ดˆ๊ธฐ ์ตœ์†Œ์น˜"๋งŒ ๋ณด๊ณ  ๊ฒฐ์ •ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค๋Š” ๋œป์ด๋‹ค.

๊ฒฐ๊ตญ ์ค‘์š”ํ•œ ๊ฒƒ์€ ์‹œ์ž‘์  + ๊ธฐ์šธ๊ธฐ

๊ธ€์“ด์ด๋Š” ์ด ๋ฒค์น˜๋งˆํฌ๊ฐ€ ๋„ค ๊ฐ€์ง€ ์ ์„ ๋‹ค์‹œ ํ™•์ธ์‹œ์ผœ ์คฌ๋‹ค๊ณ  ์ •๋ฆฌํ•œ๋‹ค.

  1. ๋ฒˆ๋“ค ํฌ๊ธฐ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์„ ํƒ ์‹œ 1๊ธ‰ ์ง€ํ‘œ์—ฌ์•ผ ํ•œ๋‹ค.
  2. "no virtual DOM + fine-grained" ๋Š” ์ค‘์š”ํ•œ ๋ฐฉํ–ฅ์ด์ง€๋งŒ, ๊ทธ ์•ˆ์—์„œ๋„ ๊ตฌํ˜„ ์ฐจ์ด๋Š” ํฌ๋‹ค.
  3. 1๊ฐœ ์ปดํฌ๋„ŒํŠธ์ผ ๋•Œ ๊ฐ€์žฅ ์ž‘์€ ๊ฒฐ๊ณผ๋งŒ ๋ด์„œ๋Š” ์•ˆ ๋œ๋‹ค.
  4. ์ค‘๋Œ€ํ˜• ์•ฑ์—์„œ๋Š” ์„ฑ์žฅ ๊ธฐ์šธ๊ธฐ(slope)๊ฐ€ ๋‹จ์ผ ์ตœ์ €์ ๋ณด๋‹ค ๋” ์ค‘์š”ํ•  ๋•Œ๊ฐ€ ๋งŽ๋‹ค.

์ •๋ฆฌ

์ด ๊ธ€์€ ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ ๋…ผ์˜๋ฅผ ์กฐ๊ธˆ ๋” ๋ƒ‰์ •ํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค. ์ฒด๊ฐ ์„ฑ๋Šฅ์€ ๊ฒฐ๊ตญ ๋‹ค์šด๋กœ๋“œ, ํŒŒ์‹ฑ, ์‹คํ–‰์ด๋ผ๋Š” ํ˜„์‹ค์„ ํ†ต๊ณผํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ํ”„๋ ˆ์ž„์›Œํฌ ๋น„๊ต๋„ "๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ์ข‹์€๊ฐ€"๋ฅผ ๋„˜์–ด์„œ,

  • ์ดˆ๊ธฐ ๋Ÿฐํƒ€์ž„ ๋น„์šฉ์ด ์–ผ๋งˆ์ธ์ง€
  • ์ปดํฌ๋„ŒํŠธ ์ˆ˜๊ฐ€ ๋Š˜ ๋•Œ ์„ ํ˜•์œผ๋กœ ์ปค์ง€๋Š”์ง€, ๊ธ‰๊ฒฉํžˆ ์ปค์ง€๋Š”์ง€
  • ์ž‘์€ ์•ฑ ์ตœ์ ํ™”์ธ์ง€, ํฐ ์•ฑ ํ™•์žฅ์„ฑ๊นŒ์ง€ ๊ดœ์ฐฎ์€์ง€

๋ฅผ ๊ฐ™์ด ๋ด์•ผ ํ•œ๋‹ค.

ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ƒˆ๋กœ ๊ณ ๋ฅผ ๋•Œ๋‚˜, ์กฐ์ง ๋‚ด ๊ธฐ์ˆ  ์„ ํƒ ๊ทผ๊ฑฐ๋ฅผ ๋งŒ๋“ค ๋•Œ ๊ฝค ์œ ์šฉํ•œ ์ž๋ฃŒ๋‹ค. ํŠนํžˆ "์ž‘์€ ๋ฐ๋ชจ์—์„œ๋Š” ๋น ๋ฅด๊ณ  ์ž‘์•„ ๋ณด์˜€๋Š”๋ฐ ์‹ค์ œ ์ œํ’ˆ ๊ทœ๋ชจ์—์„œ๋Š” ์™œ ๋ฌด๊ฒ์ง€?" ๊ฐ™์€ ์งˆ๋ฌธ์— ์ข‹์€ ๋ฐ˜๋ก€์™€ ๋น„๊ต์ถ•์„ ์ค€๋‹ค.

Chrome 148 Beta, ํ”„๋ก ํŠธ์—”๋“œ ๊ด€๋ จ ๋ณ€ํ™” ํ•œ๊ตญ์–ด ๋ฒˆ์—ญ

  • ์›๋ฌธ ์ œ๋ชฉ: Chrome 148 beta
  • ์›๋ฌธ ๋งํฌ: https://developer.chrome.com/blog/chrome-148-beta
  • ๋ฒˆ์—ญ์ผ: 2026-04-20 KST
  • ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ฐœ๊ฒฌ: This Week in React #277

๋ฒˆ์—ญ

Chrome 148 ๋ฒ ํƒ€์—๋Š” ํ”„๋ก ํŠธ์—”๋“œ ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์ฒดํฌํ•  ๋งŒํ•œ CSS, UI, ์„ฑ๋Šฅ ๊ด€๋ จ ๋ณ€ํ™”๊ฐ€ ๊ฝค ๋“ค์–ด ์žˆ๋‹ค. ์›๋ฌธ ์ „์ฒด๋Š” ๋ธŒ๋ผ์šฐ์ € ํ”Œ๋žซํผ ์ „๋ฐ˜์„ ๋‹ค๋ฃจ์ง€๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” FE ๊ด€์ ์—์„œ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์„ ์ค‘์‹ฌ์œผ๋กœ ์˜ฎ๊ธด๋‹ค.

1) Name-only container queries

์ด์ œ ์š”์†Œ์— container-type ์„ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ , container-name ๋งŒ์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ ์ฟผ๋ฆฌ๋ฅผ ๊ฑธ ์ˆ˜ ์žˆ๋‹ค.

#container {
  container-name: --foo;
}

@container --foo {
  input { background-color: green; }
}

์ด ๋ณ€ํ™”๋Š” ํŠนํžˆ ๋””์ž์ธ ์‹œ์Šคํ…œ์ด๋‚˜ ๋ณตํ•ฉ ๋ ˆ์ด์•„์›ƒ์—์„œ ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค. ๊ธฐ์กด์—๋Š” ์ฟผ๋ฆฌ๋ฅผ ์“ฐ๊ธฐ ์œ„ํ•ด type ์ง€์ •๊นŒ์ง€ ๊ฐ™์ด ํ•ด์•ผ ํ–ˆ๋Š”๋ฐ, ์ด์ œ ์ด๋ฆ„ ๊ธฐ์ค€์œผ๋กœ ๋” ๊ฐ€๋ณ๊ฒŒ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„ ๋ฐ˜์‘ํ˜• ์„ค๊ณ„๋ฅผ ์กฐ๊ธˆ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ๊ฐ€์ ธ๊ฐˆ ์—ฌ์ง€๊ฐ€ ์ƒ๊ธด๋‹ค.

2) text-overflow: ellipsis ์™€ ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ

ellipsis ์ฒ˜๋ฆฌ๋œ ํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ํŽธ์ง‘ํ•˜๊ฑฐ๋‚˜ caret ์ด๋™ ๊ฐ™์€ ์ƒํ˜ธ์ž‘์šฉ์„ ์‹œ์ž‘ํ•˜๋ฉด, ์ผ์‹œ์ ์œผ๋กœ ellipsis ๋Œ€์‹  clip ์œผ๋กœ ๋ฐ”๋€Œ์–ด ์ˆจ๊ฒจ์ง„ ๋‚ด์šฉ๊นŒ์ง€ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. editable ์š”์†Œ๋ฟ ์•„๋‹ˆ๋ผ non-editable ์š”์†Œ์—๋„ ์ ์šฉ๋œ๋‹ค.

์ž‘์•„ ๋ณด์—ฌ๋„ UX ๊ด€์ ์—์„œ๋Š” ๊ฝค ๋ฐ˜๊ฐ€์šด ๋ณ€ํ™”๋‹ค. ๊ธด ํ…์ŠคํŠธ๊ฐ€ ์ž˜๋ฆฐ ์ƒํƒœ์—์„œ ์ปค์„œ ์ด๋™์ด๋‚˜ ์„ ํƒ ๋™์ž‘์ด ๋œ ๋‹ต๋‹ตํ•ด์ง„๋‹ค.

3) Drag and Drop์˜ dropEffect ๋ฅผ ๋ช…์„ธ๋Œ€๋กœ ์ˆ˜์ •

dragEnter, dragOver, dragLeave ์ด๋ฒคํŠธ์—์„œ dataTransfer.dropEffect ๊ฐ’์„ ๋ช…์„ธ์— ๋งž๊ฒŒ ์„ค์ •ํ•˜๋„๋ก ๊ณ ์นœ๋‹ค.

๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ UI๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ํŒ€์ด๋ผ๋ฉด ๋ธŒ๋ผ์šฐ์ € ๊ฐ„ ์ผ๊ด€์„ฑ ๋ฌธ์ œ๋ฅผ ์ฒดํฌํ•ด ๋ณผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค. ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ์— ์˜์กดํ•œ ๋กœ์ง์ด ์žˆ๋‹ค๋ฉด ๋ฒ ํƒ€ ๋‹จ๊ณ„์—์„œ ํ™•์ธํ•ด ๋‘๋Š” ๊ฒŒ ์ข‹๋‹ค.

4) <video>, <audio> ๋„ loading="lazy" ์ง€์›

์ด์ œ ์ด๋ฏธ์ง€์™€ iframe์ฒ˜๋Ÿผ ๋น„๋””์˜ค, ์˜ค๋””์˜ค ์š”์†Œ์—๋„ loading ์†์„ฑ์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค. viewport ๊ทผ์ฒ˜์— ์˜ฌ ๋•Œ๊นŒ์ง€ ๋ฏธ๋””์–ด ๋ฆฌ์†Œ์Šค ๋กœ๋”ฉ์„ ๋ฏธ๋ฃฐ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์ดˆ๊ธฐ ํŽ˜์ด์ง€ ๋กœ๋“œ ๋น„์šฉ๊ณผ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ๋Ÿ‰์„ ์ค„์ด๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค.

์ฝ˜ํ…์ธ  ์‚ฌ์ดํŠธ, ๋žœ๋”ฉ ํŽ˜์ด์ง€, ๋ฏธ๋””์–ด ์นด๋“œ๊ฐ€ ๋งŽ์€ ํ”ผ๋“œ UI์—์„œ๋Š” ๊ฝค ์ง์ ‘์ ์ธ ์ตœ์ ํ™” ํฌ์ธํŠธ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.

5) @supports ์•ˆ์˜ at-rule() ์ง€์›

CSS @supports ์— at-rule() ํ•จ์ˆ˜๊ฐ€ ์ถ”๊ฐ€๋˜์–ด, ํŠน์ • at-rule ์ง€์› ์—ฌ๋ถ€๋ฅผ feature detection ํ•  ์ˆ˜ ์žˆ๋‹ค. CSS ๊ธฐ๋Šฅ ๊ฐ์ง€๋ฅผ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค.

์‹ ๊ทœ CSS ๋ฌธ๋ฒ•์„ ์ ์ง„์ ์œผ๋กœ ๋„์ž…ํ•  ๋•Œ fallback ์ „๋žต์„ ์กฐ๊ธˆ ๋” ๊น”๋”ํ•˜๊ฒŒ ์งค ์ˆ˜ ์žˆ๋‹ค.

6) revert-rule ํ‚ค์›Œ๋“œ ์ถ”๊ฐ€

revert-rule ์€ ํ˜„์žฌ ๊ทœ์น™์„ ๋ฌดํšจํ™”ํ•˜๊ณ  ์ด์ „ ๊ทœ์น™ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ๊ฐœ๋…์ด๋‹ค. revert-layer ์™€ ๋น„์Šทํ•˜์ง€๋งŒ, ๋ ˆ์ด์–ด๊ฐ€ ์•„๋‹ˆ๋ผ ์ง์ „ ๊ทœ์น™ ๋‹จ์œ„๋กœ ๋˜๊ฐ๋Š”๋‹ค.

์กฐ๊ฑด๋ถ€ ์Šคํƒ€์ผ์—์„œ ํŠนํžˆ ์“ธ๋ชจ๊ฐ€ ์žˆ๋‹ค. ์กฐ๊ฑด์ด ๋งž์ง€ ์•Š์œผ๋ฉด ํ˜„์žฌ ์„ ์–ธ๋งŒ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•˜๋Š” ์‹์˜ ํ‘œํ˜„์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

div {
  display: if(style(--layout: fancy): grid; else: revert-rule);
}

๋ณต์žกํ•œ CSS ์กฐ๊ฑด ๋ถ„๊ธฐ๋‚˜ ํ…Œ๋งˆ ์‹œ์Šคํ…œ์—์„œ ์œ ์šฉํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค.

7) text-decoration-skip-ink: all

๊ธฐ์กด auto, none ์— ๋”ํ•ด all ๊ฐ’์ด ์ง€์›๋œ๋‹ค. CJK ๋ฌธ์ž๊นŒ์ง€ ํฌํ•จํ•ด ๋ชจ๋“  glyph์— ๋Œ€ํ•ด ink-skipping์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•œ์ค‘์ผ ํ…์ŠคํŠธ๊ฐ€ ๋งŽ์€ ์„œ๋น„์Šค๋‚˜, underline ์Šคํƒ€์ผ์„ ์ •๊ตํ•˜๊ฒŒ ๋งž์ถ”๋Š” ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ ์ค‘์‹ฌ ์ œํ’ˆ์ด๋ผ๋ฉด ์ฒดํฌํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค.

8) ์„ฑ๋Šฅ ์ธก์ •๊ณผ ํ”Œ๋žซํผ ํ™•์žฅ ํฌ์ธํŠธ๋„ ๋ˆˆ์—ฌ๊ฒจ๋ณผ ๋งŒํ•จ

์›๋ฌธ์—๋Š” CSS ์™ธ์—๋„ ๋‹ค์Œ ๊ธฐ๋Šฅ์ด ๋“ค์–ด ์žˆ๋‹ค.

  • PerformanceResourceTiming.contentType: ๋ฆฌ์†Œ์Šค ํƒ€์ด๋ฐ ๋ฐ์ดํ„ฐ์— ์ฝ˜ํ…์ธ  ํƒ€์ž…์„ ์ง์ ‘ ๋‹ด๋Š”๋‹ค.
  • SharedWorker Android ์ง€์› ๋ฐ extended lifetime ์˜ต์…˜
  • Prompt API ์›น ์ง€์› ํ™•๋Œ€
  • Web Serial API Android ์ง€์›

์ฆ‰ ๋‹จ์ˆœ ์Šคํƒ€์ผ ๋ณ€ํ™”๋งŒ์ด ์•„๋‹ˆ๋ผ, ๋ธŒ๋ผ์šฐ์ € ์„ฑ๋Šฅ ๊ณ„์ธก๊ณผ ์˜จ๋””๋ฐ”์ด์Šค AI, ๋ฉ€ํ‹ฐ ์ปจํ…์ŠคํŠธ ์‹คํ–‰ ๋ชจ๋ธ๊นŒ์ง€ ์ ์  ๋„“์–ด์ง€๊ณ  ์žˆ๋‹ค๋Š” ์‹ ํ˜ธ๋‹ค.

9) Origin Trial ์ค‘ FE ์‹ค๋ฌด์ž๊ฐ€ ํŠนํžˆ ๋ณผ ๋งŒํ•œ ๊ฒƒ

Chrome 148์˜ origin trial ์ค‘์—๋Š” FE ๊ด€์ ์—์„œ ํŠนํžˆ ํฅ๋ฏธ๋กœ์šด ํ•ญ๋ชฉ๋„ ์žˆ๋‹ค.

  • Container Timing API: DOM ํ•˜์œ„ ์„น์…˜์ด ์‹ค์ œ ํ™”๋ฉด์— ๊ทธ๋ ค์ง„ ์‹œ์ ์„ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„ ์„ฑ๋Šฅ ๊ณ„์ธก์— ์œ ์šฉํ•˜๋‹ค.
  • Declarative CSS module scripts: shadow root, declarative shadow DOM ๊ณผ ํ•จ๊ป˜ ๊ณต์œ  ์Šคํƒ€์ผ์„ ๋” ์„ ์–ธ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ์‹คํ—˜์ด๋‹ค.
  • HTML-in-canvas: DOM ์š”์†Œ๋ฅผ canvas/WebGL/WebGPU ํ…์Šค์ฒ˜์— ๊ทธ๋ฆฌ๋ฉด์„œ๋„ ์ธํ„ฐ๋ž™์…˜๊ณผ ์ ‘๊ทผ์„ฑ์„ ์œ ์ง€ํ•˜๋ ค๋Š” ์‹œ๋„๋‹ค.
  • Long Animation Frames style duration: long animation frame ์•ˆ์—์„œ style ๊ณ„์‚ฐ ์‹œ๊ฐ„์ด ์–ผ๋งˆ๋‚˜ ๋“ค์—ˆ๋Š”์ง€ ๋ถ„๋ฆฌ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋””์ž์ธ ํˆด, ๊ทธ๋ž˜ํ”ฝ ํŽธ์ง‘๊ธฐ, ๋ณต์žกํ•œ ๋Œ€์‹œ๋ณด๋“œ, ์„ฑ๋Šฅ ๋ถ„์„ ๋„๊ตฌ๋ฅผ ๋งŒ๋“œ๋Š” ํŒ€์ด๋ผ๋ฉด ์ด๋Ÿฐ ์‹คํ—˜ ๊ธฐ๋Šฅ์˜ ๋ฐฉํ–ฅ์„ฑ์„ ๋ฏธ๋ฆฌ ๋ณด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค.

์ •๋ฆฌ

Chrome 148 ๋ฒ ํƒ€๋ฅผ FE ๊ด€์ ์—์„œ ์š”์•ฝํ•˜๋ฉด ์ด๋ ‡๋‹ค.

  1. ์ปจํ…Œ์ด๋„ˆ ์ฟผ๋ฆฌ, ์กฐ๊ฑด๋ถ€ CSS, ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ์ฒ˜๋Ÿผ ๋””์ž์ธ ์‹œ์Šคํ…œ์— ์ง์ ‘ ์˜ํ–ฅ์„ ์ฃผ๋Š” ๋ณ€ํ™”๊ฐ€ ๋“ค์–ด์™”๋‹ค.
  2. ๋ฏธ๋””์–ด lazy loading, ์„ฑ๋Šฅ ๊ณ„์ธก API ํ™•์žฅ์ฒ˜๋Ÿผ ์‹ค์ œ ์›น ์„ฑ๋Šฅ ์ตœ์ ํ™” ํฌ์ธํŠธ๋„ ๋Š˜์—ˆ๋‹ค.
  3. Container Timing, HTML-in-canvas ๊ฐ™์€ ์‹คํ—˜ ๊ธฐ๋Šฅ์€ ํ–ฅํ›„ ๊ณ ์„ฑ๋Šฅ UI ์„ค๊ณ„ ๋ฐฉํ–ฅ์„ ๋ฏธ๋ฆฌ ๋ณด์—ฌ ์ค€๋‹ค.

๋‹น์žฅ ์ „๋ถ€ ์จ์•ผ ํ•˜๋Š” ๊ฑด ์•„๋‹ˆ์ง€๋งŒ, ๋””์ž์ธ ์‹œ์Šคํ…œ๊ณผ ํผํฌ๋จผ์Šค ํˆด๋ง์„ ๋‹ค๋ฃจ๋Š” ํŒ€์ด๋ผ๋ฉด ์ด๋ฒˆ ๋ฒ ํƒ€๋Š” ๊ฝค ๋ณผ ๋งŒํ•˜๋‹ค.

frontend-briefing-2026-04-20-KST

  • ๋ฒˆ์—ญ์ผ: 2026-04-20 KST
  • ์„ ๋ณ„ ๊ฐœ์ˆ˜: 6

ํฌํ•จ๋œ ๊ธ€

  1. React Server Components Your Way
  2. The uphill climb of making diff lines performant
  3. The Vertical Codebase
  4. Now more then ever, you need to master custom ESLint rules
  5. Frontend Framework Bundle Size Benchmark: React/Vue/Angular vs Fine-Grained Runtimes
  6. Chrome 148 beta

์ปค๋ฎค๋‹ˆํ‹ฐ ์šฐ์„  ์†Œ์Šค(HN, DEV, This Week in React ๋“ฑ)์—์„œ ์„ ๋ณ„ํ•˜๊ณ , ํ•„์š” ์‹œ ๊ณต์‹ ์†Œ์Šค๋ฅผ ๋ณด์™„ํ–ˆ๋‹ค.

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