Files
2nd/10_Wiki/Topics/Frontend/CPU Overhead.md
T
koriweb d8a80f6272 chore(wiki): dangling 링크 canonical 정규화 (768파일/1200건)
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해
끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은
과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업.
도구: Datacollect/scripts/link_reconcile_apply.mjs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:24:15 +09:00

5.2 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-cpu-overhead CPU Overhead 10_Wiki/Topics verified self
CPU Cost
JS Main Thread Cost
none A 0.9 applied
performance
cpu
main-thread
profiling
2026-05-10 pending
language framework
JavaScript Browser/Node

CPU Overhead

매 한 줄

"매 main thread 매 free 매 fast UI". CPU overhead는 JS execution / parsing / hydration / re-render에 소비되는 main-thread time — 이게 길어지면 INP가 무너지고 user input이 lag한다. 2026 INP가 LCP를 대체한 third Core Web Vital이 되어 CPU profile + scheduling이 frontend 의 first concern.

매 핵심

매 source

  • Parse + compile: download된 JS bytes → AST → bytecode (V8 측정 시 KB 당 ~1ms low-end mobile).
  • Hydration: SSR HTML 위 React/Vue 매 attach.
  • Re-render: state change → diff → DOM commit.
  • Long task (>50ms): block input.

매 측정

  • Chrome Performance panel — flame chart, "Long tasks".
  • PerformanceObserver API on longtask.
  • React Profiler / Vue Devtools timeline.
  • Web Vitals — INP, TBT.

매 응용

  1. JS payload 줄이기 → less parse.
  2. Hydration partial / streaming.
  3. Heavy work → Web Worker / requestIdleCallback / startTransition.

💻 패턴

Long task observer

const obs = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.duration > 50) {
      console.warn(`Long task: ${entry.duration.toFixed(0)}ms`, entry);
    }
  }
});
obs.observe({ type: 'longtask', buffered: true });

Yield to main thread

function yieldToMain() {
  return new Promise(resolve => setTimeout(resolve, 0));
}

async function processChunks(items: Item[]) {
  for (let i = 0; i < items.length; i++) {
    process(items[i]);
    if (i % 100 === 0) await yieldToMain();
  }
}

scheduler.yield (Chrome 129+)

async function processBig(items: Item[]) {
  for (const item of items) {
    process(item);
    if ('scheduler' in window && 'yield' in (window as any).scheduler) {
      await (window as any).scheduler.yield();
    }
  }
}

Web Worker offload

// worker.ts
self.onmessage = (e) => {
  const result = heavyTransform(e.data);
  self.postMessage(result);
};

// main.ts
const w = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
w.postMessage(largeData);
w.onmessage = (e) => render(e.data);

React startTransition

import { startTransition, useState } from 'react';

function Search() {
  const [q, setQ] = useState('');
  const [results, setResults] = useState<Item[]>([]);

  function onChange(e) {
    setQ(e.target.value);                      // urgent
    startTransition(() => {
      setResults(filter(allItems, e.target.value)); // background
    });
  }
  return <input value={q} onChange={onChange} />;
}

useDeferredValue

function Page({ filter }) {
  const deferredFilter = useDeferredValue(filter);
  const items = useMemo(() => filterBig(deferredFilter), [deferredFilter]);
  return <List items={items} />;
}

requestIdleCallback for non-critical

const work = [...];
function schedule() {
  if (!work.length) return;
  requestIdleCallback((deadline) => {
    while (work.length && deadline.timeRemaining() > 0) {
      doOne(work.shift());
    }
    schedule();
  });
}
schedule();

Avoid layout thrash

// X — 매 read after write 매 force reflow
els.forEach(el => {
  el.style.width = '100px';
  console.log(el.offsetWidth); // forced sync layout
});

// O — 매 batch read, batch write
const widths = els.map(el => el.offsetWidth);
els.forEach((el, i) => el.style.width = widths[i] + 1 + 'px');

매 결정 기준

상황 Approach
Heavy compute (parse/transform) Web Worker
Long list render virtualization (TanStack Virtual)
Filter on input useDeferredValue / startTransition
Background prefetch requestIdleCallback
Animation CSS / RAF, no JS-driven layout

기본값: measure → smallest fix → re-measure.

🔗 Graph

🤖 LLM 활용

언제: long task identification, scheduler API generation, INP debug script. 언제 X: real device profiling — DevTools / WebPageTest 필수.

안티패턴

  • JS-driven animation: setInterval + style 변경 — RAF / CSS 사용.
  • Sync XMLHttpRequest: 매 main block — fetch async 사용.
  • Force layout in loop: read after write — batch.
  • Hydration of static page: islands / partial hydration.
  • Massive context provider: 매 모든 child re-render — split context.

🧪 검증 / 중복

  • Verified (web.dev INP guide, Chrome scheduler API, React 19 docs).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — CPU overhead pattern + scheduler API