## CLI
CLI 자체는 전혀 모던하지 않다. **그러나 다시금 CLI가 중요해진 이유는 모던하다.** 바로 클라우드 때문이다. 대부분의 클라우드 인스턴스는 리눅스 기반에 CLI만 지원한다. 때문에 인스턴스에서 돌아가는 프로그램이 CLI를 지원하지 않으면, 실행과 종료 이외에는 관리자가 할 수 있는 행동이 사실상 없어진다. 배치가 제대로 돌아가지 않은 작업을 직접 실행해야 할 때(1), 오류가 났을 때 로그를 확인하고 조치를 해야 할 때(2), 갑작스럽게 생긴 추가 작업을 지시해야 할 때(3) CLI가 없으면 너무나 불편하다.
## CLI 설정 in Python
### `click` 패키지
```bash
pip install click
```
파이썬의 내장 패키지인 `argparse`를 통해 CLI 설정할 순 있지만, 그 방법이 매우 투박하고 불편하다. 그에 반해 `click` 패키지는 데코레이터를 통한 CLI 설정을 지원하기 때문에 `click`을 사용하는 것이 좋다.
#### `click` 을 이용한 CLI 설정
>**참고 : 폴더 구성**
>```
>cli_study.py
>pyproject.toml #빌드용 파일, setup.py 대체
>```
##### `@click.group` 과 `@click.command`
CLI는 보통 `[프로그램 이름] [명령어] [인자 또는 옵션]`의 구조를 이룬다. 여기서 `[프로그램 이름]`을 설정하는 것이 `@click.group` 이고, `[명령어]`를 설정하는 것이 `@click.command`이다. 설정한 명령어는 `cli_study.add_command`로 추가할 수 있다. `click` 최근 버전에서는 이전에 다뤘던 [[1. 동적 실행과 네임스페이스를 이용한 인스턴스 관리|동적 실행]] 방식으로 명령어를 추가할 수도 있다.[^1]
```python
# cli_study.py
import click
@click.group()
def cli_study():
print("This message is displayed every time 'cli_study' is called")
@click.command()
def test():
print("test")
cli_study.add_command(test)
```
예를 들어 위와 같이 코드를 작성한 경우, `cli_study test`를 실행할 수 있고 결과는 `cli_study \n test`로 나온다.
##### `@click.argument`와 `@click.option`
다음으로 `[인자 또는 옵션]`을 설정하는 것이 `@click.argument`와 `@click.option`다. 다만, 이 둘은 설정 방법이 조금 다르다.
```python
# cli_study.py
#...
@click.command()
@click.argument("num", type=click.INT)
def argument(num):
for i in range(num):
print(i)
cli_study.add_command(argument)
```
먼저 `@click.argument`의 경우 `@click.argument("[인자 이름]", ...)` 방법으로 설정하며, 인자는 CLI에 반드시 포함되어 있어야 한다. 즉, `cli_study argument` 이렇게 실행할 경우, 에러가 발생한다.
타입 지정의 경우, click의 parameters 객체로 지정할 수 있으며 타입 종류는 [공식 문서](https://click.palletsprojects.com/en/8.1.x/parameters/)에서 확인 가능하다.
```python
# cli_study.py
#...
@click.command()
@click.option("--num", required=False, type=click.INT)
def option(num):
if num:
for i in range(num):
print(i)
cli_study.add_command(option)
```
다음으로 `@click.option`의 경우 `@click.option("--[옵션 이름]", ...)` 방법으로 설정하며, 옵션의 경우 인자와 다르게 CLI에 포함이 되어 있지 않아도 된다. 포함 여부를 강제하는 인자는 `required:boolean`이다. 그 외 타입 지정의 경우 `@click.argument`와 동일한 방법으로 지정한다.
##### CLI 등록
이제 위의 CLI 코드를 실행해보자. `click` 공식 문서에서는 `if __name__ == '__main__'` 방식보다는 `setuptools`를 이용해서 CLI를 등록하는 걸 권장하고 있다.[^2]
###### `pyproject.toml` 작성
```toml
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "cli_study"
version = "0.1.0"
description = "."
dependencies = [
"Click"
]
[project.scripts]
cli_study = "cli_study:cli_study"
```
`toml`은 `yaml`과 같이 쉽게 쓰고 이해할 수 있는 마크업 언어다. 파이썬의 `setuptools`는 이 `toml`도 지원하는데, `pyproject.toml`로 기존에 쓰이던 `setup.py`를 대체할 수 있다. [^3]
`pyproject.toml`에서 CLI 관련 설정은 `[project.scripts]`에서 할 수 있다. 설정하는 방법은 `[CLI 명령어 이름] = "[파이썬 파일 경로]:[@click.group을 단 함수 이름]`이다. 나머지 설정에 대한 내용은 [여기](https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html)에 잘 정리되어 있다.
###### CLI 등록
```bash
pip install -e .
```
pip을 통해 설치한다. `-e`는 코드를 수정하면 자동으로 반영하도록 하는 설정이다.
```bash
cli_study --help
```
```
# 실행 결과
Usage: cli_study [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
argument
option
test
```
```bash
cli_study test
```
```
# 실행 결과
cli_study
test
```
```bash
cli_study argument 2
```
```
# 실행 결과
cli_study
0
1
```
```bash
cli_study option
```
```
# 실행 결과
cli_study
```
```bash
cli_study option --num 2
```
```
# 실행 결과
cli_study
0
1
```
위 명령어들을 실행하여 결과를 확인해보자.
[^1]: [click doc - Custom Multi Commands](https://click.palletsprojects.com/en/8.1.x/commands/#custom-multi-commands)
[^2]: [click doc - Switching to Setuptools](https://click.palletsprojects.com/en/8.1.x/quickstart/#switching-to-setuptools)
[^3]: [setuptools - Configuring setuptools using pyproject.toml files](https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html)