"메인 프로세스에서 무거운 작업을 하면 IPC가 막히고, 렌더러에서 하면 UI가 얼어버린다" — Worker Threads나 UtilityProcess로 분리하면 해결됩니다.


세 가지 병렬 처리 방법

방법환경메모리 공유용도
Worker ThreadsNode.jsSharedArrayBufferCPU 작업
UtilityProcessElectron없음Node.js 기능 필요 시
Web Workers렌더러없음UI 관련 병렬 작업

Worker Threads

JAVASCRIPT
// main.js에서 Worker Thread 생성
const { Worker } = require('worker_threads');
const path = require('path');

ipcMain.handle('process:heavy', async (_event, data) => {
  return new Promise((resolve, reject) => {
    const worker = new Worker(path.join(__dirname, 'workers/heavy.js'), {
      workerData: data,
    });

    worker.on('message', resolve);
    worker.on('error', reject);
    worker.on('exit', (code) => {
      if (code !== 0) {
        reject(new Error(`Worker 종료 코드: ${code}`));
      }
    });
  });
});
JAVASCRIPT
// workers/heavy.js
const { parentPort, workerData } = require('worker_threads');

// CPU 집약적 작업 수행
function processData(data) {
  // 예: 대용량 JSON 파싱, 이미지 변환, 암호화 등
  let result = 0;
  for (let i = 0; i < data.iterations; i++) {
    result += Math.sqrt(i);
  }
  return result;
}

const result = processData(workerData);
parentPort.postMessage(result);

Worker Pool

여러 작업을 처리할 때 Worker를 재사용하는 풀입니다.

JAVASCRIPT
class WorkerPool {
  constructor(workerFile, poolSize = 4) {
    this.workerFile = workerFile;
    this.poolSize = poolSize;
    this.workers = [];
    this.queue = [];

    for (let i = 0; i < poolSize; i++) {
      this.workers.push({ worker: this.createWorker(), busy: false });
    }
  }

  createWorker() {
    return new Worker(this.workerFile);
  }

  execute(data) {
    return new Promise((resolve, reject) => {
      const available = this.workers.find(w => !w.busy);
      if (available) {
        this.runTask(available, data, resolve, reject);
      } else {
        this.queue.push({ data, resolve, reject });
      }
    });
  }

  runTask(entry, data, resolve, reject) {
    entry.busy = true;
    entry.worker.postMessage(data);

    const onMessage = (result) => {
      entry.busy = false;
      entry.worker.removeListener('message', onMessage);
      resolve(result);
      this.processQueue();
    };

    entry.worker.on('message', onMessage);
    entry.worker.once('error', reject);
  }

  processQueue() {
    if (this.queue.length === 0) return;
    const available = this.workers.find(w => !w.busy);
    if (available) {
      const { data, resolve, reject } = this.queue.shift();
      this.runTask(available, data, resolve, reject);
    }
  }

  destroy() {
    this.workers.forEach(w => w.worker.terminate());
  }
}

// 사용
const pool = new WorkerPool('./workers/heavy.js', 4);
const result = await pool.execute({ iterations: 1000000 });

UtilityProcess (Electron 22+)

JAVASCRIPT
// main.js
const { utilityProcess } = require('electron');

const child = utilityProcess.fork(path.join(__dirname, 'services/indexer.js'));

child.postMessage({ type: 'index', directory: '/path/to/docs' });

child.on('message', (msg) => {
  if (msg.type === 'progress') {
    mainWindow.webContents.send('index:progress', msg.percent);
  }
  if (msg.type === 'done') {
    mainWindow.webContents.send('index:complete', msg.results);
  }
});
JAVASCRIPT
// services/indexer.js
process.parentPort.on('message', async (e) => {
  const { type, directory } = e.data;

  if (type === 'index') {
    const files = await scanDirectory(directory);
    for (let i = 0; i < files.length; i++) {
      await indexFile(files[i]);
      process.parentPort.postMessage({
        type: 'progress',
        percent: Math.round((i / files.length) * 100),
      });
    }
    process.parentPort.postMessage({ type: 'done', results: getIndex() });
  }
});

Web Worker (렌더러)

JAVASCRIPT
// renderer에서 사용
const worker = new Worker('./renderer-worker.js');
worker.postMessage({ type: 'sort', data: largeArray });

worker.onmessage = (e) => {
  const sorted = e.data;
  renderTable(sorted);
};

면접 포인트 정리

  • Worker Threads: Node.js 스레드, SharedArrayBuffer로 메모리 공유 가능
  • UtilityProcess: Electron 전용, 별도 프로세스로 완전한 Node.js 환경
  • Worker Pool 패턴으로 Worker 재사용하여 생성/파괴 비용 절감
  • CPU 집약 작업은 반드시 별도 스레드/프로세스로 분리

Worker Threads를 다뤘으면, 다음은 플러그인 시스템을 알아봅시다.

댓글 로딩 중...