Node.js - 애플리케이션 확장
안녕하세요, 미래의 Node.js 개발자 여러분! 오늘 우리는 Node.js 애플리케이션 확장의 세계에 흥미로운 여정을 떠납니다. 여러분의 친절한 이웃 컴퓨터 과학 교사로서, 저는 이 모험을 단계별로 안내해드리겠습니다. 프로그래밍에 처음이라고 걱정하지 마세요 - 기본부터 시작하여 차츰 올라갈 테니요. 여러분의 좋아하는 음료를 마시면서 편안하게 앉아 있자고요, 그럼 시작해보겠습니다!
exec() 메서드
먼저 exec()
메서드를 살펴보겠습니다. 이 메서드는 Node.js에서 시스템 명령을 실행하는 다용도 도구인 스위스 아미리 knife와 같습니다. 바쁜 주방에서 요리사(당신, 프로그래머)로서, 가끔 다른 방에서 빠르게 도구를 가져오는 일이 필요할 것입니다. exec()
는 그 일을 합니다 - 별도의 프로세스에서 명령을 실행하고 결과를 가져옵니다.
다음은 간단한 예제입니다:
const { exec } = require('child_process');
exec('ls -l', (error, stdout, stderr) => {
if (error) {
console.error(`Error: ${error.message}`);
return;
}
if (stderr) {
console.error(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
이를 해부해보면:
-
child_process
모듈에서exec
함수를 가져옵니다. -
exec()
를 호출하여 명령('ls -l'
)과 콜백 함수를 인자로 전달합니다. - 콜백 함수는 세 가지 매개변수를 받습니다:
error
,stdout
,stderr
. - 먼저 오류를 검사한 후, stderr에 출력이 있는지 확인하고, 그런 다음 stdout을 로그합니다.
이 메서드는 빠르고 간단한 명령에 매우 유용하지만, 큰 출력을 가진 명령에 대해선 적절하지 않습니다. 메모리에 전체 출력을 버퍼링하기 때문입니다.
spawn() 메서드
이제 spawn()
메서드로 넘어가보겠습니다. exec()
는 빠르게 도구를 가져오는 것이라면, spawn()
은 당신과 함께 일하는 어시스턴트 요리사를 고용하는 것과 같습니다. 그들은 준비된 재료(데이터)를 지속적으로 전달합니다.
다음은 예제입니다:
const { spawn } = require('child_process');
const ls = spawn('ls', ['-l', '/usr']);
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}`);
});
이를 해부해보면:
-
child_process
모듈에서spawn
을 가져옵니다. -
ls -l /usr
명령을 실행하는 새로운 프로세스를 생성합니다. -
stdout
과stderr
에 대한 이벤트 리스너를 설정하여 데이터를 처리합니다. - 프로세스가 완료되었을 때
close
이벤트를 듣습니다.
spawn()
은 지속적인 프로세스나 많은 데이터를 처리할 때 매우 유용합니다. 출력을 스트리밍으로 처리하기 때문입니다.
fork() 메서드
다음은 fork()
메서드입니다. 이 메서드는 당신의 레스토랑(애플리케이션)을 다른 장소에 새로운 지점을 열 것처럼 생각해보세요. 이 메서드는 새로운 Node.js 프로세스를 생성하는 데 특별히 설계되었습니다.
다음은 예제입니다:
// main.js
const { fork } = require('child_process');
const child = fork('child.js');
child.on('message', (message) => {
console.log('Message from child:', message);
});
child.send({ hello: 'world' });
// child.js
process.on('message', (message) => {
console.log('Message from parent:', message);
process.send({ foo: 'bar' });
});
이 예제에서:
-
main.js
에서child.js
를 실행하는 새로운 Node.js 프로세스를 포크합니다. - 자식 프로세스로부터 메시지를 받는 리스너를 설정합니다.
- 자식 프로세스로 메시지를 보냅니다.
-
child.js
에서 부모로부터 메시지를 받고 메시지를 다시 보냅니다.
fork()
은 CPU 집중적인 작업을 메인 애플리케이션 스레드에서 분리하여 처리하고자 할 때 매우 유용합니다.
execFile() 메서드
마지막으로 execFile()
메서드를 소개합니다. 이 메서드는 exec()
와 유사하지만, 셸을 스폰하지 않고 파일을 실행하는 데 최적화되었습니다.
다음은 예제입니다:
const { execFile } = require('child_process');
execFile('node', ['--version'], (error, stdout, stderr) => {
if (error) {
console.error(`Error: ${error.message}`);
return;
}
if (stderr) {
console.error(`stderr: ${stderr}`);
return;
}
console.log(`Node.js version: ${stdout}`);
});
이 예제에서:
-
child_process
모듈에서execFile
을 가져옵니다. -
node
명령을--version
인자와 함께 실행합니다. - 출력을
exec()
와 유사하게 처리합니다.
execFile()
은 특정 파일을 실행하고 셸의 해석이 필요하지 않을 때 더 효율적입니다.
메서드 비교
이 표는 이 메서드들을 비교합니다:
메서드 | 사용 사례 | 버퍼링 | 셸 | 최적화된 사용 사례 |
---|---|---|---|---|
exec() | 간단한 명령 | 예 | 예 | 빠른, 작은 출력 작업 |
spawn() | 지속적인 프로세스 | 아니요 | 아니요 | 많은 데이터 스트리밍 |
fork() | 새로운 Node.js 프로세스 | 아니요 | 아니요 | Node.js에서 CPU 집중적인 작업 |
execFile() | 특정 파일 실행 | 예 | 아니요 | 셸 해석이 필요하지 않은 프로그램 실행 |
이제 주요 메서드들을 다룬 것입니다! 메서드를 선택하는 것은 여러분의 특정 요구에 따라 달라집니다. 작은, 빠른 작업을 처리할 때는 exec()
나 execFile()
을 선택하고, 많은 데이터나 지속적인 프로세스를 처리할 때는 spawn()
을 사용하세요. CPU 집중적인 작업을 메인 애플리케이션 스레드에서 분리하고자 할 때는 fork()
을 사용하세요.
이 메서드들을 연습하고 실험해보세요. 그러면 곧 여러분의 Node.js 애플리케이션에서 프로세스의 하모니를 연주하게 될 것입니다. 행복하게 코딩하세요, 여러분의 서버 항상 확장 가능하길 바랍니다!
Credits: Image by storyset