작은 CLI 스크립트에서 다듬어진 파이썬 유틸리티까지: 진짜로 배포해 보는 초심자 가이드
즉석에서 만든 파이썬 스크립트를 pyproject.toml, 엔트리 포인트, 배포 베스트 프랙티스를 활용해 제대로 된 설치형 커맨드라인 유틸리티로 바꾸는 방법을 정리했습니다.
작은 CLI 스크립트에서 다듬어진 파이썬 유틸리티까지: 진짜로 배포해 보는 초심자 가이드
대부분의 개발자에겐 자잘한 파이썬 스크립트들이 잔뜩 들어 있는 폴더가 하나쯤 있습니다. 실제 문제를 해결하긴 하지만, 나만 쓸 수 있는 스크립트들이죠. 로그를 지우는 도구일 수도 있고, CSV를 포매팅하는 스크립트일 수도 있고, 단순한 백업 스크립트일 수도 있습니다. 잘 돌아가긴 하지만, 조건이 붙습니다.
- 정확한 실행 명령을 기억하고 있어야 하고
- 어디에 저장했는지도 기억해야 하고
- 어느 가상환경에서 돌아가는지도 기억해야 합니다.
좋은 소식이 있습니다. 이런 허술한 스크립트도 진짜, 설치 가능한 커맨드라인 도구로 바꿀 수 있습니다. 예를 들면, 시스템 어디에서나 logcleaner처럼 바로 실행할 수 있는 형태 말이죠. 이 글에서는 “작은 스크립트”를 “다듬어진 파이썬 유틸리티”로 발전시키는 과정을 현대적인 패키징 방식과 함께 살펴봅니다.
이 글에서 다룰 내용은 다음과 같습니다.
pyproject.toml과 현대적인 패키징 도구 사용하기- CLI 인자와 옵션 추가하기
[project.scripts]를 통해 함수를 커맨드로 노출하기- 개발 중에 쓰기 좋은 editable(수정 가능) 설치 모드
- PyPI나 사설 인덱스로 배포할 준비하기
1. 간단한 파이썬 스크립트에서 시작하기
다음과 같은 로그 정리 스크립트가 있다고 가정해 봅시다.
# logcleaner.py import os from pathlib import Path LOG_DIR = Path("/var/log/myapp") for log_file in LOG_DIR.glob("*.log"): if log_file.stat().st_size == 0: log_file.unlink() print(f"Deleted empty log: {log_file}")
이 스크립트는 동작하지만, 문제점이 있습니다.
logcleaner.py가 어디 있는지 경로를 기억해야 하고- 다른 디렉터리를 지정하거나
--dry-run같은 옵션을 쓰기 어렵고 - 다른 사람에게 설치해서 쓰게 하기도 쉽지 않습니다.
우리가 목표로 하는 모습은 다음과 같습니다.
logcleaner --path /var/log/myapp --dry-run
2. 제대로 된 커맨드라인 인터페이스 추가하기
이 스크립트를 제대로 된 CLI 도구로 다루려면, 인자와 옵션을 받을 수 있는 인터페이스가 필요합니다. 표준 라이브러리의 argparse는 출발점으로 아주 좋습니다.
먼저 작은 패키지 구조로 리팩터링하고, main.py라는 엔트리 모듈을 만들어 보겠습니다.
프로젝트 구조:
logcleaner/ ├─ src/ │ └─ logcleaner/ │ ├─ __init__.py │ └─ main.py └─ pyproject.toml # 이 파일은 곧 추가합니다
src/logcleaner/main.py:
from __future__ import annotations import argparse from pathlib import Path def clean(path: str | Path, dry_run: bool = False) -> None: log_dir = Path(path) if not log_dir.exists() or not log_dir.is_dir(): raise SystemExit(f"Error: {log_dir} is not a valid directory") for log_file in log_dir.glob("*.log"): if log_file.stat().st_size == 0: if dry_run: print(f"[DRY RUN] Would delete: {log_file}") else: log_file.unlink() print(f"Deleted empty log: {log_file}") def main() -> None: parser = argparse.ArgumentParser(description="Clean up empty log files.") parser.add_argument( "path", nargs="?", default="/var/log/myapp", help="Path to the log directory (default: /var/log/myapp)", ) parser.add_argument( "--dry-run", action="store_true", help="Show what would be deleted without removing anything", ) args = parser.parse_args() clean(path=args.path, dry_run=args.dry_run) if __name__ == "__main__": # 여전히 `python -m logcleaner.main`으로 실행 가능 main()
이제 다음과 같은 이점이 생겼습니다.
- 테스트하기 쉬운 재사용 가능한 함수
clean()이 있고 - 커맨드라인 파싱을 담당하는
main()함수가 있고 - 패키징에 적합한 구조를 갖추게 되었습니다.
물론 Click, Typer, 또는 Argparse + Rich 같은 라이브러리를 사용해 더 편한 CLI를 만들 수도 있습니다. 하지만 처음에는 표준 라이브러리의 argparse만으로도 충분합니다.
3. pyproject.toml과 현대적인 패키징 사용하기
요즘 파이썬 패키징의 중심은 예전의 setup.py 대신(또는 더해) pyproject.toml 입니다. 이 파일에는 프로젝트 메타데이터, 의존성, 빌드 방법 등이 들어갑니다.
프로젝트 루트에 pyproject.toml 파일을 만듭니다.
[build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] name = "logcleaner" version = "0.1.0" description = "A tiny CLI tool to clean up empty log files." readme = "README.md" requires-python = ">=3.9" authors = [ { name = "Your Name", email = "you@example.com" } ] license = { text = "MIT" } keywords = ["logs", "cli", "utilities"] # 의존성이 있다면 여기에 적습니다 # dependencies = [ # "rich>=13.0.0", # ] [project.urls] Homepage = "https://github.com/yourname/logcleaner" [project.scripts] logcleaner = "logcleaner.main:main"
CLI 관점에서 가장 중요한 부분은 [project.scripts] 섹션입니다.
[project.scripts] logcleaner = "logcleaner.main:main"
이 설정은 패키징 시스템에게 이렇게 알려 주는 것입니다.
이 프로젝트가 설치될 때,
logcleaner라는 실행 파일(스크립트)을 만들고, 이 스크립트가logcleaner/main.py안의main함수를 호출하게 하라.
이렇게 여러 커맨드를 매핑할 수도 있습니다.
[project.scripts] logcleaner = "logcleaner.main:main" logcleaner-clean = "logcleaner.main:clean" # 함수를 직접 커맨드로 노출
이 방식이 바로 일반적인 파이썬 함수를 일급 커맨드라인 커맨드로 바꾸는 방법입니다.
4. 개발 중에는 editable(수정 가능) 모드로 설치하기
개발하는 동안에는 코드를 조금만 바꿔도 매번 패키지를 다시 설치하는 건 귀찮습니다. editable 모드는 설치된 패키지를 소스 디렉터리에 링크해서 이 문제를 해결합니다.
프로젝트 루트에서 다음을 실행합니다.
python -m pip install -e .
이 명령은 두 가지를 수행합니다.
- 현재 환경에
logcleaner패키지를 설치하고 [project.scripts]엔트리를 이용해logcleaner커맨드를 생성합니다.
이제 이렇게 실행할 수 있습니다.
logcleaner /tmp/logs --dry-run
같은 가상환경을 쓰는 한, src/logcleaner 아래 코드를 수정하면 재설치 없이 즉시 반영됩니다. 파이썬 CLI를 개발할 때 흔히 사용하는 기본 워크플로입니다.
5. 버전 관리와 패키징 베스트 프랙티스
언젠가 이 유틸리티를 PyPI나 사설 저장소에 배포하고 싶다면, 초반부터 몇 가지 간단한 베스트 프랙티스를 지켜 두는 것이 좋습니다.
버전 관리 규칙 정하기
Semantic Versioning 같은 예측 가능한 버전 체계를 사용하는 것이 좋습니다.
- 형식:
MAJOR.MINOR.PATCH→ 예:1.4.2 - 증가 규칙:
- PATCH: 버그 수정
- MINOR: 기존 사용을 깨지 않는 새로운 기능 추가
- MAJOR: 호환성 깨지는 변경
버전은 한 곳에서만 관리하는 게 좋습니다. 보통 pyproject.toml의 [project] version 항목에 둡니다.
배포용 아티팩트 빌드하기
배포 가능한 파일을 만들기 위해 build 패키지를 사용합니다.
python -m pip install build python -m build
그러면 dist/ 디렉터리에 다음 파일들이 생성됩니다.
- 소스 배포본(source distribution) (
.tar.gz) - 휠(wheel) 파일 (
.whl)
이 파일들을 업로드하거나 배포에 사용합니다.
깨끗한 환경에서 설치 테스트하기
배포 전에 항상 실제 사용자처럼 테스트해 보는 것이 좋습니다.
python -m venv .venv-test source .venv-test/bin/activate # Windows: .venv-test\Scripts\activate python -m pip install dist/logcleaner-0.1.0-py3-none-any.whl logcleaner --help
이 과정이 문제없이 통과되면 기본적인 준비는 된 것입니다.
6. 배포 준비하기 (PyPI 또는 사설 인덱스)
유틸리티가 마음에 들 정도로 완성되었다면, 다음을 준비합니다.
README.md작성- 도구가 무엇을 하는지
- 어떻게 설치하는지 (
pip install logcleaner) - 기본 사용법과 예제
- 선택적으로
LICENSE파일 추가 (MIT, Apache-2.0 등) pyproject.toml안의 메타데이터(작성자, URL, 설명 등)가 정확한지 확인
공개 PyPI에 배포하려면 다음을 실행합니다.
python -m pip install twine python -m twine upload dist/*
사내 Artifactory, Nexus, 단순 index 서버 같은 사설 저장소를 사용한다면 조직에서 업로드 URL을 제공할 것입니다. 그런 경우 설치는 다음과 같이 합니다.
python -m pip install --index-url https://your-private-index/simple logcleaner
지금까지의 과정을 현대적인 표준에 맞춰 진행했다면, 어디에든 호스팅하기 쉬운 패키지가 되어 있을 것입니다.
7. 검증된 CLI 라이브러리와 패턴 활용하기
argparse만으로도 충분하지만, 실제 프로덕션용 CLI는 보통 다음과 같은 라이브러리를 많이 사용합니다.
- Click – 데코레이터 기반, 서브커맨드 구조에 특히 강함
- Typer – 타입 힌트 우선, 매우 사용하기 편하며 Click 기반
- Rich-Argparse – 보기 좋은 help/에러 메시지 제공
이 라이브러리들은 공통된 설계 패턴을 잘 구현하고 있습니다.
- 비즈니스 로직과 CLI 파싱의 명확한 분리
- 일관된
--help출력 - 서브커맨드 지원 (
git commit,git push같은 형태)
처음에는 아주 작은 argparse 기반 스크립트로 시작했다가, 나중에 Click이나 Typer로 옮겨 가도 패키징 모델은 그대로 유지됩니다. 여전히 [project.scripts]에서 하나의 엔트리 함수를 가리키면 됩니다.
8. 사용자 경험을 다듬기
“내 컴퓨터에서는 잘 돌아간다” 수준에서 “완성된 도구 같다” 수준으로 가려면, 조금만 손을 더 보면 됩니다.
- 예제가 포함된 친절한
--help출력 SystemExit와 사람 눈에 잘 들어오는 메시지를 활용한 명확한 에러 메시지--verbose,--quiet같은 로그/출력 모드- 핵심 기능에 대한 간단한 테스트
- 버전별 변경 사항을 정리한 간단한 CHANGELOG
완벽할 필요는 없지만, 이런 작은 다듬기가 도구의 신뢰도와 공유 가능성을 크게 높여 줍니다.
마무리: 진짜로 뭔가를 배포해 보기
거창한 프로젝트가 아니어도, 실제 세상에서 쓰이는 파이썬 패키징을 익히기엔 충분합니다. 50줄짜리 스크립트 하나면 충분합니다.
지금까지 살펴본 것처럼:
- 코드를 패키지 구조로 정리하고
- 인자와 옵션을 받는 간단한 CLI 인터페이스를 추가하고
pyproject.toml과[project.scripts]로 엔트리 포인트를 정의하고- 개발하는 동안에는 editable 모드로 설치해 빠르게 반복하고
- 기본적인 버전 관리와 패키징 베스트 프랙티스를 따르다 보면,
…한 번 쓰고 버리는 스크립트가 다듬어진, 설치 가능한 유틸리티로 거듭나게 됩니다. 공유할 수도 있고, 재사용할 수도 있고, 심지어 배포까지 할 수 있는 형태로요.
오늘 지금 쓰는 “tools” 폴더에서 스크립트 하나만 골라 보세요. 패키지로 감싸고, 커맨드를 추가하고, 작게라도 한 번 배포해 보세요. 아주 작은 것이라도, 진짜로 뭔가를 배포해 본 경험이 큰 자산이 됩니다.