파이썬 개발, '제대로' 하고 싶다면 이 조합으로 시작하세요 uv, Ruff, Pyright

 

파이썬 개발, '제대로' 하고 싶다면 이 조합으로 시작하세요 uv, Ruff, Pyright

요즘 파이썬 개발 환경, 정말 빠르게 변하고 있거든요.

그래서 어떤 도구를 써야 할지 막막할 때가 많습니다.

특히 'uv'나 'Ruff'처럼 떠오르는 신흥 강자들이 등장하면서 기존의 venv, poetry, pip 조합을 계속 고수해야 할지 고민이 되는 시점인데요.

이 글에서는 바로 그 고민을 해결해 드릴 '최신 파이썬 개발 끝판왕 조합'을 소개해 드리려고 합니다.

프로젝트 관리부터 코드 품질, 그리고 자동화까지, 이 글 하나로 최신 파이썬 개발 워크플로우를 완벽하게 구축할 수 있도록 도와드리겠습니다.

1. 모든 것의 시작, 프로젝트 관리 with uv

파이썬 개발을 할 때 가장 먼저 부딪히는 허들이 바로 '환경 설정'이거든요.

어떤 사람은 pyenv로 파이썬 버전을 관리하고, venv로 가상 환경을 만들고, poetrypip으로 패키지를 관리합니다.

그런데 이 모든 걸 한 번에, 그것도 '엄청나게 빠른 속도'로 처리해주는 도구가 등장했는데요.

바로 'uv'입니다.

uv는 Rust 언어로 만들어져서 기존 도구들과는 비교가 안 될 정도로 빠른 속도를 자랑하고요.

pip, venv, poetry, pyenv 등의 역할을 하나로 통합한 '올인원' 툴이라고 생각하시면 됩니다.

uv 설치와 프로젝트 생성

먼저 uv부터 설치해야겠죠.

가장 간단한 방법은 brew를 이용하는 겁니다.

brew install uv

설치가 끝났다면 바로 프로젝트를 만들어 볼까요.

'my-project'라는 이름의 새 디렉터리와 함께 프로젝트를 생성해 보겠습니다.

uv init my-project

이 명령어를 실행하면 pyproject.toml을 포함한 기본적인 프로젝트 구조가 자동으로 만들어지거든요.

pyproject.toml 파일은 우리 프로젝트의 모든 설정 정보가 담기는 '설계도' 같은 파일입니다.

파이썬 버전 관리와 가상 환경

uv는 여러 버전의 파이썬을 설치하고 자유롭게 전환하는 기능도 제공하는데요.

예를 들어, 3.10, 3.11, 3.12 버전을 이렇게 한 번에 설치할 수 있습니다.

uv python install 3.10 3.11 3.12

이제 우리 프로젝트에서는 파이썬 3.11 버전을 사용하도록 지정해볼까요.

이걸 '피닝(pinning)'이라고 부릅니다.

uv python pin 3.11

만약 pyproject.toml에 설정된 파이썬 버전과 맞지 않아 오류가 발생한다면, requires-python = ">=3.11" 처럼 해당 파일의 버전을 수정해주면 간단하게 해결됩니다.

이제 이 프로젝트만을 위한 격리된 가상 환경을 만들 차례인데요.

이것도 명령어 한 줄이면 충분합니다.

uv venv

이 명령어를 실행하면 .venv라는 디렉터리가 생성되고, 앞으로 우리가 설치할 모든 패키지는 이 안에 격리되어 관리됩니다.

빛의 속도로 패키지 관리하기

가상 환경을 만들었으니 활성화를 시켜줘야 하거든요.

그래야 지금부터 설치하는 패키지들이 이 가상 환경 안으로 들어갑니다.

source .venv/bin/activate

uv로 패키지를 추가하는 방법은 uv add를 사용하는 것이 좋은데요.

uv pip install도 가능하지만, uv add를 사용해야 pyproject.toml에 의존성이 자동으로 기록되어 프로젝트 관리가 훨씬 편해집니다.

데이터 분석에 필수적인 pandasnumpy를 설치해 보겠습니다.

uv add pandas numpy

설치가 끝나고 pyproject.toml 파일을 열어보면, dependencies 항목에 두 패키지가 추가된 것을 볼 수 있습니다.

개발 환경에서만 필요한 패키지는 --dev 옵션을 붙여서 따로 관리할 수 있거든요.

예를 들어 테스트 도구인 pytest는 이렇게 설치하는 것이 일반적입니다.

uv add --dev pytest

프로젝트를 동료에게 공유할 땐 pyproject.toml 파일만 전달하면 되는데요.

동료는 프로젝트 폴더에서 아래 명령어 하나만 실행하면, 가상 환경 생성부터 모든 패키지 설치까지 한 번에 끝낼 수 있습니다.

uv sync

마지막으로, 작성한 파이썬 코드는 uv run으로 실행할 수 있습니다.

uv run main.py

uv 하나만으로 프로젝트의 시작부터 실행까지 모든 과정이 정말 깔끔하게 관리되는 것을 볼 수 있습니다.

2. 코드 품질의 기준을 세우다, Ruff

좋은 파이썬 코드는 무엇일까요?

정답은 없지만, 파이썬 커뮤니티에서는 'PEP 8'이라는 스타일 가이드를 따르는 것을 권장하고 있습니다.

하지만 개발자가 모든 규칙을 외워서 코딩하는 건 거의 불가능한데요.

그래서 우리는 '린터(Linter)'와 '포매터(Formatter)'라는 도구를 사용합니다.

그리고 이 두 가지 역할을 uv처럼 '압도적인 속도'로 처리해주는 도구가 바로 'Ruff'입니다.

Ruff 역시 Astral사가 Rust로 개발한 도구로, 기존의 Flake8, Black, isort 같은 여러 도구를 완벽하게 대체합니다.

Ruff로 코드 분석하고 정리하기

먼저 Ruff를 개발용 패키지로 설치해볼까요.

uv add --dev ruff

이제 Ruff의 강력함을 테스트해볼 간단한 파이썬 파일을 하나 만들어 보겠습니다.

# ruff-check.py
from typing import Iterable
import os

print('Hello from python-dev!')

def sum_even_numbers(numbers: Iterable[int]) -> int:
    """Given an iterable of integers, return the sum of all even numbers in the iterable."""
    return sum(
        num for num in numbers
        if num % 2 == 0
    )

이 코드에는 몇 가지 '개선할 점'이 숨어있거든요.

먼저 포매터 기능으로 코드 스타일을 검사해 보겠습니다.

uv run ruff format --diff

--diff 옵션을 주면 어떤 부분이 변경될지 미리 보여주는데요.

아마 작은따옴표를 큰따옴표로 바꾸고, 여러 줄로 작성된 return 구문을 한 줄로 합치라는 제안이 나올 겁니다.

이제 --diff 옵션을 빼고 실행하면 파일이 자동으로 수정됩니다.

uv run ruff format

다음은 린터 기능으로 코드의 논리적 오류를 찾아볼 차례입니다.

uv run ruff check

아마 'import os' 구문이 있지만 실제로는 사용되지 않았다는 'F401' 오류를 찾아낼 겁니다.

이것도 --fix 옵션을 붙여 실행하면 자동으로 불필요한 코드를 삭제해 줍니다.

uv run ruff check --fix

어떤 규칙을 적용할지는 pyproject.toml 파일에 상세하게 설정할 수 있는데요.

이를 통해 우리 팀만의 코드 컨벤션을 정하고 강제할 수 있습니다.

VS Code와 Ruff 연동하기

사실 이런 코드 검사는 개발자가 코드를 작성하는 순간에 바로바로 피드백을 받는 게 가장 좋거든요.

VS Code 마켓플레이스에서 'Ruff' 확장 프로그램을 설치하면 이 모든 과정이 자동화됩니다.

설치 후에 VS Code의 settings.json 파일에 아래 설정을 추가해주세요.

"[python]": {
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
        "source.fixAll": "explicit"
    },
    "editor.defaultFormatter": "charliermarsh.ruff"
}

이제 파이썬 파일을 저장할 때마다 Ruff가 자동으로 코드를 포맷팅하고, 간단한 오류들을 수정해 줄 겁니다.

3. 안정적인 코드의 비결, 타입 힌트와 Pyright

파이썬은 변수의 타입을 미리 지정하지 않아도 되는 '동적 타입 언어'인데요.

이게 개발 속도를 높여주기도 하지만, 코드가 복잡해지면 예상치 못한 타입 오류로 고통받는 원인이 되기도 합니다.

그래서 등장한 것이 바로 '타입 힌트(Type Hint)'입니다.

변수나 함수에 타입을 명시해서 코드의 안정성과 가독성을 높이는 기법입니다.

그리고 우리가 작성한 타입 힌트가 올바른지 검사해주는 정적 타입 체커가 필요한데요.

여기서는 Microsoft가 개발한 'Pyright'를 사용해 보겠습니다.

Pyright는 특히 VS Code의 파이썬 확장 기능인 'Pylance'에 내장되어 있어 설정이 매우 간편합니다.

dataclass로 구조화된 데이터 다루기

타입 힌트의 강력함은 'dataclass'와 만났을 때 극대화되거든요.

dataclass는 데이터 구조를 정의하기 위한 데코레이터인데, 타입과 함께 데이터를 관리할 때 정말 유용합니다.

예를 들어, 사람의 정보를 딕셔너리로 관리하면 어떤 키가 있는지, 각 값의 타입이 무엇인지 한눈에 알기 어렵습니다.

# 딕셔너리 방식
human_dict = {"name": "Alice", "age": 30}
print(human_dict["name"]) # 오타라도 나면 바로 에러!

이걸 dataclass로 바꾸면 이렇게 되는데요.

from dataclasses import dataclass

@dataclass
class Human:
    name: str
    age: int

human_obj = Human(name="Alice", age=30)
print(human_obj.name) # IDE가 자동으로 name, age를 추천해줍니다.

가독성이 훨씬 좋아지고, IDE의 자동 완성 지원을 받을 수 있어 오타로 인한 실수를 원천적으로 방지할 수 있습니다.

여기에 frozen=True 옵션을 주면 불변(immutable) 객체로 만들 수 있고, slots=True를 추가하면 메모리 사용량도 최적화할 수 있습니다.

Pyright로 실시간 타입 체크하기

VS Code에 'Pylance' 확장 프로그램이 설치되어 있다면 이미 Pyright를 사용하고 있는 셈입니다.

settings.json에서 타입 체킹 모드를 설정할 수 있는데요.

가장 엄격한 모드인 'strict'로 설정하는 것을 추천합니다.

"python.analysis.typeCheckingMode": "strict"

이제 타입 힌트와 맞지 않는 코드를 작성하면 실시간으로 코드에 빨간 줄이 표시됩니다.

예를 들어 Human 클래스의 ageint 타입인데, 문자열 "30"을 넣으려고 하면 Pyright가 즉시 오류를 알려주는 거죠.

human_obj = Human(name="Alice", age="30") # 이 부분에 Pyright가 오류를 표시합니다.

이는 버그가 될 수 있는 코드를 실행 단계가 아닌 '작성 단계'에서 미리 잡을 수 있게 해주는 아주 강력한 기능입니다.

4. 모든 것을 자동화하다, Git Hooks와 pre-commit

지금까지 소개한 Ruff와 Pyright는 정말 훌륭한 도구들이지만, 개발자가 매번 직접 실행하는 걸 잊어버리면 아무 소용이 없거든요.

그래서 이런 코드 품질 검사를 '자동화'하고 '강제'하는 장치가 필요합니다.

이때 사용하는 것이 바로 'Git hooks'입니다.

Git hooks는 git commit이나 git push 같은 특정 Git 이벤트가 발생했을 때 우리가 지정한 스크립트를 자동으로 실행시켜주는 기능입니다.

그리고 이 Git hooks를 아주 쉽게 관리하게 해주는 프레임워크가 'pre-commit'입니다.

pre-commit 설정하기

먼저 pre-commit을 설치합니다.

uv add --dev pre-commit

이제 프로젝트 최상위 경로에 .pre-commit-config.yaml 파일을 만들고, 어떤 훅을 사용할지 정의해야 하는데요.

우리는 'commit을 하기 전(pre-commit)'에 Ruff를 실행하고, 'push를 하기 전(pre-push)'에 Pyright로 전체 프로젝트의 타입을 검사하도록 설정해 보겠습니다.

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      # 커밋 전: Ruff로 코드 포맷팅 및 린팅 실행
      - id: ruff-format
        name: ruff format
        entry: uvx ruff format
        language: system
        types: [python]
        stages: [pre-commit]

      - id: ruff-lint
        name: ruff check --fix
        entry: uvx ruff check --fix --exit-non-zero-on-fix
        language: system
        types: [python]
        stages: [pre-commit]

  - repo: local
    hooks:
      # 푸시 전: Pyright로 전체 프로젝트 타입 체크
      - id: pyright
        name: pyright
        entry: uvx pyright
        language: system
        pass_filenames: false
        stages: [pre-push]

설정 파일 작성이 끝났다면, 이 설정을 우리 프로젝트의 Git 저장소에 설치해줘야 합니다.

uvx pre-commit install --hook-type pre-commit --hook-type pre-push

이제 모든 준비가 끝났습니다.

만약 스타일 가이드에 맞지 않거나 타입 오류가 있는 코드를 커밋하거나 푸시하려고 하면, pre-commit 훅이 이를 감지하고 자동으로 수정을 시도하거나 작업을 중단시켜 코드 품질을 항상 최상으로 유지해 줍니다.

마무리하며

지금까지 uv, Ruff, Pyright, 그리고 pre-commit을 조합하여 현대적인 파이썬 개발 환경을 구축하는 방법을 알아봤는데요.

이 조합의 핵심은 '속도', '통합', 그리고 '자동화'입니다.

uv로 프로젝트 환경을 빛의 속도로 설정하고, Ruff로 코드 스타일과 간단한 오류를 실시간으로 교정하며, Pyright로 코드의 타입 안정성을 확보합니다.

그리고 pre-commit으로 이 모든 품질 검사 과정을 자동화하여, 개발자는 오직 '비즈니스 로직'에만 집중할 수 있게 되는 겁니다.

물론 개발 도구의 유행은 계속 변할 수 있습니다.

하지만 중요한 것은 '왜' 이런 도구들을 사용하는지 이해하는 것이고, 그 본질은 결국 '효율적이고 유지보수하기 좋은 코드를 만들기 위함'이라는 점입니다.

오늘 소개해 드린 이 워크플로우를 여러분의 다음 파이썬 프로젝트에 꼭 한번 적용해 보시길 바랍니다.