Python 프로젝트의 빌드 백엔드를 고를 때, 팀 대부분은 프론트엔드 도구의 일관성을 기준으로 선택한다. uv를 쓰고 있으니 uv-build를 백엔드로 쓰는 것이 자연스럽다는 논리다. 이 논리가 틀리지는 않지만, 비용을 계산한 위치가 잘못될 수 있다. 이 글은 팀이 uv-build를 선택했을 때 그 비용을 직접 부담하는 사람이 누구인지를 먼저 묻는다.
빌드 백엔드가 실제로 누구에게 영향을 미치는가
PEP 517이 설계한 구조에서 빌드 백엔드는 개발자, 기여자, CI/CD 파이프라인, 그리고 소스 배포판을 직접 설치하는 사용자만 접하는 컴포넌트다. 최종 사용자 대부분은 이미 빌드된 wheel을 설치하기 때문에 백엔드를 전혀 실행하지 않는다.
이 구분이 중요한 이유는 여기 있다. 팀 내부의 개발 편의를 위해 선택한 도구가, 패키지를 소스에서 설치해야 하는 외부 사용자 집단 전체에게 추가 비용을 발생시킬 수 있다. 그 비용이 한 번이 아니라 백엔드 릴리스마다 반복된다면 계산이 달라진다.
uv-build가 다른 백엔드와 구별되는 지점
uv-build는 Rust로 작성되어 있다. Rust의 장점은 분명하지만, 그 장점이 유효한 맥락은 주로 시스템 프로그래밍이나 성능에 민감한 런타임이다. 순수 Python 패키지를 빌드하는 백엔드, 즉 대부분의 경우 파일을 모으고 메타데이터를 생성하는 수준의 작업에서 Rust가 제공하는 성능 이점은 체감하기 어렵다.
문제는 이점보다 비용이 더 구체적이라는 점이다.
바이너리 크기. uv-build 0.13.0 기준으로 플랫폼별 wheel 크기는 1.2에서 1.5MiB 수준이다. flit-core의 wheel이 45KiB인 것과 비교하면 30배 이상 크다. 순수 Python 백엔드는 어떤 플랫폼에서든 Python 인터프리터만 있으면 설치되지만, Rust 기반 백엔드는 플랫폼별 컴파일된 바이너리가 필요하다.
릴리스 빈도. 소스에서 빌드해야 하는 환경에서는 백엔드 버전이 올라갈 때마다 재빌드가 발생한다. uv-build는 작성 시점 기준 30일 동안 12회 릴리스된 바 있다. 패키지 배포 대상이 소스 설치를 기본으로 하는 환경이라면, 이 빈도가 사용자 측 빌드 시간에 직접 곱해진다.
이식성 제한. Python은 C 컴파일러가 동작하는 거의 모든 환경에서 실행된다. Rust는 지원 플랫폼 목록이 다르고, 특정 아키텍처에서는 Rust 의존 패키지 자체가 해당 플랫폼을 지원하지 않는 경우가 있다. 32비트 플랫폼에서는 실행 파일 크기 자체가 주소 공간 한계에 부딪히는 사례도 보고되어 있다. 이러한 이식성 문제는 패키지를 범용으로 배포하려는 팀에게는 직접적인 지원 비용이 된다.
성능 이점이 실제로 유의미한지 따져보기
uv-build 도입의 두 번째 논거는 빌드 속도다. 그런데 이 주장을 실제 맥락에 놓으면 설득력이 흐려진다.
설령 빌드 시간이 100ms 단축된다 해도, 소스 배포판 사용자 입장에서는 1.5MiB 크기의 uv-build wheel을 내려받고 (소스 배포 환경이라면) 빌드하는 시간이 먼저 발생한다. 순수 Python 백엔드를 쓸 때보다 총 설치 시간이 줄어든다고 단정하기 어렵다.
또 하나의 조건이 있다. 빌드 백엔드의 속도가 의미를 갖는 시나리오는 하루에도 수십 번 빌드를 반복하는 CI 환경이다. 이 환경에서는 wheel을 캐시하기 때문에 백엔드 빌드 비용은 초기 한 번에 그친다. 반대로, 소스 배포판을 매번 새로 설치해야 하는 환경에서는 백엔드 교체가 오히려 성능을 악화시킬 수 있다.
비교 축: 언제 무엇을 선택해야 하는가
아래 조건을 기준으로 정리하면 선택의 윤곽이 보인다.
uv-build가 합리적인 선택인 경우:
- 배포 대상이 사전 빌드된 wheel 전용이어서, 소스 배포판을 설치하는 사용자가 없는 환경
- 지원 플랫폼을 팀이 직접 통제하며, 지원 범위가 Rust 공식 Tier 1 플랫폼 안에 있는 경우
- 팀 전체가 이미 uv 생태계를 내부 도구로 표준화했고, 외부 배포 경로가 없는 내부 패키지
순수 Python 백엔드(flit-core, hatchling 등)가 더 나은 선택인 경우:
- 패키지를 오픈소스로 배포하며 소스 배포판 설치가 가능해야 하는 경우
- 임베디드, 레거시, 비주류 아키텍처처럼 플랫폼 다양성이 요구사항에 포함된 경우
- 다운스트림 패키저(Linux 배포판, 시스템 패키지 관리자 등)가 소비자에 포함된 경우
- 배포 이후 유지보수 리소스가 제한적이어서 백엔드 업데이트 추적 비용을 최소화해야 하는 경우
프론트엔드 도구와 백엔드 도구는 PEP 517 설계상 분리되어 있다. uv를 프론트엔드로 사용하면서 flit-core나 hatchling을 백엔드로 쓰는 것은 기술적으로 아무 문제가 없다. 일관성을 위해 백엔드를 맞춰야 한다는 논리는 PEP 517의 목적 자체와 어긋난다.
요구사항 단계에서 정해야 하는 질문
백엔드 선택은 팀 내부 편의의 문제가 아니라 패키지를 소비하는 사람 전체에 영향을 미치는 결정이다. 요구사항 단계에서 아래 질문에 먼저 답해야 한다.
배포 경로: 패키지를 wheel로만 배포할 것인가, 소스 배포판도 제공해야 하는가. 소스 배포판을 제공한다면 그 사용자가 누구이며 어떤 환경에서 설치하는가.
플랫폼 범위: 지원해야 하는 플랫폼 목록이 명시되어 있는가. 그 목록이 Rust 지원 플랫폼과 완전히 겹치는가.
유지보수 주기: 백엔드 버전이 올라갈 때마다 다운스트림에서 재빌드가 발생한다면, 그 빈도와 비용을 팀이 감당할 수 있는가. 또는 감당의 주체가 사용자라면, 그것이 프로젝트 입장에서 허용 가능한가.
의존성 정책: 팀의 패키지가 의존하는 다른 패키지들의 백엔드 선택도 간접적으로 영향을 준다. 핵심 의존성 하나가 Rust 기반 백엔드로 전환하면, 그 비용은 모든 다운스트림 사용자에게 퍼진다. 자신의 패키지가 다른 프로젝트의 핵심 의존성이 될 가능성을 고려했는가.
이 질문들에 대한 답이 "우리 상황에서는 해당 없음"이라면 uv-build를 선택해도 실질적 문제가 없다. 그렇지 않다면, 기능 요구사항이 동일할 때 더 가볍고 이식성이 높은 백엔드를 선택하지 않을 이유가 없다. 유명한 도구라는 사실이 선택의 근거가 되어서는 안 된다. 배포 대상, 플랫폼 범위, 유지보수 비용을 먼저 확인하고, 그다음에 도구를 고른다.