출시·고도화 중
Python 안내서 · 4/6
프로그램이 커지면 코드를 여러 파일로 나누고, 남이 만든 라이브러리를 가져다 쓰고, 때로는 내 코드를 다른 사람에게 배포하게 됩니다. 이 장에서는 모듈과 패키지의 구조, import 문의 여러 형태, pip와 requirements 파일로 의존성을 관리하는 법, 그리고 pyproject.toml로 프로젝트를 정의하고 배포하는 기초를 다룹니다.
Python에서 .py 파일 하나가 곧 모듈입니다. 파일 이름(확장자 제외)이 모듈 이름이 됩니다. 여러 모듈을 폴더로 묶은 것이 패키지이며, 폴더 안에 __init__.py 파일을 두는 것이 일반적입니다. __init__.py는 비어 있어도 되고, 패키지를 가져올 때 실행할 초기화 코드나 바깥에 공개할 이름을 둘 수도 있습니다.
myapp/
pyproject.toml
src/
myapp/
__init__.py
cli.py
text/
__init__.py
slug.py
tests/
test_slug.py위처럼 패키지를 src/ 아래에 두는 방식을 src 레이아웃이라고 합니다. 설치하지 않은 코드가 실수로 import되는 일을 막아 주므로 라이브러리 프로젝트에서 널리 씁니다.
import에는 몇 가지 형태가 있습니다. 모듈은 처음 import될 때 한 번만 실행되고, 그 뒤로는 캐시된 객체를 다시 씁니다.
import json # 모듈 전체
import datetime as dt # 별칭
from pathlib import Path # 특정 이름만
from collections import Counter, defaultdict
data = json.loads('{"a": 1}')
today = dt.date.today()
config = Path("config") / "app.toml"패키지 안에서는 같은 패키지의 다른 모듈을 상대 경로로 가져올 수 있습니다. 점 하나(.)는 현재 패키지, 점 두 개(..)는 상위 패키지를 뜻합니다.
# src/myapp/text/slug.py
import re
def slugify(title: str) -> str:
return re.sub(r"[^a-z0-9]+", "-", title.lower()).strip("-")
# src/myapp/cli.py
from .text.slug import slugify
def main() -> None:
print(slugify("Hello Python World")) # hello-python-worldfrom module import *는 어떤 이름이 들어오는지 알기 어려우므로 피합니다. 또 두 모듈이 서로를 import하는 순환 참조가 생기면 오류가 나기 쉬우니, 공통 코드를 별도 모듈로 빼서 해결합니다.
애플리케이션을 다른 컴퓨터나 서버에서 똑같이 재현하려면 의존성 목록이 필요합니다. 가장 단순한 방법은 requirements.txt입니다.
# 현재 환경의 패키지와 정확한 버전을 기록합니다
python -m pip freeze > requirements.txt
# 다른 환경에서 같은 구성을 설치합니다
python -m pip install -r requirements.txtpip freeze는 직접 설치한 패키지뿐 아니라 그 의존성까지 모두 고정합니다. 배포용 애플리케이션에서는 이렇게 고정하는 편이 안전하고, 라이브러리는 범위를 넓게 열어 두는 편이 좋습니다. 이런 고정 작업을 더 체계적으로 해 주는 pip-tools, Poetry, uv 같은 도구도 있습니다.
pyproject.toml은 Python 프로젝트의 표준 설정 파일입니다. 프로젝트 이름과 버전, 의존성, 빌드 도구, 실행 명령을 한곳에 적습니다. 많은 개발 도구(pytest, Ruff, mypy 등)도 이 파일에서 설정을 읽습니다.
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "myapp"
version = "0.1.0"
description = "Small text utilities"
readme = "README.md"
dependencies = [
"requests",
]
[project.optional-dependencies]
dev = ["pytest", "mypy"]
[project.scripts]
myapp = "myapp.cli:main"[build-system]은 패키지를 만들 빌드 백엔드를, [project]는 메타데이터와 의존성을 정합니다. 지원하는 Python 범위는 requires-python 항목으로 밝힐 수 있습니다. [project.scripts]에 적은 항목은 설치하면 터미널 명령이 됩니다. 개발 중에는 편집 가능 모드로 설치해 두면 소스를 고칠 때마다 다시 설치할 필요가 없습니다.
python -m pip install -e ".[dev]"
myapp # hello-python-world내 패키지를 PyPI에 올리면 누구나 pip install로 설치할 수 있습니다. 순서는 다음과 같습니다.
pyproject.toml의 이름이 PyPI에서 아직 쓰이지 않았는지 확인합니다.build로 소스 배포본(sdist)과 휠(wheel)을 dist/ 폴더에 만듭니다.twine으로 먼저 연습용 TestPyPI에 올려 설치를 시험합니다.python -m pip install build twine
python -m build
python -m twine upload --repository testpypi dist/*
python -m twine upload dist/*.py 파일 하나가 모듈이고, 모듈을 묶은 폴더가 패키지입니다.import, from ... import, 별칭, 상대 import를 상황에 맞게 쓰고 import *는 피합니다.requirements.txt 등으로 고정해 재현할 수 있게 합니다.pyproject.toml에 프로젝트 정보를 모으고, build와 twine으로 배포합니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.