Y J S

๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง ๋”ฅ๋‹ค์ด๋ธŒ: DOM๋ถ€ํ„ฐ ํ”„๋ ˆ์ž„๊นŒ์ง€

์™œ ์ค‘์š”ํ•œ๊ฐ€

  • ์‚ฌ์šฉ์ž๋Š” ๋น ๋ฅด๊ณ  ๋งค๋„๋Ÿฌ์šด ํ™”๋ฉด์„ ๊ธฐ๋Œ€ํ•œ๋‹ค. ๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ์„ ์ดํ•ดํ•˜๋ฉด LCP/CLS/INP ๊ฐ™์€ ํ•ต์‹ฌ ์ง€ํ‘œ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ , ์ฝ”๋“œยท์Šคํƒ€์ผยท์ด๋ฏธ์ง€ ์ „๋žต์„ ๊ตฌ์กฐ์ ์œผ๋กœ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ ๊ฐœ์š”

  1. HTML ํŒŒ์‹ฑ โ†’ DOM ๊ตฌ์„ฑ
  2. CSS ํŒŒ์‹ฑ โ†’ CSSOM ๊ตฌ์„ฑ
  3. Render Tree ์ƒ์„ฑ(DOM + CSSOM ๊ฒฐํ•ฉ)
  4. Layout(Reflow): ๊ฐ ๋…ธ๋“œ์˜ ํฌ๊ธฐยท์œ„์น˜ ๊ณ„์‚ฐ
  5. Paint: ํ”ฝ์…€ ๊ทธ๋ฆฌ๊ธฐ(๋ฐฐ๊ฒฝ, ํ…์ŠคํŠธ, ๋ณด๋” ๋“ฑ)
  6. Composite: ๋ ˆ์ด์–ด(ํƒ€์ผ)๋ฅผ GPU๋กœ ํ•ฉ์„ฑํ•ด ํ™”๋ฉด ํ‘œ์‹œ

Tip: transform/opacity ๋ณ€๊ฒฝ์€ ๋ณดํ†ต ๋ ˆ์ด์•„์›ƒ ์—†์ด ํ•ฉ์„ฑ๋งŒ ๋ฐœ์ƒ. width/height/top/left ๋“ฑ์€ ๋ ˆ์ด์•„์›ƒ์„ ์œ ๋ฐœํ•ด ๋น„์šฉ์ด ํฌ๋‹ค.

Critical Rendering Path(CRP)

  • ๋ Œ๋”์— ๊ผญ ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๊ฐ€ ํ™”๋ฉด ํ‘œ์‹œ๋ฅผ ์ง€์—ฐ์‹œํ‚ค๋Š” ๊ฒฝ๋กœ
  • ์›์น™
    • CSS๋Š” ๋ Œ๋” ์ฐจ๋‹จ. ํฌ๋ฆฌํ‹ฐ์ปฌ CSS(Above-the-fold) ์ตœ์†Œํ™”, ๋‚˜๋จธ์ง€๋Š” ์ง€์—ฐ ๋กœ๋“œ
    • JS๋Š” defer/async๋กœ ํŒŒ์„œ ์ฐจ๋‹จ์„ ์ค„์ด๊ณ , ๋ชจ๋“ˆ ๋ถ„ํ• ๋กœ ์ดˆ๊ธฐ ๋ฒˆ๋“ค ์ถ•์†Œ
    • preload(์ฆ‰์‹œ ํ•„์š”), prefetch(์ถ”๊ฐ€ ํƒ์ƒ‰ ๋Œ€๋น„)๋กœ ์šฐ์„ ์ˆœ์œ„ ํžŒํŠธ ์ œ๊ณต
    • HTTP ์บ์‹œ/์••์ถ•/HTTP2/3 ๋ณ‘๋ ฌ์„ฑ์œผ๋กœ ๋„คํŠธ์›Œํฌ ๋ ˆ์ดํ„ด์‹œ ์™„ํ™”

Layout / Paint / Composite ์‹ฌํ™”

  • Layout(Reflow)
    • ๋ฐœ์ƒ ์กฐ๊ฑด: ๋ ˆ์ด์•„์›ƒ ์˜ํ–ฅ ์†์„ฑ ๋ณ€๊ฒฝ, DOM ์ถ”๊ฐ€/์‚ญ์ œ, ํฐํŠธ ๋กœ๋“œ, ๋ทฐํฌํŠธ ๋ณ€ํ™” ๋“ฑ
    • ์•ˆํ‹ฐํŒจํ„ด: ๋ ˆ์ด์•„์›ƒ ์Šค๋ž˜์‹ฑ(์ฝ๊ธฐ/์“ฐ๊ธฐ ๊ต์ฐจ ๋ฐ˜๋ณต)
    • ๊ฐœ์„ : ์ฝ๊ธฐโ†’๊ณ„์‚ฐโ†’์“ฐ๊ธฐ ์ˆœ์„œ๋กœ ๋ฐฐ์น˜, ์Šคํƒ€์ผ ๋ณ€๊ฒฝ์€ ํด๋ž˜์Šค ํ† ๊ธ€๋กœ ๋ฌถ๊ธฐ
  • Paint
    • ๋ฐ•์Šค ๊ทธ๋ฆผ์ž, ๊ทธ๋ผ๋ฐ์ด์…˜, ํฐ ์ด๋ฏธ์ง€ ์Šค์ผ€์ผ๋ง ๋“ฑ์€ ํŽ˜์ธํŠธ ๋น„์šฉ ์ฆ๊ฐ€
    • ๊ฐœ์„ : ๊ณผ๋„ํ•œ ์‹œ๊ฐ ํšจ๊ณผ ์ตœ์†Œํ™”, ์ ์ ˆํ•œ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์‚ฌ์šฉ
  • Composite(ํ•ฉ์„ฑ)
    • ๋ ˆ์ด์–ด ๋ถ„๋ฆฌ๋กœ ์Šคํฌ๋กค/์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ. will-change, transform: translateZ(0) ๋“ฑ์œผ๋กœ ๋ ˆ์ด์–ด ์Šน๊ฒฉ
    • ๋‚จ์šฉ ์ฃผ์˜: ๋ ˆ์ด์–ด ์ฆ๊ฐ€โ†’๋ฉ”๋ชจ๋ฆฌ/์—…๋กœ๋“œ ๋น„์šฉ ์ƒ์Šน. ์ผ์‹œ์  ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ตฌ๊ฐ„์—๋งŒ ์‚ฌ์šฉ

๋ฉ”์ธ ์Šค๋ ˆ๋“œ, ์ด๋ฒคํŠธ ๋ฃจํ”„, ํ”„๋ ˆ์ž„ ๋ฒ„์ง“

  • 60fps ๊ธฐ์ค€ ํ”„๋ ˆ์ž„๋‹น ์•ฝ 16.7ms. ์ด ๋‚ด์— JS ์‹คํ–‰, ์Šคํƒ€์ผ/๋ ˆ์ด์•„์›ƒ, ํŽ˜์ธํŠธ/ํ•ฉ์„ฑ์ด ์™„๋ฃŒ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • Long Task(>50ms) ํƒ์ง€ ๋ฐ ๋ถ„ํ• . Web Worker๋กœ CPU ๋ฐ”์šด๋“œ ์ž‘์—…์„ ๋ถ„๋ฆฌ.
  • ์Šค์ผ€์ค„๋ง
    • requestAnimationFrame(rAF): ํŽ˜์ธํŠธ ์ง์ „ ์ฝœ๋ฐฑ, ๋ ˆ์ด์•„์›ƒ ์•ˆ์ „ํ•œ ํƒ€์ด๋ฐ
    • requestIdleCallback: ์œ ํœด ์‹œ๊ฐ„ ์ž‘์—… ์ฒ˜๋ฆฌ(ํ•„์ˆ˜ ์ž‘์—…์€ ๊ธˆ์ง€)
    • ๋งˆ์ดํฌ๋กœํƒœ์Šคํฌ(Promise) vs ๋งคํฌ๋กœํƒœ์Šคํฌ(setTimeout) ์ˆœ์„œ ์ดํ•ด๋กœ ์ง€์—ฐ/์ •์ฒด ๋ฐฉ์ง€

์ด๋ฏธ์ง€ยทํฐํŠธ ๋กœ๋”ฉ ์ „๋žต

  • ์ด๋ฏธ์ง€
    • ์ ์ ˆํ•œ ์‚ฌ์ด์ฆˆ์™€ ํฌ๋งท(WebP/AVIF) ์‚ฌ์šฉ, loading="lazy", srcset/sizes๋กœ ๋ฐ˜์‘ํ˜• ์ œ๊ณต
    • LCP ๋Œ€์ƒ ์ด๋ฏธ์ง€์— priority(ํ”„๋ ˆ์ž„์›Œํฌ ์˜ต์…˜) ๋˜๋Š” preload ์ ์šฉ
  • ํฐํŠธ
    • font-display: swap|optional๋กœ FOIT ๋ฐฉ์ง€, ์„œ๋ธŒ์…‹ ํฐํŠธ๋กœ ํŒŒ์ผ ํฌ๊ธฐ ์ถ•์†Œ
    • ์ค‘์š”ํ•œ ํ…์ŠคํŠธ๋Š” ์‹œ์Šคํ…œ ํฐํŠธ ํด๋ฐฑ ๊ณ ๋ ค, ๊ฐ€๋ณ€ ํฐํŠธ๋กœ ์Šคํƒ€์ผ ํ†ตํ•ฉ

React ๊ด€์ : ๊ฐ€์ƒ DOM, CSR/SSR/SSG/ISR, ํ•˜์ด๋“œ๋ ˆ์ด์…˜

  • ๊ฐ€์ƒ DOM๊ณผ Reconciliation: key ์•ˆ์ •์„ฑ์œผ๋กœ ์ตœ์†Œ ๋ณ€๊ฒฝ ๋ณด์žฅ. ๋ถˆ์•ˆ์ •ํ•œ key๋Š” ๋ถˆํ•„์š” ์žฌ๋งˆ์šดํŠธ/๋ ˆ์ด์•„์›ƒ ๋ณ€๋™ ์œ ๋ฐœ
  • ๋ Œ๋”๋ง ๋ฐฉ์‹
    • CSR: ํ’๋ถ€ํ•œ ์ธํ„ฐ๋ž™์…˜์— ์œ ๋ฆฌํ•˜๋‚˜ ์ดˆ๊ธฐ ๋กœ๋“œ ๋น„์šฉโ†‘
    • SSR/SSG/ISR: ์ดˆ๊ธฐ ํ‘œ์‹œ/SEO ์šฐ์ˆ˜. ์ดํ›„ ํ•˜์ด๋“œ๋ ˆ์ด์…˜์œผ๋กœ ์ƒํ˜ธ์ž‘์šฉ ํ™œ์„ฑํ™”
  • Next.js App Router
    • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ(๋ฐ์ดํ„ฐ ๊ทผ์ ‘, ๊ธฐ๋ณธ SSR) + ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ(์ƒํƒœ/์ด๋ฒคํŠธ)
    • ์ŠคํŠธ๋ฆฌ๋ฐ/๋ถ€๋ถ„ ํ•˜์ด๋“œ๋ ˆ์ด์…˜์œผ๋กœ TTFBยทLCP ๊ท ํ˜•

Web Vitals๋กœ ์ธก์ •ํ•˜๊ณ  ๊ฐœ์„ ํ•˜๊ธฐ

  • LCP: ์ตœ๋Œ€ ์ฝ˜ํ…์ธ  ํ‘œ์‹œ ์‹œ์ . ์ด๋ฏธ์ง€/ํฐํŠธ ์ตœ์ ํ™”, SSR/SSG๋กœ ๊ฐœ์„ 
  • CLS: ๋ ˆ์ด์•„์›ƒ ์ด๋™. ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์˜ˆ์•ฝ, ๊ด‘๊ณ /์ž„๋ฒ ๋“œ์— ๊ณ ์ • ๋ฐ•์Šค, ํฐํŠธ ์Šค์™‘
  • INP(์‹ ๊ทœ): ์ „์ฒด ์ƒํ˜ธ์ž‘์šฉ ์ง€์—ฐ์˜ ๋Œ€ํ‘œ ์ง€ํ‘œ. ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ์ ์œ  ๊ฐ์†Œ, ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๊ฒฝ๋Ÿ‰ํ™”
  • TTFB/TBT: ์„œ๋ฒ„/JS ์ ์œ  ์‹œ๊ฐ„. ์บ์‹ฑยท์ฝ”๋“œ ๋ถ„ํ• ยท์ง€์—ฐ ๋กœ๋“œ
  • ๋„๊ตฌ: Lighthouse, WebPageTest, Chrome DevTools, web-vitals ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(์‹ค์‚ฌ์šฉ ์ธก์ •)

์ฒดํฌ๋ฆฌ์ŠคํŠธ(์š”์•ฝ)

  • ์ค‘์š”ํ•œ ์ž์‚ฐ ๋จผ์ €: ํฌ๋ฆฌํ‹ฐ์ปฌ CSS ์ตœ์†Œํ™”, ํ•ต์‹ฌ ์ด๋ฏธ์ง€ Preload
  • ์• ๋‹ˆ๋ฉ”์ด์…˜์€ transform/opacity ์ค‘์‹ฌ, rAF ์‚ฌ์šฉ
  • ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋ถ„๋ฆฌ๋กœ ๋ ˆ์ด์•„์›ƒ ์Šค๋ž˜์‹ฑ ๋ฐฉ์ง€, ๋ฐฐ์น˜ ์—…๋ฐ์ดํŠธ
  • ์ฝ”๋“œ ๋ถ„ํ• ๊ณผ ์ง€์—ฐ ๋กœ๋“œ๋กœ ์ดˆ๊ธฐ ๋ฒˆ๋“ค ์ถ•์†Œ
  • ์•ˆ์ •์ ์ธ key๋กœ ๊ฐ€์ƒ DOM ๋ณ€๊ฒฝ ์ตœ์†Œํ™”
  • ํฐํŠธ/์ด๋ฏธ์ง€ ์ „๋žต์œผ๋กœ LCPยทCLS ๊ฐœ์„ 

์ฝ”๋“œ ์Šค๋‹ˆํŽซ

  • ๋ ˆ์ด์•„์›ƒ ์Šค๋ž˜์‹ฑ ๋ฐฉ์ง€(์ฝ๊ธฐโ†’์“ฐ๊ธฐ ๋ฐฐ์น˜)
function batchDomUpdates(callback: () => void) {
  // ์ฝ๊ธฐ ๋‹จ๊ณ„
  const width = element.offsetWidth;
  const height = element.offsetHeight;

  // ๊ณ„์‚ฐ ๋‹จ๊ณ„
  const nextScale = Math.min(2, (width + height) / 500);

  // ์“ฐ๊ธฐ ๋‹จ๊ณ„(rAF๋กœ ํŽ˜์ธํŠธ ์ง์ „ ๋ณด์žฅ)
  requestAnimationFrame(() => {
    element.style.transform = `scale(${nextScale})`;
  });
}
  • transform/opacity ๊ธฐ๋ฐ˜ ์• ๋‹ˆ๋ฉ”์ด์…˜
.card {
  will-change: transform, opacity; /* ์žฅ์‹œ๊ฐ„ ์ƒ์‹œ ์‚ฌ์šฉ์€ ์ง€์–‘ */
  transition: transform 200ms ease, opacity 200ms ease;
}
.card--enter {
  transform: translateY(8px);
  opacity: 0.8;
}
.card--enterActive {
  transform: translateY(0);
  opacity: 1;
}
  • Next.js(SSR + ์ŠคํŠธ๋ฆฌ๋ฐ ์˜ˆ์‹œ ๋А๋‚Œ)
// app/articles/page.tsx (Server Component)
export default async function Page() {
  const res = await fetch("https://api.example.com/articles", {
    cache: "no-store",
  });
  const articles = await res.json();
  return (
    <main>
      <h1>Articles</h1>
      <ul>
        {articles.map((a: any) => (
          <li key={a.id}>{a.title}</li>
        ))}
      </ul>
    </main>
  );
}

๋งˆ๋ฌด๋ฆฌ

  • ๋ Œ๋”๋ง์€ ๋„คํŠธ์›Œํฌโ†’ํŒŒ์„œโ†’์Šคํƒ€์ผ/๋ ˆ์ด์•„์›ƒโ†’ํŽ˜์ธํŠธโ†’ํ•ฉ์„ฑ์œผ๋กœ ์ด์–ด์ง€๋Š” ์ฒด์ธ์˜ ๋ฌธ์ œ๋‹ค. ๊ฐ ๋‹จ๊ณ„์˜ ๋ณ‘๋ชฉ์„ ์ •๋Ÿ‰ ์ธก์ •ํ•˜๊ณ (์›น ๋ฐ”์ดํƒˆ/ํ”„๋กœํŒŒ์ผ๋Ÿฌ), ์›์ธ์„ ๋‹จ๊ณ„๋ณ„๋กœ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํ™•์‹คํ•œ ์ตœ์ ํ™”๋‹ค. ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ธฐ๋Šฅ(์ŠคํŠธ๋ฆฌ๋ฐ, ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ, ์ฝ”๋“œ ๋ถ„ํ• )๊ณผ ๋ธŒ๋ผ์šฐ์ €์˜ ํžŒํŠธ(preload/prefetch)๋ฅผ ํ•จ๊ป˜ ํ™œ์šฉํ•˜์ž.