워로디스

Python. uv로 재사용 가능한 Python 라이브러리 만들기 본문

개발/Python

Python. uv로 재사용 가능한 Python 라이브러리 만들기

워로디스 2026. 7. 11. 19:09

1. 개요

여러 Python 프로젝트에서 공통으로 사용할 코드를 별도 라이브러리로 분리하려면, 해당 코드를 단순한 .py 파일로 보관하기보다 Python 패키지 형태로 구성하고 각 프로젝트의 의존성으로 등록하는 방식이 좋다.

uv를 사용하는 경우 다음 흐름으로 구성할 수 있다.

  1. uv init --lib로 라이브러리 프로젝트 생성
  2. 재사용할 코드를 src/<package_name>/ 아래에 작성
  3. 외부에 공개할 함수나 클래스를 __init__.py에서 노출
  4. 사용하는 프로젝트에서 uv add --editable <라이브러리 경로> 실행
  5. 일반 Python 패키지처럼 import하여 사용

2. uv init, --package, --lib의 차이

일반 프로젝트

uv init my-app

일반적인 애플리케이션 프로젝트를 만든다.

주로 직접 실행하는 프로그램, 스크립트, 서비스 등을 만드는 데 적합하다.

패키지 프로젝트

uv init --package my-package

프로젝트 자체를 Python 패키지로 설치할 수 있도록 구성한다.

다른 코드에서 import할 수 있는 패키지를 만들거나, CLI 프로그램을 패키징할 때 사용할 수 있다.

라이브러리 프로젝트

uv init --lib my-library

다른 프로젝트에서 import해서 사용하는 라이브러리를 만들 때 적합하다.

--lib는 패키지로 설치할 수 있는 구조를 만들며, 기본적으로 src 레이아웃을 사용한다.

공용 유틸리티, API 클라이언트, 데이터 처리 코드, 사내 공통 모듈 등을 만들 목적이라면 일반적으로 --lib가 가장 자연스럽다.

3. 간단한 라이브러리 생성

예제로 숫자 계산 기능을 제공하는 simple-math 라이브러리를 만든다고 가정한다.

uv init --lib simple-math

생성된 프로젝트는 대략 다음과 같은 구조를 가진다.

simple-math/
├─ pyproject.toml
├─ README.md
└─ src/
   └─ simple_math/
      ├─ __init__.py
      └─ py.typed

프로젝트 이름은 simple-math처럼 하이픈을 사용할 수 있지만, Python import 이름에는 하이픈을 사용할 수 없다.

따라서 import할 때는 밑줄이 들어간 이름을 사용한다.

import simple_math

4. 라이브러리 코드 작성

src/simple_math/calculator.py 파일을 생성한다.

def add(left: float, right: float) -> float:
    """두 숫자를 더한다."""
    return left + right


def multiply(left: float, right: float) -> float:
    """두 숫자를 곱한다."""
    return left * right

프로젝트 구조는 다음과 같이 바뀐다.

simple-math/
├─ pyproject.toml
├─ README.md
└─ src/
   └─ simple_math/
      ├─ __init__.py
      ├─ calculator.py
      └─ py.typed

5. __init__.py에서 공개 API 구성

사용자가 내부 파일 구조를 알지 않아도 함수를 바로 import할 수 있도록 src/simple_math/__init__.py에 공개 API를 정의한다.

from .calculator import add, multiply

__all__ = [
    "add",
    "multiply",
]

이제 사용하는 쪽에서는 다음처럼 간단히 import할 수 있다.

from simple_math import add, multiply

다음처럼 내부 모듈 경로를 직접 지정할 수도 있다.

from simple_math.calculator import add

하지만 라이브러리의 내부 파일 구조가 나중에 변경될 수 있으므로, 가능하면 __init__.py를 통해 공개한 API를 사용하는 편이 좋다.

6. 라이브러리 내부에서 실행 확인

라이브러리 프로젝트 디렉터리에서 다음 명령으로 import 여부를 확인할 수 있다.

cd simple-math

uv run python -c "from simple_math import add; print(add(10, 20))"

출력:

30

7. 다른 프로젝트에서 라이브러리 사용

다음과 같은 폴더 구조를 가정한다.

workspace/
├─ simple-math/
└─ sample-app/

먼저 애플리케이션 프로젝트를 만든다.

cd workspace

uv init sample-app

그다음 sample-app에 로컬 라이브러리를 의존성으로 추가한다.

cd sample-app

uv add --editable ../simple-math

Windows에서는 다음과 같이 실행할 수 있다.

uv add --editable ..\simple-math

--editable은 라이브러리를 복사해서 설치하는 대신 원본 소스 디렉터리를 현재 프로젝트에 연결한다.

따라서 simple-math의 코드를 수정하면 일반적으로 다시 설치하지 않아도 변경 내용이 반영된다.

8. 사용하는 프로젝트의 코드

sample-app/main.py를 다음처럼 작성한다.

from simple_math import add, multiply


def main() -> None:
    print(add(10, 20))
    print(multiply(4, 5))


if __name__ == "__main__":
    main()

실행:

uv run python main.py

출력:

30
20

9. pyproject.toml의 로컬 의존성

uv add --editable ../simple-math를 실행하면 사용하는 프로젝트의 pyproject.toml에 로컬 라이브러리 의존성이 기록된다.

대략 다음과 같은 형태다.

[project]
name = "sample-app"
version = "0.1.0"
dependencies = [
    "simple-math",
]

[tool.uv.sources]
simple-math = {
    path = "../simple-math",
    editable = true,
}

실제 출력 형식은 uv 버전에 따라 조금 달라질 수 있지만, 핵심은 다음 두 가지다.

  • dependencies에 패키지 이름이 추가됨
  • tool.uv.sources에 실제 로컬 경로가 기록됨

10. 여러 프로젝트에서 같은 라이브러리 사용

여러 프로젝트가 같은 공용 라이브러리를 사용할 수도 있다.

workspace/
├─ simple-math/
├─ project-a/
├─ project-b/
└─ project-c/

각 프로젝트에서 라이브러리를 등록한다.

cd project-a
uv add --editable ../simple-math
cd project-b
uv add --editable ../simple-math
cd project-c
uv add --editable ../simple-math

각 프로젝트는 독립적인 가상환경과 의존성을 가지지만, 동일한 라이브러리 소스 코드를 참조한다.

from simple_math import add

11. 절대 경로 사용

라이브러리와 사용하는 프로젝트의 상대 위치가 일정하지 않다면 절대 경로를 사용할 수 있다.

Windows:

uv add --editable D:\dev\simple-math

macOS 또는 Linux:

uv add --editable /home/user/dev/simple-math

다만 pyproject.toml에 절대 경로가 기록되면 다른 컴퓨터나 다른 사용자의 환경에서는 그대로 사용할 수 없을 수 있다.

팀 프로젝트나 여러 컴퓨터에서 공유할 계획이라면 상대 경로, Git 저장소 또는 패키지 저장소 사용을 고려하는 것이 좋다.

12. 일반 설치와 editable 설치의 차이

일반 로컬 설치:

uv add ../simple-math

editable 설치:

uv add --editable ../simple-math

개발 중에는 editable 설치가 편리하다.

라이브러리 코드 수정
        ↓
사용 프로젝트에서 즉시 반영

반면 배포 환경이나 재현 가능한 운영 환경에서는 특정 버전으로 빌드된 패키지나 Git 태그를 사용하는 것이 더 안전할 수 있다.

13. 라이브러리에 외부 의존성 추가

라이브러리가 외부 패키지를 사용한다면 라이브러리 프로젝트에서 직접 추가한다.

cd simple-math

uv add numpy

그러면 simple-math/pyproject.toml에 의존성이 기록된다.

[project]
name = "simple-math"
version = "0.1.0"
dependencies = [
    "numpy>=2.0.0",
]

sample-app에서 simple-math를 추가하면 uv가 라이브러리의 의존성도 함께 설치한다.

사용하는 프로젝트에서 라이브러리의 내부 의존성을 다시 별도로 등록할 필요는 없다.

14. 라이브러리 제거

사용 중인 프로젝트에서 로컬 라이브러리 의존성을 제거하려면 다음 명령을 실행한다.

uv remove simple-math

이 작업은 사용하는 프로젝트의 의존성 설정에서 simple-math를 제거한다.

원본 simple-math 디렉터리는 삭제되지 않는다.

15. 모듈을 찾을 수 없다는 오류가 발생할 때

다음 오류가 발생할 수 있다.

ModuleNotFoundError: No module named 'simple_math'

주요 확인 사항은 다음과 같다.

올바른 프로젝트에서 실행했는지 확인

uv run python main.py

단순히 시스템 Python으로 실행하면 uv 프로젝트 환경에 설치된 패키지를 찾지 못할 수 있다.

python main.py

라이브러리가 의존성으로 등록되었는지 확인

uv tree

또는:

uv run python -c "import simple_math; print(simple_math)"

import 이름 확인

프로젝트 이름:

simple-math

Python import 이름:

simple_math

다음 코드는 잘못된 문법이다.

import simple-math

src 구조 확인

라이브러리 코드가 다음 위치에 있어야 한다.

simple-math/
└─ src/
   └─ simple_math/
      └─ __init__.py

다음처럼 패키지 디렉터리 밖에 코드가 놓이면 설정에 따라 import되지 않을 수 있다.

simple-math/
├─ calculator.py
└─ src/
   └─ simple_math/

16. sys.path를 직접 수정하는 방식

다음처럼 Python 코드 안에서 경로를 추가할 수도 있다.

import sys

sys.path.append(r"D:\dev\simple-math\src")

from simple_math import add

간단한 테스트에서는 동작할 수 있지만, 정식 프로젝트 구성에는 권장하지 않는다.

이 방식은 다음 문제가 있다.

  • 파일마다 경로 코드가 반복됨
  • 컴퓨터마다 경로가 달라짐
  • IDE와 테스트 도구 설정이 복잡해짐
  • 프로젝트 의존성이 pyproject.toml에 기록되지 않음
  • 배포 및 재현이 어려움

따라서 uv add --editable로 의존성을 명시적으로 등록하는 편이 좋다.

17. 권장 구성 요약

공용 라이브러리 생성:

uv init --lib simple-math

cd simple-math

라이브러리 코드:

simple-math/
├─ pyproject.toml
└─ src/
   └─ simple_math/
      ├─ __init__.py
      └─ calculator.py

사용 프로젝트 생성:

uv init sample-app

cd sample-app

로컬 라이브러리 추가:

uv add --editable ../simple-math

Python 코드에서 사용:

from simple_math import add

result = add(10, 20)
print(result)

실행:

uv run python main.py

18. 최종 정리

여러 프로젝트에서 공통으로 사용할 Python 코드를 만들 때는 다음 방식을 권장한다.

uv init --lib
        ↓
src 레이아웃으로 라이브러리 작성
        ↓
__init__.py에서 공개 API 정의
        ↓
uv add --editable로 사용하는 프로젝트에 연결
        ↓
일반 패키지처럼 import

핵심 명령은 다음 두 개다.

uv init --lib simple-math
uv add --editable ../simple-math

uv init --package도 import 가능한 패키지를 만들 수 있지만, 다른 프로젝트에서 사용하는 재사용 라이브러리가 목적이라면 uv init --lib가 더 명확하고 적합하다.

반응형