커맨드 라인 인터페이스 가이드라인
(clig.dev)전통적인 유닉스 원칙을 따르면서 현대적으로 업데이트한 오픈소스 가이드
- CLI 디자인 철학
ㅤ→ 사람 우선
ㅤ→ 함께 작동하는 간단한 부속
ㅤ→ 프로그램간 일관성 유지
ㅤ→ 필요한 만큼만 말하기(적지도 많지도 않은 출력)
ㅤ→ 쉽게 발견할수 있게(포괄적인 도움말, 예제, 다음 수행할 명령 제안, 에러가 있을때 할 작업 제안)
ㅤ→ 일반적인 대화처럼
ㅤ→ 견고하게
ㅤ→ 사용자에게 공감하기
ㅤ→ 혼돈 : 규칙을 어겨야 한다면 의도와 목적을 명확히 할 것
- CLI 가이드 라인
ㅤ→ 기초
ㅤㅤ✓ 명령행 파싱 라이브러리 사용할 것 : Go(Cobra,cli), Node(oclif), Python (Click,Typer), Ruby(TTY)
ㅤㅤ✓ 성공시 0, 에러시 0이외의 코드 리턴
ㅤㅤ✓ 출력은 stdout
ㅤㅤ✓ 로그, 에러 등 메시지는 stderr
ㅤ→ 도움말
ㅤㅤ✓ 옵션 지정없이 실행하면 도움말 출력 (-h, --help)
ㅤㅤ✓ 기본적으로 간결한 도움말을 출력
ㅤㅤㅤㅤ· 이 프로그램이 뭐하는지
ㅤㅤㅤㅤ· 한두개의 호출 예제
ㅤㅤㅤㅤ· 플래그 설명(많지 않다면)
ㅤㅤㅤㅤ· 추가 설명용 --help
ㅤㅤ✓ -h, --help 옵션시 전체 도움말 출력
ㅤㅤ✓ 피드백/이슈를 받기위한 경로 제공
ㅤㅤ✓ 도움말에서는 웹버전 문서로의 링크를 제공
ㅤㅤ✓ 예제로 설명할 것
ㅤㅤ✓ 예제가 많다면, 어딘가 다른곳에 올려 둘것 (치트시트 또는 웹페이지)
ㅤㅤ✓ man 페이지엔 신경쓰지 말 것 (많이 안쓰고, 윈도우에서 동작도 안함)
ㅤㅤ✓ 도움말이 길다면 pager로 파이프 할 것
ㅤㅤ✓ 가장 많이 쓰는 플래그와 명령을 도움말 처음에 표시
ㅤㅤ✓ 도움말에서 포맷팅 사용할 것 (볼드체)
ㅤㅤ✓ 사용자가 뭔가를 잘못했고, 그걸 추측할수 있다면 추천할 것
ㅤㅤ✓ 당신의 명령이 뭔가를 pipe로 받아들이길 바라는데, stdin 이 인터랙티브 터미널이면 도움말을 표시하고 바로 종료할 것
ㅤ→ 출력
ㅤㅤ✓ Human-readable(사람이 읽을 수 있는) 출력이 가장 중요
ㅤㅤ✓ 사용성에 문제가 되지 않는다면 machine-readable 출력을 제공
ㅤㅤ✓ human-readable 때문에 machine-readable 이 불가능 하다면 --plain 옵션으로 grep / awk 등과 연계 가능하도록
ㅤㅤ✓ --json 입력을 받으면 JSON 포맷으로 출력
ㅤㅤ✓ 성공시 출력은 없는게 좋지만, 있어야 할 경우 간결하게 할 것. -q 옵션으로 필요없는 출력 제외 지원
ㅤㅤ✓ 상태를 바꾼다면, 사용자에게 말할 것 (git push의 출력 참조)
ㅤㅤ✓ 현재 시스템의 상태를 보기 쉽게 할 것
ㅤㅤ✓ 사용자가 실행할수 있는 명령을 추천할 것. (git status 치면 git add / restore 를 보여주듯이)
ㅤㅤ✓ 프로그램의 내부를 넘어가는 행동들은 명시적 이어야 함. 사용자가 지시하지 않은 파일을 읽고 쓴다거나(캐쉬), 원격서버와 연결하는 등(파일 다운로드)
ㅤㅤ✓ ASCII 아트를 이용해서 정보 밀도 증가 시키기
ㅤㅤ✓ 의도를 가지고 컬러 사용하기. 오남용은 말 것
ㅤㅤ✓ 터미널이 아니거나, 사용자가 요청시 컬러는 끌것
ㅤㅤ✓ stdout 이 인터랙티브 터미널이 아니면 애니메이션을 표시하지 말 것
ㅤㅤ✓ 뭔가를 명확히 할때만 심볼/이모지를 사용할 것
ㅤㅤ✓ 기본적으로, 제작자만 알아볼수 있는 정보들은 출력하지 말 것
ㅤㅤ✓ stderr 를 로그파일처럼 사용하지 말 것 (적어도 기본값으로는 설정하지 말 것. 상세모드에서나 ERR,WARN 같은 로그 레벨을 출력하세요)
ㅤㅤ✓ 많은 텍스트를 출력한다면 less 와 같은 페이징 도구를 사용할 것
ㅤ→ Error
ㅤㅤ✓ 에러를 Catch 하고 사람을 위해 문구를 재작성할 것
ㅤㅤ✓ Signal-to-noise ratio(신호 대 잡음비)가 중요. 똑같은 에러가 여러번 난다면 설명하는 헤더와 함께 묶어서 출력
ㅤㅤ✓ 사용자가 제일 먼저 보는게 어디인지를 고려할 것
ㅤㅤ✓ 예상치 못한/설명할수 없는 오류가 나면, debug/trace 정보를 제공하고, 이 버그를 개발자에게 보내는 방법을 설명할 것
ㅤㅤ✓ 버그 리포트를 별도의 노력없이 보낼수 있도록 할 것. (모든 정보를 다 담은 URL을 만들어주고 브라우저에 넣는 것만으로 신고가 끝나게)
ㅤ→ Argument & Flags : 인자와 플래그
ㅤㅤ✓ 인자 : 위치 파라미터. 순서가 중요. cp bar foo 와 cp foo bar 는 다름
ㅤㅤ✓ 플래그 : 이름붙은 파라미터. "-r" 처럼 한글자 또는 "--recursive" 처럼 여러 글자. 순서는 일반적으로 중요하지 않음.
ㅤㅤㅤㅤㅤㅤㅤㅤ사용자 값을 포함하기도 함. --file foo.txt 또는 --file=foo.txt
ㅤㅤ✓ 인자보다 플래그를 선호할 것. 더 많은 타이핑이 필요하지만, 보다 명확함. 인자가 많으면 나중에 기능 확장을 할 때 어려움
ㅤㅤ✓ 플래그의 짧은 버전과 풀버전을 모두 가질 것. 스크립트에서 풀버전을 쓰면 다른 설명이 필요 없음
ㅤㅤ✓ 주로 사용 하는 플래그에만 한글자 플래그를 사용
ㅤㅤ✓ 간단한 동작을 위해서 여러개의 인자를 받는 것도 가능
ㅤㅤ✓ 서로 다른 두개 이상의 인자가 필요하다면 뭔가를 잘못하고 있는 것일수도
ㅤㅤ✓ 플래그는 (이미 있다면) 표준적인 이름을 쓸 것
ㅤㅤㅤㅤ-a --all , -d --debug , -f --force , -h --help , -o --output , -p --port , -q --quiet , -u --user
ㅤㅤ✓ 대부분 사용자에게 적합한 것을 기본값으로 할 것
ㅤㅤ✓ 사용자가 입력이 필요한 인자/플래그를 넣었는데 받은 값이 없다면, 사용자에게 입력을 요구할 것
ㅤㅤ✓ 항상 인자/플래그로 값을 넘기는 방법을 제공하고, 무조건 입력(prompt)을 요구하지 말 것
ㅤㅤ✓ 뭔가 위험한 일을 하기전엔 항상 확인을 요구할 것
ㅤㅤ✓ 입력 또는 출력이 파일이라면 - 로 stdin 에서 받거나 stdout으로 출력하는 것을 지원할 것
ㅤㅤㅤㅤ$ curl https://example.com/something.tar.gz | tar xvf -
ㅤㅤ✓ 플래그가 추가 값을 받을수 있다면, "none" 같은 특수 단어를 허용할 것. ssh -F none
ㅤㅤ✓ 가능하면 인자와 플래그, 서브커맨드 들을 순서와 상관없이 만들 것
ㅤㅤ✓ 민감한(암호같은) 인자 값들은 파일로 입력될 수 있게 할 것
ㅤ→ Interactivity
ㅤㅤ✓ stdin 이 인터랙티브 터미널일때만 prompt 나 인터랙티브 기능을 사용할 것
ㅤㅤ✓ --no-input 이 전달되면, prompt 나 어떤 인터랙티브 기능도 사용하지 말 것
ㅤㅤ✓ 암호를 입력받을땐, 사용자가 입력하는 값을 보이지 말 것
ㅤㅤ✓ 사용자가 쉽게 나갈수 있게 할것 (vim처럼 하지 말것). Ctrl-C 가 동작하게 할 것. ssh,tmux 등 프로그램 실행관련이라 ctrl-c가 불가능하면 SSH 처럼 ~ 로 시작하는 escape sequence 를 제공한다는 것을 명확히 표시할 것
ㅤ→ Subcommands
ㅤㅤ✓ 복잡한 도구는 subcommand 를 제공해서 복잡도를 줄일수 있음.
ㅤㅤ✓ 또한 긴밀히 연관된 여러도구가 있다면 이걸 하나의 명령으로 묶어서 편하게 사용하게 할 수 있음
ㅤㅤ✓ 서브코맨드 간에 일관적일 것. 같은 플래그는 같은 의미로, 비슷한 출력 포맷을 가지게
ㅤㅤ✓ 여러 단계의 서브 코맨드간에 일관적인 이름을 사용할 것
ㅤㅤ✓ 헷갈리거나 비슷한 이름의 명령을 넣지 말 것. update 와 upgrade 같은
ㅤ→ Robustness
ㅤㅤ✓ 사용자 입력을 모두 검증 할 것. 빨리 체크하고, 이해가능한 에러를 표시할 것
ㅤㅤ✓ 빠른 것보다 반응성이 더 중요함
ㅤㅤ✓ 오래 걸린다면 진행상황을 보일 것
ㅤㅤ✓ 가능하다면 병렬적으로 일을 처리할 것. 하지만 깊이 생각하고 할 것.
ㅤㅤ✓ 타임아웃을 넣을 것
ㅤㅤ✓ 멱등(idempotent)으로 만들 것. (다시 실행해도 결과가 달라지지 않는 것). 에러가 났을때 쉘에서 화살표 위로 올려서 다시 이전부터 이어서 수행가능하게
ㅤㅤ✓ Crash-only로 만들 것. Idempotence 의 다음 단계. 작업후 정리를 수행할 필요가 없거나, 다음 실행시까지 정리를 연기할수 있다면, 프로그램은 실패 또는 중단시 즉시 종료가 가능.
ㅤㅤ✓ 사람들은 당신의 프로그램을 오용할 것임
ㅤ→ Future-proofing
ㅤㅤ✓ 가능하면 변경은 추가(additive)하는 방식으로. 기존 기능을 바꿔서 호환을 깨지말고, 새로운 플래그를 추가할 것
ㅤㅤ✓ 추가방식 변경이 아니라면 먼저 경고할 것
ㅤㅤ✓ 사람들을 위한 출력변경은 대부분 OK
ㅤㅤ✓ 사람들이 주로 쓰는 서브코맨드가 있다고, 명시하지 않아도 그걸 수행하는 catch-all Subcommand 는 만들지 말 것
ㅤㅤ✓ 임이의 Subcommand 명령 약자는 허용하지 말 것
ㅤㅤ✓ 언젠가는 잘 동작하지 않게 되는 "시한 폭탄"은 만들지 말 것
ㅤ→ Signals and control Characters
ㅤㅤ✓ 사용자가 Ctrl-C(INT signal)을 입력하면 최대한 빨리 중단할 것
ㅤㅤ✓ 사용자가 오래 걸리는 Clean-up 상태에서 Ctrl-C를 누른다면 무시할 것. 다시 누르면 강제종료 가능하게
ㅤㅤㅤㅤㅤ^CGracefully stopping... (press Ctrl+C again to force)
ㅤ→ Configuration
ㅤㅤ✓ XDG(X Desktop Group) 스펙을 따를 것
ㅤㅤ✓ 당신의 프로그램 것이 아닌 설정을 수정한다면 사용자에게 확인받고, 명확히 뭘 하는 것인지 알려줄 것
ㅤㅤ✓ 설정 파라미터는 우선 순위대로 적용할 것
ㅤㅤㅤㅤ플래그 > 쉘 환경변수 > 프로젝트 레벨 설정 (.env) > 사용자 설정 > 시스템 설정
ㅤ→ Environment Variables
ㅤㅤ✓ 환경변수는 명령이 실행되는 컨텍스트에 따라 달라지는 동작을 위한 것
ㅤㅤ✓ 이식성을 극대화 하기 위해, 환경변수는 대문자/숫자/밑줄만 포함되어야 하고 숫자로 시작하면 안됨
ㅤㅤ✓ 가능하면 환경변수에는 한줄짜리(single-line) 값을 사용
ㅤㅤ✓ 널리 사용되는 이름을 사용하지 말 것
ㅤㅤ✓ 가능하면 범용 환경변수를 체크하고 사용
ㅤㅤㅤㅤNO_COLOR, DEBUG, EDITOR, HTTP_PROXY, SHELL, TERM, TERMINFO, HOME, TMPDIR, PAGER, LINES ..
ㅤㅤ✓ 필요하다면 .env 에서 환경변수를 로딩
ㅤㅤ✓ 설정 파일로 .env 확장을 사용하지 말 것
ㅤ→ Naming
ㅤㅤ✓ 이름은 간단하고 외우기 쉬운 단어로
ㅤㅤ✓ 소문자만 사용하고, 꼭 필요할때만 -(대쉬) 사용
ㅤㅤ✓ 가능하면 짧게
ㅤㅤ✓ 키보드에서 치기 쉽게
ㅤ→ Distribution
ㅤㅤ✓ 가능하면 싱글 바이너리로 배포
ㅤㅤ✓ 쉽게 언인스톨 가능하게
ㅤ→ Analytics
ㅤㅤ✓ 도구의 사용 및 크래쉬 데이터를 사용자 동의없이 당신에게 보내지 말 것
싱글바이너리로 잘 만들어주는 Rust 와 Go 때문에 좋은 커맨드라인 도구들이 점점 많아지는 것 같습니다.
- 요즘 유용한 커맨드 라인 도구 모음 https://news.hada.io/topic?id=793
- 생산성을 높여주는 rust command line 유틸리티 https://news.hada.io/topic?id=2958
만드는 것도 더욱 편해지고 강력해지는 중
- Rust로 Command Line 앱 만들기 https://news.hada.io/topic?id=972
- Caporal.js - Node CLI 개발용 풀 프레임워크 https://news.hada.io/topic?id=2378
- create-node-cli - Node.js로 CLI 쉽게 만들기 https://news.hada.io/topic?id=3268
- Gooey 로 모든 언어 및 CLI 도구의 GUI 만들기 https://news.hada.io/topic?id=582
- ink - React로 CLI만들기 https://news.hada.io/topic?id=2041