🔥 uv 완전 가이드 (2): 프로젝트 관리와 도구 활용
강의 목차

첫 번째 글에서 uv로 Python을 설치하고 스크립트를 실행하는 법을 다뤘다. 거기까지는 솔직히 "빠른 pip" 느낌이었다. 그런데 프로젝트 관리 기능을 써보는 순간 생각이 바뀌었다. 이건 pip가 아니라 Poetry의 대체재에 가깝다. 그것도 훨씬 빠른.
이 글에서는 uv로 프로젝트를 만들고, 의존성을 관리하고, CLI 도구를 활용하는 방법을 다룬다.
프로젝트 만들기
uv init
새 프로젝트를 만드는 건 한 줄이다:
uv init hello-world
cd hello-worlduv init hello-world
cd hello-world이미 있는 디렉토리에서 초기화할 수도 있다:
mkdir hello-world && cd hello-world
uv initmkdir hello-world && cd hello-world
uv inituv가 생성하는 파일 구조는 이렇다:
hello-world/
├── .gitignore
├── .python-version
├── README.md
├── main.py
└── pyproject.tomlhello-world/
├── .gitignore
├── .python-version
├── README.md
├── main.py
└── pyproject.tomlmain.py에는 기본 "Hello world" 코드가 들어있다. 바로 실행해 보자:
uv run main.py
# Hello from hello-world!uv run main.py
# Hello from hello-world!프로젝트 구조
처음 uv run, uv sync, 또는 uv lock을 실행하면 가상환경과 락파일이 자동으로 생긴다:
hello-world/
├── .venv/ # 가상환경 (자동 관리)
│ ├── bin/
│ ├── lib/
│ └── pyvenv.cfg
├── .python-version # 기본 Python 버전
├── README.md
├── main.py
├── pyproject.toml # 프로젝트 메타데이터 + 의존성
└── uv.lock # 크로스 플랫폼 락파일hello-world/
├── .venv/ # 가상환경 (자동 관리)
│ ├── bin/
│ ├── lib/
│ └── pyvenv.cfg
├── .python-version # 기본 Python 버전
├── README.md
├── main.py
├── pyproject.toml # 프로젝트 메타데이터 + 의존성
└── uv.lock # 크로스 플랫폼 락파일각 파일의 역할을 짚어보자.
pyproject.toml — 프로젝트의 핵심 설정 파일이다:
[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [][project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []의존성 선언, 프로젝트 메타데이터, uv 설정까지 이 파일 하나에 담긴다. requirements.txt와 setup.py를 합쳐놓은 셈이다.
.python-version — 프로젝트의 기본 Python 버전이 적혀있다. uv가 가상환경을 만들 때 이 버전을 참조한다.
uv.lock — 크로스 플랫폼 락파일이다. requirements.txt와 달리 하나의 파일로 모든 플랫폼(Windows, macOS, Linux)의 의존성을 커버한다. 사람이 읽을 수 있는 TOML 형식이지만, 직접 수정하면 안 된다. 버전 관리에 반드시 포함시켜야 한다.
.venv/ — 프로젝트 전용 가상환경이다. uv가 알아서 관리하니까 직접 건드릴 일은 거의 없다.
의존성 관리
패키지 추가
uv add requestsuv add requests이 한 줄이 하는 일이 많다:
pyproject.toml의dependencies에requests추가- 의존성 해석 후
uv.lock업데이트 .venv에 패키지 설치
Poetry의 poetry add와 비슷한데, 체감 속도가 다르다.
버전 제약을 걸거나 Git 소스에서 가져올 수도 있다:
# 정확한 버전
uv add 'requests==2.31.0'
# Git 저장소에서
uv add git+https://github.com/psf/requests# 정확한 버전
uv add 'requests==2.31.0'
# Git 저장소에서
uv add git+https://github.com/psf/requests기존 requirements.txt에서 한 번에 가져오는 것도 가능하다:
uv add -r requirements.txt -c constraints.txtuv add -r requirements.txt -c constraints.txt기존 프로젝트를 마이그레이션할 때 이 명령이 정말 유용하다.
패키지 제거
uv remove requestsuv remove requests패키지 업그레이드
특정 패키지만 최신으로 올리고 싶을 때:
uv lock --upgrade-package requestsuv lock --upgrade-package requests나머지 의존성은 그대로 두고, 지정한 패키지만 호환 가능한 최신 버전으로 업데이트한다.
개발 의존성
테스트나 린트처럼 프로덕션에는 필요 없는 패키지는 개발 의존성으로 분리한다:
uv add --dev pytest ruff mypyuv add --dev pytest ruff mypypyproject.toml에 이렇게 들어간다:
[dependency-groups]
dev = ["pytest", "ruff", "mypy"][dependency-groups]
dev = ["pytest", "ruff", "mypy"]임의의 그룹 이름도 쓸 수 있다:
uv add --group docs sphinx mkdocsuv add --group docs sphinx mkdocs프로젝트 실행
uv run
uv run은 프로젝트 환경에서 명령을 실행한다:
uv add flask
uv run -- flask run -p 3000uv add flask
uv run -- flask run -p 3000uv run을 호출할 때마다 uv는 이런 검증을 자동으로 한다:
pyproject.toml이 변경되었으면uv.lock업데이트uv.lock과.venv가 일치하는지 확인하고 동기화
수동으로 pip install을 까먹는 실수가 원천 차단된다. "왜 이 패키지가 없지?"라는 상황이 안 생긴다.
스크립트도 동일하게 실행 가능하다:
# example.py
import flask
print("hello world")# example.py
import flask
print("hello world")uv run example.pyuv run example.pyuv sync + 직접 활성화
uv run 대신 가상환경을 직접 활성화해서 쓸 수도 있다:
uv sync
source .venv/bin/activate # macOS / Linux
flask run -p 3000uv sync
source .venv/bin/activate # macOS / Linux
flask run -p 3000에디터나 IDE에서 가상환경을 인식시켜야 할 때 유용하다.
버전 확인
uv version
# hello-world 0.1.0
uv version --short
# 0.1.0
uv version --output-format json
# {"package_name": "hello-world", "version": "0.1.0", "commit_info": null}uv version
# hello-world 0.1.0
uv version --short
# 0.1.0
uv version --output-format json
# {"package_name": "hello-world", "version": "0.1.0", "commit_info": null}도구(Tools) 활용
uv의 또 다른 킬러 기능이다. Python 생태계에는 Ruff, Black, mypy 같은 CLI 도구가 많은데, uv로 이걸 아주 깔끔하게 관리할 수 있다.
uvx: 임시 실행
uvx는 uv tool run의 축약형이다. 도구를 설치하지 않고 일회성으로 실행한다:
uvx ruff check
uvx black --check .
uvx pycowsay hello from uvuvx ruff check
uvx black --check .
uvx pycowsay hello from uvnpx를 써본 적 있다면 비슷한 개념이다. 격리 환경을 만들고, 도구를 설치하고, 실행한다. 환경은 캐시에 보관되어 다음 실행 시 재사용된다.
프로젝트의 코드를 검사해야 하는 도구(pytest, mypy 등)는 uvx 대신 uv run을 써야 한다. uvx는 격리 환경에서 실행되기 때문에 프로젝트 패키지에 접근할 수 없다.
패키지 이름과 명령이 다를 때
패키지 이름과 실행 명령이 다른 경우가 있다. --from으로 패키지를 지정하면 된다:
# httpie 패키지의 http 명령 실행
uvx --from httpie http# httpie 패키지의 http 명령 실행
uvx --from httpie http버전 지정 실행
# 정확한 버전
uvx ruff@0.3.0 check
# 최신 버전
uvx ruff@latest check
# 범위 지정
uvx --from 'ruff>0.2.0,<0.3.0' ruff check# 정확한 버전
uvx ruff@0.3.0 check
# 최신 버전
uvx ruff@latest check
# 범위 지정
uvx --from 'ruff>0.2.0,<0.3.0' ruff check플러그인과 함께 실행
추가 의존성이 필요한 경우 --with를 쓴다:
# mkdocs를 mkdocs-material 테마와 함께 실행
uvx --with mkdocs-material mkdocs serve# mkdocs를 mkdocs-material 테마와 함께 실행
uvx --with mkdocs-material mkdocs serveextras 포함 실행
uvx --from 'mypy[faster-cache,reports]' mypy --xml-report mypy_reportuvx --from 'mypy[faster-cache,reports]' mypy --xml-report mypy_reportGit 소스에서 실행
릴리스 전 버전이나 특정 커밋의 도구를 실행할 수도 있다:
# 최신 main 브랜치
uvx --from git+https://github.com/httpie/cli@master httpie
# 특정 태그
uvx --from git+https://github.com/httpie/cli@v3.1.0 httpie
# 특정 커밋
uvx --from git+https://github.com/httpie/cli@2843b87 httpie# 최신 main 브랜치
uvx --from git+https://github.com/httpie/cli@master httpie
# 특정 태그
uvx --from git+https://github.com/httpie/cli@v3.1.0 httpie
# 특정 커밋
uvx --from git+https://github.com/httpie/cli@2843b87 httpieuv tool install: 영구 설치
자주 쓰는 도구는 영구 설치하는 게 낫다:
uv tool install ruffuv tool install ruff설치 후에는 uv 없이 바로 실행할 수 있다:
ruff --versionruff --version실행 파일은 PATH에 있는 bin 디렉토리에 들어간다. PATH에 없다면 경고가 뜨는데, uv tool update-shell로 해결할 수 있다.
중요한 점은 — 도구를 설치해도 해당 패키지가 현재 환경에 들어가지는 않는다는 것이다:
uv tool install ruff
python -c "import ruff" # 이건 실패한다!uv tool install ruff
python -c "import ruff" # 이건 실패한다!각 도구가 자체 격리 환경에서 실행되니까 의존성 충돌이 원천적으로 차단된다.
여러 실행 파일을 제공하는 패키지도 자동으로 전부 설치된다:
uv tool install httpie
# http, https, httpie 명령 모두 사용 가능uv tool install httpie
# http, https, httpie 명령 모두 사용 가능추가 의존성과 함께 설치하려면:
uv tool install mkdocs --with mkdocs-materialuv tool install mkdocs --with mkdocs-material도구 업그레이드
# 특정 도구
uv tool upgrade ruff
# 전체 도구
uv tool upgrade --all# 특정 도구
uv tool upgrade ruff
# 전체 도구
uv tool upgrade --all업그레이드는 설치 시 지정한 버전 제약을 존중한다. uv tool install ruff >=0.3,<0.4로 설치했으면, 업그레이드해도 0.4 미만으로 유지된다. 제약을 바꾸려면 다시 uv tool install을 실행하면 된다.
Python 버전 지정
도구 실행이나 설치 시 Python 버전을 지정할 수 있다:
uvx --python 3.10 ruff
uv tool install --python 3.10 ruffuvx --python 3.10 ruff
uv tool install --python 3.10 ruff다음 글에서는
이번 글에서는 uv로 프로젝트를 만들고 관리하는 법과, uvx/uv tool로 CLI 도구를 다루는 법을 다뤘다. pyproject.toml 하나로 의존성 관리가 깔끔해지고, 크로스 플랫폼 락파일 덕분에 "내 컴퓨터에서는 되는데"가 사라진다.
다음 글에서는 실전 활용을 다룬다. 패키지를 빌드해서 PyPI에 배포하는 법, 기존 pip 프로젝트를 uv로 마이그레이션하는 법, 그리고 Docker와 GitHub Actions에서 uv를 쓰는 법까지.
시리즈: uv 완전 가이드
참고 자료
- uv 공식 문서 — Working on projects — 프로젝트 관리 가이드
- uv 공식 문서 — Using tools — 도구 실행 및 관리 가이드
- pyproject.toml 작성 가이드 — Python 공식 패키징 가이드
- uv GitHub 저장소 — 소스 코드 및 이슈 트래커









