byteforce

CPN 한국어 자습서 · 외부 문서 한국어 미러

UV 문서 · Concepts

워크스페이스 사용

Using workspaces · 원문: docs.astral.sh/uv/concepts/projects/workspaces/

아래는 원문을 한국어로 옮긴 미러입니다. 코드·명령은 원문 그대로이며, 가장 최신 정보는 하단 원문 링크에서 확인하세요.

같은 이름의 Cargo 개념에서 영감을 받은 워크스페이스(workspace)는 "함께 관리되는 하나 이상의 패키지, 즉 _워크스페이스 멤버_의 모음"입니다.

워크스페이스는 공통 의존성을 가진 여러 패키지로 분리하여 대형 코드베이스를 구성합니다. 예를 들어, FastAPI 기반 웹 애플리케이션과 동일한 Git 저장소 내에서 별도의 Python 패키지로 버전 관리되는 일련의 라이브러리를 함께 관리할 수 있습니다.

워크스페이스에서 각 패키지는 자체 pyproject.toml을 정의하지만, 워크스페이스는 단일 락파일(lockfile)을 공유하여 일관된 의존성 집합으로 운영됩니다.

따라서 uv lock은 전체 워크스페이스에 대해 한 번에 작동하며, uv runuv sync는 기본적으로 워크스페이스 루트에서 작동합니다. 다만 둘 다 --package 인수를 허용하므로, 어느 워크스페이스 디렉터리에서도 특정 워크스페이스 멤버에서 명령어를 실행할 수 있습니다.

시작하기

워크스페이스를 만들려면 pyproject.tomltool.uv.workspace 테이블을 추가합니다. 이렇게 하면 해당 패키지를 루트로 하는 워크스페이스가 암묵적으로 생성됩니다.

팁: 기본적으로 기존 패키지 내에서 uv init을 실행하면 새로 생성된 멤버가 워크스페이스에 추가됩니다.

워크스페이스를 정의할 때는 members(필수)와 exclude(선택) 키를 지정해야 합니다. 이 키들은 각각 특정 디렉터리를 멤버로 포함하거나 제외하도록 지시하며, glob 목록을 허용합니다:

코드 · 명령
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]

[tool.uv.sources]
bird-feeder = { workspace = true }

[tool.uv.workspace]
members = ["packages/*"]
exclude = ["packages/seeds"]

members glob에 포함된(그리고 exclude glob에서 제외되지 않은) 모든 디렉터리에는 pyproject.toml 파일이 있어야 합니다. 워크스페이스 멤버는 애플리케이션이나 라이브러리 모두 가능합니다. 워크스페이스 컨텍스트에서 둘 다 지원됩니다.

모든 워크스페이스에는 루트가 필요하며, 루트도 워크스페이스 멤버입니다. 위 예시에서 albatross가 워크스페이스 루트이며, seeds를 제외한 packages 디렉터리 아래의 모든 프로젝트가 워크스페이스 멤버입니다.

기본적으로 uv runuv sync는 워크스페이스 루트에서 작동합니다. 예를 들어, 위 예시에서 uv runuv run --package albatross는 동일하며, uv run --package bird-feederbird-feeder 패키지에서 명령어를 실행합니다.

워크스페이스 소스(Workspace sources)

워크스페이스 내에서 워크스페이스 멤버에 대한 의존성은 tool.uv.sources를 통해 처리됩니다:

코드 · 명령
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]

[tool.uv.sources]
bird-feeder = { workspace = true }

[tool.uv.workspace]
members = ["packages/*"]

[build-system]
requires = ["uv_build>=0.11.24,<0.12"]
build-backend = "uv_build"

이 예시에서 albatross 프로젝트는 워크스페이스 멤버인 bird-feeder 프로젝트에 의존합니다. tool.uv.sources 테이블의 workspace = true 키-값 쌍은 bird-feeder 의존성을 PyPI나 다른 레지스트리에서 가져오지 않고 워크스페이스에서 제공해야 함을 나타냅니다.

참고: 워크스페이스 멤버 간의 의존성은 편집 가능(editable) 상태입니다.

워크스페이스 루트의 tool.uv.sources 정의는 특정 멤버의 tool.uv.sources에서 재정의하지 않는 한 모든 멤버에 적용됩니다. 예를 들어:

코드 · 명령
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]

[tool.uv.sources]
bird-feeder = { workspace = true }
tqdm = { git = "https://github.com/tqdm/tqdm" }

[tool.uv.workspace]
members = ["packages/*"]

[build-system]
requires = ["uv_build>=0.11.24,<0.12"]
build-backend = "uv_build"

기본적으로 모든 워크스페이스 멤버는 GitHub에서 tqdm을 설치합니다. 단, 특정 멤버가 자체 tool.uv.sources 테이블에서 tqdm 항목을 재정의한 경우는 예외입니다.

참고: 워크스페이스 멤버가 어떤 의존성에 대해 tool.uv.sources를 제공하면, 멤버의 소스가 현재 플랫폼과 일치하지 않는 마커로 제한되더라도 워크스페이스 루트의 동일 의존성에 대한 tool.uv.sources를 무시합니다.

워크스페이스 레이아웃(Workspace layouts)

가장 일반적인 워크스페이스 레이아웃은 루트 프로젝트와 일련의 동반 라이브러리로 구성됩니다.

위 예시를 계속하면, 이 워크스페이스는 albatross에 명시적인 루트가 있고 packages 디렉터리에 두 개의 라이브러리(bird-feederseeds)가 있습니다:

코드 · 명령
albatross
├── packages
│   ├── bird-feeder
│   │   ├── pyproject.toml
│   │   └── src
│   │       └── bird_feeder
│   │           ├── __init__.py
│   │           └── foo.py
│   └── seeds
│       ├── pyproject.toml
│       └── src
│           └── seeds
│               ├── __init__.py
│               └── bar.py
├── pyproject.toml
├── README.md
├── uv.lock
└── src
    └── albatross
        └── main.py

seedspyproject.toml에서 제외되었으므로, 워크스페이스는 albatross(루트)와 bird-feeder, 두 멤버를 가집니다.

워크스페이스를 사용할 때와 사용하지 않을 때

워크스페이스는 단일 저장소 내에서 여러 상호 연결된 패키지의 개발을 용이하게 하기 위해 설계되었습니다. 코드베이스가 복잡해질수록, 각자의 의존성과 버전 제약을 가진 더 작고 조합 가능한 패키지로 분리하는 것이 도움이 될 수 있습니다.

워크스페이스는 격리와 관심사 분리를 강화합니다. 예를 들어, uv에는 핵심 라이브러리와 명령줄 인터페이스를 위한 별도 패키지가 있어 각 구성 요소를 독립적으로 테스트할 수 있습니다.

워크스페이스의 다른 일반적인 사용 사례:

워크스페이스는 멤버의 요구사항이 충돌하거나 각 멤버에 대해 별도의 가상환경이 필요한 경우에는 적합하지 않습니다. 이 경우 경로 의존성(path dependencies)이 더 적합한 경우가 많습니다. 예를 들어, albatross와 멤버를 워크스페이스로 묶는 대신, 각 패키지를 독립적인 프로젝트로 정의하고 패키지 간 의존성을 tool.uv.sources의 경로 의존성으로 정의할 수 있습니다:

코드 · 명령
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]

[tool.uv.sources]
bird-feeder = { path = "packages/bird-feeder" }

[build-system]
requires = ["uv_build>=0.11.24,<0.12"]
build-backend = "uv_build"

이 접근 방식은 동일한 이점을 많이 제공하지만, 의존성 해석 및 가상환경 관리에 대한 더 세밀한 제어를 허용합니다(단, uv run --package를 더 이상 사용할 수 없으며, 명령어는 관련 패키지 디렉터리에서 실행해야 합니다).

마지막으로, uv의 워크스페이스는 전체 워크스페이스에 단일 requires-python을 적용하며, 모든 멤버의 requires-python 값의 교집합을 취합니다. 워크스페이스의 나머지 부분에서 지원하지 않는 Python 버전으로 특정 멤버를 테스트해야 하는 경우, uv pip를 사용하여 해당 멤버를 별도의 가상환경에 설치해야 할 수 있습니다.

참고: Python은 의존성 격리를 제공하지 않으므로, uv는 패키지가 선언된 의존성만 사용하고 다른 것은 사용하지 않음을 보장할 수 없습니다. 특히 워크스페이스에서 uv는 패키지가 다른 워크스페이스 멤버가 선언한 의존성을 가져오지 않음을 보장할 수 없습니다.

원문(영어): https://docs.astral.sh/uv/concepts/projects/workspaces/ · 본 문서는 학습용 한국어 번역이며 원본의 권리는 원저작자(Astral)에게 있습니다.

원문(영어): https://docs.astral.sh/uv/concepts/projects/workspaces/