Python 패키지 빌드: 개발부터 PyPI까지 가이드
코드부터 PyPI 배포까지 Python 패키징 마스터하기
Python 패키징은 현대적인 도구와 표준 덕분에 코드를 배포하는 것이 지금까지보다 훨씬 쉬워졌습니다.
이 가이드는 전문적인 Python 패키지를 생성하고 PyPI에 게시하는 방법을 안내합니다. Python에 익숙하지 않거나 빠른 참조가 필요한 경우, Python 체크시트를 확인하여 Python 기초를 빠르게 익히세요.

왜 Python 코드를 패키징해야 하나요?
Python 프로젝트를 패키징하면 다음과 같은 이점이 있습니다:
- 재사용성: 여러 프로젝트에서 코드를 복사-붙여넣기 없이 공유할 수 있습니다.
- 배포:
pip install명령어로 다른 사람이 쉽게 설치할 수 있도록 합니다. - 의존성 관리: 의존성을 명확하게 지정하고 관리할 수 있습니다.
- 버전 관리: 릴리스를 추적하고 뒤로가기 호환성을 유지할 수 있습니다.
- 전문 표준: Python 커뮤니티의 최고 실천 방식을 따릅니다.
- 문서화: 문서화와 테스트를 위한 구조를 장려합니다.
현대적인 Python 패키지 구조
잘 정리된 패키지는 다음과 같은 구조를 따릅니다:
my-package/
├── src/
│ └── mypackage/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
├── tests/
│ ├── __init__.py
│ └── test_core.py
├── docs/
│ └── index.md
├── .github/
│ └── workflows/
│ └── publish.yml
├── pyproject.toml
├── README.md
├── LICENSE
└── .gitignore
src-layout은 평평한 레이아웃보다 선호되는 이유는 다음과 같습니다:
- 개발 중에 설치되지 않은 코드를 실수로 임포트하는 것을 방지합니다.
- 설치를 강제하여 테스트를 더 신뢰할 수 있게 만듭니다.
- 소스 코드와 다른 프로젝트 파일을 명확하게 분리합니다.
패키지 내부 구조를 정리할 때는 깔끔한 아키텍처 원칙을 적용하여 코드의 유지보수성과 테스트 가능성을 높이는 것이 좋습니다. 우리의 Python 설계 패턴을 활용한 깔끔한 아키텍처 가이드는 SOLID 원칙, 의존성 주입, 계층형 아키텍처 패턴에 대해 자세히 설명합니다.
pyproject.toml 파일: 현대적인 설정
pyproject.toml (PEP 518, 621)은 Python 프로젝트 설정의 현대 표준이며, 구형 setup.py를 대체합니다:
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mypackage"
version = "0.1.0"
description = "A fantastic Python package"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{name = "Your Name", email = "you@example.com"}
]
keywords = ["example", "tutorial", "package"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"requests>=2.28.0",
"click>=8.1.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.4.0",
"black>=23.0.0",
"flake8>=6.0.0",
"mypy>=1.5.0",
]
docs = [
"sphinx>=7.0.0",
"sphinx-rtd-theme>=1.3.0",
]
[project.urls]
Homepage = "https://github.com/yourusername/mypackage"
Documentation = "https://mypackage.readthedocs.io"
Repository = "https://github.com/yourusername/mypackage"
Issues = "https://github.com/yourusername/mypackage/issues"
[project.scripts]
mypackage-cli = "mypackage.cli:main"
[tool.setuptools.packages.find]
where = ["src"]
주요 설정 섹션
- build-system: 빌드 백엔드 (setuptools, hatchling, poetry-core, flit-core)를 지정합니다.
- project: 핵심 메타데이터 및 의존성을 포함합니다.
- project.optional-dependencies: 추가 기능 의존성 (dev, docs, 테스트)을 포함합니다.
- project.scripts: 명령줄 진입점을 포함합니다.
- tool.*: 도구별 설정 (black, pytest, mypy 등)을 포함합니다.
빌드 백엔드 선택
Setuptools (표준 선택)
가장 널리 사용되고 호환성이 좋은 옵션입니다:
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"
장점: 보편적인 호환성, 다양한 기능, 큰 생태계 단점: 더 복잡한 설정, 새로운 대안보다 느리다
Hatchling (현대적이고 빠름)
가볍고 성능이 좋은 현대 백엔드:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
장점: 빠른 빌드, 간단한 설정, 좋은 기본값 단점: setuptools보다 플러그인 수가 적다
Poetry (모든 것을 포함)
의존성 및 환경 관리가 완전한 솔루션:
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "mypackage"
version = "0.1.0"
description = "A fantastic Python package"
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.28.0"
장점: 의존성 해결, 락 파일, 통합 워크플로우 단점: 다른 워크플로우, 더 큰 도구 표면
Flit (미니멀리즘)
간단한 패키지에 적합한 간단한 도구:
[build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"
장점: 매우 간단, 최소한의 설정 단점: 복잡한 패키지에 기능이 제한적
패키지 빌드
빌드 도구 설치
# 현대적인 빌드 도구 설치
pip install build
# 또는 Poetry 사용
pip install poetry
# 또는 Hatchling 사용
pip install hatch
분배 파일 빌드
build 패키지 사용 (모든 백엔드와 호환):
# 이전 빌드 정리
rm -rf dist/ build/ *.egg-info
# 소스 분배 및 휠 빌드
python -m build
# 이는 생성합니다:
# dist/mypackage-0.1.0.tar.gz (소스 분배)
# dist/mypackage-0.1.0-py3-none-any.whl (휠)
Poetry 사용:
poetry build
Hatch 사용:
hatch build
분배 형식 이해
소스 분배 (sdist) - .tar.gz
- 소스 코드 및 빌드 지시사항 포함
- 사용자의 pip가 설치 시 빌드
- 테스트, 문서 및 기타 개발 파일 포함
휠 - .whl
- 사전 빌드된 바이너리 분배
- 빠른 설치 (빌드 단계 없음)
- 플랫폼별 또는 순수 Python
- 분배에 권장되는 형식
PyPI에 게시
방법 1: Twine으로 수동 업로드 (전통적)
# Twine 설치
pip install twine
# 업로드 전 패키지 확인
twine check dist/*
# 먼저 TestPyPI에 업로드 (권장)
twine upload --repository testpypi dist/*
# TestPyPI에서 설치 테스트
pip install --index-url https://test.pypi.org/simple/ mypackage
# 생산 PyPI에 업로드
twine upload dist/*
~/.pypirc에 자격 증명 구성:
[distutils]
index-servers =
pypi
testpypi
[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmc...
[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-AgENdGVzdC5weXBpLm9yZw...
방법 2: 신뢰할 수 있는 게시 (CI/CD에 권장)
신뢰할 수 있는 게시는 CI/CD 플랫폼에서 토큰을 저장하지 않고 OpenID Connect (OIDC)를 사용하여 인증합니다.
PyPI에서 설정:
- PyPI 프로젝트 설정으로 이동
- “게시” 섹션으로 이동
- 새로운 “대기 중인 게시자” 추가
- 설정:
- 소유자: GitHub 사용자명/조직
- 저장소: 저장소 이름
- 워크플로우:
publish.yml - 환경:
release(선택 사항이지만 권장됨)
GitHub Actions 워크플로우 (.github/workflows/publish.yml):
name: Publish to PyPI
on:
release:
types: [published]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Python 설정
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: 빌드 의존성 설치
run: |
python -m pip install --upgrade pip
pip install build
- name: 패키지 빌드
run: python -m build
- name: 분배 패키지 저장
uses: actions/upload-artifact@v3
with:
name: python-package-distributions
path: dist/
publish:
needs: build
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
steps:
- name: 분배 다운로드
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: PyPI에 게시
uses: pypa/gh-action-pypi-publish@release/v1
장점:
- 관리하거나 보호해야 할 API 토큰 없음
- OIDC를 통한 자동 인증
- 환경 보호 규칙을 통한 보안 강화
- 모든 게시에 대한 감사 추적
최고의 실천 방법
1. 버전 관리
세마틱 버전 관리 (SemVer) 사용: MAJOR.MINOR.PATCH
# 버전 증가 도구 설치
pip install bump2version
# 버전 증가
bump2version patch # 0.1.0 -> 0.1.1
bump2version minor # 0.1.1 -> 0.2.0
bump2version major # 0.2.0 -> 1.0.0
.bumpversion.cfg에 설정:
[bumpversion]
current_version = 0.1.0
commit = True
tag = True
[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"
[bumpversion:file:src/mypackage/__init__.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"
2. 필수 파일 포함
README.md: 명확한 프로젝트 설명, 설치, 사용 예시 LICENSE: 적절한 라이선스 선택 (MIT, Apache 2.0, GPL 등) CHANGELOG.md: 버전 간 변경 사항 기록 .gitignore: 빌드 아티팩트, 캐시, 가상 환경 제외
3. 포괄적인 테스트
# 테스트 의존성 설치
pip install pytest pytest-cov
# 테스트 실행 및 커버리지 확인
pytest --cov=mypackage tests/
# 커버리지 보고서 생성
pytest --cov=mypackage --cov-report=html tests/
pyproject.toml에 설정:
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "--strict-markers --cov=mypackage"
4. 코드 품질 도구
# 포맷팅
black src/ tests/
# 린팅
flake8 src/ tests/
# 타입 검사
mypy src/
# 임포트 정렬
isort src/ tests/
pre-commit 훅에 통합 (.pre-commit-config.yaml):
repos:
- repo: https://github.com/psf/black
rev: 23.9.1
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 6.1.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
hooks:
- id: mypy
5. 문서화
Sphinx를 사용하여 문서화:
# Sphinx 설치
pip install sphinx sphinx-rtd-theme
# 문서 초기화
cd docs
sphinx-quickstart
# 문서 빌드
make html
또는 MkDocs를 사용하여 간단한 마크다운 기반 문서:
pip install mkdocs mkdocs-material
mkdocs new .
mkdocs serve
6. 지속적 통합
다양한 플랫폼 및 환경에서 Python 패키지를 테스트하는 것은 신뢰성에 매우 중요합니다. Python이 다양한 배포 시나리오에서 어떻게 수행되는지에 대한 인사이트를 얻고자 한다면, 우리의 AWS Lambda 성능: JavaScript vs Python vs Golang 비교 글을 참조하세요.
완전한 CI/CD 워크플로우 (.github/workflows/ci.yml):
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- name: Python 설정
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: 의존성 설치
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: 테스트 실행
run: pytest --cov=mypackage --cov-report=xml
- name: 커버리지 업로드
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Python 설정
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: 의존성 설치
run: |
pip install black flake8 mypy
- name: Black 포맷팅 확인
run: black --check src/ tests/
- name: Flake8
run: flake8 src/ tests/
- name: MyPy
run: mypy src/
일반적인 문제와 해결 방법
문제 1: 설치 후 임포트 오류
문제: 패키지가 설치되었지만 임포트가 실패
해결: __init__.py 파일과 올바른 pyproject.toml 설정을 통해 패키지 구조를 보장하세요:
[tool.setuptools.packages.find]
where = ["src"]
include = ["mypackage*"]
문제 2: 누락된 의존성
문제: 패키지가 설치되었지만 런타임 시 의존성이 누락되어 실패
해결: pyproject.toml에 모든 런타임 의존성을 명시하세요:
[project]
dependencies = [
"requests>=2.28.0",
"click>=8.1.0",
]
문제 3: 버전 충돌
문제: 개발 중에는 작동하지만 프로덕션에서는 실패
해결: 가상 환경을 사용하고 최소 버전을 지정하세요:
# 고립된 환경 생성
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 개발용으로 설치
pip install -e ".[dev]"
문제 4: 대규모 패키지 크기
문제: 패키지가 다운로드/설치에 너무 오랜 시간이 걸림
해결: MANIFEST.in을 사용하여 불필요한 파일을 제외하세요:
include README.md
include LICENSE
include pyproject.toml
recursive-include src *.py
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
recursive-exclude tests *
recursive-exclude docs *
문제 5: 플랫폼별 문제
문제: 한 OS에서는 작동하지만 다른 OS에서는 실패
해결: CI/CD 매트릭스 빌드를 사용하여 여러 플랫폼에서 테스트 (위 CI 예시 참조)
게시 확인 목록
패키지를 PyPI에 게시하기 전에 확인하세요:
- 모든 Python 버전에서 테스트가 통과
- 코드가 포맷팅 및 린팅됨
- README.md가 예제를 포함한 완전함
- LICENSE 파일 포함
- 버전 번호 업데이트
- CHANGELOG.md 업데이트
- 의존성이 올바르게 지정됨
- 패키지가 오류 없이 빌드됨 (
python -m build) - TestPyPI에서 패키지 테스트
- 문서가 최신 상태
- Git 저장소에 버전 태그가 있음
- GitHub 릴리스가 생성됨 (신뢰할 수 있는 게시를 위해)
고급 주제
진입점 및 CLI 도구
명령줄 인터페이스를 생성:
[project.scripts]
mypackage = "mypackage.cli:main"
src/mypackage/cli.py에서 구현:
import click
@click.command()
@click.option('--name', default='World', help='Greet this name')
def main(name):
"""Simple CLI tool example"""
click.echo(f'Hello, {name}!')
if __name__ == '__main__':
main()
외부 API와 상호작용하는 Python 패키지를 생성하는 실제 사례를 보고 싶다면, 우리의 Ollama와 Python 통합 가이드를 참조하세요. 이 가이드는 REST API와 공식 라이브러리 통합을 사용하여 Python 클라이언트를 생성하는 방법을 보여줍니다.
플러그인 시스템
플러그인 탐색을 활성화:
[project.entry-points."mypackage.plugins"]
plugin_a = "mypackage_plugin_a:PluginA"
C 확장
성능이 중요한 코드에:
[tool.setuptools.ext-modules]
name = "mypackage._speedups"
sources = ["src/mypackage/_speedups.c"]
네임스페이스 패키지
공통 네임스페이스 아래에 분산된 패키지에:
[tool.setuptools.packages.find]
where = ["src"]
include = ["company.*"]
[tool.setuptools.package-data]
"company.mypackage" = ["py.typed"]
유용한 도구 및 자원
패키지 개발 도구
- cookiecutter: Python 패키지 프로젝트 템플릿
- tox: Python 버전 간 테스트 자동화
- nox: 유연한 테스트 자동화 (tox 대안)
- pre-commit: 코드 품질을 위한 Git 훅 프레임워크
- commitizen: 커밋 메시지 및 버전 관리 표준화
문서 플랫폼
- Read the Docs: 무료 문서 호스팅
- GitHub Pages: MkDocs 또는 Sphinx 문서 호스팅
- pdoc: 간단한 API 문서 생성기
패키지 저장소
- PyPI: 공식 Python 패키지 인덱스 (pypi.org)
- TestPyPI: 테스트 환경 (test.pypi.org)
- Anaconda.org: Conda 패키지 배포
- GitHub Packages: 프라이빗 패키지 호스팅
모니터링 및 분석
- PyPI Stats: 패키지 다운로드 통계
- Libraries.io: 의존성 모니터링 및 경고
- Snyk: 보안 취약점 스캔
- Dependabot: 자동 의존성 업데이트
결론
현대적인 Python 패키징은 pyproject.toml과 강력한 빌드 백엔드, 신뢰할 수 있는 게시를 통한 보안을 통해 개발자 친화적으로 진화했습니다. 이 가이드를 따라, 사용자에게 효과적으로 서비스할 수 있는 전문적이고 유지보수가 쉬운 Python 패키지를 생성할 수 있습니다.
핵심 요약:
- pyproject.toml을 모든 새 프로젝트에 사용하세요.
- 요구 사항에 맞는 적절한 빌드 백엔드를 선택하세요.
- 자동화된 테스트 및 CI/CD를 구현하세요.
- 보안이고 토큰 없이 배포할 수 있는 신뢰할 수 있는 게시를 사용하세요.
- 세마틱 버전 관리를 따르고 변경 로그를 유지하세요.
- 종합적인 문서 및 예제를 제공하세요.
Python 패키징 생태계는 더 나은 도구, 표준 및 보안 기능으로 지속적으로 개선되고 있습니다. PEP (Python Enhancement Proposals)와 커뮤니티 최고 실천 방식을 업데이트하여 패키지를 현대적이고 유지보수가 쉬운 상태로 유지하세요.
유용한 링크
- Python 패키징 사용자 가이드
- PEP 518 - 빌드 시스템 요구 사항 지정
- PEP 621 - pyproject.toml에 프로젝트 메타데이터 저장
- PyPI 신뢰할 수 있는 게시 문서
- Setuptools 문서
- Poetry 문서
- Hatch 문서
- TestPyPI
- Python Packaging Authority GitHub
- 세마틱 버전 관리
- 라이선스 선택