Node.js worker_threads로 CPU 집약 작업 메인 스레드에서 분리하기
문제
Node.js에서 이미지 리사이징이나 대용량 JSON 파싱 같은 CPU 집약 작업을 돌리면 이벤트 루프가 블로킹된다. API 응답이 멈추고, 다른 요청도 처리 못 하는 상황이 발생한다.
해결
worker_threads 모듈로 무거운 연산을 별도 스레드에서 처리하면 된다.
// worker.js - 워커 스레드에서 실행될 코드
const { parentPort, workerData } = require('worker_threads');
function heavyCalculation(data) {
// CPU 집약적인 작업 (예: 해시 계산, 데이터 변환)
let result = 0;
for (let i = 0; i < data.iterations; i++) {
result += Math.sqrt(i) * Math.random();
}
return result;
}
const result = heavyCalculation(workerData);
parentPort.postMessage(result);
// main.js - 메인 스레드
const { Worker } = require('worker_threads');
function runWorker(data) {
return new Promise((resolve, reject) => {
const worker = new Worker('./worker.js', { workerData: data });
worker.on('message', resolve);
worker.on('error', reject);
});
}
// Express 라우트에서 사용
app.get('/heavy', async (req, res) => {
const result = await runWorker({ iterations: 10_000_000 });
res.json({ result });
// 이 동안 다른 요청은 정상적으로 처리된다
});
워커를 매번 생성하면 오버헤드가 있으니, 반복 작업이면 워커 풀을 쓰는 게 낫다.
// 간단한 워커 풀
const os = require('os');
const poolSize = os.cpus().length;
const workers = Array.from({ length: poolSize },
() => new Worker('./worker.js')
);
핵심 포인트
worker_threads는 Node.js 내장 모듈이라 별도 설치 불필요workerData로 데이터 전달,parentPort.postMessage()로 결과 반환- 메인 스레드 이벤트 루프를 블로킹하지 않아 API 응답성 유지
- 반복 사용 시 워커 풀 패턴으로 생성 오버헤드를 줄이자