라떼군 이야기
Python Poetry 프로젝트를 UV 패키지 매니저로 마이그레이션하는 방법
Problem
Python 프로젝트에서 기존에 사용하던 Poetry 패키지 매니저를 더 빠르고 효율적인 UV로 전환하려 할 때 발생하는 문제입니다. Poetry는 독자적인 pyproject.toml 구조([tool.poetry])와 빌드 시스템(poetry-core)을 사용하기 때문에, 단순히 도구를 바꾼다고 해서 바로 호환되지 않습니다. 개발자는 기존의 의존성 설정과 개발 환경 설정을 어떻게 UV가 인식할 수 있는 표준 포맷으로 변환해야 하는지, 그리고 build-system은 어떻게 변경해야 하는지 막막함을 느낄 수 있습니다.
Background
Poetry는 오랫동안 Python 생태계에서 의존성 관리와 패키징을 위한 강력한 도구로 사용되어 왔지만, 설정 파일이 PEP 621 표준([project] 테이블)보다는 자체적인 [tool.poetry] 섹션에 의존합니다. 반면 UV는 Rust로 작성된 고성능 패키지 매니저로, Python 표준(PEP 621)을 준수하며 매우 빠른 속도를 자랑합니다. 마이그레이션을 위해서는 Poetry 전용 설정을 표준 Python 프로젝트 메타데이터 형식으로 변환하고, 의존성 그룹 관리 방식을 UV 스타일에 맞게 변경해야 합니다.
Solution
Poetry에서 UV로 마이그레이션하는 가장 효과적인 방법은 자동화 도구를 사용하는 것입니다. 또한, 기존 의존성 버전을 엄격하게 유지해야 하는 경우를 위한 전략도 함께 소개합니다.
방법 1: 자동화 도구 사용 (권장)
가장 간단하고 강력한 방법은 migrate-to-uv 도구를 사용하는 것입니다. 이 도구는 pyproject.toml을 파싱하여 UV 호환 형식으로 재작성하고 poetry.lock을 제거합니다.
# uvx를 사용하여 별도 설치 없이 마이그레이션 도구 실행
uvx migrate-to-uv
# 마이그레이션 후 새로운 lock 파일 생성 및 동기화
uv lock
uv sync
이 명령어를 실행하면 [tool.poetry] 섹션이 표준 [project] 섹션으로 변환되고, 빌드 시스템도 hatchling 등으로 변경됩니다.
방법 2: PDM을 이용한 수동 변환
만약 자동화 도구가 실패하거나 세밀한 제어가 필요하다면, 또 다른 패키지 매니저인 PDM의 import 기능을 활용할 수 있습니다.
- 설정 가져오기:
uvx pdm import pyproject.toml - 파일 정리:
pyproject.toml파일에서[tool.poetry...]로 시작하는 모든 섹션을 제거합니다. - 의존성 그룹 수정:
[tool.pdm.dev-dependencies]섹션을 UV가 사용하는[dependency-groups]로 변경합니다.
방법 3: 의존성 버전 고정 (안전한 마이그레이션)
단순히 마이그레이션만 수행할 경우, poetry.lock에 있던 정확한 패키지 버전들이 무시되고 최신 버전으로 업데이트되어 호환성 문제가 발생할 수 있습니다. 이를 방지하기 위해 마이그레이션 전 현재 버전을 pyproject.toml에 하드코딩하는 스크립트를 사용할 수 있습니다.
아래 Python 스크립트는 poetry.lock을 읽어 현재 설치된 정확한 버전을 pyproject.toml에 명시합니다.
from pathlib import Path
import toml
# 파일 경로 설정
lockfile_path = Path("poetry.lock")
pyproject_path = Path("pyproject.toml")
# poetry.lock 파일 로드
with lockfile_path.open("r") as lockfile:
lock_data = toml.load(lockfile)
# pyproject.toml 파일 로드
with pyproject_path.open("r") as pyproject_file:
pyproject_data = toml.load(pyproject_file)
# lock 파일에서 패키지 이름과 버전을 매핑
locked_versions = {package["name"]: package["version"] for package in lock_data["package"]}
# 의존성 업데이트 함수
def update_dependencies(dependencies):
for dep_name, dep_constraint in dependencies.items():
if dep_name in locked_versions:
# 기존 제약조건 대신 lock된 버전으로 덮어쓰기
dependencies[dep_name] = locked_versions[dep_name]
print(f"{dep_name} 버전을 {locked_versions[dep_name]}로 고정했습니다.")
else:
print(f"{dep_name}을(를) lock 파일에서 찾을 수 없습니다.")
# 메인 의존성과 개발 의존성 업데이트 실행
# 주의: pyproject.toml 구조에 따라 경로가 다를 수 있음
if "dependencies" in pyproject_data["tool"]["poetry"]:
update_dependencies(pyproject_data["tool"]["poetry"]["dependencies"])
if "group" in pyproject_data["tool"]["poetry"] and "dev" in pyproject_data["tool"]["poetry"]["group"]:
update_dependencies(pyproject_data["tool"]["poetry"]["group"]["dev"]["dependencies"])
# 수정된 내용을 파일에 저장
with pyproject_path.open("w") as pyproject_file:
toml.dump(pyproject_data, pyproject_file)
print("pyproject.toml이 특정 버전으로 업데이트되었습니다.")
워크플로우:
- 위 스크립트 실행 (버전 고정)
uvx migrate-to-uv실행 (포맷 변환)uv lock실행 (UV lock 파일 생성)pyproject.toml에서 고정된 버전을 다시 유연한 범위(예:^1.0.0)로 수동 복구 (선택 사항)
Deep Dive
Build System 변경
질문자가 언급한 [build-system] 섹션의 poetry-core는 UV 환경에서 필수적이지 않습니다. UV는 표준을 준수하므로 hatchling, flit, setuptools 등 다양한 백엔드를 지원합니다. migrate-to-uv를 사용하면 보통 hatchling으로 설정됩니다.
표준화의 이점
Poetry에서 UV로의 이동은 단순히 도구의 변경을 넘어, 프로젝트 설정을 PEP 621 표준으로 맞추는 과정입니다. 이는 향후 다른 도구로의 전환이나 CI/CD 파이프라인 통합 시 호환성을 높여줍니다. UV는 uv pip, uv venv 등 기존 pip/venv 워크플로우를 대체할 수 있는 하위 명령어들도 제공하므로 점진적인 도입도 가능합니다.
Conclusion
Poetry에서 UV로의 마이그레이션은 uvx migrate-to-uv 명령어를 통해 손쉽게 자동화할 수 있습니다. 하지만 운영 환경의 안정성을 위해 기존 poetry.lock의 버전을 유지해야 한다면, 마이그레이션 전 버전을 고정하는 작업이 선행되어야 합니다. UV로의 전환은 빌드 속도 향상뿐만 아니라 Python 프로젝트 표준 준수라는 장점을 제공합니다.