👷

Web Workers — JavaScript에서 멀티스레드를 쓰는 법

postMessage의 구조화된 복제(Structured Clone)와 SharedArrayBuffer

JavaScript는 싱글 스레드다. 무거운 계산(이미지 처리, 암호화, 파싱)을 메인 스레드에서 하면 UI가 멈춘다. Web Worker는 이걸 해결한다.

// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: bigArray });
worker.onmessage = (e) => console.log('결과:', e.data);

// worker.js
self.onmessage = (e) => {
  const result = heavyComputation(e.data);  // 별도 스레드에서 실행
  self.postMessage(result);                 // 결과를 메인에 전달
};

postMessage의 데이터 전달 방식

구조화된 복제(Structured Clone) — 기본값. 데이터를 깊은 복사(deep copy)해서 Worker에 전달한다. 1GB 배열을 보내면 1GB를 복사하는 비용이 든다.

Transferable Objects — 소유권 이전. 복사 대신 메모리 소유권을 Worker로 넘긴다. 전송 후 원본은 사용 불가.

const buffer = new ArrayBuffer(1024 * 1024 * 100); // 100MB
worker.postMessage(buffer, [buffer]); // 두 번째 인자가 transfer list
// buffer.byteLength === 0 ← 원본은 이제 빈 껍데기

SharedArrayBuffer — 공유 메모리. 복사도 이전도 아닌, 같은 메모리를 두 스레드가 동시에 접근. Atomics API로 동기화. COOP/COEP 헤더 필요.

DOM에 접근할 수 없다

Worker에서는 document, window가 없다. UI 조작은 메인 스레드에 postMessage로 결과를 보내서 메인이 처리해야 한다.

Tooscut이 Worker를 쓰는 이유

앞에서 다룬 Tooscut 영상 편집기가 OffscreenCanvas를 Worker에 transfer하고, Worker 안에서 WASM 컴포지터로 WebGPU 렌더링을 하는 게 바로 이 패턴이다.

핵심 포인트

1

new Worker("worker.js")로 별도 스레드 생성

2

postMessage로 데이터 전송 — 구조화된 복제(기본) or Transferable(소유권 이전)

3

Worker에서 계산 후 postMessage로 결과 반환

4

SharedArrayBuffer로 공유 메모리 — Atomics로 동기화 (COOP/COEP 필수)

사용 사례

이미지 처리 — 리사이즈/필터를 Worker에서 실행하여 UI 안 멈추게 WASM 추론 — AI 모델 추론을 Worker에서 실행 (background-removal-js 패턴)

참고 자료