1 Javascript로 CLI기반의 앱을 만들어보기
2 명령어 실행의 성공과 실패에 대한 이야기
3 매개변수에 대해서
4 앱을 좀더 그럴듯 하게 만들기

앱을 좀더 그럴듯 하게 만들기

CLI 기반의 앱들은 암묵적 룰처럼 정해놓고 보통 공통적으로 가지는 옵션이 있습니다.
-v, --version, --help 와 같은것들인데 버전과 도움말 화면을 볼 수 있습니다.
node --version
node --help
해보면 그 모습을 볼 수 있죠

우리의 hello 앱은 그런게 아직 없죠
hello --version
라고 입력하면
Two numbers required
라고 나올뿐이죠

그래서 이런 도움말페이지나 버전정보를 보여줄 수 있도록 만들어보겠습니다.
이런 기본적인 틀을 제공해주는 모듈이 있습니다
commander 이라는 모듈입니다
npm i commander 해서 설치합니다

IyEvdXNyL2Jpbi9lbnYgbm9kZQppbXBvcnQgeyBDb21tYW5kIH0gZnJvbSAiY29tbWFuZGVyIjsKY29uc3QgcHJvZ3JhbSA9IG5ldyBDb21tYW5kKCk7CnByb2dyYW0KICAgIC5uYW1lKCdoZWxsbycpCiAgICAuZGVzY3JpcHRpb24oJ015IGZpcnN0IENMSSBBcHAnKQogICAgLnZlcnNpb24oJzAuMC4xJykKICAgIC5vcHRpb24oJy1hJywgJ015IGN1c3RvbSBvcHRpb24gMScpCiAgICAub3B0aW9uKCctaycsICdNeSBjdXN0b20gb3B0aW9uIDInKQogICAgLmFjdGlvbigoc3RyLCBvcHRpb25zKSA9PiB7CiAgICAgICAgY29uc29sZS5sb2coJ09QVElPTlMnLCBzdHIpCiAgICAgICAgY29uc29sZS5sb2coJ0FSR1MnLCBvcHRpb25zLmFyZ3MpCiAgICB9KTsKcHJvZ3JhbS5wYXJzZSgpOw==
#!/usr/bin/env node import { Command } from "commander"; const program = new Command(); program .name('hello') .description('My first CLI App') .version('0.0.1') .option('-a', 'My custom option 1') .option('-k', 'My custom option 2') .action((str, options) => { console.log('OPTIONS', str) console.log('ARGS', options.args) }); program.parse();

이렇게 해서 hello --help 라고 실행해봅니다.
그럴듯한 도움말 화면이 나옵니다
hello -V 혹은 hello --version 로 실행해보면 앱의 버전도 나옵니다.
기존에 만든앱은 process.argv 배열값을 이용해서 매개변수를 받아온 반면 commander 를 사용하면 알아서 구분해서 준비해줍니다.

hello -a a b c 라고 실행하면
OPTIONS { a: true }
ARGS [ 'a', 'b', 'c' ]
라고 출력됩니다

hello -a -k a b c 혹은 hello -ak a b c 라고 실행하면
OPTIONS { a: true, k: true }
ARGS [ 'a', 'b', 'c' ]
라고 출력됩니다

hello -ad a b c 라고 실행하면
d는 정의되지 않은 옵션인 이유로 error: unknown option '-d' 가 출력됩니다
이때 echo $? 를 확인해보면 1이 나오는것이 확인됩니다

기존에 만든 hello 앱을 commander 를 이용해서 조금 더 개선해보자면 다음과 같아집니다

IyEvdXNyL2Jpbi9lbnYgbm9kZQppbXBvcnQgeyBDb21tYW5kIH0gZnJvbSAiY29tbWFuZGVyIjsKaW1wb3J0IGNoYWxrIGZyb20gImNoYWxrIjsKaW1wb3J0IHJlYWRsaW5lIGZyb20gInJlYWRsaW5lIjsKY29uc3QgcHJvZ3JhbSA9IG5ldyBDb21tYW5kKCk7CnByb2dyYW0KICAgIC5uYW1lKCdoZWxsbycpCiAgICAuZGVzY3JpcHRpb24oJ015IGZpcnN0IENMSSBBcHAnKQogICAgLnZlcnNpb24oJzAuMC4xJykKICAgIC5vcHRpb24oJy1uLCAtLW51bWJlciA8bnVtYmVycy4uLj4nLCAnc3BlY2lmeSBudW1iZXJzJykKICAgIC5hY3Rpb24oYXN5bmMgKHN0ciwgb3B0aW9ucykgPT4gewogICAgICAgIGlmICghc3RyLm51bWJlcikgcmV0dXJuOwogICAgICAgIGlmIChzdHIubnVtYmVyLmxlbmd0aCA8IDIpIHsKICAgICAgICAgICAgY29uc29sZS5sb2coY2hhbGsuYm9sZC5yZWQoJ1R3byBudW1iZXJzIHJlcXVpcmVkJykpOwogICAgICAgICAgICBwcm9jZXNzLmV4aXQoMSk7CiAgICAgICAgfQogICAgICAgIGxldCBwZXJjZW50ID0gMDsKICAgICAgICB3aGlsZSAodHJ1ZSkgewogICAgICAgICAgICBwZXJjZW50Kys7CiAgICAgICAgICAgIHJlYWRsaW5lLmN1cnNvclRvKHByb2Nlc3Muc3Rkb3V0LCAwKTsKICAgICAgICAgICAgcHJvY2Vzcy5zdGRvdXQud3JpdGUoYFByb2dyZXNzICR7Y2hhbGsuYm9sZC55ZWxsb3coYCR7cGVyY2VudH1gKX0gJWApOwogICAgICAgICAgICBpZiAocGVyY2VudCA9PT0gMTAwKSBicmVhazsKICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2UociA9PiBzZXRUaW1lb3V0KHIsIDIwKSk7CiAgICAgICAgfQogICAgICAgIGNvbnNvbGUubG9nKCcnKTsKICAgICAgICBjb25zb2xlLmxvZyhgUmVzdWx0IGlzICR7Y2hhbGsuYm9sZC5ncmVlbihzdHIubnVtYmVyWzBdICogc3RyLm51bWJlclsxXSl9YCk7CiAgICAgICAgcHJvY2Vzcy5leGl0KDApOwogICAgfSk7CnByb2dyYW0ucGFyc2UoKTs=
#!/usr/bin/env node import { Command } from "commander"; import chalk from "chalk"; import readline from "readline"; const program = new Command(); program .name('hello') .description('My first CLI App') .version('0.0.1') .option('-n, --number <numbers...>', 'specify numbers') .action(async (str, options) => { if (!str.number) return; if (str.number.length < 2) { console.log(chalk.bold.red('Two numbers required')); process.exit(1); } let percent = 0; while (true) { percent++; readline.cursorTo(process.stdout, 0); process.stdout.write(`Progress ${chalk.bold.yellow(`${percent}`)} %`); if (percent === 100) break; await new Promise(r => setTimeout(r, 20)); } console.log(''); console.log(`Result is ${chalk.bold.green(str.number[0] * str.number[1])}`); process.exit(0); }); program.parse();