process.argv는 Node.js 프로그램을 시작한 명령줄을 담은 배열입니다. 인덱스 0은 node 실행 파일의 경로, 인덱스 1은 스크립트 경로이고, 내 인자는 인덱스 2부터 시작합니다. 그래서 process.argv.slice(2)를 쓰면 사용자가 파일 이름 뒤에 입력한 것을 정확히 얻을 수 있습니다. 값은 항상 문자열입니다.
처음 두 항목은 전체 경로라서 여기서는 마지막 부분(node와 index.js)만 출력하고, 이어서 [ 'Ada', '42', '--verbose' ]를 출력합니다. 인자 칸을 Grace 7로 바꾸고 다시 실행하면 배열도 따라 바뀝니다.
process.argv에 들어가는 것
셸은 node 뒤의 줄을 공백으로 나눠 그 조각을 Node.js에 넘깁니다. 파일 이름 앞에 쓴 플래그는 내 프로그램이 아니라 Node.js 자신의 것입니다.
node --max-old-space-size=4096 index.js --port=8080
# process.argv.slice(2) is [ '--port=8080' ]
# process.execArgv is [ '--max-old-space-size=4096' ]
따옴표로 감싸면 여러 단어가 인자 하나로 묶이고("Ada Lovelace"는 문자열 하나로 도착합니다), *.txt 같은 패턴은 Node.js가 보기도 전에 셸이 파일 이름으로 펼칩니다.
slice(2)로 인자 가져오기
대부분의 스크립트는 기대하는 인자를 구조 분해로 받고 각각에 기본값을 줍니다.
Hello, Ada가 출력되었습니다. 인자 하나가 name을 채웠고 greeting은 기본값을 썼습니다. 인자 칸에서 Ada 뒤에 Hi를 추가하면 Hi, Ada가 출력되고, 칸을 비우면 Hello, world가 나옵니다.
숫자는 문자열로 들어옵니다
셸에는 타입이 없습니다. 모든 인자가 문자열이라서 변환하기 전까지는 계산이 틀어집니다.
a + b는 문자열을 이어 75가 되었고, Number()는 12를 만들었습니다. 숫자가 아닌 단어는 NaN이 되므로, Number.isNaN으로 확인하고 망가진 값으로 계속 진행하는 대신 사용법 메시지를 출력하세요.
--name=value 플래그 직접 파싱하기
플래그가 두어 개라면 짧은 반복문이면 충분합니다. 이 반복문은 --port=8080을 port: '8080'으로, 값 없는 --verbose를 true로 바꾸고, 나머지는 위치 인자로 남깁니다.
{"port":"8080","verbose":true}와 [ 'build', 'src' ]가 출력되었고, port는 여전히 문자열입니다. 이 반복문은 -p 같은 짧은 플래그나 다음 인자에 쓴 값(--port 8080)은 처리하지 못합니다. 그럴 때는 아래의 내장 파서를 쓰세요.
내장 파서 util.parseArgs
Node.js 18.3 이후에는 util.parseArgs가 들어 있어서 긴 플래그와 짧은 플래그, 다음 인자에 쓴 값, 불리언, 기본값을 처리합니다.
const { parseArgs } = require('node:util');
// node index.js -p 8080 --verbose src
const { values, positionals } = parseArgs({
options: {
port: { type: 'string', short: 'p', default: '3000' },
verbose: { type: 'boolean', short: 'v' },
},
allowPositionals: true,
});
console.log(values); // [Object: null prototype] { port: '8080', verbose: true }
console.log(positionals); // [ 'src' ]
parseArgs는 기본적으로 process.argv.slice(2)를 읽습니다. 선언하지 않은 옵션이 있으면 에러를 던지므로 --prot 같은 오타를 잡아냅니다. 모르는 플래그도 받으려면 strict: false를 넘기세요. 옵션 타입은 string과 boolean뿐이라 숫자는 여전히 Number()가 필요합니다. values는 프로토타입이 없는 객체라서 [Object: null prototype]으로 출력되지만, 다른 객체처럼 읽으면 됩니다.
흔한 실수
process.argv[0]을 첫 번째 인자로 읽는 것. 그건 node의 경로입니다. 첫 인자는 process.argv[2]입니다.
모든 값이 문자열이라는 걸 잊는 것. '10' > '9'는 false입니다. 문자열은 한 글자씩 비교하기 때문입니다. 먼저 변환하세요.
인자에 비밀 값을 넣는 것. 명령줄은 셸 기록과, 같은 컴퓨터의 다른 사용자가 볼 수 있는 프로세스 목록에 남습니다. 비밀번호와 API 키는 대신 환경 변수로 넘기세요.
자주 묻는 질문
npm 스크립트에 인자를 넘기려면 어떻게 하나요?
-- 뒤에 쓰세요. npm run start -- --port=4000처럼 쓰면 npm이 -- 뒤의 모든 것을 스크립트에 넘기고, 그것이 process.argv.slice(2)에 나타납니다.
process.argv[2]가 undefined인 이유는 뭔가요?
그 인자 없이 프로그램을 시작했기 때문입니다. 기본값을 주거나(process.argv[2] ?? 'guest'), 사용법을 출력하고 process.exit(1)로 멈추세요.
process.argv 대신 yargs나 commander를 써야 하나요?
플래그가 몇 개인 스크립트라면 내장된 util.parseArgs로 충분합니다. commander나 yargs 같은 패키지는 도움말, 하위 명령, 검증을 더해 주므로 도구에 명령이 여러 개 생기면 쓸 가치가 있습니다.