TL;DR
- Click은 Python CLI를 빠르게 만드는 데코레이터 기반 프레임워크 (Pallets 프로젝트)
@click.command·@click.option·@click.argument로 선언형 CLI 구성,@click.group으로 서브커맨드와 Context 전달- Typer는 Click 위에 구축된 type hint 기반 모던 대안
AI-assisted
Source
1. Click이 뭔가
Click(Command Line Interface Creation Kit)은 Python CLI 도구를 만드는 프레임워크다. Flask를 만든 Pallets 프로젝트에서 개발하고 있다.
Python 표준 라이브러리의 argparse도 CLI를 만들 수 있지만, Click은 데코레이터 기반으로 더 적은 코드와 직관적인 구조를 제공한다.
pip install clickimport click
@click.command()
@click.option('--name', '-n', required=True, help='이름')
@click.option('--count', '-c', default=1, help='반복 횟수')
def hello(name, count):
"""인사를 출력하는 CLI"""
for _ in range(count):
click.echo(f"Hello {name}!")
if __name__ == '__main__':
hello()$ python hello.py --name World --count 3
Hello World!
Hello World!
Hello World!
$ python hello.py --help
Usage: hello.py [OPTIONS]
인사를 출력하는 CLI
Options:
-n, --name TEXT 이름 [required]
-c, --count INTEGER 반복 횟수
--help Show this message and exit.도움말이 자동 생성되고, 타입 검증도 자동으로 처리된다.
2. 핵심 개념
2-1. Command와 Parameter
Click의 기본 단위는 Command다. 함수에 @click.command() 데코레이터를 붙이면 CLI 명령이 된다.
파라미터는 두 종류로 나뉜다.
| 종류 | 선언 | 특징 |
|---|---|---|
| Option | @click.option('--name') | --name value 형태. 순서 무관, 기본값 가능 |
| Argument | @click.argument('filename') | 위치 기반. -- 접두사 없음, 순서 중요 |
@click.command()
@click.argument('src', type=click.Path(exists=True))
@click.argument('dst', type=click.Path())
@click.option('--overwrite', is_flag=True, help='기존 파일 덮어쓰기')
def copy(src, dst, overwrite):
"""SRC 파일을 DST로 복사"""
click.echo(f"Copying {src} -> {dst} (overwrite={overwrite})")$ python cli.py source.txt dest.txt --overwrite2-2. 타입 시스템
Click은 파라미터에 다양한 타입을 지정할 수 있다.
# 기본 타입
@click.option('--count', type=int)
@click.option('--rate', type=float)
# 선택지 제한
@click.option('--env', type=click.Choice(['dev', 'staging', 'prod']))
# 범위 제한
@click.option('--port', type=click.IntRange(1024, 65535))
# 파일 자동 open
@click.option('--output', type=click.File('w'))
# 경로 검증
@click.option('--config', type=click.Path(exists=True, dir_okay=False))2-3. Group (서브커맨드)
@click.group()으로 여러 커맨드를 하나의 CLI에 묶을 수 있다. git이나 docker 같은 서브커맨드 구조를 만드는 방법이다.
@click.group()
@click.option('--verbose', '-v', is_flag=True)
@click.pass_context
def cli(ctx, verbose):
"""파일 관리 CLI"""
ctx.ensure_object(dict)
ctx.obj['VERBOSE'] = verbose
@cli.command()
@click.argument('filename')
@click.pass_context
def create(ctx, filename):
"""새 파일 생성"""
if ctx.obj['VERBOSE']:
click.echo(f"Creating {filename}...")
open(filename, 'w').close()
click.echo(f"Created: {filename}")
@cli.command()
@click.argument('filename')
def delete(filename):
"""파일 삭제"""
import os
os.remove(filename)
click.echo(f"Deleted: {filename}")$ python cli.py --verbose create test.txt
Creating test.txt...
Created: test.txt
$ python cli.py delete test.txt
Deleted: test.txt2-4. Context (상태 전달)
@click.pass_context를 사용하면 그룹에서 설정한 값을 하위 커맨드로 전달할 수 있다. ctx.obj에 딕셔너리를 넣어서 공유 데이터를 관리하는 패턴이 일반적이다.
3. 유틸리티 함수
Click은 CLI 개발에 유용한 함수를 여러 개 제공한다.
| 함수 | 용도 |
|---|---|
click.echo(msg) | print 대체. 파이프, 유니코드, 색상 안전 |
click.style(text, fg='green', bold=True) | ANSI 색상/스타일 적용 |
click.prompt('값 입력', type=int) | 사용자 입력 프롬프트 |
click.confirm('계속?') | yes/no 확인 |
click.progressbar(iterable) | 진행률 표시 |
click.edit(text) | 외부 에디터 호출 |
click.launch(url) | 브라우저/앱 열기 |
# 색상 출력
click.echo(click.style("SUCCESS", fg="green", bold=True))
click.echo(click.style("ERROR", fg="red"))
# 진행률 표시
with click.progressbar(range(1000)) as bar:
for item in bar:
process(item)4. 테스트
Click은 CliRunner를 내장하고 있어서 CLI를 코드에서 테스트할 수 있다.
from click.testing import CliRunner
def test_hello():
runner = CliRunner()
result = runner.invoke(hello, ['--name', 'World'])
assert result.exit_code == 0
assert 'Hello World!' in result.output
# stdin 시뮬레이션
result = runner.invoke(prompt_cmd, input='yes\n')
# 격리된 파일시스템에서 테스트
with runner.isolated_filesystem():
with open('test.txt', 'w') as f:
f.write('test data')
result = runner.invoke(cat_cmd, ['test.txt'])
assert result.exit_code == 05. Click vs Typer
Typer는 Click 위에 구축된 모던 CLI 프레임워크다. FastAPI를 만든 Sebastian Ramirez가 개발했다. Flask와 FastAPI의 관계처럼, Click과 Typer도 같은 구도다.
| 항목 | Click | Typer |
|---|---|---|
| 기반 | 독립 라이브러리 | Click 위에 구축 |
| 파라미터 선언 | @click.option() 데코레이터 | 함수 파라미터 type hint |
| 에디터 자동완성 | 제한적 | 우수 (표준 함수 시그니처) |
| Shell 자동완성 | 별도 설정 | 내장 |
| 설계 시기 | Python 2.x 시대 | 모던 Python (3.6+ type hint) |
같은 CLI를 두 방식으로 비교하면 다음과 같다.
# Click 방식
@click.command()
@click.option('--name', required=True, help='Who to greet')
@click.option('--count', default=1, help='Number of greetings')
def hello(name, count):
for _ in range(count):
click.echo(f"Hello {name}!")# Typer 방식
import typer
def hello(name: str, count: int = 1):
for _ in range(count):
typer.echo(f"Hello {name}!")
typer.run(hello)Typer가 더 간결하지만, 복잡한 그룹 구조나 커스텀 타입, 파이프라인 체이닝이 필요하면 결국 Click의 하위 레이어를 직접 다뤄야 한다. 복잡한 프로덕션 CLI라면 처음부터 Click을 쓰는 것이 낫고, 간단~중간 복잡도라면 Typer가 개발 속도에서 유리하다.
6. Click vs argparse
| 항목 | argparse | Click |
|---|---|---|
| 제공 | Python 표준 라이브러리 | 서드파티 (pip install click) |
| 선언 방식 | parser.add_argument() 메서드 체인 | @click.option() 데코레이터 |
| 서브커맨드 | add_subparsers() (번거로움) | @click.group() (직관적) |
| 도움말 | 자동 생성 | 자동 생성 + 더 보기 좋음 |
| 테스트 | 직접 구현 | CliRunner 내장 |
| 외부 의존성 | 없음 | pip install 필요 |
외부 의존성을 추가할 수 없는 환경이면 argparse, 그 외에는 Click이나 Typer가 개발 경험에서 유리하다.