파이썬 자동화 #7 나만의 명령어 만들기: typer와 rich로 CLI 하나로 묶기

6 분 소요

스크립트가 한두 개일 때는 문제가 없습니다. 그런데 6편까지 따라오면서 폴더 정리, 엑셀 보고서, 스크래핑, 메일 발송 스크립트가 쌓이고 나면 새로운 문제가 생깁니다. “그거 어떻게 돌리더라"입니다. 어떤 스크립트는 인자가 세 개이고, 어떤 스크립트는 특정 폴더에서만 돌아갑니다. 시리즈 마지막 편에서는 이 흩어진 스크립트들을 하나의 CLI 도구로 묶고, 터미널 어디서나 한 단어로 부르는 명령으로 만들겠습니다.

argparse에서 typer로 #

1편에서는 표준 라이브러리인 argparse로 인자를 받았습니다. parser.add_argument("folder")처럼 인자 하나마다 선언을 한 줄씩 쌓는 방식이라, 잘 동작하지만 인터페이스가 커질수록 선언부가 장황해집니다. typer는 같은 일을 타입 힌트로 해결합니다. 함수 시그니처가 곧 CLI 정의입니다.

cli.py: typer 방식
from pathlib import Path
import typer

def organize(folder: Path, dry_run: bool = False):
    """다운로드 폴더를 확장자별로 정리합니다."""
    print(f"{folder} 정리 시작 (dry_run={dry_run})")

if __name__ == "__main__":
    typer.run(organize)

선언 코드가 따로 없는데도 typer가 시그니처에서 전부 읽어냅니다. folder: Path는 필수 인자가 되고 입력값이 Path 객체로 자동 변환되며, dry_run: bool = False--dry-run 플래그가 되고, docstring은 도움말 설명문이 됩니다.

uv add typer rich로 두 라이브러리를 설치한 뒤, uv run cli.py ~/Downloads --dry-run처럼 바로 실행할 수 있습니다.

서브커맨드로 묶기 #

진짜 가치는 여기부터입니다. typer.Typer() 객체에 함수를 여러 개 등록하면 git처럼 하위 명령을 가진 도구가 됩니다. 1〜6편에서 만든 스크립트들을 함수 하나씩으로 옮겨 한 도구에 모으고, 하위 명령으로 부르겠습니다.

mytools/cli.py
from pathlib import Path
import typer

app = typer.Typer(help="내 자동화 도구 모음")

@app.command()
def organize(folder: Path, dry_run: bool = False):
    """다운로드 폴더를 확장자별로 정리합니다."""
    ...

@app.command()
def scrape(url: str, output: Path = Path("result.csv")):
    """게시판 글 목록을 수집해 CSV로 저장합니다."""
    ...

@app.command()
def report(month: str, send: bool = False):
    """월간 엑셀 보고서를 만들고, --send면 메일로 발송합니다."""
    ...

if __name__ == "__main__":
    app()
하위 명령 사용
uv run mytools/cli.py organize ~/Downloads --dry-run
uv run mytools/cli.py scrape https://example.com/board
uv run mytools/cli.py report 2026-07 --send

이제 명령 체계가 생깁니다. 스크립트 파일 일곱 개를 기억하는 대신, 도구 하나와 하위 명령 이름만 기억하면 됩니다.

–help가 공짜로 생긴다는 것 #

typer로 묶고 나면 도움말을 따로 작성할 필요가 없습니다.

uv run mytools/cli.py --help 출력
 내 자동화 도구 모음
 Commands:
   organize   다운로드 폴더를 확장자별로 정리합니다.
   scrape     게시판 글 목록을 수집해 CSV로 저장합니다.
   report     월간 엑셀 보고서를 만들고, --send면 메일로 발송합니다.

이 화면의 가치는 석 달 뒤의 자신이 증명합니다. 스크립트 시절에는 코드를 열어 인자를 다시 읽어야 했지만, 이제는 --help 한 번이면 무엇이 있고 어떻게 쓰는지 도구가 직접 알려줍니다. 하위 명령마다 organize --help도 따로 생성됩니다.

rich로 보기 좋게 #

typer와 함께 설치한 rich는 터미널 출력을 담당합니다. 자동화 도구에서 특히 쓸모 있는 것은 진행 바와 표입니다. organize 명령의 본문을 이렇게 바꾸겠습니다.

진행 바와 결과 표
from rich.console import Console
from rich.progress import track
from rich.table import Table

console = Console()
counts: dict[str, int] = {}
for file in track(list(folder.iterdir()), description="정리하는 중..."):
    ext = file.suffix.lstrip(".") or "기타"
    counts[ext] = counts.get(ext, 0) + 1  # 이동 로직은 1편 그대로
table = Table(title="정리 결과")
table.add_column("확장자")
table.add_column("개수", justify="right")
for ext, count in sorted(counts.items()):
    table.add_row(ext, str(count))
console.print(table)

track()으로 감싸기만 하면 반복문에 진행 바가 붙고, Table은 결과를 정렬된 표로 출력합니다. 수백 개 파일을 옮기는 동안 화면이 멈춘 것처럼 보이는 문제가 사라지고, 끝났을 때 무엇을 얼마나 처리했는지 한눈에 들어옵니다. console.print("[red]실패[/red]")처럼 색을 입히는 것도 한 줄입니다.

어디서나 부르는 명령으로 설치하기 #

지금은 프로젝트 폴더에서 uv run mytools/cli.py ...로 불러야 하지만, 진짜 도구라면 어느 디렉터리에서든 mytools 한 단어로 떠야 합니다. pyproject.toml[project.scripts]가 그 연결 고리입니다.

pyproject.toml
[project]
name = "mytools"
version = "0.1.0"
dependencies = ["typer", "rich"]

[project.scripts]
mytools = "mytools.cli:app"

[build-system]
requires = ["uv_build"]
build-backend = "uv_build"

mytools = "mytools.cli:app"은 “mytools라는 명령이 들어오면 mytools/cli.pyapp 객체를 실행하라"는 선언입니다. 이제 도구로 설치합니다.

도구로 설치
uv tool install .
mytools organize ~/Downloads

uv tool install은 격리된 전용 환경에 패키지를 설치하고 명령만 PATH에 노출합니다. 다른 프로젝트의 가상환경과 섞이지 않고, 어느 폴더에서든 호출할 수 있습니다. uv를 쓰지 않는 환경이라면 pipx install .이 같은 역할을 합니다.

팀에 나눠줄 때도 어렵지 않습니다. 사내 PyPI 서버 없이 git 저장소만 있으면 동료는 uv tool install git+https://github.com/our-team/mytools.git 한 줄로 같은 도구를 설치하고, uv tool upgrade mytools로 업데이트합니다. 스크립트 파일을 메신저로 주고받던 방식과 비교하면 버전 관리와 의존성 설치가 전부 자동으로 해결되는 셈입니다.

시리즈를 마치며 #

일곱 편을 한 줄씩 돌아보겠습니다.

  • #1 파일을 옮기는 첫 스크립트로 손으로 하던 일을 코드로 옮겼습니다
  • #2 openpyxl로 엑셀 반복 작업을 끝냈습니다
  • #3 httpx와 BeautifulSoup으로 정적 페이지를 수집했습니다
  • #4 Playwright로 자바스크립트 렌더링 페이지까지 다뤘습니다
  • #5 결과를 메일과 메신저 알림으로 받아보게 만들었습니다
  • #6 스케줄러에 올려 사람 없이 돌게 했습니다
  • #7 전부를 하나의 CLI 도구로 포장해 명령 하나로 만들었습니다

돌아보면 도구는 거들 뿐이고, 자동화의 시작은 반복을 발견하는 눈이었습니다. 매주 같은 폴더를 정리하고 있다는 것, 매달 같은 표를 복사하고 있다는 것을 알아차리는 순간이 출발점입니다. 그 반복을 하나씩 코드로 옮기다 보면 어느새 이번 편처럼 자기만의 도구 상자가 만들어집니다. 그리고 도구가 커지면 다음 질문이 따라옵니다. “고쳤는데 다른 명령이 망가지지 않았을까?“입니다. 그때 필요한 것이 테스트이고, 파이썬 테스트 시리즈가 pytest로 그 답을 다룹니다. 자동화 도구를 오래 쓸 계획이라면 다음 행선지로 추천합니다.

X