본문 바로가기
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 위에서 함수 서명과 타입 힌트를 CLI 명세로 읽어. 필수 매개변수는 위치 인자, 기본값이 있는 값은 선택 항목이 되고 변환·검증·도움말·자동 완성이 따라와. FastAPI와 닮은 타입 중심 감각이야.

Typer() 앱에 명령을 등록하고, 큰 도구는 add_typer로 하위 앱을 묶어. Annotatedtyper.Option·typer.Argument를 쓰면 범위, 도움말, 입력 질문 같은 부가 정보를 타입 옆에 둘 수 있어.

Typer가 Click 기능을 없앤 건 아니야. 내부에 Click을 쓰므로 필요한 경우 그대로 내려갈 수 있어. 특히 CWK 자동화에서는 대화형 입력 질문보다 안정된 종료 코드, 표준 출력과 표준 오류의 분리, 사람이 없어도 끝까지 실행되는 동작이 먼저야.

Code

타입 힌트로 만드는 Typer 명령·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를 하위 앱으로 나누기·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

앞 수업의 greet·farewell Click CLI를 같은 동작의 Typer 앱으로 다시 써서 길이와 명료성을 비교해. 보너스로 Annotated[int, typer.Option(min=1, max=100)] 선택 항목을 더하고 범위 검증을 확인해.

Progress

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

댓글 0

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

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