logo hsb.horse
← 블로그 목록으로 돌아가기

블로그

OffscreenCanvas와 Web Worker로 이미지 처리를 구현하기

Web Worker와 OffscreenCanvas를 사용해 메인 스레드를 막지 않고 이미지 처리를 수행하는 방법. WebP 변환 예제를 중심으로 정리했다.

게시일:

OffscreenCanvas와 Web Worker를 함께 쓰면 메인 스레드를 막지 않고 이미지 처리를 실행할 수 있다.

사용 기술

  • Web Worker
  • Offscreen Canvas
  • Blob
  • Bitmap

인터페이스

export interface WorkerRequestBody {
buffer: ArrayBuffer;
type: string;
size: {
height: number;
width: number;
};
}
export interface WorkerResponseBody {
buffer: ArrayBuffer;
contentType: string;
}

Web Worker에서 이미지 처리하기

async function imageProcessing(
data: WorkerRequestBody
): Promise<WorkerResponseBody> {
const { buffer, type, size } = data;
// ArrayBuffer를 다시 Blob으로 변환
const blob = new Blob([buffer], { type });
// OffscreenCanvas 생성
const canvas = new OffscreenCanvas(size.width, size.height);
// Blob에서 Bitmap 생성
const bitmap = await createImageBitmap(blob);
// CanvasRenderingContext2D 가져오기
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Context2D is not defined");
// 이미지를 Canvas에 그리기
ctx.drawImage(bitmap, 0, 0);
// WebP로 변환
const webp = await canvas.convertToBlob({
type: "image/webp",
quality: 0.85,
});
// WebP Blob을 ArrayBuffer로 변환
const buf = await webp.arrayBuffer();
return {
buffer: buf,
contentType: "image/webp",
};
}
self.addEventListener("message", async (e: MessageEvent<WorkerRequestBody>) => {
const output = await imageProcessing(e.data);
// ArrayBuffer 소유권을 UI 스레드로 넘기기 위해 두 번째 인자를 지정
self.postMessage(output, [output.buffer]);
});

Worker에 이미지를 전달하고 변환된 결과 받기

import MyWorker from "./worker.ts";
const worker = new MyWorker();
const fileInput = document.querySelector<HTMLInputElement>("#js-file-input");
worker.addEventListener("message", (e: MessageEvent<WorkerResponseBody>) => {
const { buffer, contentType } = e.data;
const blob = new Blob([buffer], { type: contentType });
// do something
});
fileInput.addEventListener("change", async (e) => {
const [file] = Array.from(fileInput.files);
if (!file) return;
const buffer = await file.arrayBuffer();
const type = file.type;
const size = { height: 1080, width: 1980 }; // 이미지 크기 취득
// Web Worker로 전송하고 소유권 이전
worker.postMessage(
{
buffer,
type,
size
},
[buffer]
);
});

포인트

  1. OffscreenCanvas: Web Worker 안에서 쓸 수 있는 Canvas API
  2. ArrayBuffer 전송: 소유권 이전으로 효율적으로 데이터 전달
  3. convertToBlob: OffscreenCanvas 기능으로 WebP 같은 포맷으로 변환

정리

OffscreenCanvas와 Web Worker를 조합하면 메인 스레드를 막지 않고 이미지 처리를 실행할 수 있다.

대량 이미지 처리나 무거운 변환 작업이 필요한 경우에도 UI 응답성을 유지할 수 있어서 사용자 경험이 좋아진다.