--- id: wiki-2026-0508-메인-스레드-main-thread title: 메인 스레드 (Main Thread) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Main Thread, UI Thread, JavaScript Thread] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [browser, performance, javascript, frontend] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: javascript framework: browser --- # 메인 스레드 (Main Thread) ## 매 한 줄 > **"매 single thread 매 JS, layout, paint, event 매 모두 처리"**. 매 50ms+ task — 매 long task — 매 jank/INP 악화. 매 해결 = 매 break up + Web Worker (CPU) + scheduler.yield (cooperative) + offscreen canvas (rendering). ## 매 핵심 ### 매 main thread 책임 - **JavaScript** 실행. - **Style** 계산. - **Layout** (reflow). - **Paint**. - **Composite** 매 일부 (대부분 GPU). - **Event** dispatching. - **rAF** callbacks. ### 매 long task - **>50ms** task — Lighthouse "long task" — 매 user input 차단. - **TBT** (Total Blocking Time) — long task time 합산. - **INP** (Interaction to Next Paint, 2024 Web Vital) — 매 main thread block 매 직접 영향. ### 매 yielding 전략 1. `setTimeout(fn, 0)` — 매 macrotask — 매 input priority 의 X. 2. `requestIdleCallback` — 매 idle 만 — 매 best effort. 3. `scheduler.postTask` (priority) — 매 modern. 4. `scheduler.yield()` (Chrome 129+) — 매 explicit yield + continue. 5. `MessageChannel` postMessage — 매 lower latency. 6. **Web Worker** — 매 다른 thread — 매 CPU heavy. 7. **OffscreenCanvas** — 매 worker 매 렌더링. ### 매 응용 1. Heavy parsing (CSV, JSON 매 수십 MB). 2. ML inference (TensorFlow.js — Worker). 3. Image processing — OffscreenCanvas. 4. List rendering — virtualization + chunking. ## 💻 패턴 ### Long task break up — scheduler.yield ```javascript async function processItems(items) { for (const item of items) { process(item); if (navigator.scheduling?.isInputPending() || performance.now() - lastYield > 50) { await scheduler.yield(); // 매 main thread 풀어줌 lastYield = performance.now(); } } } ``` ### scheduler.postTask (priority) ```javascript scheduler.postTask(() => doImportant(), { priority: 'user-blocking' }); scheduler.postTask(() => doIdle(), { priority: 'background' }); ``` ### Web Worker (CPU heavy) ```javascript // main.js const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' }); worker.postMessage({ data: largeArray }); worker.onmessage = e => console.log('result', e.data); // worker.js self.onmessage = e => { const result = heavyComputation(e.data.data); self.postMessage(result); }; ``` ### Comlink (worker RPC) ```javascript // main.js import * as Comlink from 'comlink'; const api = Comlink.wrap(new Worker('./worker.js')); const result = await api.heavyTask(largeData); // worker.js import * as Comlink from 'comlink'; Comlink.expose({ heavyTask: data => heavyComputation(data) }); ``` ### OffscreenCanvas (worker render) ```javascript // main const canvas = document.querySelector('canvas'); const offscreen = canvas.transferControlToOffscreen(); worker.postMessage({ canvas: offscreen }, [offscreen]); // worker self.onmessage = e => { const ctx = e.data.canvas.getContext('2d'); function frame() { ctx.fillRect(...); requestAnimationFrame(frame); } frame(); }; ``` ### Chunked processing (rAF) ```javascript function processChunked(items, chunkSize = 100) { let i = 0; function chunk() { const end = Math.min(i + chunkSize, items.length); for (; i < end; i++) process(items[i]); if (i < items.length) requestAnimationFrame(chunk); } chunk(); } ``` ### isInputPending (yield 시점 결정) ```javascript function workLoop(deadline) { while (tasks.length && !navigator.scheduling.isInputPending()) { tasks.shift()(); } if (tasks.length) scheduler.postTask(() => workLoop()); } ``` ### React 18 transition (yield 자동) ```jsx import { useTransition } from 'react'; const [isPending, startTransition] = useTransition(); const onChange = e => { setQuery(e.target.value); // 매 urgent startTransition(() => { setResults(filter(e.target.value)); // 매 interruptible }); }; ``` ### Long Animation Frames API (LoAF) 측정 ```javascript new PerformanceObserver(list => { list.getEntries().forEach(e => { console.log('LoAF:', e.duration, 'ms', e.scripts); }); }).observe({ type: 'long-animation-frame', buffered: true }); ``` ## 매 결정 기준 | 작업 | 처리 | |---|---| | Heavy CPU (parse, ML inference) | Web Worker | | List 1000+ items render | virtualization + chunking | | Canvas animation heavy | OffscreenCanvas in Worker | | User-blocking + background mix | `scheduler.postTask` priority | | React state update non-urgent | `useTransition` | | Iterative loop break up | `scheduler.yield()` 매 50ms | | Idle prefetch | `requestIdleCallback` | **기본값**: CPU heavy = Worker + Comlink, list = virtual + chunked, React urgent/non = `useTransition`, loop = `scheduler.yield`. ## 🔗 Graph - 부모: [[Browser Architecture]] · [[Web Performance]] - 변형: [[Web Worker]] · [[OffscreenCanvas]] · [[Service Worker]] - 응용: [[INP 최적화]] · [[가상화 (Virtualization)]] · [[React Concurrent]] - Adjacent: [[Long Animation Frames]] · [[scheduler API]] · [[Comlink]] ## 🤖 LLM 활용 **언제**: INP 디버깅, "왜 input 매 lag", Worker offload 결정. **언제 X**: 매 specific framework scheduler internals (React fiber) — 매 framework docs. ## ❌ 안티패턴 - **`while(true)` heavy loop**: 매 page freeze. - **Sync XHR**: 매 main thread block — 매 deprecated. - **JSON.parse on 50MB**: 매 Worker 의 사용. - **`setTimeout(0)` 매 yield 가정**: 매 input priority 의 X — 매 `scheduler.yield`. - **Worker 매 message 매 large clone**: 매 transferable (`ArrayBuffer.transfer`) 의 사용. ## 🧪 검증 / 중복 - Verified (web.dev INP/Long Tasks, Chrome DevRel scheduler API, MDN Web Workers). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — yielding + Worker + OffscreenCanvas |