소개
2022년 기준으로 Python 패키지를 처음부터 끝까지 제작하고 공식 저장소에 배포하는 방법을 설명합니다. 이 과정은 단순한 코드 작성 이상의 작업을 포함하며, 지속적 통합, 자동 형식화, 버전 관리, 문서화까지 아우릅니다.
기본 구조 설정
패키지 개발의 첫 단계는 디렉터리 구조와 메타데이터를 구성하는 것입니다. 여기서는 poetry를 사용하여 프로젝트를 초기화합니다. 패키지 이름은 extendedjson으로 정했으며, PyPI에서 중복되지 않는지 반드시 확인해야 합니다.
poetry new extendedjson --name extendedjson
cd extendedjson
생성된 pyproject.toml 파일은 패키지 정보, 의존성, 빌드 설정 등을 담고 있습니다. 기본적으로 아래와 같은 내용을 포함합니다:
[tool.poetry]
name = "extendedjson"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.8"
[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
pre-commit = "^3.0"
scriv = { version = "^2.0", extras = ["toml"] }
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
가상 환경에서 모든 의존성을 설치합니다:
poetry install
버전 관리 및 사전 커밋 훅
Git 저장소를 초기화하고 기본 브랜치를 main으로 설정합니다:
git init
git branch -M main
git add .
git commit -m "chore: initial commit"
코드 품질을 유지하기 위해 pre-commit 훅을 도입합니다. 이를 통해 커밋 전 자동으로 코드 포맷팅과 정적 검사를 수행할 수 있습니다. 먼저 설정 파일 .pre-commit-config.yaml을 생성합니다:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
language_version: python3
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
args: ["--profile", "black"]
훅을 설치하고 적용합니다:
poetry run pre-commit install
poetry run pre-commit run --all-files
변경 사항을 커밋합니다:
git add .pre-commit-config.yaml pyproject.toml poetry.lock
git commit -m "feat: add pre-commit hooks for code quality"
라이선스 추가
Mit 라이선스를 선택하고 저장소 루트에 LICENSE 파일을 추가합니다. GitHub 인터페이스를 통해 직접 생성하거나 다음 명령어로 처리할 수 있습니다:
curl -sL https://raw.githubusercontent.com/licenses/mit/master/MIT.txt > LICENSE
git add LICENSE
git commit -m "docs: add MIT license"
실제 기능 구현
이번 예제에서는 확장 가능한 JSON 인코더/디코더 클래스를 구현합니다. 소스 파일 extendedjson/__init__.py에 아래 코드를 삽입합니다:
import json
from datetime import date, datetime
from typing import Any, Union
class ExtendedEncoder(json.JSONEncoder):
def default(self, obj: Any) -> Union[str, float]:
if isinstance(obj, (date, datetime)):
return obj.isoformat()
try:
return super().default(obj)
except TypeError:
return str(obj)
class ExtendedDecoder(json.JSONDecoder):
def __init__(self):
super().__init__(object_hook=self.object_hook)
@staticmethod
def object_hook(dct: dict) -> dict:
return dct
변경 로그 관리
scriv 도구를 사용해 변경 사항을 추적합니다. 먼저 새 항목을 생성합니다:
poetry run scriv create
생성된 마크다운 조각 파일에 다음과 같이 기록합니다:
### Added
- Implemented `ExtendedEncoder` and `ExtendedDecoder` for enhanced JSON serialization.
모든 조각을 하나의 CHANGELOG.md로 병합합니다:
poetry run scriv collect
변경 사항을 커밋합니다:
git add extendedjson/__init__.py changelog.d/ CHANGELOG.md
git commit -m "feat: implement custom JSON encoder/decoder"
테스트 저장소에 업로드
실제 PyPI에 올리기 전, TestPyPI를 통해 배포 테스트를 수행합니다. 먼저 저장소를 등록합니다:
poetry config repositories.testpypi https://test.pypi.org/legacy/
TestPyPI 계정에서 생성한 API 토큰을 설정합니다:
poetry config http-basic.testpypi __token__ pypi-your-test-token
빌드 후 업로드합니다:
poetry build
poetry publish -r testpypi
성공 시, https://test.pypi.org/project/extendedjson 에서 패키지를 확인할 수 있습니다.
정식 PyPI 배포
본격적인 배포를 위해 실 저장소용 토큰을 설정합니다:
poetry config pypi-token.pypi pypi-your-production-token
빌드와 동시에 배포합니다:
poetry publish --build
배포 후, 다른 환경에서 설치 테스트를 수행합니다:
pip install extendedjson
python -c "from extendedjson import ExtendedEncoder; print(ExtendedEncoder)"
GitHub 릴리스 생성
버전 태그를 추가합니다:
git tag v0.1.0
git push origin v0.1.0
GitHub에서 Releases 탭으로 이동해 해당 태그에 대해 릴리스를 생성하고, CHANGELOG.md 내용을 본문에 붙여넣습니다. 프로젝트 설명과 주요 해시태그도 추가합니다.
결론
이 과정을 통해 현대적인 Python 패키징 워크플로우를 완성했습니다. poetry 기반 빌드, 자동 형식화, 문서화, 그리고 CI 준비까지 모두 포함됩니다. 이후에는 GitHub Actions를 활용한 자동 테스트 및 배포 파이프라인 구축으로 확장할 수 있습니다.