๐Ÿ”ฅ ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง๋ถ€ํ„ฐ React Fiber๊นŒ์ง€

๊ด€ํƒœยท2026๋…„ 2์›” 18์ผ

๋ชจ๋‹ฅ๋ถˆ

๋ชฉ๋ก ๋ณด๊ธฐ
5/7
post-thumbnail

๋“ค์–ด๊ฐ€๋ฉฐ

์†”์งํžˆ ๋งํ•˜๋ฉด ์ €๋„ ์˜ˆ์ „์— ์ฝ”๋”ฉ์„ ์ฒ˜์Œ ๋ฐฐ์šธ ๋•Œ <script> ํƒœ๊ทธ๋ฅผ <body> ๋งจ ์•„๋ž˜์— ๋‘๋Š” ๊ฒŒ ๊ทธ๋ƒฅ "๊ด€๋ก€"์ธ ์ค„๋งŒ ์•Œ์•˜์–ด์š”.
์‹ค์ œ๋กœ ์Šคํฌ๋ฆฝํŠธ๋ฅผ <head> ์•ˆ์— ๋’€๋‹ค๊ฐ€ ํŽ˜์ด์ง€ ๋ Œ๋”๋ง์ด ๋ˆˆ์— ๋„๊ฒŒ ๋А๋ ค์ง€๋Š” ๊ฑธ ๊ฒช๊ณ  ๋‚˜์„œ์•ผ ๊ถ๊ธˆ์ฆ์ด ์ƒ๊ฒผ์—ˆ์–ด์š”.
"์™œ ๋А๋ ธ์ง€?"๋ผ๋Š” ์งˆ๋ฌธ ํ•˜๋‚˜๊ฐ€ ๋ธŒ๋ผ์šฐ์ € ๋‚ด๋ถ€ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์ „์ฒด๋ฅผ ์ดํ•ดํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์คฌ์–ด์š”.

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


๋ธŒ๋ผ์šฐ์ € ์—”์ง„๊ณผ ํŒŒ์‹ฑ ์ฐจ๋‹จ ๊ตฌ์กฐ

ํ˜„๋Œ€ ๋ธŒ๋ผ์šฐ์ €๋Š” ์„ฑ๋Šฅ์„ ์œ„ํ•ด ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค ์•„ํ‚คํ…์ฒ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ๋ณด๋Š” ์›นํŽ˜์ด์ง€ ํ•˜๋‚˜๋Š” ๋ณดํ†ต ํ•˜๋‚˜์˜ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค(Renderer Process) ๋‚ด๋ถ€์—์„œ ๋™์ž‘ํ•ด์š”.

์ด ํ”„๋กœ์„ธ์Šค ์•ˆ์˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ๋งค์šฐ ๋ฐ”๋น ์š”.

  • ๋ Œ๋”๋ง ์—”์ง„: HTML๊ณผ CSS๋ฅผ ํ•ด์„ํ•ด์„œ ํ™”๋ฉด์— ์‹œ๊ฐ์ ์œผ๋กœ ์ถœ๋ ฅํ•ด์š”. (Chrome์˜ Blink)
  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ํ•ด์„ํ•˜๊ณ  ์‹คํ–‰ํ•ด์š”. (Chrome์˜ V8)

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

์ ์ง„์  ํŒŒ์‹ฑ๊ณผ Script ์ฐจ๋‹จ

๋ธŒ๋ผ์šฐ์ €๋Š” HTML ํŒŒ์ผ์„ ์ „๋ถ€ ๋‹ค์šด๋กœ๋“œ๋ฐ›์„ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์•„์š”. ๋„คํŠธ์›Œํฌ ๋ฒ„ํผ์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ค๋Š” ๋Œ€๋กœ ์ฆ‰์‹œ ํ† ํฐํ™”๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ์ ์ง„์  ํŒŒ์‹ฑ(Incremental Parsing) ๋ฐฉ์‹์„ ์จ์š”. ์ด ๋ฐฉ์‹์ด LCP(Largest Contentful Paint)์™€ ์ง๊ฒฐ๋ผ์š”.

ํŒŒ์‹ฑ ํ๋ฆ„์€ ์•„๋ž˜์™€ ๊ฐ™์•„์š”.

  1. Bytes โ†’ Characters: 8๋น„ํŠธ ๋ฐ์ดํ„ฐ๋ฅผ UTF-8 ์ธ์ฝ”๋”ฉ์— ๋”ฐ๋ผ ๋ฌธ์ž๋กœ ๋ณ€ํ™˜ํ•ด์š”.
  2. Tokens: ๋ฌธ์ž๋ฅผ <html>, <body> ๊ฐ™์€ ์˜๋ฏธ ์žˆ๋Š” ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์š”.
  3. Nodes: ๊ฐ ํ† ํฐ์„ ์†์„ฑ์„ ๊ฐ€์ง„ ๊ฐ์ฒด(Node)๋กœ ๋ณ€ํ™˜ํ•ด์š”.
  4. DOM Tree: ๋…ธ๋“œ๋“ค์„ ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„์˜ ํŠธ๋ฆฌ๋กœ ์—ฐ๊ฒฐํ•ด์š”.

๊ทธ๋Ÿฐ๋ฐ ์ด ๊ณผ์ •์—์„œ <script> ํƒœ๊ทธ๋ฅผ ๋งŒ๋‚˜๋Š” ์ˆœ๊ฐ„ ํŒŒ์‹ฑ์ด ๋ฉˆ์ถฐ์š”. ์ด์œ ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ "๊ฐ•๋ ฅํ•œ ๊ถŒํ•œ" ๋•Œ๋ฌธ์ด์—์š”. document.write() ๊ฐ™์€ ๋ช…๋ น์œผ๋กœ ํŒŒ์‹ฑ ์ค‘์ธ HTML ํ๋ฆ„์— ์ƒˆ ํƒœ๊ทธ๋ฅผ ์ฆ‰์„์—์„œ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํŒŒ์‹ฑ์„ ๊ณ„์†ํ•˜๋ฉด์„œ JS๋„ ๋™์‹œ์— ์‹คํ–‰ํ–ˆ๋Š”๋ฐ ๊ทธ JS๊ฐ€ ๋ฐฉ๊ธˆ ํŒŒ์‹ฑํ•œ ๋…ธ๋“œ๋ฅผ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ์ƒˆ ๋…ธ๋“œ๋ฅผ ๋ผ์›Œ ๋„ฃ์œผ๋ฉด ์ƒํƒœ ๋ถˆ์ผ์น˜(Race Condition)์— ๋น ์ ธ์š”.

๊ฒฐ๊ตญ "CSS๋Š” JS ์‹คํ–‰์„ ๋ง‰๊ณ (์Šคํƒ€์ผ ์ •๋ณด ์ฐธ์กฐ๋ฅผ ์œ„ํ•ด) JS๋Š” HTML ํŒŒ์‹ฑ์„ ๋ง‰๋Š”" ์—ฐ์‡„ ์ฐจ๋‹จ์ด ๋ฐœ์ƒํ•ด์š”. ์ด๋ฅผ Critical Rendering Path(CRP)์˜ ๋ณ‘๋ชฉ์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

async์™€ defer์˜ ์ฐจ์ด

ํŒŒ์‹ฑ ์ฐจ๋‹จ์„ ํ”ผํ•˜๋Š” ๋ฐฉ๋ฒ•์ด async์™€ defer์ธ๋ฐ ๋‘˜์ด ํ—ท๊ฐˆ๋ฆฌ๊ธฐ ์‰ฌ์›Œ์š”.

<!-- async: ๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ ์ฆ‰์‹œ ์‹คํ–‰, ํŒŒ์‹ฑ ๋„์ค‘์— ๋ผ์–ด๋“ค ์ˆ˜ ์žˆ์Œ -->
<script async src="analytics.js"></script>

<!-- defer: ๋‹ค์šด๋กœ๋“œ๋Š” ๋ณ‘๋ ฌ๋กœ ํ•˜๋˜, DOM ํŒŒ์‹ฑ์ด ๋๋‚œ ๋’ค์— ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰ -->
<script defer src="app.js"></script>

async๋Š” ๋‹ค์šด๋กœ๋“œ๊ฐ€ ๋๋‚˜๋Š” ์ˆœ๊ฐ„ ๋ฐ”๋กœ ์‹คํ–‰๋ผ์š”. ํŒŒ์‹ฑ ์ค‘์ด์–ด๋„ ๋ฉˆ์ถ”๊ณ  JS๋ฅผ ๋จผ์ € ์‹คํ–‰ํ•˜๊ณ  ์‹คํ–‰ ์ˆœ์„œ๋„ ๋ณด์žฅํ•˜์ง€ ์•Š์•„์š”. ์„œ๋กœ ๋…๋ฆฝ์ ์ธ ์Šคํฌ๋ฆฝํŠธ(Google Analytics, ๊ด‘๊ณ  ํƒœ๊ทธ ๋“ฑ)์— ์จ์•ผ ํ•˜๋Š” ์ด์œ ์˜ˆ์š”.

defer๋Š” ๋‹ค์šด๋กœ๋“œ๋ฅผ ํŒŒ์‹ฑ๊ณผ ๋ณ‘๋ ฌ๋กœ ์ง„ํ–‰ํ•˜์ง€๋งŒ ์‹คํ–‰์€ DOM์ด ์™„์„ฑ๋œ ๋’ค๋กœ ๋ฏธ๋ค„์š”. ์„ ์–ธ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰๋„ ๋ณด์žฅํ•ด์š”. ์‹ค๋ฌด์—์„œ ๊ธฐ๋ณธ๊ฐ’์€ defer๋กœ ๋‘๊ณ  async๋Š” ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํŠธ์™€ ์ „ํ˜€ ๊ด€๊ณ„์—†๋Š” ๋…๋ฆฝ ์Šคํฌ๋ฆฝํŠธ์—๋งŒ ์„ ๋ณ„ํ•ด์„œ ์“ฐ๋Š” ๊ฒŒ ์•ˆ์ „ํ•ด์š”.

์‹ค๋ฌด์—์„œ ๊ธฐ๋ณธ๊ฐ’์€ defer๋กœ ๋‘๊ณ  async๋Š” ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํŠธ์™€ ์ „ํ˜€ ๊ด€๊ณ„์—†๋Š” ๋…๋ฆฝ ์Šคํฌ๋ฆฝํŠธ์—๋งŒ ์„ ๋ณ„ํ•ด์„œ ์“ฐ๋Š” ๊ฒŒ ์•ˆ์ „ํ•ด์š”.

CSSOM๊ณผ Preload Scanner

HTML์ด DOM์„ ๋งŒ๋“ค ๋•Œ CSS๋Š” CSSOM(CSS Object Model)์„ ๋งŒ๋“ค์–ด์š”. CSSOM์˜ ํ•ต์‹ฌ์€ Cascade(์šฐ์„ ์ˆœ์œ„ ๊ฒฐ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜)์˜ˆ์š”. ๋ถ€๋ชจ๋กœ๋ถ€ํ„ฐ ๋‚ด๋ ค์˜ค๋Š” ๊ทœ์น™๊ณผ ๋ช…์‹œ๋„(Specificity) ์„ ์–ธ ์ˆœ์„œ๋ฅผ ์ข…ํ•ฉํ•ด์„œ ์ตœ์ข… ์Šคํƒ€์ผ ๊ฐ’์„ ๊ฒฐ์ •ํ•ด์š”.

์˜คํ•ดํ•˜๊ธฐ ์‰ฌ์šด ๋ถ€๋ถ„์ด ์žˆ์–ด์š”. CSSOM์ด ์ƒ์„ฑ๋˜๋Š” ๋™์•ˆ DOM ํŒŒ์‹ฑ ์ž์ฒด๋Š” ๋ฉˆ์ถ”์ง€ ์•Š์•„์š”. ๋‹ค๋งŒ ์ƒ์„ฑ๋œ DOM์„ ํ™”๋ฉด์— ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•œ Render Tree ์ƒ์„ฑ๊ณผ JS ์‹คํ–‰์€ CSSOM์ด ์™„์„ฑ๋  ๋•Œ๊นŒ์ง€ ์ฐจ๋‹จ๋ผ์š”. CSS๋Š” HTML ๊ตฌ์กฐ ํ•ด์„ ์ž์ฒด์˜ ๋ณ‘๋ชฉ์ด ์•„๋‹ˆ๋ผ ํ™”๋ฉด ์ถœ๋ ฅ๊ณผ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰์˜ ๋ณ‘๋ชฉ์ด์—์š”.

ํ˜„๋Œ€ ๋ธŒ๋ผ์šฐ์ €๋Š” Preload Scanner๋ฅผ ํƒ‘์žฌํ•˜๊ณ  ์žˆ์–ด์š”. ๋ฉ”์ธ ํŒŒ์„œ๊ฐ€ JS์— ๋ง‰ํ˜€ ๋ฉˆ์ถฐ์žˆ์„ ๋•Œ ๋ณ„๋„์˜ ๊ฐ€๋ฒผ์šด ํŒŒ์„œ๊ฐ€ ๋’ค์ชฝ HTML์„ ๋ฏธ๋ฆฌ ํ›‘์–ด์„œ <img>, <link>, <script> ๋ฆฌ์†Œ์Šค๋ฅผ ์ฐพ์•„ ๋ฏธ๋ฆฌ ๋‹ค์šด๋กœ๋“œํ•ด์š”.

Preload Scanner์˜ ํ•ต์‹ฌ ํ•œ๊ณ„๋Š” ์ •์ ์ธ ๋งˆํฌ์—…๋งŒ ์ฝ๋Š”๋‹ค๋Š” ๊ฑฐ์˜ˆ์š”. JS๋กœ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฆฌ์†Œ์Šค๋Š” ๋ฏธ๋ฆฌ ์ฐพ์•„๋‚ผ ์ˆ˜ ์—†์–ด์š”.

<!-- Preload Scanner๊ฐ€ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ์Œ - ๋งˆํฌ์—…์— ์ง์ ‘ ์จ์žˆ์Œ -->
<img src="hero.webp" alt="ํžˆ์–ด๋กœ ์ด๋ฏธ์ง€">

<!-- Preload Scanner๊ฐ€ ๊ฐ์ง€ ๋ชปํ•จ - JS๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „๊นŒ์ง€ ์กด์žฌํ•˜์ง€ ์•Š์Œ -->
<script>
  const img = document.createElement('img');
  img.src = 'hero.webp'; // ์Šค์บ๋„ˆ ์ž…์žฅ์—์„  ๋ณด์ด์ง€ ์•Š๋Š” ๋ฆฌ์†Œ์Šค
  document.body.appendChild(img);
</script>

JS๋กœ ๋™์  ์ƒ์„ฑํ•˜๋Š” ์ด๋ฏธ์ง€๋‚˜ ํฐํŠธ๊ฐ€ LCP์— ์˜ํ–ฅ์„ ์ค€๋‹ค๋ฉด <link rel="preload">๋กœ ์ง์ ‘ ํžŒํŠธ๋ฅผ ์ค˜์•ผ ํ•ด์š”.

<link rel="preload" href="hero-image.webp" as="image">
<script src="analytics.js" defer></script>
<link rel="stylesheet" href="critical.css">

preload๋ฅผ ๋‚จ๋ฐœํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์šฐ์„ ์ˆœ์œ„ ํŒ๋‹จ์„ ํฌ๊ธฐํ•ด์„œ ์ •์ž‘ ์ค‘์š”ํ•œ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋Šฆ๊ฒŒ ๋ถˆ๋Ÿฌ์™€์ง€๋Š” ์—ญํšจ๊ณผ๊ฐ€ ์ƒ๊ฒจ์š”. LCP์— ์˜ํ–ฅ์„ ์ฃผ๋Š” hero ์ด๋ฏธ์ง€๋‚˜ ์ฒซ ํ™”๋ฉด์— ํ•„์š”ํ•œ ํฐํŠธ ์ •๋„์—๋งŒ ์„ ๋ณ„์ ์œผ๋กœ ์จ์•ผ ํ•ด์š”.


๋ ˆ์ด์•„์›ƒ๊ณผ ํŽ˜์ธํŠธ: Reflow ๋น„์šฉ

ํŒŒ์‹ฑ์ด ๋๋‚˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ์‹ค์ œ๋กœ ํ”ฝ์…€์„ ์ฐ๋Š” ์ž‘์—…์„ ์‹œ์ž‘ํ•ด์š”.

๋ Œ๋” ํŠธ๋ฆฌ์™€ ๋ ˆ์ด์•„์›ƒ

๋ธŒ๋ผ์šฐ์ €๋Š” DOM ํŠธ๋ฆฌ ๋ฃจํŠธ๋ถ€ํ„ฐ ์ˆœํšŒํ•˜๋ฉด์„œ ํ™”๋ฉด์— ์‹ค์ œ๋กœ ๋ณด์ด๋Š” ๋…ธ๋“œ๋“ค๋งŒ ๊ณจ๋ผ CSSOM ์Šคํƒ€์ผ์„ ์ ์šฉํ•œ ๋ Œ๋” ํŠธ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด์š”.

  • display: none โ†’ ๋ Œ๋” ํŠธ๋ฆฌ์— ํฌํ•จ๋˜์ง€ ์•Š์•„์š”
  • visibility: hidden โ†’ ๊ณต๊ฐ„์€ ์ฐจ์ง€ํ•˜๋‹ˆ๊นŒ ํฌํ•จ๋ผ์š”
  • ::before, ::after ๊ฐ€์ƒ ์š”์†Œ๋„ ์ด ๋‹จ๊ณ„์—์„œ ๋…ธ๋“œํ™”๋ผ์š”
๋‹จ๊ณ„๋ฐœ์ƒ ์œ ๋ฐœ ์†์„ฑ (์˜ˆ์‹œ)๋น„๊ณ 
Reflowwidth, height, margin, top, left์ฃผ๋ณ€ ์š”์†Œ๊นŒ์ง€ ์žฌ๊ณ„์‚ฐ (๋น„์Œˆ)
Repaintcolor, background-color, visibility์‹œ๊ฐ์  ์š”์†Œ๋งŒ ๋ณ€๊ฒฝ
Compositetransform, opacityGPU ๊ฐ€์† ํ™œ์šฉ (๊ฐ€์žฅ ์ €๋ ด)

๋ Œ๋” ํŠธ๋ฆฌ๊ฐ€ ์™„์„ฑ๋์–ด๋„ ๊ฐ ์š”์†Œ์˜ ์ •ํ™•ํ•œ ์œ„์น˜์™€ ํฌ๊ธฐ๋Š” ์•„์ง ๋ชฐ๋ผ์š”. ๋ ˆ์ด์•„์›ƒ(Layout/Reflow) ๋‹จ๊ณ„์—์„œ ๋ทฐํฌํŠธ ํฌ๊ธฐ์— ๋งž์ถฐ ๊ณ„์‚ฐํ•ด์š”. %, em, vh ๊ฐ™์€ ์ƒ๋Œ€ ๋‹จ์œ„๋“ค์ด ์ด ๋‹จ๊ณ„์—์„œ ๋ฌผ๋ฆฌ์ ์ธ ํ”ฝ์…€ ๊ฐ’์œผ๋กœ ํ™•์ •๋ผ์š”.

Layout Thrashing

๋ธŒ๋ผ์šฐ์ €๋Š” ์Šคํƒ€์ผ ๋ณ€๊ฒฝ์„ ํ์— ์Œ“์•„๋’€๋‹ค๊ฐ€ ํ•œ๊บผ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๋ ค๊ณ  ํ•ด์š”. ๊ทธ๋Ÿฐ๋ฐ ์Šคํƒ€์ผ์„ ๋ฐ”๊พผ ์งํ›„์— ํฌ๊ธฐ๋‚˜ ์œ„์น˜๋ฅผ ์ฝ๋Š” ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ์ฆ‰์‹œ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด์•ผ ํ•ด์š”. ์ด๊ฒŒ ๊ฐ•์ œ ๋™๊ธฐ ๋ ˆ์ด์•„์›ƒ(Forced Synchronous Layout)์ด์—์š”.

๋ฃจํ”„ ์•ˆ์—์„œ ์ด๊ฑธ ๋ฐ˜๋ณตํ•˜๋ฉด ๋งค ๋ฐ˜๋ณต๋งˆ๋‹ค Reflow๊ฐ€ ํ„ฐ์ง€๋Š” Layout Thrashing์ด ๋ฐœ์ƒํ•ด์š”.

// ์„ฑ๋Šฅ์„ ๋ง๊ฐ€๋œจ๋ฆฌ๋Š” ํŒจํ„ด
function updateBad(elements) {
  for (let i = 0; i < elements.length; i++) {
    // offsetWidth๋ฅผ ์ฝ์„ ๋•Œ๋งˆ๋‹ค Reflow ๊ฐ•์ œ ๋ฐœ์ƒ
    elements[i].style.width = (container.offsetWidth + 10) + 'px';
  }
}

// ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ๋ฅผ ๋ถ„๋ฆฌํ•œ ํŒจํ„ด
function updateGood(elements) {
  const width = container.offsetWidth; // ํ•œ ๋ฒˆ๋งŒ ์ฝ๊ธฐ
  requestAnimationFrame(() => {
    for (let i = 0; i < elements.length; i++) {
      elements[i].style.width = (width + 10) + 'px'; // ์“ฐ๊ธฐ๋งŒ ๋ฐ˜๋ณต
    }
  });
}

Composite Layer์™€ GPU ๊ฐ€์†

ํ˜„๋Œ€ ๋ธŒ๋ผ์šฐ์ €๋Š” ํ™”๋ฉด ์ „์ฒด๋ฅผ ํ•œ๊บผ๋ฒˆ์— ๊ทธ๋ฆฌ์ง€ ์•Š์•„์š”. ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ ˆ์ด์–ด(Layer)๋กœ ๋‚˜๋ˆ ์„œ ๊ทธ๋ฆฐ ๋’ค ํ•ฉ์ณ์š”(Composite).

top์ด๋‚˜ left๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด Reflow โ†’ Repaint โ†’ Composite ์ „ ๊ณผ์ •์ด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ผ์–ด๋‚˜์š”. JS๊ฐ€ ๋ฐ”์˜๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋š๋š ๋Š๊ธฐ๋Š”๋ฐ ์ด๊ฑธ Jank๋ผ๊ณ  ํ•ด์š”.

๋ฐ˜๋ฉด transform๊ณผ opacity๋Š” ํ•ด๋‹น ์š”์†Œ๋ฅผ ๋ณ„๋„์˜ Composite Layer๋กœ ์Šน๊ฒฉ์‹œ์ผœ์š”. ์ด๋ฏธ ๊ทธ๋ ค์ง„ ๋น„ํŠธ๋งต ์ด๋ฏธ์ง€๋ฅผ GPU๊ฐ€ ์ขŒํ‘œ๋งŒ ๋ฐ”๊ฟ”์„œ ์˜ฌ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ 60fps๋ฅผ ์œ ์ง€ํ•ด์š”.

/* ๋ ˆ์ด์–ด ์Šน๊ฒฉ์„ ๋ฏธ๋ฆฌ ํžŒํŠธ๋กœ ์•Œ๋ ค์ฃผ๋Š” ๋ฐฉ๋ฒ• */
.animated-element {
  will-change: transform;
}

/* transform์œผ๋กœ ์ด๋™ - GPU์—์„œ ์ฒ˜๋ฆฌ, ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ๋ฌด๊ด€ */
.move-with-transform {
  transform: translateX(100px);
}

/* left๋กœ ์ด๋™ - Reflow ๋ฐœ์ƒ, ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ์ ์œ  */
.move-with-left {
  left: 100px;
}

will-change๋„ ๋‚จ์šฉํ•˜๋ฉด ์•ˆ ๋ผ์š”. ๋ ˆ์ด์–ด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๊ฑฐ๋“ ์š”. ์‹ค์ œ๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ผ์–ด๋‚˜๋Š” ์š”์†Œ์—๋งŒ ์จ์•ผ ํ•ด์š”. Reflow๋ฅผ ์œ ๋ฐœํ•˜๋Š” CSS ์†์„ฑ์€ ๋ฏธ๋ฆฌ ์•Œ์•„๋‘๋ฉด ์ข‹์•„์š”. width, height, margin, padding, top, left๋Š” ์ „๋ถ€ Reflow๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์š”. ๋ฐ˜๋ฉด color, background-color๋Š” Repaint๋งŒ ์ผ์œผํ‚ค๊ณ  transform๊ณผ opacity๋Š” Composite๋งŒ ๊ฑด๋“œ๋ ค์š”.


๋ฆฌ์•กํŠธ์˜ ์ถ”์ƒํ™”: ๊ฐ€์ƒ ๋”๊ณผ ์žฌ์กฐ์ •

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

๋ Œ๋”๋ง์ด๋ผ๋Š” ๋‹จ์–ด์˜ ๋‘ ์–ผ๊ตด

"๋ Œ๋”๋ง"์ด๋ผ๋Š” ๋‹จ์–ด๋ฅผ ๋ฆฌ์•กํŠธ์™€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์™„์ „ํžˆ ๋‹ค๋ฅธ ์˜๋ฏธ๋กœ ์จ์š”.

  • ๋ฆฌ์•กํŠธ์˜ ๋ Œ๋”๋ง: ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ƒˆ ๊ฐ€์ƒ ๋” ์Šค๋ƒ…์ƒท์„ ์ฐ๊ณ  ์ด์ „ ๊ฒƒ๊ณผ ๋น„๊ตํ•˜๋Š” JS ๊ณ„์‚ฐ ๊ณผ์ •์ด์—์š”.
  • ๋ธŒ๋ผ์šฐ์ €์˜ ๋ Œ๋”๋ง: ๋ฆฌ์•กํŠธ๊ฐ€ DOM์— ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•œ ๋’ค ๋ธŒ๋ผ์šฐ์ €๊ฐ€ Layout โ†’ Paint โ†’ Composite๋ฅผ ๊ฑฐ์ณ ํ”ฝ์…€์„ ๊ทธ๋ฆฌ๋Š” ๊ณผ์ •์ด์—์š”.

๋ฆฌ์•กํŠธ ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚ฌ๋‹ค๊ณ  ํ•ด์„œ ๋ฐ˜๋“œ์‹œ ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง(Reflow, Repaint)์ด ์ผ์–ด๋‚˜๋Š” ๊ฑด ์•„๋‹ˆ์—์š”. ์ด ๊ตฌ๋ถ„์ด ์ตœ์ ํ™” ํฌ์ธํŠธ๋ฅผ ์ฐพ๋Š” ๋ฐ ํ•ต์‹ฌ์ด์—์š”.

๊ฐ€์ƒ ๋”๊ณผ Diffing

๊ฐ€์ƒ ๋”(Virtual DOM)์€ ์‹ค์ œ DOM์ด ์•„๋‹Œ ๋…ธ๋“œ ์ •๋ณด๋ฅผ ๋‹ด์€ ๊ฐ€๋ฒผ์šด JS ๊ฐ์ฒด์˜ˆ์š”.

// ๊ฐ€์ƒ ๋” ๊ฐ์ฒด์˜ ์‹ค์ œ ์ƒ๊น€์ƒˆ
{
  type: 'div',
  props: {
    className: 'container',
    style: { color: 'red' }
  },
  children: [
    {
      type: 'h1',
      props: { id: 'title' },
      children: ['์•ˆ๋…•ํ•˜์„ธ์š”']
    }
  ]
}

์ด JS ๊ฐ์ฒด๋ฅผ ์ด์ „ ๊ฒƒ๊ณผ ๋น„๊ต(Diffing)ํ•ด์„œ ๋ฐ”๋€ ๋ถ€๋ถ„๋งŒ ์ฐพ์•„๋‚ด๊ณ  ๊ทธ ์ฐจ์ด์ ๋งŒ ์‹ค์ œ DOM์— ๋”ฑ ํ•œ ๋ฒˆ ๋ฐ˜์˜(Patching)ํ•ด์š”.

๋‘ ํŠธ๋ฆฌ๋ฅผ ์™„์ „ํžˆ ๋น„๊ตํ•˜๋ฉด ์›๋ž˜ O(nยณ)์˜ ๋น„์šฉ์ด ๋“ค์–ด์š”. ๋…ธ๋“œ ์ˆ˜๊ฐ€ 1000๊ฐœ๋ฉด 10์–ต ๋ฒˆ์˜ ๋น„๊ต๊ฐ€ ํ•„์š”ํ•œ ์…ˆ์ด์—์š”. ๋ฆฌ์•กํŠธ๋Š” ๋‘ ๊ฐ€์ง€ ํœด๋ฆฌ์Šคํ‹ฑ์œผ๋กœ ์ด ๋น„์šฉ์„ O(n)์œผ๋กœ ์ค„์˜€์–ด์š”.

  1. ํƒ€์ž… ๊ฐ€์ •: ๋ถ€๋ชจ ๋…ธ๋“œ์˜ ํƒ€์ž…์ด ๋ฐ”๋€Œ๋ฉด ์ž์‹ ํŠธ๋ฆฌ ์ „์ฒด๋ฅผ ํ๊ธฐํ•˜๊ณ  ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์š”. <div>๊ฐ€ <section>์œผ๋กœ ๋ฐ”๋€Œ๋ฉด ๊ทธ ์•ˆ์˜ ์ž์‹๋“ค์„ ์ผ์ผ์ด ๋น„๊ตํ•˜์ง€ ์•Š๊ณ  ์ „๋ถ€ ๋ฒ„๋ฆฌ๋Š” ๊ฑฐ์˜ˆ์š”.
  2. Key ๊ฐ€์ •: ๊ฐ™์€ ๋ ˆ๋ฒจ์˜ ์ž์‹ ๋ชฉ๋ก์—์„œ Key๊ฐ€ ๊ฐ™์€ ๋…ธ๋“œ๋Š” ๋™์ผํ•œ ๋…ธ๋“œ๋กœ ๋ณด๊ณ  ์žฌ์‚ฌ์šฉํ•ด์š”. Key๊ฐ€ ์—†์œผ๋ฉด ์ˆœ์„œ์— ์˜์กดํ•ด์•ผ ํ•ด์„œ ๋ชฉ๋ก ์ค‘๊ฐ„์— ์•„์ดํ…œ์ด ์‚ฝ์ž…๋  ๋•Œ ์ทจ์•ฝํ•ด์ ธ์š”.

๊ฐ€์ƒ ๋”์ด ์‹ค์ œ DOM๋ณด๋‹ค ํ•ญ์ƒ ๋น ๋ฅธ ๊ฑด ์•„๋‹ˆ์—์š”. ์—ฐ์‚ฐ์„ ํ•œ ๊ฒน ๋” ์–น๋Š” ๊ฑฐ๋ผ์„œ ๋‹จ์ˆœํ•œ ๋ณ€๊ฒฝ์ด๋ผ๋ฉด ์ง์ ‘ DOM ์กฐ์ž‘์ด ๋” ๋น ๋ฅผ ์ˆ˜ ์žˆ์–ด์š”. ํ•˜์ง€๋งŒ ๋ฆฌ์•กํŠธ์˜ ์ง„๊ฐ€๋Š” "๋ˆ„๊ฐ€ ์ฝ”๋“œ๋ฅผ ์งœ๋”๋ผ๋„ ์„ฑ๋Šฅ์˜ ์ตœ์ € ํ•˜ํ•œ์„ ์„ ์ง€์ผœ์ฃผ๋Š” ๊ฒƒ"์— ์žˆ์–ด์š”.

Key์™€ ์žฌ์กฐ์ •

Key ๊ฐ€์ •์—์„œ ์ด์–ด์ง€๋Š” ์–˜๊ธฐ์ธ๋ฐ์š”. ๋ฆฌ์•กํŠธ๋Š” Key๋ฅผ ๋ณด๊ณ  ๋…ธ๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•ด์š”. key={index}๋ฅผ ์“ฐ๋ฉด ๋ฐฐ์—ด ์ค‘๊ฐ„์— ์•„์ดํ…œ์ด ์ถ”๊ฐ€๋  ๋•Œ ์ธ๋ฑ์Šค๊ฐ€ ๋ฐ€๋ฆฌ๊ณ  ๋ฆฌ์•กํŠธ๋Š” ๋ฉ€์ฉกํ•œ ๋…ธ๋“œ๋ฅผ ์ „๋ถ€ ์—…๋ฐ์ดํŠธํ•ด์š”. ๋” ์‹ฌ๊ฐํ•œ ๊ฑด Input ๊ฐ™์€ ๋‚ด๋ถ€ ์ƒํƒœ๊ฐ€ ์—‰๋šฑํ•œ ์œ„์น˜๋กœ ๋ถ™์–ด๋‹ค๋‹ˆ๋Š” ๋ฒ„๊ทธ์˜ˆ์š”. ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ๋ฒ„๊ทธ ์ค‘์— ์›์ธ ์ฐพ๊ธฐ ์ œ์ผ ์–ด๋ ค์šด ๋ถ€๋ฅ˜๊ฐ€ ์—ฌ๊ธฐ์„œ ๋‚˜์™€์š”.

Key๋Š” ์ „์—ญ ์œ ์ผํ•˜์ง€ ์•Š์•„๋„ ๋ผ์š”. ๊ฐ™์€ ๋ฐฐ์—ด ์•ˆ์—์„œ๋งŒ ์œ ์ผํ•˜๋ฉด ๋ผ์š”. UUID๋ฅผ ๋งค๋ฒˆ ์ƒˆ๋กœ ์ƒ์„ฑํ•ด์„œ key๋กœ ์“ฐ๋ฉด ๋ Œ๋”๋ง๋งˆ๋‹ค key๊ฐ€ ๋ฐ”๋€Œ์–ด์„œ ๋ฆฌ์•กํŠธ๊ฐ€ ํ•ญ์ƒ ์ƒˆ ๋…ธ๋“œ๋กœ ์ธ์‹ํ•˜๊ณ  ์žฌ์‚ฌ์šฉ์„ ๋ชป ํ•ด์š”.

useEffect vs useLayoutEffect

Key๋งŒํผ์ด๋‚˜ ์ค‘์š”ํ•œ ๊ฒŒ ๋ฆฌ์•กํŠธ๊ฐ€ DOM์„ ์–ธ์ œ ์ฝ๊ณ  ์“ฐ๋А๋ƒ์˜ˆ์š”. ํ›…์˜ ์‹คํ–‰ ํƒ€์ด๋ฐ์„ ์ž˜๋ชป ์žก์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ € Paint์™€ ์ถฉ๋Œํ•ด์„œ ๊นœ๋นก์ž„(Flickering)์ด ์ƒ๊ฒจ์š”.

// useEffect: Paint๊ฐ€ ๋๋‚œ ๋’ค ๋น„๋™๊ธฐ ์‹คํ–‰ โ†’ ๊นœ๋นก์ž„ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
useEffect(() => {
  const rect = element.getBoundingClientRect();
  setTooltipPosition(rect); // ํ™”๋ฉด ๊ทธ๋ฆฐ ํ›„ ์‹คํ–‰ โ†’ ์œ„์น˜ ์žฌ๊ณ„์‚ฐ์œผ๋กœ ๊นœ๋นก์ž„
}, []);

// useLayoutEffect: Paint ์ง์ „์— ๋™๊ธฐ ์‹คํ–‰ โ†’ ๊นœ๋นก์ž„ ์—†์Œ
useLayoutEffect(() => {
  const rect = element.getBoundingClientRect();
  setTooltipPosition(rect); // Paint ์ง์ „ ์‹คํ–‰ โ†’ ์‚ฌ์šฉ์ž ๋ˆˆ์— ๋ณด์ด์ง€ ์•Š์Œ
}, []);

useLayoutEffect๋Š” ๋™๊ธฐ๋กœ ์‹คํ–‰๋˜๋‹ˆ๊นŒ ๋ฌด๊ฑฐ์šด ์ž‘์—…์„ ์—ฌ๊ธฐ์„œ ํ•˜๋ฉด Paint๊ฐ€ ๋ธ”๋กœํ‚น๋ผ์š”. DOM ์ˆ˜์น˜ ์ธก์ •์ด๋‚˜ ์ฆ‰๊ฐ์ ์ธ ์Šคํƒ€์ผ ๋™๊ธฐํ™”์ฒ˜๋Ÿผ ์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์จ์•ผ ํ•ด์š”.


React Fiber: ์ค‘๋‹จ ๊ฐ€๋Šฅํ•œ ๋ Œ๋”๋ง

5000๊ฐœ ์•„์ดํ…œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ๊ณผ๊ฑฐ ๋ฆฌ์•กํŠธ(Stack Reconciler)๋Š” ์žฌ์กฐ์ •์ด ์‹œ์ž‘๋˜๋ฉด ๋ฉˆ์ถœ ์ˆ˜ ์—†์—ˆ์–ด์š”. ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ๋ฅผ ์ฝœ ์Šคํƒ์„ ๋”ฐ๋ผ ์žฌ๊ท€์ ์œผ๋กœ ๋‚ด๋ ค๊ฐ€๋Š” ๊ตฌ์กฐ์˜€๊ธฐ ๋•Œ๋ฌธ์— ๋ Œ๋”๋ง ๋„์ค‘์—” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ๊ฝ‰ ์žก๊ณ  ์žˆ์–ด์„œ ์‚ฌ์šฉ์ž ์ž…๋ ฅ์ด ์”นํžˆ๋Š” ํ”„๋ฆฌ์ง•(Freezing) ํ˜„์ƒ์ด ์ƒ๊ฒผ์–ด์š”. ์‚ฌ๋žŒ์ด ํƒ€์ดํ•‘ํ•  ๋•Œ 100ms ์ด์ƒ ๋ฐ˜์‘์ด ์—†์œผ๋ฉด "์•ฑ์ด ์ฃฝ์—ˆ๋‚˜?"๋ผ๊ณ  ๋А๊ปด์š”.

Fiber๋Š” ๊ทธ ํ•œ๊ณ„๋ฅผ ๊ทผ๋ณธ์ ์œผ๋กœ ๋œฏ์–ด๊ณ ์นœ ๋‚ด๋ถ€ ์žฌ์„ค๊ณ„์˜ˆ์š”.

์žฌ๊ท€์—์„œ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋กœ

Stack Reconciler์˜ ๊ทผ๋ณธ ๋ฌธ์ œ๋Š” ์žฌ๊ท€ ํ˜ธ์ถœ์ด์—์š”. ์žฌ๊ท€๋Š” ์ค‘๊ฐ„์— ๋ฉˆ์ถœ ์ˆ˜ ์—†์–ด์š”. Fiber๋Š” ์ด ๊ตฌ์กฐ๋ฅผ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ(Linked List)๋กœ ๋ฐ”๊ฟจ์–ด์š”.

// Fiber ๋…ธ๋“œ์˜ ๊ฐœ๋…์  ๊ตฌ์กฐ
{
  type: MyComponent,
  stateNode: ์ปดํฌ๋„ŒํŠธ_์ธ์Šคํ„ด์Šค,
  return: ๋ถ€๋ชจ_Fiber,   // ์œ„๋กœ ์˜ฌ๋ผ๊ฐ€๋Š” ํฌ์ธํ„ฐ
  child: ์ž์‹_Fiber,    // ์•„๋ž˜๋กœ ๋‚ด๋ ค๊ฐ€๋Š” ํฌ์ธํ„ฐ
  sibling: ํ˜•์ œ_Fiber,  // ์˜†์œผ๋กœ ๊ฐ€๋Š” ํฌ์ธํ„ฐ
  pendingProps: {},
  memoizedState: {}
}

๊ฐ Fiber ๋…ธ๋“œ๊ฐ€ ๋‹ค์Œ ๋…ธ๋“œ์˜ ํฌ์ธํ„ฐ๋ฅผ ๊ฐ€์ง€๋‹ˆ๊นŒ ์ค‘๊ฐ„์— ์ž‘์—…์„ ๋ฉˆ์ถ”๊ณ  ํฌ์ธํ„ฐ๋งŒ ์ €์žฅํ•ด๋‘๋ฉด ๋‚˜์ค‘์— ์ •ํ™•ํžˆ ๊ทธ ์ง€์ ๋ถ€ํ„ฐ ์žฌ๊ฐœํ•  ์ˆ˜ ์žˆ์–ด์š”.

์Šค์ผ€์ค„๋ง๊ณผ ๋™์‹œ์„ฑ ๋ชจ๋“œ

Fiber๋Š” ๋ Œ๋”๋ง์„ ์ž‘์€ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์„œ ๊ด€๋ฆฌํ•ด์š”.

  • ์•ฝ 5ms๋งˆ๋‹ค ์ž‘์—…์„ ๋ฉˆ์ถ”๊ณ  ๊ธ‰ํ•œ ์ผ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์š”.
  • ๊ธ‰ํ•œ ์ž…๋ ฅ์ด ์žˆ์œผ๋ฉด ์ œ์–ด๊ถŒ์„ ๋จผ์ € ๋„˜๊ธฐ๊ณ  ๋‚˜์ค‘์— ์žฌ๊ฐœํ•ด์š”.
  • Current ํŠธ๋ฆฌ์™€ Work-in-progress ํŠธ๋ฆฌ๋ฅผ ๋ถ„๋ฆฌ(Double Buffering)ํ•ด์„œ ์™„์„ฑ๋˜์ง€ ์•Š์€ UI๊ฐ€ ํ™”๋ฉด์— ๋…ธ์ถœ๋˜๋Š” ๊ฑธ ๋ฐฉ์ง€ํ•ด์š”.

๋”๋ธ” ๋ฒ„ํผ๋ง (Double Buffering)

๋ฆฌ์•กํŠธ๊ฐ€ ๊ณ„์‚ฐ ์ค‘์ธ ์–ด์„คํ”ˆ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋“คํ‚ค์ง€ ์•Š๋Š” ๋น„๊ฒฐ์€ ๋ฌด์—‡์ผ๊นŒ์š”? ๋ฆฌ์•กํŠธ๋Š” ๋งˆ์น˜ ์—ฐ๊ทน ๋ฌด๋Œ€๋ฅผ ๊ต์ฒดํ•˜๋“ฏ ๋‘ ๊ฐœ์˜ ํŠธ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” '๋”๋ธ” ๋ฒ„ํผ๋ง' ์ „๋žต์„ ์‚ฌ์šฉํ•ด์š”.

  • Current ํŠธ๋ฆฌ: ํ˜„์žฌ ๊ด€๊ฐ์ด ๋ณด๊ณ  ์žˆ๋Š” '๊ณต์—ฐ ์ค‘์ธ ๋ฌด๋Œ€'์˜ˆ์š”.
  • Work-in-progress(WIP) ํŠธ๋ฆฌ: ๋ฌด๋Œ€ ๋’ค์—์„œ ์†Œํ’ˆ์„ ๋ฐฐ์น˜ํ•˜๊ณ  ์กฐ๋ช…์„ ์กฐ์ ˆํ•˜๋Š” '์ค€๋น„ ์ค‘์ธ ๋ฌด๋Œ€'์˜ˆ์š”.

์ด ๊ณผ์ •์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹จ๊ณ„๋กœ ์ •๊ตํ•˜๊ฒŒ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค

  1. ๋ฌด๋Œ€ ๋’ค์—์„œ ์ค€๋น„ (Render Phase): ๋ Œ๋”๋ง ๋‚ด๋‚ด ๋ฆฌ์•กํŠธ๋Š” ์˜ค์ง WIP ํŠธ๋ฆฌ๋งŒ ์ˆ˜์ •ํ•ด์š”. ์ด๋•Œ ์‹ค์ œ ํ™”๋ฉด์ธ Current ํŠธ๋ฆฌ๋Š” ์ „ํ˜€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜์ฃ .

  2. ์™„์„ฑ ํ›„ ํ•œ ๋ฒˆ์— ๊ต์ฒด (Commit Phase): ๋ชจ๋“  ๊ณ„์‚ฐ๊ณผ ์†Œํ’ˆ ๋ฐฐ์น˜๊ฐ€ ์™„๋ฒฝํžˆ ๋๋‚˜๋ฉด ๋ฆฌ์•กํŠธ๋Š” ๋‹จ ํ•œ ๋ฒˆ์˜ ์กฐ์ž‘์œผ๋กœ ๋‘ ๋ฌด๋Œ€์˜ ์œ„์น˜๋ฅผ ์ˆœ์‹๊ฐ„์— ๋ฐ”๊ฟ”์š”(Swap).

  3. ์›์ž์ (Atomic) ์‹คํ–‰: ์ด ๊ต์ฒด ์ž‘์—…์€ '์ „๋ถ€ ์•„๋‹ˆ๋ฉด ์ „๋ฌด(All or Nothing)'๋กœ ์ฒ˜๋ฆฌ๋˜์š”. ๋•๋ถ„์— ์‚ฌ์šฉ์ž๋Š” ์†Œํ’ˆ์ด ํ•˜๋‚˜์”ฉ ๋ฐฐ์น˜๋˜๋Š” ๊ณผ์ • ์—†์ด ๋Š˜ ์™„์„ฑ๋œ ํ”„๋ ˆ์ž„๋งŒ ๋ณด๊ฒŒ ๋˜์š”.

Git์—์„œ feature ๋ธŒ๋žœ์น˜ ์ž‘์—…์„ ๋งˆ์น˜๊ณ  main์— ํ•œ ๋ฒˆ์— ๋จธ์ง€ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์›๋ฆฌ์˜ˆ์š”.

const [isPending, startTransition] = useTransition();

function handleInput(e) {
  // ์ฆ‰๊ฐ ๋ฐ˜์‘์ด ํ•„์š”ํ•œ ์ž‘์—… (Urgent)
  setInputValue(e.target.value);

  // ์กฐ๊ธˆ ๋Šฆ์–ด๋„ ๋˜๋Š” ์ž‘์—… (Transition)
  startTransition(() => {
    setSearchResults(heavyFilter(e.target.value));
  });
}

startTransition ์•ˆ์˜ ์ž‘์—…์€ ํƒ€์ดํ•‘ ์ค‘์— ์ƒˆ ์ž…๋ ฅ์ด ๋“ค์–ด์˜ค๋ฉด ๊ธฐ์กด ๊ณ„์‚ฐ์„ ํ๊ธฐํ•˜๊ณ  ์ž…๋ ฅ์— ๋จผ์ € ๋ฐ˜์‘ํ•ด์š”. "๋น ๋ฅธ ๋ Œ๋”๋ง"์ด ์•„๋‹ˆ๋ผ "์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ฅธ ์„ ํƒ์  ๋ Œ๋”๋ง"์ด์—์š”.

componentWillMount๊ฐ€ Unsafe ํŒ์ •์„ ๋ฐ›์€ ์ด์œ ๋„ ์—ฌ๊ธฐ์— ์žˆ์–ด์š”. Fiber์—์„œ Render Phase๋Š” ์–ธ์ œ๋“  ๋ฉˆ์ถ”๊ฑฐ๋‚˜ ๋ฒ„๋ ค์ง€๊ฑฐ๋‚˜ ๋‹ค์‹œ ์‹œ์ž‘๋  ์ˆ˜ ์žˆ์–ด์š”. componentWillMount์—์„œ API๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์—ฌ๋Ÿฌ ๋ฒˆ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋Š” ๋ถ€์ž‘์šฉ์ด ์ƒ๊ฒจ์š”. ๋ถ€์ˆ˜ ํšจ๊ณผ๋Š” ๋ฐ˜๋“œ์‹œ Commit Phase ์ดํ›„์ธ useEffect์—์„œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ด์š”.


์ •๋ฆฌ

๋ธŒ๋ผ์šฐ์ € ํŒŒ์‹ฑ ์ฐจ๋‹จ๋ถ€ํ„ฐ Fiber ์Šค์ผ€์ค„๋ง๊นŒ์ง€ ์ญ‰ ์ด์–ด์ง€๋Š” ์ด ํ๋ฆ„์˜ ํ•ต์‹ฌ์€ ๊ฒฐ๊ตญ ํ•˜๋‚˜์˜ˆ์š”.
๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์–ผ๋งˆ๋‚˜ ์ž˜๊ฒŒ ์ชผ๊ฐœ์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ์–ด๊ถŒ์„ ๋Œ๋ ค์ฃผ๋А๋ƒ๊ฐ€ ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ๋ณธ์งˆ์ด์—์š”.

๊ฐœ๋…๋งŒ ์ฝ๊ณ  ๋„˜์–ด๊ฐ€๋ฉด ๊ธˆ๋ฐฉ ์žŠํžˆ๋Š”๋ฐ Chrome DevTools Performance ํƒญ์„ ์—ด์–ด์„œ ์ง์ ‘ ํƒ€์ž„๋ผ์ธ์„ ์ฐ์–ด๋ณด๋ฉด ๋‹ฌ๋ผ์ ธ์š”.
๋…นํ™” ํ›„ ํƒ€์ž„๋ผ์ธ์—์„œ ๋ณด๋ผ์ƒ‰ ๋ธ”๋ก์ด Layout, ์ดˆ๋ก์ƒ‰์ด Paint, startTransition ์ ์šฉ ์ „ํ›„๋กœ Long Task(๋นจ๊ฐ„ ์‚ผ๊ฐํ˜•)๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ๊ฑธ ๋ˆˆ์œผ๋กœ ๋ณด๋ฉด ์ด ๊ธ€์—์„œ ๋‹ค๋ฃฌ ๊ฐœ๋…๋“ค์ด ํƒ€์ž„๋ผ์ธ ์œ„ ์–ด๋””์— ํ•ด๋‹นํ•˜๋Š”์ง€ ๋ฐ”๋กœ ๋ณด์—ฌ์š”.


์ฐธ๊ณ  ์ž๋ฃŒ

profile
์–ด์ œ๋ณด๋‹ค ๋‚˜์€ ์˜ค๋Š˜

0๊ฐœ์˜ ๋Œ“๊ธ€