1 개요
Python 프로젝트에서 외부 패키지(특히 사내/비공개 저장소)를 의존성으로 추가하거나, 여러 빌드 시스템을 혼용할 때 pyproject.toml과 빌드 표준(PEP 517/518)의 이해가 필수적이다. 현대 Python 패키징은 다양한 빌드 도구가 공존할 수 있도록 설계되어 있다.
1.1 이 글이 다루는 핵심 질문
- Poetry로 관리되는 프로젝트에서 setuptools 기반 외부 패키지를 설치할 수 있는가?
- pyproject.toml은 Poetry 전용인가, 아니면 범용 표준인가?
- 배포 이름과 import 이름이 달라도 되는가?
결론부터 말하면 모두 가능하다. 그 이유가 PEP 517/518 표준이다.
2 빌드 시스템의 일반 원칙
2.1 소프트웨어 공학에서 “빌드”란 무엇인가
소프트웨어 공학에서 빌드(build)는 소스코드를 실행 가능한 혹은 배포 가능한 형태로 변환하는 과정 전체를 가리킨다.
| 언어/플랫폼 | 빌드 도구 | 결과물 |
|---|---|---|
| Java | Maven, Gradle | .jar, .war |
| C/C++ | Make, CMake | 실행 파일(binary) |
| JavaScript | Webpack, Vite, esbuild | bundled .js |
| Python | setuptools, Poetry, Hatch | wheel(.whl), sdist(.tar.gz) |
| Rust | Cargo | 실행 파일 또는 .rlib |
공통적으로 빌드 시스템은 다음을 담당한다:
- 의존성 선언 — 어떤 외부 라이브러리가 필요한지 명시
- 변환/컴파일 — 소스를 배포 가능한 형태로 변환
- 메타데이터 포함 — 패키지 이름, 버전, 진입점 등을 결과물에 넣음
- 검증 — 빌드 환경이 올바른지 체크
2.2 Python 빌드의 역사 — PEP 517/518 이전과 이후
Python은 오랫동안 setup.py 스크립트 하나로 빌드를 처리했다. 이 방식은 다음 문제를 낳았다.
| 문제 | 설명 |
|---|---|
| 빌드 의존성 미표준 | setup.py 실행 전에 어떤 패키지가 필요한지 알 방법이 없었다 |
| 인터페이스 비표준 | pip이 setuptools에만 강하게 의존 → 다른 빌드 도구 등장 불가 |
| 보안 위험 | setup.py를 직접 실행하면 임의 코드가 실행됨 |
PEP 518 (2016) 은 pyproject.toml의 [build-system] 섹션을 도입해 “이 패키지를 빌드하려면 어떤 도구가 필요한가”를 사전에 선언할 수 있게 했다.
PEP 517 (2017) 은 빌드 프론트엔드(pip, poetry)와 빌드 백엔드(setuptools, poetry-core, flit 등) 사이의 표준 인터페이스를 정의했다.
[빌드 프론트엔드] → [표준 인터페이스(PEP 517)] → [빌드 백엔드]
pip / poetry setuptools / poetry-core / flit
이 분리 덕분에 빌드 도구가 바뀌어도 pip/poetry 같은 설치 도구는 동일한 방식으로 패키지를 설치할 수 있다.
2.3 sdist vs wheel — 배포 형태 비교
Python 패키지는 두 가지 형태로 배포된다.
| 형식 | 확장자 | 설명 | 설치 시 빌드 필요 |
|---|---|---|---|
| sdist (소스 배포) | .tar.gz |
소스코드 압축본, setup.py/pyproject.toml 포함 | 필요 (사용자 환경에서 빌드) |
| wheel (빌드 배포) | .whl |
이미 빌드된 결과물, 압축 해제만 하면 됨 | 불필요 |
결론: wheel이 있으면 사용자 환경에 빌드 도구가 없어도 설치된다. 순수 Python 패키지는 py3-none-any.whl로 빌드와 무관하게 모든 환경에서 동작한다.
Poetry로 관리되는 agent 프로젝트에 setuptools 기반의 data_standardization 패키지를 의존성으로 추가하는 과정에서, 두 빌드 시스템의 pyproject.toml 구조 차이와 Poetry 2.x 혼용 패턴을 이해해야 했다. 아래부터는 이 실전 사례를 기반으로 설명한다.
3 PEP 517/518 이란?
PEP 517/518은 Python 패키지의 빌드 시스템과 설치 과정을 표준화해, setuptools, poetry, flit 등 다양한 빌드 도구가 pyproject.toml 하나로 통합 관리될 수 있게 만든 핵심 규격이다. 이 표준 덕분에 빌드 도구가 달라도 pip/poetry 등 설치 도구가 자동으로 인식해 설치를 진행할 수 있다.
- PEP 517과 PEP 518은 Python 패키지 빌드와 설치 방식을 표준화한 규격
- 어떤 빌드 도구(예: setuptools, poetry, flit 등)와 의존 패키지를 사용할지 명시한다.
- 어떤 빌드 도구(예: setuptools, poetry, flit 등)와 의존 패키지를 사용할지 명시한다.
- PEP 517: pip, poetry 같은 설치 도구와 빌드 백엔드(예: setuptools, poetry-core) 사이의 인터페이스를 표준화했다.
- 설치 도구가 pyproject.toml을 읽고, 지정된 빌드 백엔드를 호출해 wheel을 만들고 설치할 수 있게 해준다고 한다 (자세히는 모름).
- PEP 518: pyproject.toml의 [build-system] 섹션을 표준화했다.
- 빌드 시스템과 의존 패키지를 명시하는 표준 형식을 정의했다.
즉, pyproject.toml에 빌드 시스템을 명확히 적으면, pip/poetry가 어떤 빌드 도구든 자동으로 인식해서 설치를 진행할 수 있게 해주는 표준이다. 이 덕분에 Poetry와 setuptools 등 서로 다른 빌드 시스템이 공존할 수 있다.
4 빌드 시스템이란?
Python 패키지는 소스코드만으로 배포되는 것이 아니라, 빌드 시스템(예: setuptools, poetry-core, flit 등)이 소스코드를 wheel 등 설치 가능한 형태로 변환해준다. pyproject.toml의 [build-system] 섹션에 어떤 빌드 도구를 쓸지 명시한다.
Python 패키지를 설치할 때 내부적으로 일어나는 일은 다음과 같다.
pip install some-package
→ 소스코드 다운로드
→ pyproject.toml 읽기
→ 빌드 백엔드 호출 (wheel 생성)
→ wheel 설치
- 빌드 백엔드는 소스코드를 설치 가능한 형태(wheel)로 변환하는 도구
- 대표적인 빌드 백엔드:
| 빌드 백엔드 | 설정 방식 | 특징 |
|---|---|---|
setuptools |
pyproject.toml + setup.cfg |
전통적, 가장 범용 |
poetry-core |
pyproject.toml |
Poetry 전용, 간결한 문법 |
hatchling |
pyproject.toml |
신흥 표준 |
flit-core |
pyproject.toml |
단순한 순수 Python 패키지용 |
4.1 .toml
pyproject.toml은 현대 Python 패키지의 표준 설정 파일로, 빌드 도구와 무관하게 모든 패키지에서 사용된다. 빌드 시스템마다 필요한 설정을 [build-system] 섹션에 추가해 활용한다.
- pyproject.toml은 Poetry뿐만 아니라 setuptools, flit, hatch 등 다양한 빌드 시스템에서 공통적으로 사용하는 Python 패키지 표준 설정 파일이다.
- 빌드 시스템마다 [build-system] 섹션에 자신이 필요한 설정을 추가해서 활용한다.
- 즉, pyproject.toml은 “이 프로젝트를 어떤 빌드 도구로, 어떤 방식으로 빌드할지”를 명시하는 공식 표준 파일이며, Poetry뿐 아니라 모든 현대 Python 패키지에서 사용된다.
- 빌드 도구마다 [build-system] 섹션에 자신이 필요한 설정을 추가해서 활용한다.
4.2 Wheel이란?
wheel(.whl)은 Python 패키지의 표준 배포 포맷으로, 소스코드를 미리 빌드해 빠르게 설치할 수 있게 해준다. pip install 명령으로 바로 설치 가능하며, 빌드 환경이 필요 없다.
- wheel은 .whl 확장자를 가진 아카이브(압축 파일)로, Python 패키지의 표준 배포 파일 포맷(.whl)이다.
- wheel 파일은 소스코드를 미리 빌드해서 압축한 것으로, pip가 빠르게 설치할 수 있도록 해준다.
- 빌드가 끝난 상태(컴파일된 바이트코드, 메타데이터 등 포함)
- pip install 명령으로 바로 설치 가능
- 소스 배포(sdist)보다 설치가 훨씬 빠르고, 빌드 환경이 필요 없다
- 빌드가 끝난 상태(컴파일된 바이트코드, 메타데이터 등 포함)
- 즉, wheel은 Python 패키지의 “설치 가능한 완제품”이라고 생각하면 된다.
5 PEP 517/518이 해결한 것
PEP 517/518 표준 덕분에, 설치 도구(pip, poetry 등)와 빌드 도구(setuptools, poetry-core 등)가 분리되어 있어도 pyproject.toml만 있으면 서로 호환이 가능하다. 즉, 설치하는 쪽과 빌드하는 쪽이 달라도 문제없이 동작한다.
5.1 PEP 517 (2017)
pip, poetry 등 설치 도구(installer) 와 빌드 백엔드 사이의 인터페이스를 표준화했다.
[pip / poetry install]
↓
PEP 517 표준 인터페이스 호출
↓
[setuptools / poetry-core / hatchling ...]
각자 알아서 wheel 생성
↓
설치 완료
즉, 설치하는 쪽(poetry)과 빌드하는 쪽(setuptools)이 달라도 된다.
5.2 PEP 518 (2016)
pyproject.toml의 [build-system] 섹션을 표준화했다.
6 실제 프로젝트 구성 비교
실제로는 하나의 프로젝트(예: agent)는 Poetry, 다른 프로젝트(예: data_standardization)는 setuptools 등 서로 다른 빌드 시스템을 쓸 수 있다. pyproject.toml의 [build-system] 설정만 맞으면, 서로 다른 빌드 도구 간에도 의존성 추가와 설치가 가능하다.
이번 프로젝트에서의 두 패키지:
6.1 agent repo (설치하는 쪽) — Poetry 2.x 혼용 형식
# /home/azureuser/projects/kmkim/agent/pyproject.toml
[project] # PEP 621 표준 메타데이터
name = "agent"
version = "0.1.0"
requires-python = "^3.11"
dependencies = [
"streamlit (>=1.52.1,<2.0.0)",
"langchain (>=1.1.0,<2.0.0)",
# ...
]
[tool.poetry] # Poetry 전용 설정
packages = [{include = "agent", from = "src"}]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"6.2 data_standardization (설치되는 쪽) — setuptools
# /home/azureuser/projects/data_standardization/pyproject.toml
[project] # PEP 621 표준 메타데이터
name = "sg-data-standardization"
version = "1.0.0"
requires-python = ">=3.11"
dependencies = [
"pandas>=1.5.0",
"openpyxl>=3.0.0",
"tqdm>=4.64.0",
]
[tool.setuptools]
packages = ["data_standardization"]
[tool.setuptools.package-dir]
"data_standardization" = "source" # source/ 디렉토리를 data_standardization 이름으로 노출
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"7 Poetry 2.x의 [project] + [tool.poetry] 혼용
Poetry 2.x부터는 Python 표준 메타데이터([project], PEP 621)를 우선 사용하고, Poetry 전용 설정만 [tool.poetry]에 남기는 방식이 권장된다. 이로 인해 설정 위치가 혼동될 수 있으니 주의가 필요하다.
Poetry 1.x에서는 모든 설정이 [tool.poetry] 아래에 있었다.
# Poetry 1.x 방식
[tool.poetry]
name = "agent"
version = "0.1.0"
dependencies = { python = "^3.11", requests = "^2.28" }Poetry 2.x부터는 Python 표준인 [project] (PEP 621)를 우선으로 사용하고, Poetry 전용 기능만 [tool.poetry]에 남겨둔다.
# Poetry 2.x 방식
[project] # 표준 메타데이터
name = "agent"
dependencies = ["requests>=2.28"]
[tool.poetry] # Poetry 전용 (패키지 경로 등)
packages = [{include = "agent", from = "src"}]이 혼용 방식은 git dependency를 추가할 때 위치 선택에서 혼란을 일으킨다. 이 문제는 다음 블로그에서 자세히 다룬다.
8 패키지 이름 vs import 이름 분리
Python 패키지는 배포용 이름(설치 시 사용)과 import용 이름(코드에서 import할 때 사용)이 다를 수 있다. 보통 배포 이름에는 조직 prefix를 붙여 충돌을 방지하고, import 이름은 간결하게 유지한다. 이는 [tool.setuptools.package-dir] 등으로 매핑할 수 있다.
[project]
name = "sg-data-standardization" # pip/poetry에서 설치할 때 이름
[tool.setuptools.package-dir]
"data_standardization" = "source" # Python에서 import할 때 이름이렇게 설계한 이유: - 배포 이름(sg-data-standardization)에 조직 prefix를 붙여 충돌 방지 * 패키지를 PyPI나 사내 저장소에 배포할 때 다른 프로젝트나 외부 패키지와 이름이 겹치는 것을 방지 - import 이름(data_standardization)은 간결하게 유지
9 정리
다양한 빌드 시스템이 pyproject.toml로 통합 관리되고, PEP 517/518 표준 덕분에 Poetry와 setuptools 등 서로 다른 빌드 도구가 공존할 수 있다. 패키지 이름과 import 이름을 분리해 관리하는 것도 실무에서 자주 쓰이는 패턴이다.
| 항목 | 내용 |
|---|---|
| Poetry와 setuptools는 공존 가능한가? | 가능 — by PEP 517 표준 인터페이스 |
| poetry가 setuptools 패키지를 설치하는 방법 | pyproject.toml의 [build-system] 을 읽어 setuptools에게 빌드 위임 |
| Poetry 2.x에서 달라진 점 | [project] (PEP 621) 표준 메타데이터 우선 사용 |
| 패키지 이름 ≠ import 이름 | [tool.setuptools.package-dir]로 매핑 가능 |
다음 블로그에서는 실제로 Poetry git dependency URL을 구성하는 방법과 위치 문제를 다룬다.
10 관련 포스트
- Poetry 프로젝트에서 외부 Git 패키지 통합 — 실전 사례: archive 상태 repo, pyproject.toml 없음, 빌드 시스템 불일치 해결
- Poetry git dependency — URL, 위치, PEP 508 — git URL 구성과 Poetry 2.x 위치 문제 실전 패턴