Node.js 멀티 프로세스로 CPU 집약 작업 처리하기
문제 상황
이미지 업로드 API에서 썸네일 생성 작업을 동기로 처리하다가, 대용량 이미지 업로드 시 다른 요청들이 모두 대기하는 현상이 발생했다. Sharp 라이브러리로 리사이징을 하는데, CPU 집약적인 작업이라 이벤트 루프를 완전히 블로킹했다.
해결 과정
1. Cluster 모듈 도입
먼저 CPU 코어 수만큼 워커 프로세스를 띄워 부하를 분산했다.
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`워커 ${worker.process.pid} 종료, 재시작`);
cluster.fork();
});
} else {
require('./app');
}
2. Worker Threads로 작업 분리
Node 10.5부터 실험적으로 제공되는 Worker Threads를 사용해 이미지 처리만 별도 스레드로 분리했다.
const { Worker } = require('worker_threads');
function resizeImage(imagePath, options) {
return new Promise((resolve, reject) => {
const worker = new Worker('./workers/image-worker.js', {
workerData: { imagePath, options }
});
worker.on('message', resolve);
worker.on('error', reject);
});
}
3. 성능 측정
- 기존: 5MB 이미지 처리 시 평균 응답 시간 3.2초
- 개선 후: 평균 응답 시간 0.8초, 다른 요청 영향 없음
배운 점
Node.js는 I/O 작업에는 탁월하지만 CPU 집약 작업에는 취약하다. Cluster로 프로세스를 분산하고, Worker Threads로 무거운 작업을 격리하는 전략이 효과적이었다. 다만 Worker Threads는 아직 실험 단계라 프로덕션 적용 시 주의가 필요하다.