← 블로그로 돌아가기
튜토리얼 2026년 1월 30일
Python CLI 도구 — argparse부터 배포까지
Python으로 실용적인 CLI 도구를 만들고 pip로 배포하는 전체 과정.
Python CLI argparse pip
왜 CLI인가
GUI가 필요 없는 자동화 도구는 CLI가 가장 효율적이다. PromoEngine을 만들 때 GUI를 고려했지만, 주 사용자가 개발자이고 스크립트에서 호출할 수 있어야 했다. CLI가 정답이었다.
argparse 기본
Python 표준 라이브러리의 argparse로 시작한다.
import argparse
def main():
parser = argparse.ArgumentParser(
prog='promo',
description='멀티채널 콘텐츠 자동 생성 CLI'
)
subparsers = parser.add_subparsers(dest='command')
# generate 서브커맨드
gen = subparsers.add_parser('generate', help='콘텐츠 생성')
gen.add_argument('--source', '-s', required=True, help='원본 파일 경로')
gen.add_argument('--channel', '-c', default='all', help='대상 채널 (쉼표 구분)')
gen.add_argument('--dry-run', action='store_true', help='API 호출 없이 프롬프트만 출력')
# list 서브커맨드
ls = subparsers.add_parser('list', help='생성 이력 조회')
ls.add_argument('--project', '-p', help='프로젝트 이름으로 필터')
args = parser.parse_args()
if args.command == 'generate':
handle_generate(args)
elif args.command == 'list':
handle_list(args)
else:
parser.print_help()
서브커맨드 패턴(git style)을 사용하면 기능이 늘어나도 구조가 깔끔하다.
설정 파일
API 키나 기본값을 매번 인자로 전달하는 건 번거롭다. TOML 설정 파일을 지원한다.
# ~/.promo/config.toml
[api]
provider = "anthropic"
model = "claude-sonnet-4-20250514"
[defaults]
channels = ["reddit", "twitter", "blog"]
language = "ko"
import tomllib
from pathlib import Path
def load_config() -> dict:
config_path = Path.home() / '.promo' / 'config.toml'
if config_path.exists():
with open(config_path, 'rb') as f:
return tomllib.load(f)
return {}
Python 3.11+의 tomllib을 사용한다. 외부 패키지가 필요 없다.
출력 포맷팅
CLI의 출력은 가독성이 중요하다. rich 라이브러리로 테이블과 색상을 적용한다.
from rich.console import Console
from rich.table import Table
console = Console()
def show_results(results: dict):
table = Table(title="생성 결과")
table.add_column("채널", style="cyan")
table.add_column("글자수", justify="right")
table.add_column("상태", style="green")
for channel, content in results.items():
table.add_row(channel, str(len(content)), "완료")
console.print(table)
에러 처리
CLI에서 에러 메시지는 사용자가 다음에 무엇을 해야 하는지 알려줘야 한다.
import sys
def handle_generate(args):
source = Path(args.source)
if not source.exists():
console.print(f"[red]파일을 찾을 수 없습니다: {source}[/red]")
console.print("[dim]--source에 올바른 파일 경로를 지정하세요[/dim]")
sys.exit(1)
config = load_config()
if not config.get('api', {}).get('key'):
console.print("[red]API 키가 설정되지 않았습니다[/red]")
console.print("[dim]~/.promo/config.toml에 api.key를 설정하세요[/dim]")
sys.exit(1)
패키징과 배포
pyproject.toml로 패키지를 정의하고 pip로 설치할 수 있게 만든다.
[project]
name = "promoengine"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"anthropic>=0.40.0",
"rich>=13.0",
]
[project.scripts]
promo = "promoengine.cli:main"
[project.scripts]가 핵심이다. pip install . 후 터미널에서 promo 명령을 바로 쓸 수 있다.
# 로컬 설치
pip install -e .
# PyPI 배포
pip install build twine
python -m build
twine upload dist/*
테스트
CLI 테스트는 subprocess보다 직접 함수를 호출하는 게 낫다.
def test_generate_dry_run():
args = argparse.Namespace(
source='fixtures/sample.md',
channel='reddit',
dry_run=True,
)
result = handle_generate(args)
assert 'reddit' in result
assert len(result['reddit']) > 0
--dry-run 플래그는 테스트에서도 유용하다. API 비용 없이 프롬프트 생성 로직을 검증할 수 있다.
정리
Python CLI 도구를 만드는 흐름: argparse로 인터페이스 정의 → TOML로 설정 관리 → rich로 출력 포맷팅 → pyproject.toml로 패키징. 이 패턴을 따르면 대부분의 CLI 도구를 깔끔하게 만들 수 있다.