2P by GN⁺ | ★ favorite | 댓글 1개
  • 이 가이드는 전통적 UNIX 원칙을 현대적으로 갱신해, 사람이 쓰기 좋고 자동화에도 견고한 CLI를 만들기 위한 오픈소스 설계 지침임
  • 좋은 CLI는 사람 우선으로 동작하되 stdout/stderr, 종료 코드, 파이프, JSON 같은 관례를 지켜 다른 프로그램과 자연스럽게 조합되어야 함
  • 도움말, 문서, 오류 메시지, 출력은 사용자가 다음 행동을 알 수 있게 해야 하며, --help, 예제, 제안, 진행 표시, 상태 설명이 핵심 역할을 함
  • 인자·플래그·상호작용·설정·환경 변수는 스크립트와 터미널 사용을 모두 고려해야 하며, 비밀값은 플래그나 환경 변수로 직접 받지 않는 편이 안전함
  • CLI의 이름, 배포, 분석 수집까지 사용자의 통제감과 장기 호환성을 해치지 않아야 하며, 관례를 깨야 할 때도 목적이 분명해야 함

CLI 설계의 기본 철학

  • 이 가이드는 오픈소스 문서로, 전통적 UNIX 원칙을 바탕으로 현대적인 명령줄 프로그램 설계 원칙과 구체적 지침을 제공함
  • 과거 명령줄은 스크립팅 플랫폼 위의 REPL에 가까운 기계 우선 환경이었지만, 오늘날의 CLI는 여러 도구·시스템·플랫폼에 접근하는 텍스트 기반 UI로서 사람 우선 성격이 강함
  • 명령줄은 오래된 제약과 특이한 관습이 있지만, 거의 모든 노트북에서 사용할 수 있고 대화식 사용과 자동화가 모두 가능하며, 다른 시스템 구성요소보다 덜 빠르게 변함
  • 이 가이드는 emacs나 vim 같은 전체 화면 터미널 프로그램을 다루지 않으며, 특정 프로그래밍 언어나 도구 체인에도 종속되지 않음

사람 우선이면서 조합 가능한 CLI

  • CLI가 주로 사람이 사용하는 도구라면 사람을 먼저 고려해야 함
    • 많은 CLI 프로그램이 인간 사용자를 대상으로 하면서도 과거 기계 중심 상호작용 설계를 그대로 유지함
  • 작은 프로그램이 깨끗한 인터페이스로 함께 동작하는 UNIX 철학은 여전히 중요함
    • 표준 입력·출력·오류, 시그널, 종료 코드 같은 관례가 프로그램 간 조합을 가능하게 함
    • 줄 단위 일반 텍스트는 파이프로 전달하기 쉽고, JSON은 더 구조화된 데이터가 필요할 때 유용함
  • 일관성은 CLI 학습 비용을 장기 효율로 바꾸는 핵심임
    • 기존 패턴을 따르면 사용자가 명령과 옵션을 더 쉽게 추측할 수 있음
    • 관례가 사용성을 해칠 때는 신중하게 깨도 됨
  • 좋은 CLI는 너무 말이 없거나 너무 많은 출력을 내지 않고, 사용자가 필요한 만큼의 정보를 얻도록 해야 함
  • CLI는 GUI보다 발견성이 약하다고 여겨지지만, 포괄적 도움말, 예제, 다음 명령 제안, 오류 시 대응 안내로 학습 가능성을 높일 수 있음

대화형 상호작용과 견고함

  • 명령줄 사용은 한 번의 실행보다 여러 번의 시도와 수정으로 이어지는 대화에 가까움
    • 잘못된 입력에는 가능한 수정안을 제시할 수 있음
    • 다단계 작업의 중간 상태를 명확히 보여줄 수 있음
    • 위험한 작업 전 확인을 받을 수 있음
  • CLI는 실제로 견고해야 하며, 동시에 사용자가 견고하다고 느낄 수 있어야 함
    • 예상치 못한 입력을 우아하게 처리해야 함
    • 가능한 경우 작업은 멱등적이어야 함
    • 무서워 보이는 스택 트레이스를 기본 출력으로 보여주지 않아야 함
  • 사용자에게 소프트웨어가 자기 편이라는 느낌을 주는 공감이 중요함
    • 이모지를 쓰거나 게임처럼 만드는 것이 아니라, 사용자가 성공하도록 세심히 설계하는 것이 핵심임
  • 터미널 생태계에는 불일치와 혼란이 많지만, 낮은 제약 덕분에 새로운 발명이 가능했음
    • 기존 패턴을 따르되, 생산성이나 사용자 만족을 해치는 표준은 의도적으로 버릴 수 있음

반드시 지켜야 할 기본 규칙

  • 가능한 경우 명령줄 인자 파싱 라이브러리를 사용해야 함
  • 성공 시 종료 코드는 0, 실패 시에는 0이 아닌 값을 반환해야 함
    • 스크립트는 종료 코드를 통해 프로그램의 성공·실패를 판단함
  • 명령의 주 출력은 stdout으로 보내야 함
    • 기계가 읽을 수 있는 출력도 기본적으로 stdout으로 가야 파이프가 정상 동작함
  • 로그 메시지, 오류 등 사용자에게 보여줄 메시지는 stderr로 보내야 함
    • 파이프로 연결할 때 메시지가 다음 명령의 입력으로 섞이지 않음

도움말 설계

  • -h--help가 전달되면 충분한 도움말을 보여줘야 함
    • 하위 명령도 자체 도움말을 가질 수 있음
    • -h는 다른 의미로 오버로드하지 않아야 함
  • 인자가 필요한 명령을 아무 인자 없이 실행하면 간결한 도움말을 보여줘야 함
    • 프로그램 설명
    • 한두 개의 예제 호출
    • 플래그 설명
    • 더 자세한 정보는 --help를 사용하라는 안내
  • 도움말에는 지원 경로를 포함하는 것이 좋음
    • 웹사이트나 GitHub 링크가 일반적임
  • 웹 문서가 있다면 도움말에서 해당 문서로 연결해야 함
    • 하위 명령에 특정 페이지나 앵커가 있다면 직접 연결하는 것이 유용함
  • 사용자는 문서보다 예제를 먼저 활용하는 경향이 있으므로, 도움말에는 예제를 앞쪽에 배치하는 것이 좋음
    • 복잡하지만 흔한 사용 사례를 먼저 보여줄 수 있음
    • 예제가 많다면 별도 치트시트 명령이나 웹 페이지에 두는 것이 적절함
  • 도움말은 스캔하기 쉽게 포맷팅할 수 있음
    • 굵은 제목은 가독성을 높임
    • 단말 독립적인 방식으로 처리해 이스케이프 문자가 그대로 보이지 않게 해야 함
  • 사용자가 잘못 입력했고 의도를 추측할 수 있으면 제안해야 함
    • brew update jqbrew upgrade jq를 안내하는 식으로 동작할 수 있음
    • 제안 명령을 실행할지 물을 수는 있지만 강제 실행은 피해야 함
  • stdin으로 입력을 받아야 하는 명령인데 stdin이 대화형 터미널이라면 즉시 도움말을 보여주고 종료하거나 stderr에 메시지를 출력해야 함

문서화

  • 도움말은 즉각적인 요약과 흔한 작업 안내를 제공하고, 문서는 도구의 목적·비목적·동작 방식·전체 사용법을 자세히 다룸
  • 웹 기반 문서를 제공해야 함
    • 검색 가능하고 특정 부분으로 링크할 수 있음
    • 웹 문서는 가장 포괄적인 문서 형식임
  • 터미널 기반 문서도 제공해야 함
    • 빠르게 접근할 수 있음
    • 설치된 도구 버전과 동기화됨
    • 인터넷 없이도 작동함
  • man 페이지 제공을 고려할 수 있음
    • 많은 사용자가 man mycmd를 먼저 확인함
    • gitnpm처럼 help 하위 명령을 통해 man 페이지에 접근할 수 있음

출력 원칙

  • 사람이 읽기 좋은 출력이 가장 중요함
    • 특정 출력 스트림이 사람이 읽는 대상인지 판단하는 간단한 기준은 TTY 여부임
  • 사용성을 해치지 않는 범위에서 기계가 읽을 수 있는 출력을 제공해야 함
    • 일반 텍스트 줄 스트림은 UNIX의 보편적 인터페이스임
    • 사용자는 출력 결과를 grep 같은 도구로 넘겨 예상대로 동작하길 기대함
  • 사람 친화적 출력이 기계 친화적 출력을 깨뜨린다면 --plain을 제공할 수 있음
    • 표 형태 출력에서 셀을 여러 줄로 나누면 한 레코드 한 줄이라는 기대를 깨뜨릴 수 있음
    • --plain은 스크립트용으로 조작 없는 표 형식 출력을 제공함
  • --json이 전달되면 포맷팅된 JSON을 출력해야 함
    • JSON은 복잡한 데이터 구조를 다루기 쉽고, jq 및 여러 JSON CLI 도구와 함께 사용할 수 있음
    • 웹 서비스와 curl을 통해 직접 파이프 연결하기에도 적합함
  • 성공 시에는 출력하되 짧게 유지해야 함
    • 전통적 UNIX 명령은 문제가 없으면 출력하지 않는 경우가 많지만, 사람에게는 멈춘 것처럼 보일 수 있음
    • 스크립트용 무출력이 필요하면 -q 옵션으로 비필수 출력을 숨길 수 있음
  • 상태를 바꾸는 명령은 사용자에게 무엇이 바뀌었는지 알려야 함
    • git push는 수행 중인 작업과 원격 브랜치의 새 상태를 보여주는 예시임
  • 시스템의 현재 상태를 쉽게 볼 수 있어야 함
    • git status는 저장소 상태와 다음에 실행할 수 있는 명령 힌트를 함께 보여줌
  • 프로그램 내부 세계의 경계를 넘는 작업은 보통 명시적이어야 함
    • 사용자가 인자로 넘기지 않은 파일을 읽거나 쓰는 경우
    • 원격 서버와 통신해 파일을 내려받는 경우
  • 색상은 의도를 가지고 사용해야 함
    • 너무 많이 쓰면 의미가 사라지고 읽기 어려워짐
    • TTY가 아니거나, NO_COLOR가 설정됐거나, TERM=dumb이거나, --no-color가 전달되면 색상을 꺼야 함
  • stdout이 대화형 터미널이 아니면 애니메이션을 출력하지 않아야 함
    • CI 로그에서 진행 표시가 지저분해지는 일을 막을 수 있음
  • 많은 텍스트를 출력할 때는 less 같은 페이저를 사용할 수 있음
    • stdin 또는 stdout이 대화형 터미널일 때만 사용하는 것이 좋음
    • less -FIRX가 합리적인 옵션 집합으로 쓰일 수 있음

오류 메시지

  • 오류를 문서처럼 만들면 사용자가 문서를 찾아보는 시간을 줄일 수 있음
  • 예상 가능한 오류는 잡아서 사람이 이해할 수 있게 다시 써야 함
    • 예: file.txt에 쓸 수 없고 chmod +w file.txt가 필요할 수 있다는 식의 안내
  • 신호 대 잡음비가 중요함
    • 관련 없는 출력이 많을수록 사용자가 문제를 파악하기 어려움
    • 같은 유형의 오류가 여러 개라면 하나의 설명 헤더 아래 묶을 수 있음
  • 가장 중요한 정보는 출력의 끝에 두는 것이 좋음
    • 사용자의 시선은 빨간 글자에 끌리므로 의도적으로 적게 사용해야 함
  • 예상치 못했거나 설명하기 어려운 오류라면 디버그·트레이스백 정보와 버그 제출 방법을 제공해야 함
    • 사용자를 압도하지 않도록 디버그 로그를 터미널 대신 파일에 쓸 수 있음
  • 버그 제출은 쉽게 만들어야 함
    • 가능한 정보를 미리 채운 URL을 제공하는 방식이 예시임

인자와 플래그

  • 인자는 위치 기반 매개변수이고, 플래그는 이름 있는 매개변수임
    • cp foo bar에서 파일 경로는 인자임
    • -r, --recursive, --file foo.txt는 플래그임
  • 가능한 경우 인자보다 플래그를 선호해야 함
    • 더 많이 입력해야 하지만 의미가 명확함
    • 향후 입력 방식을 변경하기 쉬움
  • 모든 플래그에는 긴 이름을 제공해야 함
    • -h--help를 함께 두는 식임
    • 스크립트에서 의미를 명확히 드러내는 데 유용함
  • 한 글자 플래그는 흔히 쓰는 옵션에만 사용해야 함
    • 나중에 추가할 플래그를 위한 짧은 이름 공간을 보존할 수 있음
  • 여러 파일에 같은 단순 작업을 적용하는 경우에는 여러 인자가 적절함
    • rm file1.txt file2.txt file3.txt 같은 형태는 글로빙과 잘 맞음
  • 서로 다른 의미의 인자가 2개 이상이면 설계가 잘못됐을 가능성이 높음
    • 예외로 cp <source> <destination>처럼 흔하고 핵심적인 동작은 짧음이 기억할 가치가 있음
  • 표준적인 플래그 이름이 있다면 따라야 함
    • -a, --all
    • -d, --debug
    • -f, --force
    • --json
    • -h, --help
    • -n, --dry-run
    • --no-input
    • -o, --output
    • -p, --port
    • -q, --quiet
    • -u, --user
    • --version
  • 기본값은 대부분의 사용자에게 올바른 동작이어야 함
    • 사용자가 적절한 플래그를 찾아 매번 기억해서 쓰리라고 가정하면 대부분의 사용자 경험이 나빠짐
  • 입력이 없으면 프롬프트로 물어볼 수 있지만, 프롬프트가 필수여서는 안 됨
    • 항상 플래그나 인자로 입력을 전달할 방법을 제공해야 함
    • stdin이 대화형 터미널이 아니면 프롬프트를 건너뛰고 필요한 플래그나 인자를 요구해야 함
  • 위험한 작업 전에는 확인해야 함
    • 대화형 실행에서는 yyes를 입력하게 할 수 있음
    • 비대화형 실행에서는 -f--force를 요구할 수 있음
    • 심각한 작업은 삭제 대상 이름을 직접 입력하게 하거나 --confirm="name-of-thing" 같은 플래그를 제공할 수 있음
  • 파일 입력·출력을 받는다면 -stdin 또는 stdout을 지원해야 함
    • 임시 파일 없이 다른 명령 출력과 연결할 수 있음
  • 가능한 경우 인자, 플래그, 하위 명령 순서를 독립적으로 처리해야 함
    • 사용자가 이전 명령 끝에 옵션을 추가해 다시 실행하는 일이 흔하기 때문임
  • 비밀값은 플래그로 직접 읽지 않아야 함
    • --password 값은 ps 출력이나 셸 히스토리에 노출될 수 있음
    • --password-file 같은 파일 입력이나 stdin을 고려해야 함

상호작용

  • 프롬프트나 대화형 요소는 stdin이 TTY일 때만 사용해야 함
    • 스크립트나 파이프 입력 중에는 프롬프트가 작동하지 않으므로 어떤 플래그를 전달해야 하는지 오류로 알려야 함
  • --no-input이 전달되면 프롬프트나 대화형 동작을 하지 않아야 함
    • 입력이 필요하면 실패하고 플래그로 전달하는 방법을 안내해야 함
  • 비밀번호를 입력받을 때는 사용자가 타이핑하는 값을 출력하지 않아야 함
    • 터미널의 echo를 끄는 방식으로 처리함
  • 사용자가 빠져나갈 수 있어야 함
    • 네트워크 I/O 등으로 멈춰 있어도 Ctrl-C가 작동해야 함
    • SSH, tmux, telnet처럼 Ctrl-C로 종료할 수 없는 래퍼라면 탈출 방법을 명확히 알려야 함

하위 명령

  • 도구가 충분히 복잡하면 하위 명령 집합으로 복잡도를 줄일 수 있음
    • 여러 관련 도구를 하나의 명령으로 묶으면 사용과 발견이 쉬워질 수 있음
    • 전역 플래그, 도움말, 설정, 저장 메커니즘을 공유하기 좋음
  • 하위 명령 간에는 일관성이 필요함
    • 같은 의미에는 같은 플래그 이름을 사용해야 함
    • 출력 포맷도 비슷해야 함
  • 여러 단계의 하위 명령에는 일관된 이름 체계를 써야 함
    • docker container create처럼 명사와 동사 두 단계로 구성하는 패턴이 흔함
    • noun verbverb noun 모두 가능하지만, noun verb가 더 흔해 보임
  • 모호하거나 비슷한 이름의 명령은 피해야 함
    • updateupgrade가 함께 있으면 혼란스러울 수 있음

견고성

  • 사용자가 입력하는 모든 데이터는 검증해야 함
    • 잘못된 데이터가 들어올 수 있으므로 빨리 검사하고 이해 가능한 오류로 중단해야 함
  • 빠른 것보다 반응성이 더 중요함
    • 100ms 안에 사용자에게 무언가를 출력하는 것이 좋음
    • 네트워크 요청 전에도 메시지를 출력해 멈춘 것처럼 보이지 않게 해야 함
  • 오래 걸리는 작업에는 진행 상황을 보여줘야 함
    • 출력이 한동안 없으면 프로그램이 고장 난 것처럼 보임
    • 진행 표시가 오래 한 곳에 멈추면 남은 시간 추정이나 애니메이션으로 계속 작업 중임을 보여줄 수 있음
  • 병렬 처리는 가능하면 하되 신중해야 함
    • 병렬 작업의 진행률을 셸에서 보여주는 일은 어렵고, 출력이 뒤섞이면 혼란스러움
    • docker pull의 여러 진행 막대는 무슨 일이 일어나는지 보여주는 예시임
    • 정상 동작 때 로그를 진행 막대 뒤에 숨기더라도, 오류가 나면 로그를 출력해야 디버깅이 가능함
  • 네트워크 작업은 타임아웃을 가져야 함
    • 타임아웃은 설정 가능해야 하며 합리적인 기본값이 필요함
  • 실패 후 복구 가능해야 함
    • 일시적 실패 후 <up><enter>로 다시 실행했을 때 중단 지점부터 이어갈 수 있어야 함
  • 가능하면 crash-only 방식이 좋음
    • 실패나 중단 시 즉시 종료할 수 있고, 정리는 다음 실행으로 미룰 수 있음
  • 사용자는 도구를 예상 밖 방식으로 사용할 수 있음
    • 스크립트로 감싸거나, 불안정한 인터넷에서 쓰거나, 여러 인스턴스를 동시에 실행하거나, 테스트하지 않은 환경에서 실행할 수 있음

미래 호환성

  • 하위 명령, 인자, 플래그, 설정 파일, 환경 변수는 모두 인터페이스이며, 오래 동작하도록 유지해야 함
  • 가능한 변경은 추가적이어야 함
    • 기존 플래그 동작을 깨는 대신 새 플래그를 추가할 수 있음
  • 비추가적 변경이 필요하면 미리 경고해야 함
    • 더 이상 권장되지 않는 플래그를 사용했을 때 향후 변경을 알리고, 지금부터 사용할 수 있는 미래 호환 방식도 알려야 함
  • 사람용 출력은 바뀌어도 대체로 괜찮음
    • 사용자는 스크립트에서 안정적 출력을 위해 --plain이나 --json을 사용하도록 유도해야 함
  • catch-all 하위 명령은 피해야 함
    • 알려진 하위 명령이 아니면 자동으로 run으로 처리하는 방식은 나중에 새 하위 명령을 추가할 때 기존 사용을 깨뜨릴 수 있음
  • 하위 명령의 임의 약어를 허용하지 않아야 함
    • installiins로 허용하면 나중에 i로 시작하는 다른 명령을 추가하기 어려움
    • 별칭은 명시적이고 안정적으로 유지해야 함
  • 20년 뒤에도 명령이 같은 방식으로 실행될지 고려해야 함
    • 외부 인터넷 의존성이 바뀌거나 사라져 명령이 멈추는 시한폭탄을 만들지 않아야 함

시그널과 제어 문자

  • 사용자가 Ctrl-C, 즉 INT 시그널을 보내면 가능한 빨리 종료해야 함
    • 정리 작업을 시작하기 전에 즉시 무언가를 말해야 함
    • 정리 코드에는 타임아웃을 둬야 함
  • 오래 걸리는 정리 중 Ctrl-C가 다시 입력되면 정리를 건너뛸 수 있어야 함
    • Docker Compose는 종료 중 Ctrl-C를 한 번 더 누르면 컨테이너를 즉시 강제 중지할 수 있다고 안내함
  • 프로그램은 이전 정리 작업이 실행되지 않은 상태에서 시작될 수 있다고 가정해야 함

설정과 환경 변수

  • 설정 방식은 구체성, 안정성, 복잡성에 따라 달라져야 함
    • 실행마다 자주 달라지는 설정은 플래그가 적합함
    • 사용자·프로젝트·머신에 따라 달라지는 비교적 안정적인 설정은 플래그와 환경 변수가 적합함
    • 프로젝트 안에서 모든 사용자에게 안정적인 설정은 버전 관리되는 명령 전용 설정 파일이 적합함
  • XDG Base Directory Specification을 따르는 것이 좋음
    • ~/.config 같은 일반 목적 설정 위치를 지원해 홈 디렉터리의 dotfile 증가를 줄이는 목적이 있음
  • 자신의 프로그램 설정이 아닌 파일을 자동 수정한다면 사용자 동의를 받고 정확히 무엇을 하는지 알려야 함
    • 기존 시스템 설정 파일에 덧붙이기보다 새 설정 파일을 만드는 것이 좋음
  • 설정 우선순위는 높은 것부터 낮은 것 순으로 적용해야 함
    • 플래그
    • 실행 중인 셸의 환경 변수
    • 프로젝트 수준 설정
    • 사용자 수준 설정
    • 시스템 전체 설정
  • 환경 변수는 명령이 실행되는 맥락에 따라 달라지는 동작에 적합함
  • 환경 변수 이름은 최대 이식성을 위해 대문자, 숫자, 밑줄만 사용하고 숫자로 시작하지 않아야 함
  • 값은 가능하면 한 줄이어야 함
    • 여러 줄 값은 env 명령과 함께 쓸 때 사용성 문제가 생김
  • 널리 쓰이는 이름을 함부로 점유하지 않아야 함
    • POSIX 표준 환경 변수 목록을 참고할 수 있음
  • 가능한 경우 범용 환경 변수를 확인해야 함
    • NO_COLOR, FORCE_COLOR
    • DEBUG
    • EDITOR
    • HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, NO_PROXY
    • SHELL
    • TERM, TERMINFO, TERMCAP
    • TMPDIR
    • HOME
    • PAGER
    • LINES, COLUMNS
  • 적절한 경우 .env에서 환경 변수를 읽을 수 있음
    • 특정 디렉터리에서 작업하는 동안 거의 바뀌지 않는 변수에 유용함
  • .env는 정식 설정 파일의 대체물이 아님
    • 소스 관리에 저장되지 않는 경우가 흔함
    • 문자열 타입만 있음
    • 정리가 나빠지기 쉬움
    • 인코딩 문제가 생기기 쉬움
    • 민감한 자격 증명과 키 자료가 들어가기 쉬움
  • 비밀값은 환경 변수에서 읽지 않아야 함
    • 환경 변수는 프로세스, 로그, docker inspect, systemctl show 등을 통해 노출될 수 있음
    • 비밀은 자격 증명 파일, 파이프, AF_UNIX 소켓, 비밀 관리 서비스, 다른 IPC 방식으로 받아야 함

이름, 배포, 분석 수집

  • CLI 프로그램 이름은 사용자가 자주 입력하므로 단순하고 기억하기 쉬워야 함
    • 너무 일반적이면 다른 명령과 충돌하거나 사용자를 혼란스럽게 할 수 있음
  • 이름은 소문자와 필요할 때의 대시만 사용하는 것이 좋음
    • curl은 좋은 이름이고 DownloadURL은 그렇지 않은 예시임
  • 이름은 짧아야 하지만 너무 짧은 이름은 cd, ls, ps 같은 매우 흔한 유틸리티에 어울림
  • 입력하기 쉬운 이름이어야 함
    • Docker Compose의 초기 이름이 plum이었다가 한 손으로 치기 어색해 fig로 바뀐 사례가 있음
  • 가능하면 단일 바이너리로 배포해야 함
    • 단일 바이너리가 어렵다면 플랫폼의 기본 패키지 설치기를 사용해 제거하기 쉬운 방식으로 배포해야 함
    • 언어별 도구, 예를 들어 코드 린터는 사용자가 해당 언어 인터프리터를 갖고 있다고 가정해도 됨
  • 제거가 쉬워야 함
    • 설치 직후 제거하고 싶어 하는 경우가 흔하므로, 제거 안내를 설치 안내 아래에 두는 것이 좋음
  • 사용량 지표는 도구 개선에 도움이 될 수 있지만, CLI 사용자는 자신의 환경을 통제한다고 기대함
  • 동의 없이 사용량이나 크래시 데이터를 전송하지 않아야 함
    • 무엇을 수집하는지, 왜 수집하는지, 얼마나 익명화되는지, 어떻게 익명화하는지, 얼마나 오래 보관하는지 명시해야 함
    • 이상적으로는 opt-in이 좋고, 기본 수집인 opt-out을 택한다면 웹사이트나 첫 실행 때 명확히 알리고 쉽게 끌 수 있어야 함
  • 분석 수집의 대안도 고려할 수 있음
    • 웹 문서를 계측함
    • 다운로드를 계측함
    • 사용자와 직접 이야기하고 문서·저장소에서 피드백과 기능 요청을 장려함

댓글과 토론

Hacker News 의견들
  • “요즘 대부분은 명령줄이 뭔지도 모른다”는 말은 맞지만, TFA가 기준으로 삼은 1980년대에도 마찬가지였음
    차이는 지금이 명령줄을 알고 쓸 수 있는 사람이 역사상 가장 많다는 점이고, 적어도 한 자릿수 규모, 어쩌면 두 자릿수 규모로 늘었으니 CLI 황금기라고 해도 됨

    • 모놀리스를 쪼개기 위해 앱 일부에 명령줄 도구를 붙이고 있음
      전역 상태와 의존성은 추론을 방해하고, 결국 디버깅과 성능 최적화도 어렵게 만듦
      500줄, 1,000줄, 5,000줄, 10,000줄, 때로는 5만 줄짜리 코드를 따로 실행되게 빼내면 많은 것이 훨씬 명확해짐
      제대로 하면 Functional Core, Imperative Shell 구조도 유도할 수 있는데, 유닉스 철학에 맞는 명령줄은 부작용 없는 동작이 많고 부작용 있는 동작은 적어야 하기 때문임
      나쁜 결정이 내려지기 직전의 시스템 상태를 생성해 출력하는 작은 명령을 만들 수 있고, 운영 환경에서도 사실상 안전하게 실행 가능함
      그러면 버스 팩터에 포함되어야 하는 사람에게도 이런 도구를 넘겨 참여시키기 쉬워짐
    • “사람”을 “컴퓨터 사용자”로 바꾸면 저자의 요지는 맞음
      외딴 아마존 문명 마을 사람이 터미널을 어떻게 생각하는지는 관련이 없음
    • 절대 수와 비율을 구분해야 함
      수량이 커진 대상의 질적 변화를 판단하려면 비율을 봐야지, 절대 수만 보면 참이지만 의미 없는 문장이 나옴
      예를 들어 오늘날 재즈 청취자의 절대 수는 장르의 문화적 전성기보다 많을 수도 있지만, 그건 재즈가 더 인기 있어서가 아니라 음악을 듣는 사람 자체가 늘었기 때문임
      그렇다고 미국에서 재즈가 전성기보다 더 중요하고 영향력 있다고 말하긴 어려움
    • 1980년대 컴퓨터 사용자 중 비율로 봐도 그랬는지가 핵심임
  • 실제 변경 없이 어떤 작업이 일어날지 미리 보여주는 --dry-run 옵션도 고려해줬으면 함
    도구를 배우거나, 되돌리기 어려운 작업을 실행하기 전에 복잡한 옵션과 파일 글롭을 제대로 썼는지 확인하는 데 정말 유용함

    • 명령을 되돌릴 수 없고 부작용이 크다면 기본값을 --dry-run 으로 두고, 실제 실행에는 --execute 플래그를 요구하는 편이 나음
    • PowerShell의 WhatIf는 가장 좋아하는 기능 중 하나임
      이것 없이는 어떻게 했을지 모르겠고, 여러 번 완전히 망가뜨릴 뻔한 상황에서 살려줬음
    • 반대로 기본 동작은 실제 변경을 하지 않게 만들고, 정말 실행하려면 --commit 을 넘기게 하는 편을 선호함
      되돌릴 수 없거나 되돌리기 어려운 작업을 하는 스크립트에는 이 방식이 더 안전하게 느껴짐
    • dry run 플래그가 있는 명령줄 도구 예시가 궁금함
      만들고 있는 도구들에 어떻게 설계해야 할지 혼란스러움
      데이터를 가져오려고 API를 호출하는 경우, 그걸 어떻게 dry run으로 만들 수 있는지 모르겠음
      가짜 예시와 가짜 출력을 줘야 하는 건가?
    • dry-run은 어떤 명령에도 파이프로 연결 가능한 함수여야 한다고 봄: do_thing.py | dry-run
      프롬프트에서 “작업 과정을 단계별로 설명해줘”라고 요청하는 것처럼 생각하면 됨
  • 표준 출력이 대화형 터미널이 아니면 애니메이션을 표시하지 말라는 정도가 아니라, stdout에는 애니메이션을 절대 표시하지 말아야 함
    TFA는 전반적으로 좋았지만, stderr와 stdout의 차이를 설명할 부분을 찾다가 그러지 않는다는 걸 보고 아쉬웠음
    stderr는 오류만이 아니라 로그와 정보성 출력 전부가 가야 하는 곳이고, 터미널이면 애니메이션을 넣을 수도 있는 영역임
    stdout은 유용한 실제 출력이어야 하며, 터미널 여부와 관계없이 일관되어야 함
    예를 들어 echo foo | mysed 's/oo/aa/' | cat에서 mysed의 stdout은 faa이고, stderr에는 버전 정보나 찾은 내용 같은 로그가 가야 함
    실제 출력만 얻으려고 grep으로 도구와 싸우고 싶지 않고, | cat을 빼면 동작이 달라지는 식으로 디버깅이 어려워지는 것도 원치 않음

    • “stdout은 유용한 출력”이라는 규칙을 조금 다듬으면 stdout은 사용자가 요청한 것이어야 함
      --help를 요청했다면 stdout으로 가야 하고, 로그를 요청했다면 로그도 stdout으로 가야 함
      요청하지 않은 정보라면 stderr가 맞음
    • 좋은 규칙이지만 이름이 아쉬움
      시간을 되돌릴 수 있다면 stdin, stdout, stdext 같은 이름으로 만들었으면 사람들이 이 관례를 따를 가능성이 있었을 것 같음
      하지만 이름이 stderr라서 개발자는 합리적으로 “오류 보고용”이라고 생각하고, 오류가 아니면 stdout에 넣게 됨
      그래도 프로그램 구조로는 이 방식이 더 낫고, 처음엔 더 헷갈릴 수 있어도 출력으로 더 유용한 일을 할 수 있으니 이득임
    • 그렇다면 애니메이션은 어디에 표시해야 하는지 궁금함
      색상도 정보라고 보긴 어렵다고 봄
  • “명확해지는 곳에는 기호와 이모지를 쓰라”는 조언은 제발 피했으면 함
    예시로 든 yubikey-agent는 GitHub README와 장난스러운 사용자 인터페이스에서 싫어하는 점을 그대로 보여줌
    기술적으로는 기호와 이모지가 터미널마다 다르게 렌더링되어 메시지를 헷갈리게 만들 수 있음
    미학적으로도 장난기와 발랄함에 대한 허용치는 사람마다 크게 다르므로 아주 절제해서, 정말 무엇을 하는지 아는 경우에만 써야 함

    • 난 그런 출력이 좋음
      색 있는 터미널 출력도 좋고, 이모지도 색이 있어서 상황의 전체상을 빠르게 파악하는 데 도움이 됨
      구문 강조도 좋아하고, 없으면 코드를 훨씬 읽기 어렵게 느낌
      모두가 그런 건 아니며, 내 취향이 기본으로 맞춰지길 기대하지 않듯 반대 취향도 기본으로 강요하면 안 됨
    • 선택 기능이라면 좋음
      좋아하는 사람도 있고 싫어하는 사람도 있으니 기본은 꺼두되 사용자가 고를 수 있게 하면 됨
    • 지침으로는 출력에 쓰는 이모지 어휘를 최대 두 개로 제한하면 좋겠음
      예를 들어 성공 하나, 실패 하나 정도면 충분함
      그리고 이모지가 전달하는 정보는 반드시 텍스트로도 중복해서 전달해야 함
    • 강하게 공감함
      처음 CLI Guidelines를 봤을 때도 그 섹션에서 읽기를 멈췄음
      이번에는 나머지도 읽어봤고, 전체적으로는 꽤 괜찮았음
  • 일부 CLI가 aws처럼 매우 커서 중첩이 필요하다는 건 이해하지만, 중첩 CLI를 따라 들어가는 건 정말 답답함
    대부분의 앱은 help에 모든 옵션을 뱉고, 내가 less로 필요한 걸 찾게 해주는 편이 더 좋음

    • TUI 앱을 많이 만들고 있는데, 모든 앱에 덜 기술적인 사람들, 예를 들어 QA가 쓸 수 있는 좋은 TUI 프런트엔드를 붙임
      이 TUI는 원하면 쓰는 순수 UI/UX이고, 선택한 설정을 별도의 생성 파일이나 함수로 넘김
      그 파일이나 함수는 항상 터미널에서 직접 호출할 수 있고, TUI에서 쓰는 모든 옵션과 도움말도 제공함
      그래서 스크립트로도 쓸 수 있고, 여러 숙련도 수준의 사용자에게 유용함
    • 하위 명령의 수와 그 명령들이 얼마나 명확한지에 따라 다름
      필요한 하위 명령을 알고 있다면 옵션을 필요한 것만으로 줄여주는 게 꽤 유용하지만, 모르면 고통스러울 수 있음
      큰 옵션 목록을 grep으로 뒤지는 것도 힘드니 균형이 필요함
    • 그게 man 페이지의 역할 아닌가?
    • 하위 명령의 --help를 상위 명령에 펼쳐 넣는 방식이 도움이 될 수 있음
      예를 들어 git stash --help가 각 항목을 포함한 git stash 도움말을 그대로 보여주는 식임
  • “전통적으로 UNIX 명령은 주로 다른 프로그램이 쓸 것이라는 가정하에 작성됐다”는 설명은 정확하지 않음
    원래는 로그인 셸 안에서 대화형 사용을 주로 의도했음
    stdout으로 출력을 만드는 프로그램들(ls, cat, find, tty, who, date)과 조용한 텍스트 필터들(tr, grep, cut, uniq, sort, wc)이 있었고, 그 시대에는 한 줄 명령으로 기본적인 컴퓨팅 작업을 할 수 있었음
    복잡한 프로그램은 C로 작성됐고, sed와 AWK 같은 도메인 특화 언어가 등장한 뒤 문자열 처리가 많은 일부 프로그램이 셸로 옮겨갔음
    셸은 정상적인 프로그래밍 환경이 아니며, 그런 용도로 의도된 적도 없음

    • 1만 줄짜리 C 프로그램이 셸 10줄이면 되는 경우도 있고, 1천 줄짜리 셸 프로그램이 C 100줄이면 됐을 경우도 있음
      요즘은 둘 다 Python이나 Lua로 하는 편이 더 나을 때가 많지만, 셸과 C가 가장 보편적으로 깔려 있음
    • 정상적이든 아니든 셸은 프로그래밍 언어이고, 초기 Unix에서는 그 점이 훨씬 더 두드러졌음
      sh, bash, ksh, csh로 갈라진 것도 좋은 예임
      표준 POSIX 도구 모음을 여러 셸 언어의 표준 라이브러리로 보는 건 꽤 타당하다고 봄
  • 현재 Unix 명령줄은 한편으로는 “엄청나게 유용”하고, 다른 한편으로는 설계상 깨져 있음
    아래를 C나 Rust로 작성하려면 얼마나 오래 걸릴지 생각해보면 유용성은 분명함:
    curl -sS https://go.dev/doc/devel/release | html2text | grep -o -P '\bgo\d+\.\d+\.\d+\b' | sort -V | uniq | tail -1
    하지만 왜 설계상 깨졌는지는 https://news.ycombinator.com/item?id=29747034를 보면 됨
    문제는 명령줄 인터페이스가 사람이 읽기 쉬우면서 동시에 기계가 읽기 쉬워야 한다는 점이고, 이를 해결하는 표준적인 방법이 없음

    • 그 예시 자체가 왜 설계상 깨졌는지를 잘 보여줌
      사람과 기계가 동시에 읽을 수 있어야 한다는 문제에는 원래 표준 해법이 있을 수도 있었음
      Apple은 데스크톱 UI의 시각적 추상화 의미를 구체화하려고 Human Interface Guidelines를 만들었음
      하지만 명령줄은 Apple 디자이너처럼 생각하는 사람들이 만든 게 아니라, “게으름, 조급함, 오만함이 미덕이니 내가 원하는 걸 최소 코드로 어떻게 표현할까”라고 생각하는 사람들이 만들었음
      당시에는 틀린 선택도 아니었고 바이트 하나하나가 중요했지만, 그 설계 결정이 지금은 움직일 수 없는 도구 체인에 박혀버렸음
      지금보다 나은 것을 얻으려면 POSIX 도구 체인을 사실상 포기해야 할 것 같고, 현재 기반 위에 발견 가능하고 개념적으로 일관된 UX를 쌓기는 상상하기 어려움
    • 컴퓨터는 결국 우리를 섬기기 위해 있는 것이니, 최종 해법은 기계가 사람만큼 잘 읽게 만드는 것이어야 함
    • 실제로는 “동시에”라기보다는 보통 사람과 기계가 같은 명령을 다른 시점에 사용함
      같은 시점에도 서로 다른 출력을 가능하게 하는 방법은 많음
      다만 모두가 따르는 표준이 있어야 하고, 바로 그 지점에서 복잡해짐
    • 그 예시는 설계상 깨졌다는 걸 증명하지 못할 뿐 아니라, 비교적 쉬운 해결책까지 보여주는 듯함
      ls 구현 중 파일명을 줄바꿈 대신 NUL 문자로 끝낼 수 있게 해주는 것이 거의 없다는 문제가 있다면, 해결책은 너무 명확해 보여서 뭔가 놓치고 있는 것 같음
      셸 인터페이스가 어떤 이유로 거부하는 건가?
    • 글에서 다루는 방식은 --json 같은 기계 판독용 출력 모드
  • “비밀값을 환경 변수에서 읽지 말라”면서 자격 증명 파일, 파이프, AF_UNIX 소켓, 비밀 관리 서비스, 기타 프로세스 간 통신을 제안하는데, 이 중 무엇이 가장 편하고 이식성이 좋은지 궁금함
    비밀 관리 서비스는 업무에서만 쓰는지, 개인 프로젝트에서도 쓰는지도 궁금함

    • CLI Guidelines 저자 중 한 명임
      명령줄에서 비밀값을 다루는 내용을 조금 더 깊게 쓴 글은 https://smallstep.com/blog/command-line-secrets/에 있음
      자격 증명 파일은 단순하고 이식성 좋은 선택지임
      파일에는 이미 권한 모델이 있고, 외부 서비스나 독점 API에 의존하지 않음
      프로그램이 자격 증명 파일을 받으면 systemd credentials와도 호환됨
      systemd credentials는 암호화되지 않은 자격 증명 파일보다 더 안전하고, 암호화되며 TPM에 묶을 수도 있지만, 자격 증명을 쓰는 소프트웨어가 자체적으로 TPM을 지원할 필요는 없음
    • 프로그램이 비밀값을 가져오는 명령을 지정할 수 있게 해주면 좋음
      예를 들어 mbsync(https://isync.sourceforge.io/mbsync.html)는 IMAP 인증 비밀번호를 제공하는 방법이 대략 세 가지 있음
      비밀번호를 설정하지 않으면 실행 시 프롬프트가 뜨고, 설정 파일에 평문 비밀번호를 넣을 수도 있지만 공유하기엔 불편함
      또 비밀번호를 가져오는 명령을 설정할 수 있어서, pass(1)(https://www.passwordstore.org/) 같은 비밀번호 관리자나 대화형 그래픽 프롬프트에 처리를 위임할 수 있음
    • 비밀 관리 서비스가 가장 편리할 것임
      문서가 잘 되어 있어 직접 추가로 만들 필요 없이 쉽게 설정할 수 있음
      Doppler(https://Doppler.com)나 AWS Secrets Manager(https://aws.amazon.com/secrets-manager/) 같은 비밀 관리자는 비밀값을 안전한 곳에 보호하고, 그 노출을 최소화한다는 장점이 있음
      심지어 내부 개발자에게도 노출을 줄일 수 있어 쉽게 피할 수 있었던 데이터 유출을 막는 데 도움이 됨
      이런 유출은 회사 전체를 위험하게 만들 수 있고 점점 더 흔해지고 있음
  • 관련 글: Command Line Interface Guidelines - https://news.ycombinator.com/item?id=38053692 - 2023년 10월, 댓글 1개
    Command Line Interface Guidelines - https://news.ycombinator.com/item?id=31651161 - 2022년 6월, 댓글 1개
    Command Line Interface Guidelines - https://news.ycombinator.com/item?id=25492119 - 2020년 12월, 댓글 5개

  • 클립보드에서 붙여넣은 문자열 끝에 줄바꿈 문자가 있으면 일부 터미널이 자동으로 명령을 실행하는 게 정말 짜증남
    개인적으로 명령줄 인터페이스는 그러면 안 된다고 봄

    • Bash에서는 bind 'set enable-bracketed-paste on'을 쓰면 됨
    • macOS의 iTerm은 줄바꿈이 포함된 문자열을 붙여넣으려 하면 확인을 요청함
      귀찮으면 끌 수도 있음
    • Fish shell은 기본적으로 기대한 대로 삽입만 하고, 실행 전에 명령을 편집할 수 있게 해줌
      필요하면 여러 줄도 가능하고, Enter를 눌러야 실행됨
      Fish shell은 기본값이 합리적인 경우가 많았고, 프롬프트 말고는 딱히 커스터마이즈하고 싶은 부분을 아직 못 찾았음
    • 클립보드 관리자를 써볼 만함
      내가 써본 것들은 대부분 클립보드 내용에서 줄바꿈을 제거하는 옵션이 있었음
    • 줄바꿈이 최대 하나만 있을 걸 알면 먼저 # 를 입력한 뒤 붙여넣음
      줄바꿈이 해석되더라도 전체 줄이 주석이 되니 안전하고, 실제 실행 전에 줄 앞의 #만 지우면 됨