2024-04-18.md

๐Ÿก

DIL: ๋ชจ๋˜ ๋ฆฌ์•กํŠธ ๋”ฅ ๋‹ค์ด๋ธŒ, 7์ฃผ์ฐจ-4

์Šคํ„ฐ๋””: ์›”๊ฐ„ CS, https://github.com/monthly-cs/2024-03-modern-react-deep-dive
์˜ค๋Š˜ ์ง„ํ–‰: ๊ฐœ์ธ๊ณต๋ถ€


DIL-week7-4_2024-04-18

| DIL ์ฃผ์ฐจ | ๋ฒ”์œ„ | ๋‚ด์šฉ | ์˜ค๋Š˜์ฐจ ์ง„๋„ | | -------- | ---------- | --------------------------------------------------------------------------------------- | ----------- | | 7์ฃผ์ฐจ | 12์žฅ, 13์žฅ | ๋ชจ๋“  ์›น ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ด€์‹ฌ์„ ๊ฐ€์ ธ์•ผ ํ•  ํ•ต์‹ฌ ์›น ์ง€ํ‘œ, ์›น ํŽ˜์ด์ง€์˜ ์„ฑ๋Šฅ์„ ์ธก์ •ํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ• | 799~816p |

์˜ค๋Š˜ ์ฝ์€ ๋‚ด์šฉ์„ markdown์œผ๋กœ ๊ฐ„๋‹จํžˆ ๋ฉ”๋ชจ
์ฝ์€ ์‹œ๊ฐ„: ์ด 50๋ถ„


CLS, Cumulative Layout Shift

๊ธฐ์ค€ ์ ์ˆ˜

| CLS ์ ์ˆ˜ | ๊ธฐ์ค€ | | -------- | ---- | | <0.1 | ์ข‹์Œ | | <0.25 | ๋ณดํ†ต | | >=0.25 | ๋‚˜์จ |

๊ฐœ์„ ๋ฐฉ์•ˆ

  • ์‚ฝ์ž…์„ ์œ„ํ•œ ์ถ”๊ฐ€ ๊ณต๊ฐ„ ํ™•๋ณด

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

    • FOUT, flash of unstyled text // ๊ธฐ๋ณธ ํฐํŠธ๋กœ ๋ณด์ด๊ณ  ์žˆ๋‹ค๊ฐ€, ๋’ค๋Šฆ๊ฒŒ ํฐํŠธ๊ฐ€ ์ ์šฉ๋˜๋Š” ํ˜„์ƒ
    • FOIT, flash of invisible text // ๋ฌธ์„œ์— ์ง€์ •ํ•œ ํฐํŠธ๋„ ์—†๊ณ , ๊ธฐ๋ณธ ํฐํŠธ๋„ ์—†๋‹ค๊ฐ€ ๋’ค๋Šฆ๊ฒŒ ํฐํŠธ๊ฐ€ ๋กœ๋”ฉ๋˜๋ฉฐ, ํŽ˜์ด์ง€์— ๋žœ๋”๋ง๋˜๋Š” ํ˜„์ƒ
  • ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

    • <link>์˜ preload, rel=preload

    • font-family

      | ์˜ต์…˜ | ๋กœ๋”ฉ | ํด๋ฐฑ ํฐํŠธ | | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | | anto | ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๊ฒฐ์ • | | | block | ํฐํŠธ๊ฐ€ ๋กœ๋”ฉ๋˜๊ธฐ ์ „๊นŒ์ง€ ๋žœ๋”๋ง์„ ์ค‘๋‹จํ•œ๋‹ค(์ตœ๋Œ€ 3์ดˆ) | | | swap | FOUT, ํด๋ฐฑ ํฐํŠธ๋กœ ๊ธ€์ž๋ฅผ ๋žœ๋”๋ง, ์›น ํฐํŠธ์˜ ๋กœ๋”ฉ์ด ์™„๋ฃŒ๋˜๋ฉด ์›น ํฐํŠธ๋ฅผ ์ ์šฉ | O | | fallback | 100ms๊ฐ„ ํ…์ŠคํŠธ๊ฐ€ ๋ณด์ด์ง€ ์•Š๊ณ , ํด๋ฐฑ ํฐํŠธ๋กœ ๋žœ๋”๋ง, 3์ดˆ ์•ˆ์— ํฐํŠธ๊ฐ€ ๋กœ๋”ฉ๋œ๋‹ค๋ฉด? ํ•ด๋‹น ์›น ํฐํŠธ๋กœ ์ „ํ™˜, ์•„๋‹ˆ๋ผ๋ฉด? ํด๋ฐฑ ํฐํŠธ๋ฅผ ๊ณ„์† ์‚ฌ์šฉ | O | | optional | 100ms๊ฐ„ ํ…์ŠคํŠธ๊ฐ€ ๋ณด์ด์ง€ ์•Š๊ณ , ํด๋ฐฑ ํฐํŠธ๋กœ ๋žœ๋”๋ง. 0.1์ดˆ ๋‚ด๋กœ ํฐํŠธ๊ฐ€ ๋‹ค์šด๋กœ๋“œ ๋˜์–ด ์žˆ๊ฑฐ๋‚˜, ์บ์‹œ๋ผ ์žˆ์ง€ ์•Š๋‹ค๋ฉด ํด๋ฐฑ ํฐํŠธ๋ฅผ ์‚ฌ์šฉ, ๋„คํŠธ์›Œํฌ ์ƒํƒœ๋ฅผ ํŒŒ์•…ํ•ด ์—ฐ๊ฒฐ์„ ์ทจ์†Œ | O |

  • ์ ์ ˆํ•œ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์„ค์ •

    • aspect-ratio: ๋ธŒ๋ผ์šฐ์ €์˜ ์œ ์ € ์—์ด์ „ํŠธ ์Šคํƒ€์ผ์‹œํŠธ์— ํฌํ•จ๋˜์–ด ์žˆ๋‹ค. ์ด๋ฏธ์ง€์˜ ๊ฐ€๋กœ์„ธ๋กœ ๋น„์œจ์„ ์ž๋™์œผ๋กœ ๋งž์ถฐ์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
      • ์ •ํ™•ํ•œ ์ˆซ์ž๋ฅผ ์จ์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. (๋ฏธ์ง€์› ์˜ค๋ž˜๋œ ๋ธŒ๋ผ์šฐ์ €, CSS ๋กœ๋”ฉ ์‹คํŒจ)
    • srcset
    <img
      width="1000"
      height="1000"
      src="image-1000.jpg"
      srcset="image-1000.jsp 1000w, image-2000 2000w"
    >
    

ํ•ต์‹ฌ ์›น ์ง€ํ‘œ๊ฐ€ ์•„๋‹Œ ์„ฑ๋Šฅ ํ™•์ธ ์ง€ํ‘œ๋“ค

TTFB, Time To First Byte

  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์›นํŽ˜์ด์ง€์˜ ์ฒซ ๋ฒˆ์งธ ๋ฐ”์ดํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š”๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„
    • 600ms ์ด์ƒ ๊ฑธ๋ฆด ๊ฒฝ์šฐ? ๊ฐœ์„  ํ•„์š”
    • ์ตœ์ดˆ์˜ ์‘๋‹ต์ด ์˜ค๋Š” ๋ฐ”์ดํŠธ๊นŒ์ง€ ์–ผ๋งˆ๋‚˜ ๊ฑธ๋ฆฌ๋‚˜
  • ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋žœ๋”๋ง์˜ ๊ฒฝ์šฐ > ์„œ๋ฒ„์—์„œ ์ฒซ ๋ฒˆ์จฐ HTML์„ ๊ทธ๋ฆฌ๋Š” ํ•„์š”ํ•œ ์ž‘์—…์ด ๋งŽ๊ฑฐ๋‚˜๋Š๋ฆด ์ˆ˜๋ก, TTFB๊ฐ€ ๊ธธ์–ด์ง€๊ฒŒ ๋œ๋‹ค.
  • ๊ณ ๋ ค์‚ฌํ•ญ
    • ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋žœ๋”๋ง
      • ์ •์  ํŽ˜์ด์ง€ ๋žœ๋”๋ง ๋กœ์ง์„ ์ตœ์ ํ™”
      • API ํ˜ธ์ถœ ์ตœ์ ํ™”
    • CDN
    • Stream

FCP, First Contentful Paint

| FCP ์ ์ˆ˜ | ๊ธฐ์ค€ | | -------- | ---- | | <1.8 | ์ข‹์Œ | | <3.0 | ๋ณดํ†ต | | >=3.0 | ๋‚˜์จ |

๊ฐœ์„ ๋ฐฉ์•ˆ

  • ๋ญ๋ผ๋„ ๋‹ค์šด๋กœ๋“œํ•ด์„œ TTFB๋ฅผ ๋‹จ์ถ•
  • ๋žœ๋”๋ง์„ ๊ฐ€๋กœ๋ง‰๋Š” ๋ฆฌ์†Œ์Šค ์ตœ์†Œํ™”: ๋žœ๋”๋ง ๋ฐฉํ•ด ์š”์†Œ๋Š” ๋น„๋™๊ธฐ๋กœ ๋กœ๋“œ
  • Above the fold: ์Šคํฌ๋กค ํ•˜์ง€ ์•Š์•„๋„ ๋ณด์ด๋Š” ์˜์—ญ์— ๋ ˆ์ด์ง€ ๋กœ๋”ฉ์ด๋‚˜, ์Šคํฌ๋ฆฝํŠธ ์˜์กด ์ฝ”๋“œ๋ฅผ ํ”ผํ•œ๋‹ค
  • ํŽ˜์ด์ง€ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ตœ์†Œํ™”
    • ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋Š” ๋น„์šฉ > ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฌด์–ธ๊ฐ€๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” ์‹œ๊ฐ„์ด ์ง€์—ฐ๋˜๊ธฐ ๋•Œ๋ฌธ
  • DOM ํฌ๊ธฐ ์ตœ์†Œํ™”: ๊ตฌ๊ธ€ ๊ธฐ์ค€, DOM์š”์†Œ 1500๊ฐœ, depts 32๋‹จ๊ณ„, ์ž์‹๋…ธ๋“œ๋Š” 60๊ฐœ

13. ์›น ํŽ˜์ด์ง€์˜ ์„ฑ๋Šฅ์„ ์ธก์ •ํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•

  • create-react-app:
    • reportWebVitals()
      • web-vitals ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ง€ํ‘œ๋ฅผ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • PerformanObserver API๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์›น ํŽ˜์ด์ง€ ์„ฑ๋Šฅ์„ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
import { ReportHandler } from "web-vitals";

const reportWebVitals = (onPerfEntry?: ReportHandler) => {
  if (onPerfEntry && onPerfEntry instanceof Function) {
    import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFE }) => {
      getCLS(onPerfEntry);
      getFID(onPerfEntry);
      getFCP(onPerfEntry);
      getLCP(onPerfEntry);
      getTTFE(onPerfEntry);
    });
  }
};

export default reportWebVitals;

reportWebVitals();
  • ์ฝ˜์†” ์ฐ๊ธฐ
reportWebVitals(console.log);
  • ๋ถ„์„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ด๊ณ  ์‹ถ๋‹ค๋ฉด?
    • sendBeacon API
    • fetch๋กœ ๋ณด๋‚ด๊ธฐ
function sendToAnalytics(metic: ReportHandler) {
  const body = JSON.stringify(metric);
  const url = "/api/analytics";

  if (navigator.sendBeacon) {
    navigator.sendBeacon(url, body);
  } else {
    fetch(url, { body, method: "POST", keepalive: true });
  }
}

reportWebVitals(sendToAnalytics);
  • googleAnalytics
function sendToAnalytics({ id, name, value }: ReportHandler) {
  ga("send", "event", {
    eventCategory: "Web Vitals",
    eventAction: Math.round(name === "CLS" ? value * 1000 : value),
    eventLabel: id,
    nonInteraction: true,
  });
}

reportWebVitals(sendToAnalytics);