Skip to content

Latest commit

 

History

History
219 lines (146 loc) · 5.42 KB

File metadata and controls

219 lines (146 loc) · 5.42 KB

예제 16: Web Workers 활용

목적

메인 스레드 부하를 줄이기 위해 Web Workers를 활용하는 방법을 독립적으로 학습하기 위한 예제입니다. 현재는 문서만 제공하며, 실습 코드는 추후 추가될 예정입니다.


구조

아래는 구현 예정 구조입니다.

  • Before 모드

    • 무거운 계산을 메인 스레드에서 수행
    • 스크롤/입력 반응성이 저하됨
    • 긴 작업 중 UI 프리즈 발생
  • After 모드

    • Web Worker로 계산을 오프로딩
    • 메인 스레드는 UI 처리에 집중
    • 진행 상태 표시 및 결과 스트리밍
  • 레이아웃

    • Before/After 모드를 나란히 비교
    • 대용량 계산(정렬/필터/집계) 시 UI 반응성 비교
    • 작업 진행 상태와 완료 시간 비교

📚 이론: Web Workers 원리와 전략

1. 개요 (Overview)

Web Worker는 메인 스레드와 분리된 백그라운드 스레드에서 JavaScript를 실행하여 UI 렌더링을 방해하지 않게 해줍니다.

핵심 개념:

  • Main Thread: UI, 이벤트 처리, 렌더링 담당
  • Worker Thread: 계산/파싱 등 무거운 작업 담당
  • postMessage: 메시지 기반 통신
  • Structured Clone: 데이터 복사 방식 (큰 데이터는 비용 큼)
  • Transferable: 소유권 이동으로 복사 비용 절감

2. Web Workers와 성능의 관계

2.1 메인 스레드 블로킹 방지

  • Before: 큰 계산이 UI 입력/스크롤을 막음
  • After: 계산을 워커로 오프로딩, UI 즉시 반응
  • 효과: 입력 지연 감소, 프레임 드랍 감소

2.2 작업 병렬화

  • Before: 모든 계산을 순차적으로 메인에서 처리
  • After: 워커로 병렬 처리 (가능한 범위 내)
  • 효과: 전체 처리 시간 감소, 사용자 체감 개선

2.3 대용량 데이터 처리

  • Before: 큰 배열 처리 중 UI 프리즈
  • After: 워커에서 처리, UI는 진행 상태 표시
  • 효과: 사용자 경험 안정

3. 문제 상황: 메인 스레드에서 무거운 계산

❌ Bad Case

function handleClick() {
  const result = heavyCompute(largeData);
  setOutput(result);
}

문제점

  • 계산이 끝날 때까지 UI가 멈춤
  • 스크롤/입력 이벤트가 지연됨
  • 앱이 멈춘 것처럼 보임

4. Web Worker 기본 사용법

4.1 워커 생성과 메시지 통신

// 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 });
  }
};

4.2 진행 상태 업데이트

// worker.ts
for (let i = 0; i < total; i += 1) {
  // 일부 진행 후 상태 전달
  if (i % 1000 === 0) {
    self.postMessage({ type: "PROGRESS", payload: i / total });
  }
}

5. Transferable로 복사 비용 줄이기

큰 데이터를 메시지로 보낼 때 복사 비용이 큽니다. Transferable을 사용하면 소유권을 이동하여 복사 비용을 줄일 수 있습니다.

const buffer = new ArrayBuffer(1024 * 1024);
worker.postMessage(buffer, [buffer]); // 소유권 이동

주의사항:

  • 전송 후 메인 스레드에서는 buffer를 사용할 수 없음
  • 재사용해야 하면 복사본을 유지

6. 적용 패턴

  • 대용량 데이터 정렬/필터/집계
  • 이미지 처리/압축
  • JSON 파싱/직렬화
  • 암호화/해시 계산

7. 주의사항

  • 워커는 DOM 접근 불가 (UI 조작 불가)
  • 메시지 전달 비용이 큰 데이터에서는 오히려 느릴 수 있음
  • 워커 생성/종료 비용이 있으므로 재사용 고려
  • 에러 처리는 worker.onerror로 별도 처리

8. 실전 적용 체크리스트

  • 메인 스레드가 자주 막히는가? → 워커 고려
  • 작업이 순수 계산인가? → 워커 적합
  • 대용량 데이터 전송이 필요한가? → Transferable 사용
  • 진행 상태 표시가 필요한가? → PROGRESS 메시지 설계
  • 워커 재사용이 가능한가? → 풀링 고려

실행 방법

현재 예제 코드는 준비 중입니다. 코드가 추가되면 실행 방법을 업데이트하겠습니다.


측정 방법

1. Performance 패널

  • Chrome DevTools > Performance > Record
  • 메인 스레드 점유 시간과 long task 확인

2. 사용자 체감

  • 작업 중 입력/스크롤 반응성 비교
  • 진행 상태 표시 유무에 따른 체감 차이 확인

핵심 요약

  • Web Worker는 메인 스레드 부하를 분리해 UI 반응성을 개선
  • 메시지 전달 비용을 고려해 Transferable 활용
  • 진행 상태를 전달해 UX 안정성 향상
  • 워커 생성 비용과 재사용 전략을 함께 고려

📚 참고 자료 (References)

Web Workers

성능 최적화