Node.js 22 Worker Threads로 이미지 일괄 처리 성능 개선

문제 상황

사용자가 업로드한 이미지를 여러 크기로 리사이징하는 배치 작업이 있었다. 한 번에 수백 장의 이미지를 처리하다 보니 메인 스레드가 블로킹되어 API 응답 시간이 급격히 증가했다.

기존 코드는 sharp 라이브러리를 순차적으로 호출하는 구조였다.

for (const image of images) {
  await sharp(image.path)
    .resize(800, 600)
    .toFile(outputPath);
}

Worker Threads 도입

Node.js의 Worker Threads를 사용해 이미지 처리를 병렬화했다. CPU 코어 수만큼 워커를 생성하고 작업을 분산시켰다.

import { Worker } from 'worker_threads';
import os from 'os';

const numWorkers = os.cpus().length;
const workers = [];

for (let i = 0; i < numWorkers; i++) {
  workers.push(new Worker('./image-worker.js'));
}

let currentWorker = 0;

for (const image of images) {
  const worker = workers[currentWorker];
  worker.postMessage({ path: image.path, output: outputPath });
  currentWorker = (currentWorker + 1) % numWorkers;
}

워커 파일은 다음과 같이 작성했다.

// image-worker.js
import { parentPort } from 'worker_threads';
import sharp from 'sharp';

parentPort.on('message', async ({ path, output }) => {
  try {
    await sharp(path)
      .resize(800, 600)
      .toFile(output);
    parentPort.postMessage({ success: true, output });
  } catch (error) {
    parentPort.postMessage({ success: false, error: error.message });
  }
});

결과

500장의 이미지 처리 시간이 8분에서 2.5분으로 단축되었다. 메인 스레드 블로킹도 해결되어 API 응답 시간이 정상화되었다.

주의할 점은 워커 간 메모리를 공유하지 않는다는 것이다. 큰 데이터를 전달할 때는 SharedArrayBuffer나 파일 경로를 활용하는 것이 효율적이다.