•3 min read
Node.js Worker Threads로 CPU 집약적 작업 처리하기
Node.js의 싱글 스레드 한계를 극복하는 Worker Threads 활용법을 실무 예제와 함께 알아봅니다.
Node.jsPerformance
Node.js는 이벤트 루프 기반의 싱글 스레드 모델로 유명합니다. I/O 작업에는 뛰어난 성능을 보이지만, CPU 집약적인 작업에서는 한계가 있죠.
왜 Worker Threads가 필요한가?
// ❌ 이 코드는 이벤트 루프를 블로킹합니다
app.get('/heavy-computation', (req, res) => {
const result = fibonacci(45); // 몇 초간 서버 전체가 멈춤
res.json({ result });
});
// 다른 모든 요청이 대기하게 됨...Worker Threads 기본 사용법
메인 스레드 (main.js)
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
const worker = new Worker('./worker.js');
worker.postMessage({ num: 45 });
worker.on('message', (result) => {
console.log('결과:', result);
});
worker.on('error', (err) => {
console.error('Worker 에러:', err);
});
}워커 스레드 (worker.js)
const { parentPort } = require('worker_threads');
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
parentPort.on('message', (data) => {
const result = fibonacci(data.num);
parentPort.postMessage(result);
});실무 패턴: Worker Pool
매번 Worker를 생성하는 것은 비효율적입니다. Worker Pool을 만들어 재사용하세요:
import { Worker } from 'worker_threads';
import { cpus } from 'os';
class WorkerPool {
private workers: Worker[] = [];
private queue: Array<{
task: any;
resolve: (value: any) => void;
reject: (error: Error) => void;
}> = [];
private freeWorkers: Worker[] = [];
constructor(
private workerPath: string,
private poolSize: number = cpus().length
) {
this.init();
}
async execute<T>(task: any): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.processQueue();
});
}
private processQueue() {
if (this.queue.length === 0 || this.freeWorkers.length === 0) {
return;
}
const worker = this.freeWorkers.pop()!;
const { task, resolve } = this.queue.shift()!;
worker.once('message', resolve);
worker.postMessage(task);
}
}언제 Worker Threads를 사용해야 할까?
✅ 적합한 경우
- 이미지/비디오 처리
- 암호화/해시 연산
- 복잡한 수학 계산
- 대용량 데이터 파싱
❌ 부적합한 경우
- 단순 I/O 작업 (이미 비동기로 처리됨)
- 가벼운 연산 (Worker 생성 오버헤드가 더 큼)
성능 비교
// 벤치마크: fibonacci(40) 계산
// 싱글 스레드: 1,200ms (서버 블로킹)
// Worker 1개: 1,250ms (메인 스레드 자유)
// Worker 4개 병렬: 320ms결론
Worker Threads는 Node.js의 약점이었던 CPU 집약적 작업을 효과적으로 처리할 수 있게 해줍니다. 작업의 특성을 파악하고 적절히 활용하세요.