ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 파이썬 파일 하나만 보내도 실행되게 만들기: uv 스크립트 의존성
    Programming 2026. 9. 17. 22:51
    728x90
    반응형

    도구와 작은 부속품을 주머니에 담고 끈으로 묶은 파란 공구 말이
    스크립트와 실행에 필요한 정보를 함께 전달하는 AI 생성 개념 이미지입니다.

    짧은 Python 파일을 보냈는데 받는 사람은 실행 결과보다 “패키지가 없다”는 오류를 먼저 만납니다. 작성자의 가상환경에만 설치된 패키지, 따로 전달한 설치 명령, 필요한 Python 버전이 파일 밖에 흩어져 있으면 파일만 복사해서는 준비가 끝나지 않습니다.

    공유할 Python 스크립트에는 필요한 Python 범위와 패키지를 파일 안에 선언하고 uv로 실행하는 경로를 함께 적으세요.

    uv의 스크립트 메타데이터는 실행 요구사항을 코드 옆에 두는 방법입니다. 다만 파일 하나가 모든 장비에서 아무 준비 없이 실행되는 프로그램으로 바뀌는 것은 아닙니다. 실행 도구와 네트워크, 입력 자료가 필요한지까지 함께 설명해야 합니다.

    주석을 Python이 아니라 실행 도구가 읽습니다

    Python 패키징의 inline script metadata 명세는 스크립트 안에 도구가 읽을 정보를 넣는 형식을 정의합니다. # /// script로 시작해 # ///로 닫는 주석 블록 안에 필요한 Python 범위와 의존성 등을 적습니다. 내용은 TOML 형식이며 Python 실행문 자체는 아닙니다.

    uv 스크립트 안내는 이 메타데이터를 읽어 필요한 실행 환경을 준비하는 경로를 제공합니다. 일반 Python이 주석을 보고 패키지를 설치해 주는 것이 아닙니다. 따라서 받는 사람에게 python 파일.py라고만 알려주면 의도한 준비 단계가 빠질 수 있습니다.

    파일 안의 주석은 “이 코드를 실행하려면 무엇이 필요한가”를 설명하는 계약에 가깝습니다. 코드의 내용을 보고 의존성을 완벽하게 추론하거나, 실행 행동의 안전성을 자동으로 보증하는 기능으로 이해하면 안 됩니다.

    한 파일 안에 들어가는 작은 예제

    다음은 두 항목의 수량을 표로 표시하는 설명용 스크립트입니다. 입력 자료를 파일 안에 넣었으므로 별도 CSV나 API 키는 필요하지 않습니다. 예제를 show_summary.py라는 새 파일에 저장한다고 가정하겠습니다. 여기서 실제 실행한 출력이나 성능을 보고하는 것은 아닙니다.

    # /// script
    # requires-python = ">=3.12"
    # dependencies = ["rich"]
    # ///
    
    from rich.console import Console
    from rich.table import Table
    
    rows = [("alpha", 4), ("beta", 2)]
    
    table = Table(title="Sample summary")
    table.add_column("Item")
    table.add_column("Count", justify="right")
    
    for name, count in rows:
        table.add_row(name, str(count))
    
    console = Console()
    console.print(table)
    console.print(f"Total: {sum(count for _, count in rows)}")
    

    Rich의 표 사용 안내에 나오는 Table과 Console의 사용 구조를 바탕으로 만든 예입니다. 맨 위의 dependencies에는 외부 패키지인 rich를 적었습니다. Python에 기본으로 들어 있는 기능을 전부 여기에 나열하는 것은 아닙니다.

    예제에서 확인할 결과는 두 항목과 각각의 수량, 그리고 수량의 합계입니다. 터미널의 색이나 테두리가 작성자가 보는 화면과 완전히 같은지는 별개의 문제입니다. 글꼴과 터미널 환경에 따라 표시가 달라질 수 있으므로, 출력이 예쁘게 보이는지만 검사하지 말고 입력한 항목과 합계의 의미를 대조합니다.

    받는 사람의 첫 실행까지 안내합니다

    받는 쪽에는 uv를 사용할 수 있어야 합니다. 설치가 필요한 경우에는 uv 설치 안내에서 본인 환경에 맞는 방법을 고릅니다. 설치 명령을 알 수 없는 블로그에서 복사하거나 보안 정책을 끄는 방식으로 시작할 이유는 없습니다.

    설치 여부와 버전을 확인한 뒤 파일이 있는 디렉터리에서 실행합니다.

    uv --version
    uv run show_summary.py
    

    두 명령은 서로 다른 것을 확인합니다. 첫째는 실행 도구가 준비됐는지 보는 것이고, 둘째는 스크립트의 요구사항에 맞춰 실행을 요청하는 것입니다. uv가 없다는 오류와 rich를 받을 수 없다는 오류, Python 버전 조건을 만족하지 못하는 오류는 해결할 위치가 다릅니다.

    패키지나 필요한 실행 환경을 처음 준비하는 과정에는 네트워크와 저장 공간이 필요할 수 있습니다. 메타데이터를 넣었다고 의존성이 파일 안에 포함되는 것은 아닙니다. 오프라인 환경이나 사내 패키지 저장소만 허용하는 환경에서는 그 조건에 맞는 준비가 따로 필요합니다.

    이 예제는 출력만 수행하도록 작성했습니다. 실제 스크립트가 파일을 저장한다면 결과 위치와 기존 파일을 덮어쓰는지도 실행 안내에 적어야 합니다. “이 명령을 실행하세요”만 보내면 받는 사람이 어떤 변화가 생기는지 알기 어렵습니다.

    패키지를 추가할 때 코드와 선언을 함께 바꿉니다

    나중에 다른 패키지를 import하도록 코드를 바꿨다면 메타데이터도 맞춰야 합니다. 작성자의 장비에 패키지가 설치되어 있으면 선언 누락을 알아채지 못할 수 있습니다. 깨끗한 별도 환경에서 실행할 수 있는지 확인하는 이유입니다.

    uv는 스크립트를 대상으로 의존성을 추가하는 명령도 제공합니다. 다음은 rich에 버전 범위를 명시하려는 경우의 사용 예입니다.

    uv add --script show_summary.py "rich>=13"
    

    이 명령은 실행만 하는 것이 아니라 스크립트의 의존성 정보를 변경하는 작업입니다. 적용 뒤에는 파일 맨 위의 메타데이터가 어떻게 바뀌었는지 읽어보세요. 이미 다른 패키지나 조건을 선언해 둔 파일이라면 그 정보가 의도대로 유지되는지도 확인합니다.

    패키지 배포 이름과 코드에서 import하는 이름이 항상 같다는 가정도 피해야 합니다. 새 import 문만 기계적으로 복사해 의존성 목록을 만드는 대신, 실제 설치해야 하는 패키지를 확인해야 합니다. 코드에서 사용하지 않게 된 의존성을 남길지 정리할지도 별도의 유지보수 판단입니다.

    주변 프로젝트가 대신 준비해 줄 것이라고 기대하지 않습니다

    공식 안내는 inline metadata가 있는 스크립트의 실행 환경을 프로젝트 의존성과 구별합니다. 프로젝트 디렉터리 안에서 실행했다고 주변 pyproject.toml의 모든 패키지를 그대로 사용할 수 있다고 생각하면 곤란합니다.

    예를 들어 웹 서비스 저장소 안에 작은 점검 파일을 만들었다고 해보겠습니다. 본문은 서비스용 패키지를 import하지만 스크립트의 선언에는 rich만 있다면, 파일을 따로 전달했을 때 요구사항이 맞지 않을 수 있습니다. 파일이 독립적으로 실행될 목적이라면 그 파일의 실제 의존성을 명확하게 드러내야 합니다.

    반대로 프로젝트 내부의 설정과 모듈, 여러 파일을 함께 사용하는 작업이라면 단일 스크립트 규약으로 억지로 옮기는 것이 이득이 아닐 수 있습니다. 배포할 단위가 파일 하나인지 프로젝트 전체인지부터 정하면 어떤 메타데이터를 유지할지 분명해집니다.

    버전 범위와 잠금 파일은 다른 약속입니다

    예시의 >=3.12는 허용하는 Python 범위이고, rich는 한 릴리스를 고정한 표현이 아닙니다. 오늘과 나중에 같은 파일을 실행했을 때 의존성이 언제나 같은 조합으로 선택된다고 보장할 수는 없습니다.

    반복해서 재현할 스크립트라면 명시적으로 잠그는 경로를 검토할 수 있습니다.

    uv lock --script show_summary.py
    

    uv 문서는 이 명령이 스크립트 옆에 show_summary.py.lock 같은 잠금 파일을 만든다고 설명합니다. 따라서 이 단계부터 공유할 결과물은 Python 파일 하나만이 아닐 수 있습니다. 스크립트와 그에 맞는 잠금 파일을 함께 관리해야 선택된 의존성 정보를 전달할 수 있습니다.

    잠금 파일이 있어도 모든 환경이 완전히 같아지는 것은 아닙니다. 지원하는 Python과 운영체제, 외부 파일과 서비스는 여전히 실행 조건입니다. 또한 의존성을 바꾸는 작업에서는 잠금 결과도 갱신될 수 있으므로, 파일이 존재한다는 사실만 보고 변경이 금지됐다고 생각해서는 안 됩니다. uv 명령 참조에서 잠금 최신성을 요구하는 옵션과 실행 목적을 맞춰야 합니다.

    일회성 예제를 간단히 전달하는 일과, 일정 기간 같은 조건으로 반복해야 하는 점검 작업의 요구는 다릅니다. 처음부터 복잡한 프로젝트를 만들 필요는 없지만 재현성이 중요해지는 시점에는 잠금 정보와 환경 기록도 함께 다뤄야 합니다.

    전달이 끝났는지 확인하는 기준

    받는 사람이 새 위치에 스크립트를 두고, 안내된 도구와 명령으로 실행을 준비할 수 있는지 확인합니다. 이 글의 예제라면 출력에 두 항목이 나타나고 합계가 입력과 맞는지를 보면 됩니다. 실제 작업에서는 입력 예, 출력 위치와 형식, 필요한 네트워크, 실패 시 확인할 조건까지 함께 대조해야 합니다.

    마지막으로 실행 요구사항과 안전성 검토는 나눠 두세요. 메타데이터가 정확해도 스크립트가 외부 주소로 자료를 보내거나 중요한 파일을 지우는 행동을 할 수 있습니다. 전달받은 파일이라면 의존성 선언뿐 아니라 실제 코드와 변경 범위를 읽어야 합니다.

    파일 안에 요구사항을 적는 장점은 설치 설명이 코드와 함께 움직인다는 데 있습니다. 실행 도구, 입력, 출력, 잠금 여부까지 목적에 맞게 안내하면 받는 사람이 빠진 준비 단계를 추측하는 일을 줄일 수 있습니다. 단일 파일 공유를 선택했다면 이 전달 과정까지 하나의 작업으로 마무리하세요.

    728x90
    반응형
Designed by Tistory.