시작과 uv 셋업
최신 파이썬과 uv로 첫 프로젝트를 직접 만들어 보겠습니다.
본 책의 시작 챕터입니다. 파이썬 3.14와 uv를 기준으로 첫 프로젝트를 만들고, 다음 장들에서 쓸 환경까지 여기서 준비합니다.
뒤따르는 5부의 운영 챕터들(타입체커·로깅·라이브러리 배포·CLI)도 이 pyproject.toml을 확장하는 흐름으로 이어집니다. 그래서 이 장은 1부 1장 이상으로 중요합니다.
파이썬이 달라진 이유 #
파이썬은 3.14까지 오면서 꽤 많이 달라졌습니다. 핵심만 보면 다음과 같습니다.
- 타입 힌트가 표준 사용처가 됨. 라이브러리 코드 대부분이 타입을 가짐
match-case(3.10) — 자바스크립트switch와는 동작 방식이 다른 패턴 매칭- 빌트인 제네릭 (3.9) —
list[int],dict[str, int]를 그대로 쓸 수 있음 - Union 단축 문법 (3.10) —
Optional[int]대신int | None - exception group (3.11) —
except*로 동시 발생 예외를 다룸 - free-threaded (3.13~3.14, PEP 703/779) — GIL 없는 빌드가 공식 지원 단계로
- t-string (3.14, PEP 750) — f-string과 다른, 보간이 지연되는 템플릿 리터럴
- lazy annotations (3.14, PEP 649/749) — 어노테이션이 기본적으로 지연 평가됨
도구 체인도 크게 바뀌었습니다. pip 직접 호출, requirements.txt, virtualenv 수동 활성화 같은 옛 흐름은 이제 거의 쓰지 않습니다. 대신 한 가지 도구 — uv — 가 그 역할을 대부분 맡습니다.
옛 코드를 본 책의 방식으로 옮기는 절차는 부록 A(옛 파이썬 코드를 modern 스타일로 옮기기)에서 따로 정리해 두었습니다. 필요할 때 부록 A를 참고하시면 됩니다.
uv가 무엇인가 #
uv는 Astral (Ruff를 만든 곳)에서 만든 Rust 기반 파이썬 패키지·프로젝트 매니저입니다. 한 줄 요약은 다음과 같습니다.
pip+pip-tools+pipx+poetry+pyenv+virtualenv를 하나의 바이너리로 묶은 도구.
왜 이게 표준이 되어 가는지 짧게 비교해 보면 다음과 같습니다.
| 항목 | 기존 도구 | uv |
|---|---|---|
| 속도 | 수십 초~분 | 수백 ms |
| 파이썬 인터프리터 설치 | pyenv 별도 | uv python install 한 줄 |
| 프로젝트 셋업 | python -m venv + source .venv/bin/activate + pip install | uv init + uv add |
| lock 파일 | requirements.txt 직접 / poetry.lock | uv.lock 자동 |
| 표준 호환 | 도구마다 다름 | PEP 621 pyproject.toml 그대로 |
본 책은 uv를 기본으로 사용해 인터프리터·가상환경·의존성·실행을 하나로 해결합니다.
설치 #
macOS / Linux:
curl -LsSf https://astral.sh/uv/install.sh | shHomebrew를 쓴다면:
brew install uvWindows (PowerShell):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"WSL 환경에서 작업한다면 위 macOS/Linux 명령을 그대로 쓰면 됩니다. WSL 안의 파이썬과 Windows 측 파이썬은 별개로 동작하니 한쪽만 일관되게 쓰는 것이 안전합니다.
설치 후 새 터미널을 열고 버전을 확인합니다.
uv --version
# uv 0.5.x (또는 그 이상)파이썬 3.14 설치 #
uv는 파이썬 인터프리터까지 직접 설치합니다. pyenv가 따로 필요 없습니다.
uv python install 3.14설치된 인터프리터 목록을 보려면:
uv python list --installed
# cpython-3.14.x-macos-aarch64-none ...이 인터프리터는 시스템에 영구 설치된 것이지만, PATH에 직접 노출되지는 않습니다. uv가 프로젝트별로 알아서 골라 쓰는 구조입니다. 시스템의 다른 파이썬과 섞이지 않으니 안전합니다.
첫 프로젝트 #
빈 디렉터리를 만들고 uv init으로 프로젝트를 초기화합니다.
mkdir hello-py
cd hello-py
uv init --python 3.14--python 3.14가 핵심입니다. 이 프로젝트는 3.14에 고정됩니다. 다른 프로젝트가 3.12를 써도 서로 간섭하지 않습니다. 명령을 실행하면 다음 파일들이 만들어집니다.
hello-py/
├── .python-version # 3.14
├── pyproject.toml # 프로젝트 메타데이터 + 의존성
├── README.md
└── main.py # print("Hello from hello-py!")pyproject.toml을 열어보면:
[project]
name = "hello-py"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = []PEP 621 표준 형식 그대로입니다. 이 파일이 프로젝트의 단일 정의라고 생각하면 됩니다. 의존성, 파이썬 버전, 빌드 정보, 도구 설정(ruff/mypy 등)이 전부 여기로 모입니다. requirements.txt, setup.py, setup.cfg는 이제 새로 만들 일이 거의 없습니다.
5부에서 라이브러리 배포(32장)·타입체커 설정(30장)을 다룰 때도 결국 본 pyproject.toml 한 파일을 확장하는 모양으로 진행됩니다.
첫 실행 #
main.py를 그대로 실행합니다. 다만 python main.py가 아니라 **uv run main.py**입니다.
uv run main.py
# Hello from hello-py!uv run이 하는 일:
pyproject.toml의requires-python을 만족하는 인터프리터를 찾음 (없으면 자동 다운로드)- 프로젝트의 가상환경(
.venv/)을 자동으로 만들고uv.lock과 동기화 - 그 환경 안에서 명령을 실행
가상환경을 직접 활성화(source .venv/bin/activate)할 일이 없어집니다. 모든 명령 앞에 uv run을 붙이는 흐름으로 바뀝니다. 처음에는 적응이 필요하지만, 익숙해지면 환경 활성화를 잊어서 시스템 파이썬에 패키지를 설치하는 실수를 줄일 수 있습니다.
의존성 추가 #
패키지를 추가할 때는 uv add를 씁니다.
uv add httpx이 한 줄로 다음 일들이 동시에 일어납니다.
pyproject.toml의dependencies에httpx가 추가됨uv.lock에 정확한 버전과 해시가 기록됨.venv/에 설치됨
pyproject.toml을 다시 열어보면:
[project]
# ...
dependencies = [
"httpx>=0.28.1",
]개발 시점에만 필요한 도구(테스트 러너, 린터)는 --dev 그룹으로 분리합니다.
uv add --dev pytest ruff이러면 배포 시점에는 빠지고, 개발할 때만 설치됩니다. 패키지를 빼고 싶을 땐:
uv remove httpx한 번 더 — 흐름 전체 정리 #
# 1. 도구 설치 (한 번만)
curl -LsSf https://astral.sh/uv/install.sh | sh
# 2. 프로젝트 만들기
uv init my-app --python 3.14
cd my-app
# 3. 의존성 추가
uv add httpx
uv add --dev pytest
# 4. 실행
uv run main.py
uv run pytest옛 흐름과 비교하면 필요한 명령과 설정 파일이 이만큼 줄어듭니다.
# pyenv로 파이썬 설치
pyenv install 3.14.0
pyenv local 3.14.0
# venv 만들기
python -m venv .venv
source .venv/bin/activate
# 의존성 설치
pip install httpx
pip install pytest
pip freeze > requirements.txt
# 실행
python main.py같은 일을 합니다만, lock 파일이 없어서 재현 가능성이 떨어지고, pip freeze는 직간접 의존성을 구분하지 못합니다. uv 쪽은 처음부터 lock 기반이라 다른 머신에서도 정확히 같은 환경이 재현됩니다.
스크립트 단위로 쓰기 — 더 가벼운 경우 #
작은 스크립트 한 개만 돌리고 싶을 때, 프로젝트 셋업조차 부담이라면 스크립트 자체에 의존성을 적는 모드도 있습니다.
# /// script
# requires-python = ">=3.14"
# dependencies = ["httpx"]
# ///
import httpx
resp = httpx.get("https://httpbin.org/get")
print(resp.json())실행할 때:
uv run hello.pyuv가 # /// 안에 적힌 의존성을 인식해 임시 환경을 자동 구성하고 실행합니다. 일회용 스크립트, 자동화, 작은 도구를 만들 때 유용합니다.
33장 CLI 도구 만들기에서는 이 스크립트 단위 모드와 정식 프로젝트 모드를 어떻게 골라 쓰는지 다시 살펴보겠습니다.
연습문제 #
본 챕터의 환경이 실제로 잡혔는지 확인하는 짧은 실습입니다.
uv init으로weather-py라는 이름의 새 프로젝트를 만들고,httpx를 의존성으로 추가한 뒤main.py안에서https://httpbin.org/get을 호출해 응답 JSON을 출력하세요. 실행은uv run main.py로 합니다.- 위 프로젝트에
--dev그룹으로pytest를 추가하고,uv run pytest --version이 정상 동작하는지 확인하세요.pyproject.toml을 열어dependency-groups(혹은[tool.uv.dev-dependencies]) 섹션에pytest가 들어가 있는 것을 직접 확인합니다. - 스크립트 단위 모드로
# /// script헤더를 가진whoami.py를 작성하고 (httpx의존,https://httpbin.org/ip호출), 별도 프로젝트 디렉터리 없이uv run whoami.py한 줄로 실행되는지 확인하세요.
한 줄 요약: 본 책은 파이썬 3.14와 uv를 표준으로 깐다.
uv init·uv add·uv run세 명령이 일상의 전부이며, 모든 프로젝트 설정은pyproject.toml한 파일에 모인다. 가상환경 직접 활성화는 더 이상 하지 않는다.
다음 챕터 #
다음 2장 변수, 기본 타입과 타입 힌트에서는 본 챕터에서 만든 환경 위에서 가장 기본 — 타입과 타입 힌트 — 를 다룹니다. 동적 언어인데 왜 타입을 적는가, int | None 같은 모던 문법, list[int] 빌트인 제네릭, mypy / pyright까지 정리합니다.