C.W.K.
Stream
Lesson 03 of 04 · published

typer — CLI 스펙으로서의 타입 힌트

~18 min · typer, type-hints, cli, modern

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

typer — click 의 타입-힌트 사촌

typer (FastAPI 작가) 가 click 위에 앉아서 Python 타입 힌트로 인자 선언. def hello(name: str, count: int = 1) — name 이 positional 인자, count 가 옵션 --count. 타입 힌트가 모든 거 driving, 데코레이터에 반복 X. Pippa 의 CLI wrapper 가 typer.

ergonomic 승리

함수 시그니처 자체가 CLI 스펙. 타입 힌트, 디폴트, 타입 검증 다 거기서. 명시적 옵션엔 name: str = typer.Option(...). 더 풍부한 config 엔 name: Annotated[str, typer.Option()]. 결과 CLI 가 일반 Python 함수처럼 보이고, 모든 정적 타입 혜택.

서브커맨드 — click 보다 쉬움

app = typer.Typer(), 그 다음 @app.command() 로 함수 데코레이트. 각 함수가 자기 이름의 서브커맨드. 계층엔 app.add_typer(other_app, name="users") 로 sub-typer 중첩. 현대 Python 프로젝트가 새 CLI 에 click 보다 typer 자주 사용.

Rich 통합

typer 가 디폴트로 rich 라이브러리와 통합 — 색깔 출력, progress bar, 테이블, 포맷된 traceback. 일반 출력엔 typer.echo, 색깔엔 typer.secho, progress 엔 typer.progressbar. polish 가 노력 없이 광범위.

Pythonic Way: 2026 에 새 CLI 짜면 typer 디폴트. 타입-힌트 driven ergonomics 가 best-in-class. 0 의존성 중요할 때만 argparse, typer 의 추상화 안 맞을 때 click. 셋이 형제, 경쟁자 X — typer 가 안에서 click 사용.

Code

typer — 타입-힌트 driven·python
# pip install typer
import typer

app = typer.Typer()

@app.command()
def hello(name: str, count: int = 1, shout: bool = False):
    """NAME 한테 count 번 인사."""
    msg = f"Hello, {name}!"
    if shout:
        msg = msg.upper()
    for _ in range(count):
        typer.echo(msg)

if __name__ == "__main__":
    app()

# python tool.py alice --count 3 --shout
# HELLO, ALICE!
# HELLO, ALICE!
# HELLO, ALICE!
Typer 서브커맨드·python
import typer

app = typer.Typer()

@app.command()
def add(name: str):
    """사용자 추가."""
    typer.echo(f"adding {name}")

@app.command()
def remove(name: str, force: bool = False):
    """사용자 제거."""
    if not force:
        confirmed = typer.confirm(f"{name} 제거?")
        if not confirmed:
            raise typer.Abort()
    typer.echo(f"removed {name}")

if __name__ == "__main__":
    app()

# python tool.py add alice
# python tool.py remove bob --force
더 풍부한 옵션 메타데이터엔 Annotated·python
from typing import Annotated
import typer
from pathlib import Path

app = typer.Typer()

@app.command()
def process(
    input_path: Annotated[Path, typer.Argument(help="입력 파일 경로", exists=True)],
    output_path: Annotated[Path, typer.Argument(help="출력 파일 경로")],
    verbose: Annotated[bool, typer.Option("-v", "--verbose", help="verbose 출력")] = False,
    workers: Annotated[int, typer.Option(min=1, max=64)] = 4,
):
    """입력을 N worker 로 출력 처리."""
    typer.echo(f"input: {input_path}")
    typer.echo(f"output: {output_path}")
    typer.echo(f"workers: {workers}")
    if verbose:
        typer.echo("verbose 모드")

# 제약 (min/max/exists 등) 자동 체크
큰 CLI 위한 중첩 app·python
import typer

main_app = typer.Typer()
users_app = typer.Typer()
reports_app = typer.Typer()

main_app.add_typer(users_app, name="users")
main_app.add_typer(reports_app, name="reports")

@users_app.command()
def add(name: str):
    typer.echo(f"users add {name}")

@users_app.command()
def list_all():
    typer.echo("all users")

@reports_app.command()
def generate(format: str = "json"):
    typer.echo(f"generating in {format}")

if __name__ == "__main__":
    main_app()

# python tool.py users add alice
# python tool.py users list-all          (kebab-case 자동 생성)
# python tool.py reports generate --format csv

External links

Exercise

이전 lesson 의 click CLI 를 typer CLI 로 다시 짜. greet/farewell 서브커맨드, 같은 인자. 타입 힌트로 선언. 코드 길이와 명료성 비교. (보너스 — Annotated[int, typer.Option(min=1, max=100)] 사용 옵션 추가 + typer 가 범위 강제 확인.)

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.