# 명령줄 인터페이스 가이드라인 (2021)

> Clean Markdown view of GeekNews topic #13242. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=13242](https://news.hada.io/topic?id=13242)
- GeekNews Markdown: [https://news.hada.io/topic/13242.md](https://news.hada.io/topic/13242.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2024-02-07T10:35:32+09:00
- Updated: 2024-02-07T10:35:32+09:00
- Original source: [clig.dev](https://clig.dev/)
- Points: 2
- Comments: 1

## Topic Body

- 전통적인 UNIX 철학을 현대적으로 재해석한 **CLI 프로그램 설계 원칙과 구체적 가이드라인**을 오픈소스 문서로 정리한 레퍼런스로, 커맨드라인 도구를 만드는 개발자를 주요 독자로 삼음  
- CLI는 단순 스크립팅 플랫폼이 아니라 **인간 중심의 텍스트 UI**로 진화했으며, 이 변화에 맞게 설계 원칙도 갱신되어야 함  
- **컴포저빌리티(composability)와 인간 친화성**은 상충하지 않으며, 표준 입출력·파이프·종료 코드 등 UNIX 관행을 지키면 둘을 동시에 달성 가능  
- 헬프 텍스트, 에러 메시지, 출력 형식, 인터랙티비티, 설정 체계 등 실무에서 자주 놓치는 세부 항목까지 구체적 권장 사항으로 제공  
- CLI 도구의 **미래 호환성과 사용자 신뢰**는 인터페이스 안정성과 분석 데이터 투명성에서 결정되며, 이 가이드는 그 기준선을 제시  
  
---  
  
### 철학 (Philosophy)  
  
#### 인간 중심 설계  
- 전통 UNIX 명령어는 주로 다른 프로그램이 사용하는 것을 전제로 설계되었으나, 현재 CLI는 인간이 직접 사용하는 경우가 대부분이므로 **인간 우선 설계**가 필요  
- 과거 CLI는 "machine-first"였지만 현재는 "human-first" 텍스트 기반 UI로 진화  
  
#### 조합 가능한 작은 부품  
- UNIX 철학의 핵심은 **작고 단순한 프로그램을 조합해 더 큰 시스템을 구성**하는 것으로, 지금도 유효  
- 표준 stdin/stdout/stderr, 시그널, 종료 코드가 프로그램 간 연결을 보장하며, **JSON**은 더 구조화된 데이터 교환을 지원  
- 소프트웨어는 반드시 더 큰 시스템의 부품이 되므로, 잘 동작하는 부품이 될지는 설계 단계에서 결정됨  
  
#### 일관성  
- 터미널 사용자는 기존 컨벤션에 손이 익어 있으므로, CLI는 **기존 패턴을 따를 것**을 권장  
- 단, 일관성이 사용성을 해칠 경우 신중하게 관례를 깰 수 있음  
  
#### 적절한 정보량  
- 명령이 수 분간 아무 출력 없이 대기하면 "너무 적은" 정보, 대량의 디버그 로그를 쏟아내면 "너무 많은" 정보  
- **정보량의 균형**은 소프트웨어가 사용자를 지원하는 데 절대적으로 중요  
  
#### 발견 가능성 (Ease of Discovery)  
- GUI는 화면에 기능을 모두 펼쳐 보이지만, CLI는 기억에 의존하는 것으로 오해받음  
- **포괄적인 헬프 텍스트, 풍부한 예시, 다음 명령 제안** 등 GUI 기법을 차용해 CLI도 학습하기 쉽게 만들 수 있음  
  
#### 대화로서의 CLI  
- CLI 사용은 반복적 시도-실패를 통한 **대화 구조**를 가지며, 오류 수정 제안·중간 상태 표시·위험 작업 전 확인 등이 이 특성을 활용하는 설계 기법  
- 최악의 상호작용은 사용자를 무기력하게 만드는 적대적 대화, 최선은 성취감을 주는 유쾌한 교환  
  
#### 견고성 (Robustness)  
- 소프트웨어는 **실제로도, 느낌으로도** 견고해야 함  
- 예기치 않은 입력의 우아한 처리, 멱등성(idempotence) 유지, 진행 상황 안내, 스택 트레이스 노출 자제가 핵심  
- 복잡한 특수 케이스를 줄이고 단순하게 유지하면 견고성이 높아짐  
  
#### 공감 (Empathy)  
- CLI 도구는 개발자의 창의적 도구이므로 즐겁게 사용할 수 있어야 함  
- 사용자가 **자신의 편임을 느낄 수 있도록** 문제를 충분히 고민하고 설계할 것  
  
#### 혼돈 (Chaos)  
- 터미널 세계는 불일치로 가득하지만, 그 혼돈이 자유로운 창조의 원천이기도 함  
- "표준이 생산성이나 사용자 만족도에 명백히 해롭다면 그 표준을 버려라" — Jef Raskin  
  
---  
  
### 가이드라인 — 기본 (The Basics)  
  
- **인수 파싱 라이브러리**를 사용할 것: 언어별 권장 라이브러리는 Go(Cobra, cli), Python(Click, Typer, Argparse), Rust(clap), Node(oclif) 등 다수  
- 성공 시 **종료 코드 0**, 실패 시 **0이 아닌 코드** 반환 — 스크립트가 성공·실패를 판별하는 기준  
- 기본 출력은 `stdout`, 로그·에러 등 메시지는 `stderr`로 전송  
  
---  
  
### 가이드라인 — 헬프 (Help)  
  
- `-h` 또는 `--help` 플래그에 **상세 헬프 텍스트** 표시, 서브커맨드에도 동일 적용  
- 인수 없이 실행 시 **간결한 헬프** 표시 (설명, 1~2개 예시, 플래그 설명, `--help` 안내 포함)  
  - `jq`가 이를 잘 구현한 예시로 언급됨  
- `--help` / `-h` / `help subcommand` 등 **다양한 형태의 헬프 요청** 모두 지원  
- 헬프 텍스트 상단에 **웹 문서 링크 및 피드백 경로** 제공  
- **예시를 먼저** 보여줄 것 — 복잡한 활용 사례로 점진적으로 이어지는 스토리 구성 권장  
- **자주 쓰이는 플래그와 커맨드**를 헬프 텍스트 상단에 배치 (git의 구성 방식 참조)  
- **굵은 제목 등 서식**을 활용해 스캔하기 쉽게 구성하되, 터미널 독립적 방식 사용  
- 사용자가 잘못 입력했을 때 **의도를 추측해 수정 제안** 가능 — 단, 자동 실행은 신중하게 결정할 것  
  - 잘못된 입력이 단순 오타가 아닌 논리적 실수일 수 있으며, 자동 수정 시 해당 구문을 영구 지원해야 하는 부담 발생  
  
---  
  
### 가이드라인 — 문서 (Documentation)  
  
- **웹 기반 문서** 제공 — 검색 가능성과 링크 공유를 위해 필수  
- **터미널 기반 문서** 제공 — 설치된 버전과 동기화되며 오프라인에서도 접근 가능  
- **man 페이지** 제공 고려 — `ronn` 같은 도구로 생성 가능하며, `npm help ls`처럼 서브커맨드로 접근하도록 지원 권장  
  
---  
  
### 가이드라인 — 출력 (Output)  
  
- **인간 가독성 최우선** — TTY 여부로 사람이 읽는지 판별  
- 텍스트 스트림은 UNIX의 보편 인터페이스로, **머신 가독 출력**도 지원할 것  
- 인간 친화적 출력이 파이프 호환성을 해칠 경우 `--plain` 플래그로 **일반 텍스트 출력** 제공  
- `--json` 플래그 전달 시 **JSON 형식 출력** 지원  
- 성공 시 출력은 간결하게, 필요 없으면 출력 없이 — 스크립트용으로 `-q` 옵션으로 출력 억제 지원  
- **상태 변경 시 사용자에게 알림** — `git push`가 원격 브랜치 상태를 출력하는 것이 좋은 예  
- `git status`처럼 **현재 시스템 상태를 쉽게 확인**하고 다음 작업도 안내하는 출력 구성  
- 색상은 의도적으로 사용하고, 파이프 상태·`NO_COLOR`·`TERM=dumb`·`--no-color` 등 조건에서 **색상 비활성화** 필수  
- TTY가 아닌 환경에서는 **애니메이션·스피너 표시 금지** (CI 로그 오염 방지)  
- 이모지·심볼은 명확성을 높이는 경우에만 사용 (`yubikey-agent`가 예시로 제시됨)  
- 개발자만 이해할 수 있는 정보는 기본 출력에서 제외, **verbose 모드**에서만 표시  
- `stderr`를 로그 파일처럼 사용하지 말 것 — 기본적으로 로그 레벨 레이블(`ERR`, `WARN`) 출력 자제  
- 대량 출력 시 `less` 등 **페이저 사용** 고려 — TTY 환경에서만 활성화하고 `less -FIRX` 옵션 권장  
  
---  
  
### 가이드라인 — 에러 (Errors)  
  
- 예상 가능한 에러는 **인간이 이해할 수 있는 메시지로 재작성** (예: "chmod +w file.txt 실행 필요")  
- **신호 대 잡음비** 유지 — 같은 유형의 에러는 단일 헤더로 묶어 출력  
- 중요한 정보는 **출력 끝부분에 배치** — 붉은 텍스트는 의도적으로, 드물게 사용  
- 예상치 못한 에러 발생 시 디버그 정보와 **버그 리포트 제출 방법 안내** 포함  
- 버그 리포트 URL에 정보를 **자동 채워 제출하기 쉽게** 구성  
  
---  
  
### 가이드라인 — 인수와 플래그 (Arguments and Flags)  
  
- 인수(args)는 위치 기반, 플래그(flags)는 이름 기반 — **플래그를 인수보다 선호**할 것  
- 모든 플래그에 **전체 이름 버전** 제공 (예: `-h`와 `--help` 동시 지원)  
- **단일 문자 플래그**는 자주 쓰이는 플래그에만 한정  
- 표준이 있는 경우 **표준 플래그 이름** 사용 (`-f`/`--force`, `-q`/`--quiet`, `-v`, `--json` 등)  
- 기본값은 **대부분의 사용자에게 적합한 값**으로 설정  
- 인수나 플래그 미전달 시 **프롬프트로 입력 요청**, 단 비대화형 환경에서는 프롬프트 강제 금지  
- 위험한 작업 전 **확인 요청** — 위험 수준에 따라 `y/n` 확인, dry-run 제공, 또는 직접 텍스트 입력 요구  
  - mild(파일 삭제), moderate(디렉토리 삭제, 원격 리소스 변경), severe(전체 서버 삭제)로 위험도 구분  
- 파일 입출력 시 `-`로 **stdin/stdout 읽기/쓰기 지원** (예: `curl ... | tar xvf -`)  
- 플래그에서 **시크릿 직접 수신 금지** — `--password-file` 플래그나 stdin 사용 권장 (ps 출력·셸 히스토리 노출 위험)  
  
---  
  
### 가이드라인 — 인터랙티비티 (Interactivity)  
  
- 프롬프트·인터랙티브 요소는 **stdin이 TTY일 때만 표시**  
- `--no-input` 전달 시 모든 프롬프트 비활성화  
- 비밀번호 입력 시 **에코 비활성화** (입력 내용 화면 미표시)  
- 사용자가 언제든 **탈출 가능하도록 명확히 안내** — Ctrl-C는 항상 동작하게 유지  
  
---  
  
### 가이드라인 — 서브커맨드 (Subcommands)  
  
- 서브커맨드 간 **플래그 이름·출력 형식 일관성** 유지  
- 복잡한 도구는 `noun verb` 또는 `verb noun` 형태의 **2단계 서브커맨드 구조** 사용 (예: `docker container create`)  
- **모호하거나 유사한 이름의 서브커맨드** 회피 (예: update와 upgrade 동시 사용 지양)  
  
---  
  
### 가이드라인 — 견고성 (Robustness Guidelines)  
  
- **입력 검증**을 초기에 수행하고, 잘못된 데이터는 이해하기 쉬운 에러와 함께 조기 종료  
- 응답성이 속도보다 중요 — **100ms 이내에 무언가 출력**할 것  
- 오래 걸리는 작업에는 **진행 표시줄(progress bar)** 제공 — Python(tqdm), Go(schollz/progressbar), Node(node-progress) 라이브러리 활용 가능  
- 병렬 처리 시 출력이 **뒤섞이지 않도록** 주의  
- **네트워크 타임아웃 설정** — 기본값 포함, 영구 대기 방지  
- 일시적 오류 발생 후 **재시도 시 이전 상태에서 재개** 가능하도록 설계  
- **crash-only 설계** — 정리 작업 없이 즉시 종료 가능한 구조로 멱등성 확보  
  
---  
  
### 가이드라인 — 미래 호환성 (Future-proofing)  
  
- 변경은 **하위 호환 추가(additive)** 방식으로 유지  
- 호환성 깨는 변경 전 **프로그램 내에서 사전 경고** 표시  
- 인간용 출력 변경은 일반적으로 허용 — 스크립트용은 `--plain`·`--json` 사용 유도  
- **catch-all 서브커맨드 금지** — 나중에 해당 이름의 서브커맨드 추가 불가 문제 발생  
- **서브커맨드 약어 자동 허용 금지** — 명시적 alias만 허용, 안정적으로 유지  
- **"타임 폭탄" 금지** — 20년 후에도 동작할 수 있도록 외부 의존성 최소화  
  
---  
  
### 가이드라인 — 시그널과 제어 문자 (Signals)  
  
- Ctrl-C(INT 시그널) 수신 시 **즉시 종료**, 정리 작업에 타임아웃 설정  
- 정리 중 Ctrl-C 재입력 시 강제 종료 가능하도록 안내 (Docker Compose 예시 참조)  
- 프로그램은 **정리 작업이 완료되지 않은 상태에서 시작될 수 있음**을 가정하고 설계  
  
---  
  
### 가이드라인 — 설정 (Configuration)  
  
설정 적용 우선순위 (높음 → 낮음):  
- 플래그 → 현재 셸 환경변수 → 프로젝트 수준 설정(`.env`) → 사용자 수준 설정 → 시스템 전체 설정  
  
설정 유형별 권장:  
- **호출마다 달라지는 설정** (디버그 수준, dry-run): 플래그 사용  
- **프로젝트·머신별로 다른 설정** (경로, 색상, HTTP 프록시): 플래그 + 환경변수 조합  
- **프로젝트 전체 공유 설정** (Makefile, package.json 유형): 버전 관리 파일 사용  
  
- **XDG Base Directory 스펙** 준수 — `~/.config` 기반 설정 경로 권장 (yarn, fish, neovim, tmux 등 지원)  
- 다른 프로그램의 설정 파일 자동 수정 시 **사용자 동의 획득** 필수  
  
---  
  
### 가이드라인 — 환경변수 (Environment Variables)  
  
- 환경변수는 **실행 컨텍스트에 따라 달라지는 동작**에 적합  
- 이름은 **대문자·숫자·언더스코어**만 사용, 숫자로 시작 금지  
- **한 줄 값** 권장 — 멀티라인은 `env` 명령과 호환 문제 발생  
- `NO_COLOR`, `DEBUG`, `EDITOR`, `HTTP_PROXY`, `SHELL`, `TMPDIR`, `HOME`, `PAGER` 등 **범용 환경변수** 우선 확인  
- 프로젝트별 `.env` 파일 읽기 지원 권장 — 단, `.env`는 정식 설정 파일의 대체재가 아님  
  - `.env`의 한계: 버전 관리 미포함, 이력 없음, 문자열 단일 타입, 인코딩 문제 취약  
- **환경변수에서 시크릿 읽기 금지** — 모든 프로세스로 전파, 로그 유출, Docker inspect·systemctl show로 노출 위험  
  - 시크릿은 크리덴셜 파일, 파이프, `AF_UNIX` 소켓, 시크릿 관리 서비스를 통해서만 수신  
  
---  
  
### 가이드라인 — 네이밍 (Naming)  
  
- **단순하고 기억하기 쉬운 단어** 사용 — 너무 일반적이면 다른 명령과 충돌 위험  
- **소문자와 필요 시 대시만** 사용 (`curl`은 좋은 예, `DownloadURL`은 나쁜 예)  
- 짧게 유지하되, `cd`·`ls`·`ps`처럼 극도로 짧은 이름은 범용 유틸리티용으로 예약  
- Docker Compose의 전신인 `plum` → `fig` → `docker compose` 개명 사례 — 타이핑 편의성이 네이밍의 중요 기준임을 보여주는 실제 사례  
  
---  
  
### 가이드라인 — 배포 (Distribution)  
  
- 가능하면 **단일 바이너리로 배포** — PyInstaller 등 활용  
- 단일 바이너리 불가 시 **플랫폼 네이티브 패키지 인스톨러** 사용  
- **제거 방법을 설치 안내 하단에 명시**  
  
---  
  
### 가이드라인 — 분석 데이터 (Analytics)  
  
- **사용자 동의 없이 사용 데이터·크래시 데이터 전송 금지**  
- 수집 시 수집 항목, 이유, 익명화 방법, 보존 기간을 명확히 공개  
- 기본 opt-in 권장 — opt-out 방식이라면 첫 실행 또는 웹사이트에서 명확히 고지  
  - Angular.js(명시적 opt-in), Homebrew(Google Analytics, FAQ 공개), Next.js(기본 활성화 익명 통계) 세 가지 사례 소개  
- 분석 대안으로 **웹 문서 계측, 다운로드 수 계측, 사용자 직접 인터뷰** 활용 가능

## Comments



### Comment 22905

- Author: neo
- Created: 2024-02-07T10:35:33+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=39273932) 
- > 현재 많은 사람들이 커맨드 라인이 무엇인지 모르며, 왜 그것을 사용해야 하는지에 대해서도 관심이 없음.
  - 1980년대에도 이러한 상황은 마찬가지였지만, 현재는 그 어느 때보다 커맨드 라인을 아는 사람들이 많아졌음. CLI(커맨드 라인 인터페이스)의 황금기라고 할 수 있음.
- > 스크립트에서는 서브커맨드의 임의 축약을 허용하지 말 것. 예를 들어, mycmd install 대신 mycmd ins 또는 mycmd i를 허용하면, i로 시작하는 새로운 커맨드를 추가할 수 없게 됨.
  - 스크립트에서 짧은 인자 사용을 피해야 함. 짧은 인자는 사람이 사용할 때 타이핑을 줄이기 위한 편의성을 제공하지만, 스크립트에서는 명시적으로 작성하는 것이 비용이 적고, 읽기와 쓰기 비율을 고려할 때 더 바람직함.
- > --dry-run 옵션을 고려해볼 것. 실제 변경을 하지 않고 어떤 작업이 수행될지 미리 보여주는 기능은 도구를 배우고 복잡한 옵션을 올바르게 설정했는지 확인하는 데 매우 유용함.
- > stdout이 대화형 터미널이 아닌 경우, 애니메이션을 표시하지 말 것. 이는 CI 로그 출력에서 진행 상황 바가 크리스마스트리처럼 변하는 것을 방지함.
  - stdout에서는 절대 애니메이션을 표시하지 말 것. stderr는 로깅, 정보 제공 등을 위한 것이며, stdout은 tty 여부와 관계없이 유용한 출력을 제공해야 함.
- > 심볼과 이모지는 명확성을 높일 때만 사용할 것.
  - 심볼과 이모지는 터미널 간에 렌더링이 일관되지 않을 수 있으며, 사용자의 취향에 따라 호불호가 갈릴 수 있으므로 매우 신중하게 사용해야 함.
- > 현재 Unix 커맨드 라인은 한편으로는 "놀라울 정도로 유용"하고 다른 한편으로는 "설계상의 결함"이 있음.
  - Unix 커맨드 라인이 유용한 이유는 C나 Rust로 같은 작업을 수행하는 데 걸리는 시간을 생각해보면 알 수 있음.
  - 설계상의 결함은 커맨드 라인 인터페이스가 동시에 인간과 기계가 읽을 수 있어야 한다는 점에서 비롯됨. 이 문제를 해결하는 정석적인 방법은 없음.
- > CLI가 매우 크고 중첩이 필요한 경우(예: aws)를 제외하고, 대부분의 앱은 모든 옵션을 도움말에 출력하고 사용자가 less를 사용하여 필요한 내용을 찾게 하는 것을 선호함.
- > 전통적으로 UNIX 명령어는 다른 프로그램에 의해 주로 사용될 것이라고 가정하에 작성됨.
  - 실제로는 대화형 로그인 쉘 내에서 상호작용적으로 사용되기 위해 의도되었음. 출력을 생성하는 프로그램과 "조용한" 텍스트 필터로 나뉘며, 복잡한 프로그램은 C로 작성됨.
- > 환경 변수에서 비밀번호를 읽지 말 것.
  - 비밀번호는 크리덴셜 파일, 파이프, AF_UNIX 소켓, 비밀 관리 서비스 또는 다른 IPC 메커니즘을 통해서만 받아야 함.
- > CLI 가이드라인에 대한 가장 포괄적인 책은 Eric Raymond의 저서임.
  - 오랜 시간이 지났지만, clig.dev를 훑어보니 시간이 지남에 따라 의견이 많이 변화했음을 알 수 있음.
