Node.js 인터뷰 질문 20
질문: Node.js의 process 객체에 대해 설명하고 주요 사용법을 알려주세요.
답변:
Node.js의 process
객체는 현재 실행 중인 Node.js 프로세스에 대한 정보를 제공하고 제어할 수 있는 전역 객체입니다. 이 객체는 별도로 require할 필요 없이 모든 Node.js 애플리케이션에서 바로 사용할 수 있습니다.
process 객체의 주요 속성
1. 환경 변수 (process.env)
process.env
는 운영체제의 환경 변수에 접근할 수 있게 해주는 객체입니다.
// 환경 변수 접근
console.log(process.env.NODE_ENV); // 'development', 'production' 등
console.log(process.env.HOME); // 사용자 홈 디렉토리
console.log(process.env.PATH); // 시스템 경로
// 환경 변수 설정
process.env.MY_VARIABLE = "value";
환경 변수는 애플리케이션 설정, 데이터베이스 연결 정보, API 키 같은 민감한 정보를 코드와 분리하는 데 자주 사용됩니다.
2. 명령줄 인수 (process.argv)
process.argv
는 명령줄 인수를 담고 있는 배열입니다.
// 파일: app.js
console.log(process.argv);
// 실행: node app.js arg1 arg2
// 출력:
// [
// '/usr/local/bin/node', // Node.js 실행 파일 경로
// '/path/to/app.js', // 실행 중인 스크립트 경로
// 'arg1', // 첫 번째 명령줄 인수
// 'arg2' // 두 번째 명령줄 인수
// ]
명령줄 인수를 쉽게 파싱하기 위해 yargs
, commander
같은 패키지를 함께 사용하는 경우가 많습니다.
3. 현재 작업 디렉토리 (process.cwd())
process.cwd()
는 현재 Node.js 프로세스가 실행 중인 디렉토리 경로를 반환합니다.
console.log(process.cwd()); // '/path/to/current/directory'
// 작업 디렉토리 변경
process.chdir("/new/directory");
console.log(process.cwd()); // '/new/directory'
4. 프로세스 ID (process.pid)
process.pid
는 현재 프로세스의 ID를 제공합니다.
console.log(`이 프로세스의 ID는 ${process.pid}입니다.`);
5. 플랫폼 정보 (process.platform)
process.platform
은 프로세스가 실행 중인 운영체제 플랫폼을 식별합니다.
console.log(process.platform);
// 'darwin', 'win32', 'linux' 등
6. Node.js 버전 (process.version)
process.version
은 현재 사용 중인 Node.js의 버전을 반환합니다.
console.log(process.version); // 'v16.14.0' 등
7. 메모리 사용량 (process.memoryUsage())
process.memoryUsage()
는 현재 프로세스의 메모리 사용량에 대한 정보를 제공합니다.
console.log(process.memoryUsage());
// {
// rss: 30728192, // Resident Set Size: 프로세스가 사용 중인 물리적 메모리 양
// heapTotal: 5922816, // V8 힙에 할당된 총 메모리
// heapUsed: 3621248, // V8 힙에서 실제 사용 중인 메모리
// external: 877074, // V8 외부 C++ 객체에 바인딩된 메모리
// arrayBuffers: 9898 // ArrayBuffer 및 SharedArrayBuffer에 할당된 메모리
// }
process 객체의 주요 메서드
1. 프로세스 종료 (process.exit())
process.exit()
는 현재 프로세스를 즉시 종료합니다. 종료 코드를 인수로 전달할 수 있습니다.
// 성공적인 종료
process.exit(0);
// 오류로 인한 종료
process.exit(1);
2. 표준 입출력 스트림
process.stdout
, process.stderr
, process.stdin
은 각각 표준 출력, 표준 오류, 표준 입력 스트림에 대한 접근을 제공합니다.
// 표준 출력에 쓰기
process.stdout.write("Hello, World!\n");
// 표준 오류에 쓰기
process.stderr.write("오류가 발생했습니다!\n");
// 표준 입력에서 읽기
process.stdin.on("data", (data) => {
console.log(`입력 받음: ${data}`);
});
3. 다음 틱 함수 (process.nextTick())
process.nextTick()
은 이벤트 루프의 현재 반복이 완료된 후, I/O 이벤트 콜백보다 먼저 호출될 함수를 예약합니다.
console.log("시작");
process.nextTick(() => {
console.log("nextTick 콜백");
});
console.log("종료");
// 출력:
// 시작
// 종료
// nextTick 콜백
process.nextTick()
은 setTimeout(fn, 0)
보다 더 빠르게 실행되며, 이벤트 루프의 다음 단계로 넘어가기 전에 실행됩니다.
process 이벤트
process
객체는 EventEmitter이므로 다양한 이벤트를 발생시키고 리스닝할 수 있습니다.
1. exit 이벤트
프로세스가 종료될 때 발생합니다.
process.on("exit", (code) => {
console.log(`종료 코드: ${code}`);
// 참고: 이 콜백에서는 비동기 작업을 수행할 수 없습니다.
});
2. uncaughtException 이벤트
처리되지 않은 예외가 발생했을 때 트리거됩니다.
process.on("uncaughtException", (err) => {
console.error("처리되지 않은 예외:", err);
// 로그 기록이나 정리 작업 수행
process.exit(1);
});
주의: uncaughtException
은 안전망으로만 사용해야 하며, 정상적인 오류 처리 방식으로 사용해서는 안 됩니다. 프로세스 상태가 불안정할 수 있으므로, 로그를 기록하고 프로세스를 재시작하는 것이 좋습니다.
3. SIGINT, SIGTERM 등의 시그널 이벤트
운영체제 시그널이 프로세스로 전송될 때 발생합니다.
// Ctrl+C가 눌렸을 때
process.on("SIGINT", () => {
console.log("SIGINT 시그널을 받았습니다(Ctrl+C)");
process.exit(0);
});
// kill 명령으로 SIGTERM이 전송되었을 때
process.on("SIGTERM", () => {
console.log("SIGTERM 시그널을 받았습니다");
// 정리 작업 수행
server.close(() => {
process.exit(0);
});
});
4. warning 이벤트
Node.js에서 경고가 발생할 때 트리거됩니다.
process.on("warning", (warning) => {
console.warn(`경고 이름: ${warning.name}`);
console.warn(`경고 메시지: ${warning.message}`);
console.warn(`스택 트레이스: ${warning.stack}`);
});
실제 사용 예시
1. 환경 변수에 기반한 애플리케이션 설정
// config.js
const config = {
development: {
port: 3000,
databaseUrl: "mongodb://localhost:27017/dev_db",
},
production: {
port: process.env.PORT || 8080,
databaseUrl: process.env.DATABASE_URL,
},
test: {
port: 3001,
databaseUrl: "mongodb://localhost:27017/test_db",
},
};
const env = process.env.NODE_ENV || "development";
module.exports = config[env];
// app.js
const config = require("./config");
const express = require("express");
const app = express();
app.listen(config.port, () => {
console.log(`서버가 ${config.port} 포트에서 실행 중입니다.`);
});
2. 명령줄 인수를 사용한 CLI 도구
// simple-cli.js
const args = process.argv.slice(2);
if (args.length === 0) {
console.log("사용법: node simple-cli.js [명령] [인수]");
process.exit(1);
}
const command = args[0];
switch (command) {
case "greet":
const name = args[1] || "Guest";
console.log(`안녕하세요, ${name}님!`);
break;
case "time":
console.log(`현재 시간은 ${new Date().toLocaleTimeString()}입니다.`);
break;
default:
console.log(`알 수 없는 명령: ${command}`);
process.exit(1);
}
3. 정상적인 종료 처리
// graceful-shutdown.js
const express = require("express");
const app = express();
let server;
// Express 라우트 설정...
server = app.listen(3000, () => {
console.log("서버가 3000 포트에서 실행 중입니다.");
});
// 정상 종료 함수
function gracefulShutdown() {
console.log("정상 종료 시작...");
server.close(() => {
console.log("서버가 모든 연결을 종료했습니다.");
// 데이터베이스 연결 종료 등의 정리 작업
console.log("모든 정리 작업 완료. 종료합니다.");
process.exit(0);
});
// 일정 시간 후에도 종료되지 않으면 강제 종료
setTimeout(() => {
console.error("정상 종료 시간 초과. 강제 종료합니다.");
process.exit(1);
}, 30000);
}
// SIGINT와 SIGTERM 신호 처리
process.on("SIGINT", gracefulShutdown);
process.on("SIGTERM", gracefulShutdown);
모범 사례
환경 변수 활용: 중요한 설정이나 민감한 정보는 코드에 하드코딩하지 말고
process.env
를 통해 환경 변수로 관리합니다.적절한 종료 코드 사용: 프로세스 종료 시 상황에 맞는 종료 코드를 제공합니다. 0은 성공, 0이 아닌 값은 오류를 나타냅니다.
정상 종료 구현: 특히 서버 애플리케이션에서는 SIGINT, SIGTERM 같은 시그널을 처리하여 연결을 정상적으로 종료하고 자원을 정리합니다.
uncaughtException 신중히 사용: 이 이벤트는 마지막 수단으로만 사용하고, 가능한 한 개별 오류를 적절히 처리하는 것이 좋습니다.
플랫폼 독립적인 코드 작성:
process.platform
을 사용하여 필요한 경우에만 플랫폼별 코드를 작성합니다.
process
객체는 Node.js 애플리케이션의 런타임 환경과 직접 상호작용할 수 있는 강력한 인터페이스를 제공합니다. 적절하게 활용하면 더 안정적이고 효율적인 애플리케이션을 개발할 수 있습니다.