메인 스레드 부하를 줄이기 위해 Web Workers를 활용하는 방법을 독립적으로 학습하기 위한 예제입니다. 현재는 문서만 제공하며, 실습 코드는 추후 추가될 예정입니다.
아래는 구현 예정 구조입니다.
-
Before 모드
- 무거운 계산을 메인 스레드에서 수행
- 스크롤/입력 반응성이 저하됨
- 긴 작업 중 UI 프리즈 발생
-
After 모드
- Web Worker로 계산을 오프로딩
- 메인 스레드는 UI 처리에 집중
- 진행 상태 표시 및 결과 스트리밍
-
레이아웃
- Before/After 모드를 나란히 비교
- 대용량 계산(정렬/필터/집계) 시 UI 반응성 비교
- 작업 진행 상태와 완료 시간 비교
Web Worker는 메인 스레드와 분리된 백그라운드 스레드에서 JavaScript를 실행하여 UI 렌더링을 방해하지 않게 해줍니다.
핵심 개념:
- Main Thread: UI, 이벤트 처리, 렌더링 담당
- Worker Thread: 계산/파싱 등 무거운 작업 담당
- postMessage: 메시지 기반 통신
- Structured Clone: 데이터 복사 방식 (큰 데이터는 비용 큼)
- Transferable: 소유권 이동으로 복사 비용 절감
- Before: 큰 계산이 UI 입력/스크롤을 막음
- After: 계산을 워커로 오프로딩, UI 즉시 반응
- 효과: 입력 지연 감소, 프레임 드랍 감소
- Before: 모든 계산을 순차적으로 메인에서 처리
- After: 워커로 병렬 처리 (가능한 범위 내)
- 효과: 전체 처리 시간 감소, 사용자 체감 개선
- Before: 큰 배열 처리 중 UI 프리즈
- After: 워커에서 처리, UI는 진행 상태 표시
- 효과: 사용자 경험 안정
function handleClick() {
const result = heavyCompute(largeData);
setOutput(result);
}- 계산이 끝날 때까지 UI가 멈춤
- 스크롤/입력 이벤트가 지연됨
- 앱이 멈춘 것처럼 보임
// main.ts
const worker = new Worker(new URL("./worker.ts", import.meta.url));
worker.postMessage({ type: "START", payload: largeData });
worker.onmessage = (event) => {
const { type, payload } = event.data;
if (type === "DONE") {
setOutput(payload);
}
};// worker.ts
self.onmessage = (event) => {
const { type, payload } = event.data;
if (type === "START") {
const result = heavyCompute(payload);
self.postMessage({ type: "DONE", payload: result });
}
};// worker.ts
for (let i = 0; i < total; i += 1) {
// 일부 진행 후 상태 전달
if (i % 1000 === 0) {
self.postMessage({ type: "PROGRESS", payload: i / total });
}
}큰 데이터를 메시지로 보낼 때 복사 비용이 큽니다. Transferable을 사용하면 소유권을 이동하여 복사 비용을 줄일 수 있습니다.
const buffer = new ArrayBuffer(1024 * 1024);
worker.postMessage(buffer, [buffer]); // 소유권 이동주의사항:
- 전송 후 메인 스레드에서는 buffer를 사용할 수 없음
- 재사용해야 하면 복사본을 유지
- 대용량 데이터 정렬/필터/집계
- 이미지 처리/압축
- JSON 파싱/직렬화
- 암호화/해시 계산
- 워커는 DOM 접근 불가 (UI 조작 불가)
- 메시지 전달 비용이 큰 데이터에서는 오히려 느릴 수 있음
- 워커 생성/종료 비용이 있으므로 재사용 고려
- 에러 처리는
worker.onerror로 별도 처리
- 메인 스레드가 자주 막히는가? → 워커 고려
- 작업이 순수 계산인가? → 워커 적합
- 대용량 데이터 전송이 필요한가? → Transferable 사용
- 진행 상태 표시가 필요한가? → PROGRESS 메시지 설계
- 워커 재사용이 가능한가? → 풀링 고려
현재 예제 코드는 준비 중입니다. 코드가 추가되면 실행 방법을 업데이트하겠습니다.
- Chrome DevTools > Performance > Record
- 메인 스레드 점유 시간과 long task 확인
- 작업 중 입력/스크롤 반응성 비교
- 진행 상태 표시 유무에 따른 체감 차이 확인
- Web Worker는 메인 스레드 부하를 분리해 UI 반응성을 개선
- 메시지 전달 비용을 고려해 Transferable 활용
- 진행 상태를 전달해 UX 안정성 향상
- 워커 생성 비용과 재사용 전략을 함께 고려
Web Workers
성능 최적화