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 click
import 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 --overwrite

2-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.txt

2-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 == 0

5. Click vs Typer

Typer는 Click 위에 구축된 모던 CLI 프레임워크다. FastAPI를 만든 Sebastian Ramirez가 개발했다. Flask와 FastAPI의 관계처럼, Click과 Typer도 같은 구도다.

항목ClickTyper
기반독립 라이브러리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

항목argparseClick
제공Python 표준 라이브러리서드파티 (pip install click)
선언 방식parser.add_argument() 메서드 체인@click.option() 데코레이터
서브커맨드add_subparsers() (번거로움)@click.group() (직관적)
도움말자동 생성자동 생성 + 더 보기 좋음
테스트직접 구현CliRunner 내장
외부 의존성없음pip install 필요

외부 의존성을 추가할 수 없는 환경이면 argparse, 그 외에는 Click이나 Typer가 개발 경험에서 유리하다.


7. 참고 자료