Node.js 자식 프로세스 활용법: 병렬 처리와 성능 향상

작성일 :

Node.js 자식 프로세스 활용법: 병렬 처리와 성능 향상

Node.js는 비동기 I/O와 이벤트 기반 아키텍처로 유명한 서버사이드 자바스크립트 런타임입니다. 그러나 단일 스레드로 동작하기 때문에 CPU 집약적인 작업에서는 병목 현상이 발생할 수 있습니다. 이러한 문제를 해결하기 위해 Node.js는 child_process 모듈을 제공합니다. 이 글에서는 자식 프로세스를 활용해 병렬 처리를 구현하고 Node.js 애플리케이션의 성능을 향상시키는 방법에 대해 자세히 알아보겠습니다.

자식 프로세스란?

자식 프로세스(child process)는 부모 프로세스(parent process)에 의해 생성된 독립적인 실행 단위를 말합니다. 자식 프로세스는 부모 프로세스와 별도로 실행되며, 자신만의 메모리 공간과 스레드를 가집니다. Node.js에서는 child_process 모듈을 사용하여 자식 프로세스를 생성하고 관리할 수 있습니다.

자식 프로세스 생성 방법

Node.js에서 자식 프로세스를 생성하는 방법은 세 가지가 있습니다: exec, spawn, fork 함수입니다.

exec

exec 함수는 새로운 프로세스를 생성하고 명령어를 실행합니다. 모든 결과는 하나의 버퍼에 저장되므로, 큰 데이터를 처리할 때는 적절하지 않습니다.

javascript
const { exec } = require('child_process');

exec('ls -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.error(`stderr: ${stderr}`);
});

spawn

spawn 함수는 명령어와 인자를 받아 새로운 프로세스를 생성합니다. 데이터는 스트림 방식으로 처리되므로, 큰 데이터를 다룰 때 적합합니다.

javascript
const { spawn } = require('child_process');

const ls = spawn('ls', ['-l']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

fork

fork 함수는 Node.js 모듈을 실행하는 새로운 프로세스를 생성합니다. fork는 주로 멀티프로세싱을 이용한 병렬 처리를 구현할 때 사용됩니다.

javascript
const { fork } = require('child_process');

const child = fork('child.js');

child.on('message', (message) => {
  console.log('Message from child', message);
});

child.send({ hello: 'world' });

병렬 처리와 성능 향상

자식 프로세스를 활용하면 Node.js 애플리케이션에서 병렬 처리가 가능해집니다. 이를 통해 CPU 집약적인 작업을 효율적으로 분산시키고, 전체 애플리케이션의 성능을 향상시킬 수 있습니다.

예제: 병렬 데이터 처리

다음은 자식 프로세스를 사용하여 병렬로 데이터를 처리하는 예제입니다.

parent.js

javascript
const { fork } = require('child_process');

const data = [1, 2, 3, 4, 5, 6, 7, 8];
const numWorkers = 4;
const chunkSize = Math.ceil(data.length / numWorkers);

for (let i = 0; i < numWorkers; i++) {
  const start = i * chunkSize;
  const end = start + chunkSize;
  const workerData = data.slice(start, end);
  const worker = fork('worker.js');

  worker.send(workerData);

  worker.on('message', (result) => {
    console.log(`Result from worker: ${result}`);
  });
}

worker.js

javascript
process.on('message', (data) => {
  const result = data.map(x => x * 2);
  process.send(result);
  process.exit();
});

이 예제에서는 데이터를 동일한 크기만큼 분할하고 각 분할된 데이터를 개별 자식 프로세스에서 병렬로 처리합니다. parent.js는 데이터를 분할하고 각 자식 프로세스를 생성하여 작업을 할당합니다. 각 자식 프로세스(worker.js)는 할당된 데이터 부분을 처리한 후 결과를 부모 프로세스로 전송합니다. 이를 통해 병렬 처리가 가능해지고, 성능이 향상됩니다.

자식 프로세스 관리

자식 프로세스를 생성하고 나면, 이를 효율적으로 관리해야 합니다. 자식 프로세스의 상태를 모니터링하고, 필요시 프로세스를 종료하는 등의 관리 작업이 필요합니다.

예제: 자식 프로세스 모니터링 및 종료

다음은 자식 프로세스의 상태를 모니터링하고 종료하는 예제입니다.

javascript
const { fork } = require('child_process');

const child = fork('child.js');

child.on('message', (message) => {
  console.log('Message from child:', message);
});

child.on('exit', (code) => {
  console.log(`Child process exited with code ${code}`);
});

process.on('SIGINT', () => {
  console.log('Gracefully shutting down...
');
  child.kill('SIGINT');
  process.exit();
});

이 예제에서는 자식 프로세스를 생성하고, 메시지와 종료 이벤트를 처리합니다. 부모 프로세스가 SIGINT 신호를 받을 때 자식 프로세스를 종료하는 방법도 보여줍니다.

결론

Node.js의 자식 프로세스를 활용하면 단일 스레드의 한계를 극복하고 병렬 처리를 통해 애플리케이션의 성능을 크게 향상시킬 수 있습니다. exec, spawn, fork 함수를 적절히 사용하여 자식 프로세스를 생성하고 관리하는 방법을 익히면, 더욱 효율적이고 반응성 높은 애플리케이션을 개발할 수 있습니다. 이 글에서 소개한 자식 프로세스 활용법을 통해 여러분의 Node.js 프로젝트가 더욱 강력해지길 바랍니다.