# 회사 내부용 CLI 만들기

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

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=16589](https://news.hada.io/topic?id=16589)
- GeekNews Markdown: [https://news.hada.io/topic/16589.md](https://news.hada.io/topic/16589.md)
- Type: news
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2024-09-03T11:03:02+09:00
- Updated: 2024-09-03T11:03:02+09:00
- Original source: [blog.chay.dev](https://blog.chay.dev/create-an-internal-cli/)
- Points: 50
- Comments: 1

## Summary

조직 내부용 CLI를 구축하면 개발용 도구 설치 부터 상용구 코드 생성, 복잡한 명령 실행 등을 조직의 일원들이 간편하게 이용할 수 있습니다. `just`를 사용하여 크로스 플랫폼 지원 및 다양한 스크립트 작성이 가능하며, Git을 통하여 저장 및 업데이트를 하게 만들어서 개발자들이 쉽게 기여하고 최신 버전을 이용할 수 있습니다. 더 나아가 잘 설명된 문서를 내부에 포함하고, 자동 완성 기능을 제공하여 사용자 경험을 향상시킬 수 있습니다.

## Topic Body

- 개발팀에서 조직의 지식을 수집하고 보존하는 유용한 방법 중 하나는 유용한 스니펫, 스크립트 또는 워크플로우 모음을 늘리는 것  
- 그래서 많은 리포지토리에 Makefile, bash 스크립트 같은 것들이 만들어짐   
- 조직 전반에 걸쳐서 유용한 도구 설치, 상용구 코드 생성, 아무도 기억하지 못하는 복잡한 AWS 명령 실행 같은 것은 어떻게 해야 할까?   
  - Slack이나 Shopify와 같은 일부 회사에는 자체 내부 CLI가 있음   
  - 최신 터미널인 Warp에는 워크플로를 문서화하고 공유할 수 있는 기능이 있음   
- 조직 내부용 CLI는 쉽게 만들수 있음. 예로 acme란 회사용 CLI를 제작해 봄   
  
### CLI 설계 요구사항  
- `acme &lt;command&gt;`로 어디서든 명령어를 실행할 수 있는 공통 진입점을 가짐  
  - 모든 개발자는 특정 리포지토리로 먼저 이동할 필요 없이 어디서나 acme &lt;command&gt;를 실행하여 명령을 트리거할 수 있음   
- 개발자들이 새 명령어를 쉽게 기여할 수 있도록 함  
- `acme update`로 새 버전을 쉽게 배포할 수 있게 함  
- 크로스 플랫폼 지원(예: `acme download something`을 하면 Linux에서는 `curl`, Windows에서는 `Invoke-WebRequest` 사용)  
- `acme list`로 사용 가능한 명령어 목록과 간단한 설명을 볼 수 있게 함  
  
### `just`를 사용해 프로젝트 시작하기  
- [just](https://github.com/casey/just)는 `make`와 유사하지만 명령어 실행에 특화된 도구임  
- 크로스 플랫폼을 지원하고 플랫폼 특화 명령어도 실행 가능함  
- 다른 옵션으로는 Slack의 `magic-cli`(Ruby를 잘 안다면 시작하기에 훌륭함)나 `make`가 있음  
  
### 프로젝트 세팅하기   
- `just` 설치. [여기](https://github.com/casey/just/blob/master/README.md#installation) 지침 따르기  
- `~/acme/cli` 폴더 만들고 루트에 다음 `justfile` 추가:  
  
```makefile  
default:  
  just --list  
  
# arch와 os 이름 표시   
os-info:  
  echo "Arch: {{arch()}}"  
  echo "OS: {{os()}}"  
```  
  
- `just` 문서에선 명령어를 "recipes"라고 부름.  
- 레시피 없이 `just`를 실행하면 justfile의 첫번째 레시피 실행. 보통 첫번째 레시피의 이름을 "default"로 지정하는게 일반적인 패턴  
  
```bash  
$ just  
just --list  
Available recipes:  
    default  
    os-info # Show arch and os name  
```  
  
- default 레시피는 `just list` 실행. 모든 레시피 목록과 주석을 보여줌  
- default 레시피는 숨기는 게 좋음  
- 레시피 실행하면 각 명령어가 실행되기 전 출력됨. `@` 접두사로 이 출력 제거 가능. Makefile과 유사  
  
```makefile  
[private]  
@default:  
  just --list  
  
# arch와 os 이름 표시  
@os-info:  
  echo "Arch: {{arch()}}"  
  echo "OS: {{os()}}"  
```  
  
### `acme` alias 만들기  
- `acme &lt;command&gt;` 형태로 실행하기 위해 `.bashrc`에 alias 추가  
  ```bash  
  alias acme='just --justfile ~/acme/cli/justfile'  
  ```  
- `source ~/.bashrc`나 `exec bash`로 새 별칭 로드  
  
### 새 레시피 작성하기  
#### 간단한 레시피  
- AWS IAM 사용자/역할 정보 가져오기  
  ```makefile  
  @aws-id:  
    aws sts get-caller-identity  
  ```  
  - 아무도 기억하지 못하는 명령을 단순화하는 것이 아마도 내부 CLI의 주요 사용 사례  
  - awscli는 크로스 플랫폼이라고 가정하고 있으므로 이 레시피는 호출 위치에 관계없이 작동  
  
#### 플랫폼 특화 레시피  
- `systemd`와 같은 도구가 포함된 스니펫은 개발자가 Linux 머신을 사용하는 경우에만 노출  
- `[linux]` 속성을 사용해 Linux에서만 레시피 노출되게 함   
```makefile  
[linux]  
@list-systemd-services:  
  systemctl list-units --type=service  
```  
  
#### 크로스 플랫폼 레시피   
- 폴더 크기 구하기를 Windows와 Linux에서 각각 구현  
  ```makefile  
  [windows]  
  [no-cd]  
  get-folder-size path:  
    (Get-ChildItem "{{path}}" -Recurse -Force | Measure-Object -Property Length -Sum).Sum / 1MB  
  
  [linux]  
  [no-cd]  
  get-folder-size path:  
    du -sh {{path}}  
  ```  
  
#### 스크립트 레시피  
- 레시피에 전체 스크립트를 삽입할 수 있음   
- Shebang(`#!`)으로 시작하는 레시피는 별도 파일로 저장되어 실행됨  
- 제어 흐름(if-else, 루프) 사용, 변수 저장 및 조작 등과 같이 워크플로에 약간 더 복잡한 로직이 필요한 경우에 유용  
```  
# Say hello world in sh  
hello-world-sh:  
  #!/usr/bin/env sh  
  hello='Yo'  
  echo "$hello from a shell script!"  
```  
- 강력한 스크립팅 기능을 갖춘 프로그래밍 언어를 활용할 수 있다는 의미. 어떤 작업은 Bash보다 Python에서 더 쉽게 수행할 수 있음   
```  
# scale jpg image by 50%  
[no-cd]  
scale-jpg path:  
  #!/usr/bin/env python3  
  
  import PIL.Image  
  image = PIL.Image.open("{{path}}")  
  factor = 0.5  
  image = image.resize((round(image.width * factor), round(image.height * factor)))  
  image.save("{{path}}.s50.jpg")  
```  
- 모든 개발자가 컴퓨터에 파이썬을 설치하는 것은 아니며, 설치되어 있더라도 pillow가 설치되어 있지 않을 수도 있음. `nix`를 사용하여 종속성이 포함된 스크립트를 실행 가능:  
```  
# scale jpg image by 50%  
[no-cd]  
scale-jpg path:  
  #! /usr/bin/env nix-shell  
  #! nix-shell -i python3 -p python3Packages.pillow  
  
  import PIL.Image  
  ...  
```  
  
### 레시피 배포하기  
- 자체 배포 메커니즘을 롤링하는 대신 `git`을 사용  
- GitHub에 저장소 만들고 현재까지 만든 것을 푸시  
```  
$ git init  
$ git commit -m "first commit"  
$ git branch -M main  
$ git remote add origin git@github.com:acme/cli.git  
$ git push -u origin main  
```  
- 이제 이 리포지토리에 액세스할 수 있는 사람은 누구나 PR을 만들어 변경 사항을 기여할 수 있음   
- `acme update` 레시피로 `git pull` 자동화  
```  
# Update the Acme CLI  
@update:  
  git fetch  
  git checkout main  
```  
  
### 문서화  
- 내부 도구의 성공에는 채택(Adoption) 매우 중요하며, 신규 사용자가 도구를 설치하고 탐색할 수 있도록 안내하는 좋은 사용 설명서가 꼭 필요함   
- `README`에 설치 및 사용 방법 안내   
```  
# Acme CLI  
  
## Prerequisites  
  
`just`: Install just [here](https://github.com/casey/just/blob/master/README.md#installation)  
  
## Installation  
  
Clone this repo:  
...  
  
Set up the `acme` alias:  
...  
  
## Usage  
  
List all available recipes:  
...  
```  
- 이제 모든 Acme Corp 개발자가 사용할 수 있게 되었음!   
- 사내에 Slack 메시지를 게시하여 모든 사람이 사용해 보도록 독려하고, 각자가 자신만의 스니펫을 제공할 수 있음   
  
### 추가 기능   
- 자동 완성(Completion) 기능은 TAB 키를 누르면 하위 명령, 파일 경로, 옵션 등을 자동 완성할 수 있는 메커니즘  
- 대부분의 셸은 이 기능을 제공하며, 대부분의 주요 CLI 도구는 완성 기능을 설치하는 방법을 제공  
  - Python의 Click, Golang의 Cobra, Rust의 clap 등 대부분의 주요 CLI 프레임워크는 자동으로 완성 기능을 생성할 수 있음   
- `Just`는 `just --completion &lt;shell&gt;.`를 실행하여 Completion을 생성할 수 있음

## Comments



### Comment 28534

- Author: bus710
- Created: 2024-09-03T14:37:37+09:00
- Points: 1

예전부터 사내 dx 설계가 플랫폼 엔지니어링에게 중요한 주제인 것 같습니다.
