프로젝트와 릴리즈
프로젝트(Project)
패키지 이름 단위의 논리적 묶음
공식 정의 : 하나의 Python 패키지 이름(name)에 해당하는 전체 집합
쉽게 말해, PyPI에서 보이는 “패키지” 자체 및 그와 관련된 정보를 프로젝트라고
1
2
3
4
# 예시
requests
django
numpy
릴리즈(Release)
특정 프로젝트의 배포들 중 특정 버전을 가리킨다.
1
2
3
# 예시
requests 2.31.0
requests 2.30.0
릴리즈 파일(Release file)
PyPI 서버에 실제로 업로드되거나 캐시되는 파일 그 자체
하나의 릴리즈(version)에 속한 배포 파일(distribution artifact)
릴리즈 파일 종류
예시
wheel
requests-2.31.0-py3-none-any.whl
sdist
requests-2.31.0.tar.gz
프로젝트, 릴리즈, 릴리즈 파일 관계 정리
1
2
3
4
프로젝트 ( project)
└── 릴리즈 ( release / version)
├── 릴리즈 파일 ( wheel)
└── 릴리즈 파일 ( sdist)
1
2
3
4
project: requests
└── release: 2.31.0
├── requests-2.31.0-py3-none-any.whl
└── requests-2.31.0.tar.gz
패키지 만들기
패키지
Python 코드들을 하나의 이름으로 묶은 것
그러면서 import 가능하고 배포 가능한 단위
하나 이상의 Python 모듈(.py 파일)을 포함할 것
__init__.py 파일을 가지는 것이 권장됨
고유한 import 이름을 가질 것
빌드시 wheel / sdist 로 묶일 수 있을 것
1
2
3
foo.py → 모듈
foo/ → 패키지
foo/__init__.py → 패키지 성립 조건
패키지를 만들 때의 핵심
디렉터리 구조는 src/ 레이아웃을 따른다.
표준은 PEP517 / PEP518 / PEP621 을 따른다.
패키지를 빌드할 때는 python -m build 명령어를 사용한다.
PEP
의미
PEP 517
빌드 시스템 표준
PEP 518
pyproject.toml 사용
PEP 621
프로젝트 메타데이터 표준
패키지 디렉터리 구성
1
2
3
4
5
6
7
8
9
my-package/
├── pyproject.toml # ← 필수 (패키지 정의)
├── README.md # ← 권장
├── src/
│ └── my_package/ # ← 실제 패키지 (import 이름)
│ ├── __init__.py # ← 필수
│ ├── { core} .py # ← 코드
│ └── { utils} .py
└── tests/ # ← 선택
my-package : 프로젝트 이름 (배포용)
my_package : import 이름 (Python 규칙)
src/ : 권장 구조 (setuptools / pip 공식 권장)
파일 및 폴더
명칭
설명
my-package
프로젝트 루트 디렉터리
- 하나의 Python 패키지 프로젝트 단위 - 빌드, 배포, 테스트 관련 모든 파일의 기준 위치
pyproject.toml
프로젝트 설정 파일 프로젝트 메타데이터
- 패키지 정의 파일 - 빌드 시스템, 프로젝트 메타데이터가 정의됨 - PEP 517/518/621 표준을 따름
README.md
프로젝트 설명 문서
- 패키지에 대한 설명 문서 - PyPI 나 devpi에 프로젝트 설명으로 표시됨
src/
소스 루트 디렉터리
- 소스 코드를 분리하기 위한 src-layout 표준 구조 - import 경로 오염 방지를 위해 사용됨
src/my_package/
패키지 디렉터리
- Import 대상이 되는 실제 Python 패키지 - 추후 import my_package 로 import 된다.
src/my_package/__init__.py
패키지 초기화 파일
- 해당 디렉터리를 Python 패키지로 인식케 함 - 공개 API를 정의하는 용도로도 사용됨
src/my_package/core.py
모듈(module)
- 패키지의 핵심 로직을 담는 Python 모듈 파일 - 꼭 core.py 일 필요는 없으며, 알맞게 명명한다.
src/my_package/utils.py
보조 모듈
- 공통 함수, 유틸리티 로직을 담는 Python 모듈 - 꼭 utils.py 일 필요는 없으며, 알맞게 명명한다.
tests/
테스트 디렉터리
- 단위 테스트 코드를 두는 위치 - 빌드 결과물에는 포함되지 않음
pyproject.toml 형식
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[build-system]
requires = [ "setuptools" , "wheel" ]
build-backend = "setuptools.build_meta"
[project]
name = "my-package"
version = "0.1.0"
description = "example package"
readme = "README.md"
requires-python = ">=3.8"
license = { text = "MIT" }
keywords = [ "dummy" , "my-package" , "my" , "package" ]
authors = [ { name = "{author_name}" , email = "{author_email}" } ]
dependencies = [
"requests>=2.28,<2.30" ,
"pydantic>=2.0,<2.3"
]
[project.urls]
Documentation = "https://github.com/{some_id}/my-package#readme"
Issues = "https://github.com/{some_id}/my-package/issues"
Source = "https://github.com/{some_id}/my-package"
[tool.setuptools]
package-dir = { "" = "src" }
[tool.setuptools.packages.find]
where = ["src"]
build-system : 어떤 도구로 패키지를 빌드할지 정의
항목
명칭
설명
build-system
빌드 시스템 섹션
- 어떤 도구로 패키지를 빌드할지 정의하는 영역
requires
빌드 의존성
- 패키지를 빌드하기 위해 사전에 설치되어야 하는 패키지 목록
build-backend
빌드 백엔드
- 실제 빌드를 수행하는 Python 모듈
project : PEP 621 표준에 따른 프로젝트 정보 정의 영역
항목
명칭
설명
project
프로젝트 메타데이터 섹션
- PEP 621 표준에 따른 프로젝트 정보 정의 영역
name
프로젝트 이름
- 프로젝트의 배포용 이름 - PyPI나 devpi에서 보이는 프로젝트 식별자
version
버전
- 릴리즈(release)를 구분하는 버전 값
description
요약 설명
- 패키지에 대한 짧은 설명
readme
상세 설명 파일
- 프로젝트 설명으로 사용할 문서 파일
requires-python
Python 버전 조건
- 이 패키지가 지원하는 Python 버전 범위
license
라이선스
- 프로젝트 라이선스 정보
keywords
키워드
- 검색용 키워드 목록
authors
작성자 정보
- 패키지 작성자 정보 (이름과 이메일)
dependencies
런타임 의존성
- 이 패키지 설치 시 함께 설치될 패키지
항목
명칭
설명
Documentation
문서 사이트
-
Issues
소스 코드 저장소
-
Source
이슈 트래커
-
tools.setuptools : setuptools 설정 영역
항목
명칭
설명
tool.setuptools
setuptools 설정 섹션
- setuptools 전용 확장 설정 영역
package-dir
패키지 루트 매핑
- 패키지 소스가 src/ 아래에 있음을 명시
tools.setuptools.packages.find : 패키지 자동 탐색 규칙
항목
명칭
설명
tool.setuptools.packages.find
패키지 자동 탐색 설정
- setuptools가 패키지를 자동으로 찾는 규칙을 정의
where
탐색 위치
- Python 패키지를 찾을 디렉터리 경로 - 여러 개를 지정할 수도 있다.
requires 와 dependencies 의 차이
requires : 빌드 의존성. 이 패키지를 빌드하기 위해 필요한 도구들. 즉 빌드 도구.
dependencies : 런타임 의존성. 이 라이브러리를 설치하는 사람에게 필요한 의존성.
(참고) build-backend 의 종류
build-backend : PEP 517 빌드 백엔드 모듈 경로
빌드 백엔드
build-backend 값
설명
setuptools
setuptools.build_meta
가장 표준 , 레거시 호환 최강
setuptools (no setup.py)
setuptools.build_meta:__legacy__
setup.py 기반 레거시 프로젝트
flit
flit_core.buildapi
단순 라이브러리용
poetry
poetry.core.masonry.api
poetry 전용
hatchling
hatchling.build
modern / 빠름
meson-python
mesonpy
C/C++ 확장
scikit-build-core
scikit_build_core.build
CMake 기반
maturin
maturin
Rust 확장
패키지 만들기 실습
프로젝트 만들기
(1) 디렉터리 생성
hatch는 프로젝트 디렉터리를 쉽게 만드는 데 도움을 주는 라이브러리이다.
1
2
3
4
5
# 설치
pip install hatch
# 프로젝트 생성
hatch new my-package
디렉터리를 생성한 뒤, src 내의 디렉터리 명은 calc 로 변경하였다.
1
2
3
4
5
6
7
8
9
10
my-package
├── src
│ └── calc
│ ├── __about__.py
│ └── __init__.py
├── tests
│ └── __init__.py
├── LICENSE.txt
├── README.md
└── pyproject.toml
(2) pyproject.toml 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[build-system]
requires = [ "setuptools" , "wheel" ]
build-backend = "setuptools.build_meta"
[project]
name = "my-package"
version = "0.1.0"
description = "example package"
readme = "README.md"
requires-python = ">=3.8"
license = "MIT"
keywords = [ "dummy" , "my-package" , "my" , "package" ]
authors = [ { name = "tester" , email = "tester@tester.com" } ]
dependencies = [
"pydantic>=2.0,<2.8"
]
[project.urls]
Documentation = "http://dummy-site/tester/my-package#readme"
Issues = "http://dummy-site/tester/my-package/issues"
Source = "http://dummy-site/tester/my-package"
[tool.setuptools]
package-dir = { "" = "src" }
[tool.setuptools.packages.find]
where = ["src"]
(3) 코드 작성
사칙연산을 하는 간단한 코드를 만들었다.
1
2
3
4
5
6
# src/calc/models.py
from pydantic import BaseModel
class Operands ( BaseModel ):
a : int | float
b : int | float
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# src/calc/operations.py
from .models import Operands
def add ( data : Operands ) -> int | float :
return data . a + data . b
def sub ( data : Operands ) -> int | float :
return data . a - data . b
def mul ( data : Operands ) -> int | float :
return data . a * data . b
def div ( data : Operands ) -> int | float :
if data . b == 0 :
raise ZeroDivisionError ( "0으로 나눌 수 없습니다." )
return data . a / data . b
1
2
3
4
5
6
# src/calc/__init__.py
# 외부 사용자가 import 해서 사용하면 될 인터페이스를 명시한 것
from .models import Operands
from .operations import add , sub , mul , div
__all__ = [ "Operands" , "add" , "sub" , "mul" , "div" ] # from calc import * 시 노출되는 목록
1
2
3
4
5
6
7
8
9
10
11
12
my-package
├── src
│ └── calc
│ ├── __about__.py
│ ├── __init__.py
│ ├── models.py
│ └── operations.py
├── tests
│ └── __init__.py
├── LICENSE.txt
├── README.md
└── pyproject.toml
릴리즈 파일 생성
릴리즈 파일 생성을 위해 build 라이브러리 설치
1
2
3
4
5
6
7
8
9
10
11
# 출력
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
- setuptools
- wheel
* Getting build dependencies for sdist...
...
* Building sdist...
* Building wheel...
...
Successfully built my_package-0.1.0.tar.gz and my_package-0.1.0-py3-none-any.whl
1
2
3
4
5
6
# 결과
my-package
├─ ( 다른 폴더 생략)
└─ dist/
├── my_package-0.1.0-py3-none-any.whl
└── my_package-0.1.0.tar.gz
빌드 결과물을 설치하고 이용해보기
(1) 격리된 환경 준비
빌드 결과물을 설치하고 이용해보기 위해 격리된 개발환경을 준비한다.
패키지를 빌드한 디렉터리와는 별개의 디렉터리에서, uv 로 프로젝트를 생성한다.
1
uv init my-package-test
1
2
3
4
5
6
# 결과
my-package-test
├─ .python-version
├─ main.py
├─ pyproject.toml
└─ README.md
(2) 패키지 설치
lib 디렉터리를 만들고, 만들어둔 calc 패키지의 빌드파일을 복사해넣는다.
(참고) PyPI 서버나 devpi 서버에 업로드된 패키지는 pip install 등으로 바로 설치할 수 있다.
이 내용은 devpi-server 관련 포스팅을 확인하기 바란다. 링크
1
2
3
4
5
6
7
my-package-test
├─ lib
│ └─ my_package-0.1.0-py3-none-any.whl
├─ .python-version
├─ main.py
├─ pyproject.toml
└─ README.md
1
uv add lib/my_package-0.1.0-py3-none-any.whl
1
2
3
4
5
6
7
8
9
10
# 결과
...
Resolved 7 packages in 255ms
Installed 6 packages in 15ms
+ annotated-types== 0.7.0
+ my-package== 0.1.0 ( from file:...lib/my_package-0.1.0-py3-none-any.whl)
+ pydantic == 2.12.5
+ pydantic-core== 2.41.5
+ typing-extensions== 4.15.0
+ typing-inspection== 0.4.2
(3) 패키지 import 해서 사용
1
2
3
4
5
6
7
8
9
10
11
12
from calc import Operands , add , sub , mul , div
def main ():
a = 10
b = 2
print ( add ( Operands ( a = a , b = b )))
print ( sub ( Operands ( a = a , b = b )))
print ( mul ( Operands ( a = a , b = b )))
print ( div ( Operands ( a = a , b = b )))
if __name__ == "__main__" :
main ()
(참고) 자동완성
앞서서 패키지를 만들 때 __init__.py 에 인터페이스를 정의해뒀다.
그 덕분에 패키지를 import 해 사용할 때, 아래처럼 자동완성이 가능해진다.
Reference
PEP 517 – A build-system independent format for source trees
PEP 518 – Specifying Minimum Build System Requirements for Python Projects
PEP 621 – Storing project metadata in pyproject.toml
https://github.com/pypa/hatch
Tags:
build ,
from ,
hatch ,
import ,
library ,
package ,
python ,
wheel ,
whl ,
라이브러리 ,
만들기 ,
파이썬 ,
패키지
Categories:
Python
Updated: 2026-01-d
Comments