[Python 시리즈 10편] 프로젝트 위생: virtualenv, pyproject, uv

English version

예외 처리와 로깅으로 안정성을 확보했다면, 이제 실행 환경을 통제해야 팀과 서버 어디서든 같은 결과를 얻을 수 있습니다. 이번 글에서는 "가상환경 만들기 → pyproject에 적기 → uv로 설치하기"라는 핵심 루트를 먼저 잡고, 필요하면 CI나 pre-commit까지 단계적으로 확장하는 방식을 안내합니다.

이번 글에서 새로 나오는 용어

  1. pyproject.toml: 패키지 정보와 의존성을 선언하는 표준 설정 파일
  2. uv.lock: uv가 만든 잠금 파일로 의존성 버전을 정확히 기록해 재현성을 높임
  3. 환경 변수: 실행 시점에 주입하는 키-값으로 API 키나 경로 같은 비밀 설정을 관리

핵심 개념

학습 메모

  • 소요 시간: 50~60분
  • 준비물: 가상환경·uv 설치 경험, requests 기반 스크립트
  • 학습 목표: pyproject.tomluv 명령으로 의존성과 환경 변수를 통제하기

가상환경은 프로젝트 전용 실행 공간을 만들고, pyproject.toml은 의존성을 한 문서에서 관리하게 해 줍니다. 이름이 낯설더라도 "폴더 하나 모아 두기" 정도의 개념으로 출발하세요.

코드로 따라하기

가상환경이 필요한 이유 (Core)

시스템 Python에 라이브러리를 전역 설치하면 버전 충돌이 일어나고, 서버/로컬 환경을 맞추기 어렵습니다. 가상환경(virtualenv)은 프로젝트별 독립적인 site-packages 디렉터리를 만들어 패키지 충돌을 막아 줍니다. 복잡하게 느껴지면, "프로젝트마다 전용 폴더를 만들어 거기서만 패키지를 쓰는 것"으로 이해하면 충분합니다.

uv venv .venv
source .venv/bin/activate
python --version

CI나 서버에서는 source .venv/bin/activate 대신 uv run python app.py처럼 실행할 수 있어 스크립트에서 경로 걱정을 덜 수 있습니다.

pyproject.toml 구성 (Core)

[project]
name = "mealbot"
version = "0.1.0"
description = "급식 알림 봇"
requires-python = ">=3.12"

[project.dependencies]
requests = "^2.31.0"
python-dotenv = "^1.0.1"

[tool.uv]
dev-dependencies = ["pytest", "ruff"]

pyproject.toml은 패키지 메타데이터와 의존성을 선언하는 표준 문서입니다. 버전 범위를 명시해 호환 가능한 업데이트를 허용하거나 고정할 수 있습니다. 처음에는 name, version, dependencies 세 줄만 채워도 괜찮으니 겁먹지 마세요.

📦 pyproject.toml은? 빌드 도구, 패키지 정보, 의존성을 한곳에 적는 Python 표준 설정 파일입니다. setup.py 대신 이 파일 하나만 관리하면 됩니다.

uv로 의존성 관리 (Core)

uv add requests typer
uv add --dev pytest
uv pip list

uv add는 pyproject와 잠금 파일을 동시에 갱신합니다. 처음에는 uv add <패키지명>uv run python main.py 정도만 익히면 됩니다. 그 후에 uv lock이나 uv sync --frozen처럼 재현성을 강화하는 명령을 추가로 배우면 됩니다.

환경 변수 관리 (Core → Plus)

cp .env.example .env

.env.example에는 키 이름과 형식을 남기고 비밀 값은 비워둡니다. 실행 시에는

uv run --env-file .env python scripts/send_meal.py

처럼 명시적으로 파일을 지정합니다. 이 패턴을 익히면 학교 계정, 개인 계정 같은 서로 다른 설정을 간단히 바꿔 사용할 수 있습니다.

프로젝트 구조 제안 (Optional)

mealbot/
├── pyproject.toml
├── uv.lock
├── src/mealbot/__init__.py
├── tests/
├── scripts/
└── README.md

src 레이아웃을 사용하면 패키지가 설치되지 않은 상태에서 잘못된 import를 조기에 발견할 수 있습니다. 폴더 구조가 아직 부담스럽다면, pyproject와 uv.lock만 먼저 관리하다가 필요할 때 이 구성을 옮겨 와도 됩니다.

CI와 pre-commit 연동 (Optional)

  • GitHub Actions에서 uv setup-python으로 캐시된 가상환경을 재사용
  • pre-commit 훅에 uv run ruff checkuv run pytest를 등록해 local/CI 일관성 확보

이 섹션은 협업 프로젝트를 진행할 때만 필요한 확장 단계입니다. 개인 학습이라면 제목만 기억하고 넘어가도 괜찮습니다.

왜 중요한가

프로젝트 위생이 갖춰져야 동아리 노트북과 학교 서버 어디서든 같은 실행 결과를 얻을 수 있습니다. pyproject와 uv.lock을 관리하면 버전 충돌을 예방하고, .env 파일을 통해 민감한 값을 안전하게 주고받을 수 있습니다.

실습

  • 따라 하기: 빈 폴더에서 uv init, uv add requests, uv add --dev pytest까지 진행하고 pyproject.toml 변화를 확인합니다.
  • 확장하기: .env.example를 만들고 uv run --env-file로 환경 변수를 적용한 실행 로그를 남깁니다.
  • 디버깅: uv sync --frozen 상태에서 pyproject를 바꿔 오류를 만든 뒤 잠금 파일을 갱신하거나 변경을 되돌려 문제를 해결합니다.
  • 완료 기준: pyproject, uv.lock, .env.example이 갖춰진 폴더 구조에서 명령어 하나로 동일한 환경을 재현할 수 있을 때입니다.

마무리

가상환경과 pyproject를 제대로 관리하면 패키지 충돌이나 "내 컴퓨터에서는 되는데" 같은 불신을 크게 줄일 수 있습니다. 다음 편에서는 이 환경 위에서 테스트를 자동화해 신뢰도를 한 단계 더 끌어올립니다.

💬 댓글

이 글에 대한 의견을 남겨주세요