← 파이썬 강의 목록으로
🚀
고급 (Advanced)
venv · pyproject.toml · console_scripts · TestPyPI

9주차 — 패키징과 배포

가상환경, pyproject.toml 의 구조, console_scripts 로 CLI 도구 제공, 그리고 TestPyPI 업로드까지 — 자체 패키지를 만들어 배포하는 전체 흐름.

venvpyproject.tomlbuildtwine
소요 시간
2시간
난이도
📊 고급
선수 조건
🎯 고급 8주차
결과물
자체 패키지 빌드와 배포

이 강의에서 배우는 것

  • 1pyproject.toml 의 구조를 이해한다
  • 2자체 패키지를 빌드해서 설치한다
  • 3console_scripts 로 CLI 도구를 제공한다
  • 4TestPyPI 에 업로드한다

1. 가상환경

프로젝트마다 라이브러리 버전을 분리합니다.

bash
python -m venv .venv

# 활성화
source .venv/bin/activate         # macOS/Linux
.venv\Scripts\activate            # Windows

# 종료
deactivate

2. pyproject.toml

현대 파이썬 패키지의 표준 설정 파일.

toml
[project]
name = "mytool"
version = "0.1.0"
description = "내 멋진 CLI 도구"
authors = [{name = "홍길동", email = "hong@example.com"}]
requires-python = ">=3.10"
dependencies = [
    "requests>=2.28",
    "rich>=13.0",
]

[project.scripts]
mytool = "mytool.cli:main"

[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

3. 패키지 구조

text
mytool_project/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│   └── mytool/
│       ├── __init__.py
│       └── cli.py
└── tests/
    └── test_cli.py
python
# src/mytool/cli.py
def main():
    print("Hello from mytool!")

설치:

bash
pip install -e .          # 개발 모드 (수정이 즉시 반영)
mytool                    # CLI 실행

4. 빌드

bash
pip install build
python -m build           # dist/ 에 .whl, .tar.gz 생성

5. TestPyPI 업로드

bash
pip install twine
twine upload --repository testpypi dist/*

다른 환경에서 설치:

bash
pip install --index-url https://test.pypi.org/simple/ mytool

6. requirements.txt vs pyproject.toml

requirements.txtpyproject.toml
용도환경 고정 (deploy)라이브러리 메타데이터
버전정확한 핀범위
빌드안 됨
bash
pip freeze > requirements.txt   # 현재 환경 내보내기
pip install -r requirements.txt # 재현

자주 하는 실수

  1. 가상환경 활성화 안 함 — 시스템 파이썬에 설치됨
  2. __init__.py 빠뜨림 — 패키지로 인식 안 됨
  3. src/ 레이아웃 안 씀 — import 충돌 가능
  4. 버전 안 올리고 재배포 — PyPI는 같은 버전 거부

FAQ

Q1. setup.py 는? — 신규는 pyproject.toml. 레거시 호환만 setup.py.

Q2. poetry vs pip+venv? — poetry는 통합 도구. 신규는 poetry나 uv 권장.

Q3. PyPI vs TestPyPI? — PyPI는 진짜 공개. TestPyPI는 연습용.

💻 예제 (examples)

실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.

pyproject.toml패키지 설정 (전체)
CODE
[project]
name = "mytool"
version = "0.1.0"
description = "내 멋진 CLI 도구"
authors = [{name = "홍길동", email = "hong@example.com"}]
requires-python = ">=3.10"
dependencies = ["requests>=2.28"]

[project.scripts]
mytool = "mytool.cli:main"

[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
▶ 실행 결과
(설정 파일 — 직접 출력은 없음)
src/mytool/__init__.py패키지 진입
CODE
__version__ = "0.1.0"
▶ 실행 결과
(import 시 활성화)
src/mytool/cli.pyCLI 진입점
CODE
import argparse

def main():
    parser = argparse.ArgumentParser(prog="mytool")
    parser.add_argument("name", help="이름")
    parser.add_argument("--upper", action="store_true")
    args = parser.parse_args()
    msg = f"Hello, {args.name}!"
    print(msg.upper() if args.upper else msg)

if __name__ == "__main__":
    main()
▶ 실행 결과
$ mytool 홍길동
Hello, 홍길동!
$ mytool 홍길동 --upper
HELLO, 홍길동!
build_demo.sh빌드와 로컬 설치
CODE
# 가상환경
python -m venv .venv
source .venv/bin/activate

# 개발 설치
pip install -e .

# 실행
mytool 세상

# 빌드
pip install build
python -m build
▶ 실행 결과
Hello, 세상!
Successfully built mytool-0.1.0.tar.gz mytool-0.1.0-py3-none-any.whl

📝 과제 (exercises)

직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.

과제 1

최소 패키지

목표: Hello World 를 출력하는 CLI 패키지를 만든다.

요구사항
  • src/hello/__init__.py + cli.py
  • pyproject.toml + console_scripts
  • pip install -e . 후 hello 명령 실행 가능
💡 힌트

[project.scripts] hello = 'hello.cli:main'

입출력 예시
Hello, 패키지!
채점
  • · 패키지 구조
  • · CLI 등록
정답 코드 펼치기 / 접기
SOLUTION
# pyproject.toml
[project]
name = "hello"
version = "0.1.0"
[project.scripts]
hello = "hello.cli:main"
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

# src/hello/__init__.py
__version__ = "0.1.0"

# src/hello/cli.py
def main():
    print("Hello, 패키지!")

# 설치 + 실행
# pip install -e .
# hello
▶ 실행 결과
Hello, 패키지!
과제 2

argparse 가산기

목표: 두 수를 더하는 CLI 패키지를 만든다.

요구사항
  • addr 1 2 → 3 출력
  • argparse 사용
💡 힌트

type=int 지정

입출력 예시
3
채점
  • · argparse 사용
  • · 정상 출력
정답 코드 펼치기 / 접기
SOLUTION
# src/addr/cli.py
import argparse

def main():
    p = argparse.ArgumentParser(prog="addr")
    p.add_argument("a", type=int)
    p.add_argument("b", type=int)
    args = p.parse_args()
    print(args.a + args.b)

# pyproject.toml: [project.scripts] addr = "addr.cli:main"
# 실행: addr 1 2
▶ 실행 결과
3
과제 3

버전 출력 옵션

목표: --version 옵션으로 패키지 버전을 출력한다.

요구사항
  • argparse --version action
  • __init__.py 의 __version__ 사용
💡 힌트

action='version', version=f'%(prog)s {__version__}'

입출력 예시
mytool 0.1.0
채점
  • · --version 동작
  • · 버전 일치
정답 코드 펼치기 / 접기
SOLUTION
# src/mytool/__init__.py
__version__ = "0.1.0"

# src/mytool/cli.py
import argparse
from mytool import __version__

def main():
    p = argparse.ArgumentParser(prog="mytool")
    p.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
    p.parse_args()

# mytool --version
▶ 실행 결과
mytool 0.1.0
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗