명령줄 인터페이스
고친 사람 github-actions[bot]
명령줄 인터페이스는 키보드로 친 글자 한 줄로 프로그램에 일을 시킵니다. 버튼을 누르는 대신 명령을 적고 엔터를 칩니다. 프로그램은 결과를 다시 글자로 돌려줍니다.
쉽고 빠른 이해
명령줄 인터페이스는 글자 한 줄로 프로그램을 부리는 창구입니다. 터미널에 git status 를 치면 git 이 고친 파일 목록을 글자로 보여 줍니다.
버튼과 메뉴는 사람이 눈으로 보고 하나씩 눌러야 합니다. 명령은 글자라서 파일에 적어 두었다가 몇 번이고 다시 돌릴 수 있습니다. 화면이 없는 서버도 글자만 오가면 멀리서 부릴 수 있습니다.
어떻게 도나:
- 사람이 명령 이름과 동작을 바꾸는 옵션을 한 줄로 치고 엔터를 누릅니다
- 친 줄을 읽어 주는 프로그램인 셸이 그 줄을 낱말로 갈라 맞는 프로그램을 띄웁니다
- 프로그램이 일을 하고 결과를 글자로 내보낸 뒤 성공했는지를 숫자로 알립니다
대가도 있습니다. 어떤 명령이 있는지 화면이 알려 주지 않아서 이름과 옵션을 외우거나 찾아봐야 합니다. 오타 하나가 엉뚱한 일을 시키기도 합니다.
같은 일을 되풀이하거나, 멀리 있는 서버를 다루거나, 일을 자동으로 돌릴 때는 명령줄이 낫습니다. 처음 한 번 해 보는 일이라면 메뉴를 따라가는 화면이 더 편합니다.
상세
글자 한 줄이 어떻게 일이 되는지를 개발자가 날마다 치는 git 과 grep 명령 몇 줄로 따라갑니다.
식당에서 주문하는 길은 둘입니다. 메뉴판 사진을 손가락으로 가리킬 수도 있고, 주문서에 메뉴 이름과 수량을 적어 낼 수도 있습니다. 적어 낸 주문서는 복사해 두면 다음에도 똑같은 주문을 넣을 수 있습니다.
명령줄 인터페이스는 주문서 쪽입니다. 사람이 키보드로 명령 한 줄을 치면 프로그램이 그 글자를 읽고 일을 합니다. 결과도 화면에 글자로 찍힙니다.
영어로는 Command-Line Interface 입니다. 줄여서 CLI 라고 부릅니다. CLI 라는 말은 이 방식으로 다루는 프로그램 자체를 가리킬 때도 씁니다. 「GitHub CLI」나 「Docker CLI」가 그런 이름입니다.
명령 한 줄의 생김새
명령 한 줄은 낱말 몇 개로 이루어집니다. 낱말 사이는 빈칸으로 가릅니다. 아래는 커밋 하나를 만드는 git 명령입니다.
git commit -a -m "로그인 버그 고침"
이 한 줄을 조각마다 가르면 이렇습니다.
| 조각 | 이름 | 하는 일 |
|---|---|---|
git |
명령 이름 | 띄울 프로그램을 고른다 |
commit |
서브커맨드 | 그 프로그램 안에서 할 일을 고른다 |
-a |
옵션 | 동작을 바꾼다. 고친 파일을 모두 담는다 |
-m |
옵션 | 바로 뒤의 값을 커밋 메시지로 받는다 |
"로그인 버그 고침" |
인자 | 일에 쓸 값을 넘긴다 |
서브커맨드는 프로그램 하나가 여러 일을 맡을 때 씁니다. git 은 commit·push·log 처럼 할 일마다 서브커맨드를 따로 둡니다. 명령 이름 다음 낱말이 어느 일을 할지 고르는 셈입니다.
옵션은 대개 하이픈으로 시작합니다. 하이픈 하나에 글자 하나를 쓰는 짧은 꼴(-m)과 하이픈 둘에 낱말을 쓰는 긴 꼴(--message)이 흔히 함께 있습니다.
인자에 빈칸이 들어가면 따옴표로 묶습니다. 묶지 않으면 「로그인」·「버그」·「고침」이 각각 다른 인자로 갈립니다.
터미널과 셸과 프로그램
한 줄을 치고 결과가 나오기까지 셋이 일을 나눕니다. 터미널과 셸과 프로그램입니다.
터미널은 글자를 보여 주고 키 입력을 받는 창입니다. 터미널은 명령을 해석하지 않습니다. 받은 글자를 넘기고 돌아온 글자를 찍을 뿐입니다.
셸은 친 줄을 읽어 프로그램을 대신 띄워 주는 프로그램입니다. 셸은 친 줄을 낱말로 가른 뒤 첫 낱말을 명령 이름으로 봅니다. 그 이름의 프로그램을 찾아 띄우고 나머지 낱말을 인자로 넘깁니다.
셸은 입력을 기다린다는 표시로 $ 같은 글자를 줄 앞에 내밉니다. 이 표시가 프롬프트입니다. 프롬프트가 떠 있으면 다음 명령을 쳐도 된다는 뜻입니다.
아래 그림은 git status 한 줄이 지나가는 길입니다.
sequenceDiagram
participant 사람
participant 터미널
participant 셸
participant 프로그램
사람->>터미널: 명령 한 줄을 치고 엔터
터미널->>셸: 그 줄을 넘긴다
Note over 셸: 낱말로 갈라 명령 이름을 찾는다
셸->>프로그램: 프로그램을 띄우고 인자를 넘긴다
프로그램-->>터미널: 결과 글자를 찍는다
프로그램-->>셸: 끝났다고 알린다
셸-->>터미널: 다음 프롬프트를 띄운다
결과 글자는 셸을 거치지 않고 프로그램에서 터미널로 바로 갑니다. 셸은 프로그램이 끝나기를 기다렸다가 다음 줄을 받을 준비를 합니다.
프로그램이 받는 것과 돌려주는 것
명령줄 프로그램은 함수와 닮았습니다. 값을 받아 결과를 내놓습니다. 끝나면 성공했는지도 알립니다. 받는 통로는 둘, 내놓는 통로는 셋입니다.
첫째 통로는 인자입니다. 셸이 갈라 준 낱말 목록입니다. 프로그램은 시작할 때 이것을 문자열 배열로 받습니다. 자바의 main(String[] args) 에 들어오는 args 가 이 목록입니다.
둘째 통로는 표준 입력입니다. 프로그램이 돌면서 읽어 들이는 글자가 이 통로로 들어옵니다. 아무것도 잇지 않으면 키보드로 친 글자가 들어옵니다.
표준 출력은 프로그램이 결과를 내보내는 통로입니다. 아무것도 잇지 않으면 터미널 화면에 찍힙니다.
표준 오류는 오류 메시지를 내보내는 통로입니다. 이것도 기본으로는 화면에 찍힙니다. 결과와 오류의 통로를 나눠 둔 까닭은 결과만 따로 받아 쓰려는 것입니다. 그래야 오류가 결과에 섞이지 않습니다.
마지막 통로는 종료 코드입니다. 프로그램이 끝나면서 셸에 돌려주는 정수 하나입니다. 0 이면 성공이고 0 이 아니면 실패입니다.
종료 코드가 있으면 화면의 글을 읽지 않고도 성공했는지 알 수 있습니다. grep 으로 확인해 봅니다.
grep 은 파일에서 글자를 찾아 그 글자가 든 줄을 찍는 명령입니다. 찾는 글자가 든 줄이 없으면 아무것도 찍지 않고 실패로 끝납니다.
셸의 $? 에는 방금 끝난 명령의 종료 코드가 들어 있습니다. echo $? 를 치면 그 숫자가 찍힙니다.
아래 예의 members.txt 에는 kim,30 같은 이름·나이 줄이 들어 있습니다. lee 가 든 줄은 없습니다. 줄 끝의 # 뒤는 그 명령이 찍은 값입니다.
grep kim members.txt # kim,30
echo $? # 0
grep lee members.txt # (출력 없음)
echo $? # 1
두 번째 grep 은 찾는 줄이 없어서 아무것도 찍지 않고 1 을 돌려줍니다. 화면에 아무것도 안 나와도 스크립트는 이 1 을 보고 못 찾았다는 것을 압니다.
프로그램끼리 잇기
명령줄 프로그램은 대개 한 가지 일만 합니다. 대신 앞 프로그램의 출력을 뒤 프로그램의 입력으로 이어 큰 일을 해냅니다.
이렇게 잇는 기호가 파이프 | 입니다. 아래는 로그 파일에서 오류 줄이 몇 개인지 세는 한 줄입니다.
grep ERROR app.log | wc -l # 17
grep 이 ERROR 가 든 줄만 골라 표준 출력으로 내보냅니다. 파이프가 그 줄들을 wc -l 의 표준 입력으로 넘깁니다. wc -l 은 받은 줄의 수를 세어 찍습니다. 17 은 그 결과의 예입니다.
리다이렉션은 출력을 화면 대신 파일로 보내는 것입니다. 명령 뒤에 > 와 파일 이름을 적으면 표준 출력이 그 파일에 담깁니다.
리다이렉션을 해도 표준 오류는 화면에 남습니다. 통로가 둘로 나뉘어 있어서 오류 메시지가 결과 파일에 섞이지 않습니다.
스크립트로 굳히기
명령은 글자라서 파일에 적어 둘 수 있습니다. 명령 여러 줄을 파일에 적어 셸이 차례로 돌리게 한 것을 셸 스크립트라고 부릅니다.
스크립트는 몇 번을 돌려도 같은 순서로 같은 명령을 칩니다. 버튼을 누르는 순서는 문서로 남겨도 매번 손으로 다시 해야 합니다. 명령은 적어 둔 문서가 곧 실행할 수 있는 절차가 됩니다.
그래서 자동화는 대개 명령줄 위에 섭니다. 사람이 손으로 누르지 않아도 적어 둔 명령을 기계가 대신 돌립니다.
예를 들어 지속적 통합(CI, Continuous Integration)은 코드를 올릴 때마다 빌드와 테스트를 저절로 돌리는 일입니다. CI 가 무엇을 돌릴지 적는 설정 파일에도 대부분 명령이 한 줄씩 적힙니다.
화면이 없는 서버도 명령줄로 부립니다. SSH(Secure Shell)는 다른 컴퓨터의 셸에 네트워크로 접속하게 해 주는 도구입니다. 글자만 오가면 되므로 화면 전체를 보내지 않아도 됩니다.
그래픽 사용자 인터페이스와의 차이
명령줄과 맞세워지는 방식이 그래픽 사용자 인터페이스(GUI, Graphical User Interface)입니다. 창과 버튼과 메뉴를 마우스로 눌러 프로그램을 다룹니다. 두 방식은 같은 일을 하면서 비용을 서로 다른 쪽에 둡니다.
아래 표는 축마다 둘을 가른 것입니다.
| 축 | 명령줄 | GUI |
|---|---|---|
| 처음 배울 때 | 명령 이름과 옵션을 외우거나 찾아본다 | 화면의 메뉴를 따라간다 |
| 할 수 있는 일 찾기 | 도움말을 따로 연다 | 메뉴와 버튼이 보여 준다 |
| 같은 일 되풀이 | 줄을 다시 돌리거나 스크립트로 적는다 | 매번 손으로 다시 누른다 |
| 대상 여럿을 한꺼번에 | 반복문이나 이름 패턴으로 한 줄에 담는다 | 대상마다 누른다 |
| 멀리 있는 서버 | 글자만 오가면 된다 | 화면을 보내야 한다 |
| 실수 | 오타 하나가 엉뚱한 일을 시킬 수 있다 | 누르기 전에 화면으로 확인한다 |
표의 둘째 줄은 어떤 명령이 있는지 화면이 안 알려 주는 약점입니다. 이 약점은 도움말로 메웁니다. 명령 대부분은 --help 옵션을 주면 쓰는 법을 찍습니다.
유닉스 계열에는 명령마다 긴 설명서가 따로 있습니다. 이 설명서를 man page라고 부릅니다. man git 처럼 man 명령 뒤에 명령 이름을 적어 엽니다.
API 와의 차이
API(Application Programming Interface, 애플리케이션 프로그래밍 인터페이스)는 프로그램이 다른 프로그램을 부르는 창구입니다. 함수를 부르거나 네트워크로 요청을 보내서 코드가 코드를 부릅니다.
사람이 누르는 GUI와 코드가 부르는 API 사이에 명령줄이 섭니다. 사람이 손으로 치기도 하고 스크립트가 대신 치기도 합니다. 그래서 명령줄 도구는 사람과 프로그램을 둘 다 독자로 삼습니다.
사내 개발자들이 함께 쓸 도구와 기반을 만드는 팀은 같은 기능을 세 창구로 함께 내놓기도 합니다. 웹 화면과 명령줄 도구와 API 가 그 셋입니다. 사람은 웹이나 명령줄로, 다른 시스템은 API 로 같은 기능에 닿습니다.
명령줄 도구를 만들 때 지킬 것
명령줄 도구는 사람도 읽고 스크립트도 읽습니다. 둘을 함께 독자로 삼으면 지킬 것이 몇 가지 생깁니다.
사람이 읽기 편한 표 모양 출력은 스크립트가 잘라 쓰기 어렵습니다. 그래서 기계가 읽을 형식을 따로 내주는 도구가 많습니다. 흔한 것이 JSON(JavaScript Object Notation)입니다. 키와 값을 글자로 적는 데이터 형식입니다.
앞에서 본 GitHub CLI 의 gh 명령이 그렇습니다. gh pr list --json number,title 은 열린 풀 리퀘스트 목록을 번호와 제목만 담은 JSON 으로 찍습니다. 스크립트는 이 출력에서 필요한 키만 골라 씁니다.
실패하면 0 이 아닌 종료 코드를 돌려줍니다. 오류 메시지만 찍고 0 을 돌려주면 스크립트는 성공한 줄 알고 다음 단계로 넘어갑니다.
오류 메시지는 표준 오류로 내보냅니다. 표준 출력에 섞이면 파이프 뒤의 프로그램이 오류 문장을 결과로 읽습니다.
옵션 이름과 출력 모양은 한 번 내놓으면 바꾸기 어렵습니다. 누군가의 스크립트가 그 이름과 모양에 기대고 있기 때문입니다. 옵션 이름을 바꾸거나 지우면 그 스크립트가 깨집니다. 이런 변경을 하위 비호환 변경이라고 부릅니다.
백엔드 개발자가 날마다 치는 명령줄 도구
백엔드 개발자는 하루에도 여러 명령줄 도구를 칩니다. 아래는 그중 흔한 것과 한 줄 예입니다.
| 도구 | 한 줄 예 | 하는 일 |
|---|---|---|
| Git | git log --oneline |
커밋 이력을 한 줄씩 본다 |
| Docker | docker ps |
돌고 있는 컨테이너 목록을 본다 |
| kubectl | kubectl get pods |
쿠버네티스 클러스터에 떠 있는 컨테이너 묶음을 본다 |
| curl | curl localhost:8080/health |
주소로 요청을 보내고 응답을 찍는다 |
도구는 달라도 생김새는 같습니다. 명령 이름 뒤에 서브커맨드와 옵션과 인자가 붙습니다. 결과는 표준 출력으로, 성공 여부는 종료 코드로 나옵니다.
직접 만드는 서비스에도 명령줄 진입점을 두는 일이 흔합니다. 데이터 정리나 테이블 구조를 바꾸는 마이그레이션처럼 가끔 손으로 돌리는 일이 그렇습니다. 웹 화면을 만드는 대신 명령 하나로 내놓습니다.
관련 항목
명령 한 줄을 이루는 구성 요소
명령어 · 서브커맨드 · 옵션 · 플래그 · 인자 · 프롬프트
명령줄 인터페이스가 도는 실행 환경
터미널 · 터미널 에뮬레이터 · 셸 · Bash · Zsh · PowerShell · 프로세스 · 운영체제 · 유닉스
명령줄 프로그램이 주고받는 입출력 통로
표준 입력 · 표준 출력 · 표준 오류 · 종료 코드 · 환경 변수 · PATH
명령줄 프로그램을 잇고 묶는 수단
파이프 · 파이프라인 · 리다이렉션 · 셸 스크립트 · 자동화 · 지속적 통합
명령줄 인터페이스와 맞세워지는 인터페이스
그래픽 사용자 인터페이스 · 텍스트 사용자 인터페이스 · API · REPL · 웹 포털
명령줄 인터페이스가 속하는 상위 분류
인터페이스 · 사용자 인터페이스 · 개발자 경험 · 플랫폼 엔지니어링
명령줄 도구를 만들 때 따르는 규약
유닉스 철학 · POSIX · man page · JSON · 하위 비호환 변경
명령줄 인터페이스를 내주는 도구
다른 이름: CLI · command-line interface · command line interface · 커맨드 라인 인터페이스 · 명령행 인터페이스