python - uv & ruff 설치부터 project initializing, 왜 주목 받는가?

정현우·3일 전
7
post-thumbnail

[ 글의 목적: 의존성 관리와 패키징을 위한 uv 세팅 부터 활용 기록, 린팅과 포맷팅을 동시에 지원하는 ruff 까지 같이하는 세팅 기록 ]

uv & ruff 설치부터 project initializing

Uv, Ruff 모두 Astral(아스트랄) 회사의 오픈소스, 라이브러리이다. python 생태계는 정말 "파이써닉" 한 방향으로 계속 진화하고 있다. poetry 만 해도 엇그제 같은데, 아스트랄의 제품들이 정말 무서울 정도로의 속도로 관련 오픈소스들을 압살하고 있다..! 제일 대표작 Uv, Ruff 오픈소스를 개괄적으로 살펴보자!

실제 세팅만 보고 싶다면 2. uv & ruff 설치와 세팅 을 바로 보면 됩니다~

1. 왜 지금 uv와 ruff인가?

0) Astral(아스트랄) 을 아시나요?!

"파이썬 생태계의 생산성을 높이는 고성능 개발 도구를 만든다" 라는 일념하에 세워진 아스트랄은 창업자 Charlie Marsh“파이썬 툴링은 훨씬 더 빨라질 수 있다” 는 가설을 Ruff 로 스스로 검증하며 쏘아올려졌다. (상남자다..) - Announcing Astral, the company behind Ruff

23년 4월, "We’ve raised $4m in seed funding led by Accel" 라고 언급되어 있듯, "400만 달러 - 한화 약 57억" 을 시드받고 날개를 활짝 핀 것 같다. 도구는 오픈소스(무료)로 유지하고, 그 위에 유료 hosted service를 만든다는 유료화 목표가 있는 것 같다. 아마 클라우드를 도입할 것 같은데, 어떻게 BM을 만들지 미래가 정말 궁금하다.

대표적으로 아래 3개에 집중하고 있다. 출시 순서도 ruff -> uv -> pyx -> ty 이다. 이 글에서는 ruffuv 만 다뤄보고자 한다.

  1. Ruff: 린팅·포매팅(편집기 통합 포함)
  2. uv: 패키지/프로젝트/스크립트/도구/파이썬 버전까지 원스톱 관리
  3. pyx: "Python-native package registry" -> 얘가 아스트랄의 첫번째 유료화 서비스이다. 아직 상용화는 안한 듯 하다. 클로즈 베타인지, 위시리스트를 받는 것 같다. - pyx: a Python-native package registry, now in Beta. 꽤 신박하니 소개 글 추천!
  4. ty: 타입 체커, 얘는 진짜 핫하다. 2025-09-19 공개된 문서 - https://docs.astral.sh/ty/

1) 인기/성장 지표

uv

Poetry 는 수년간 꾸준히 발전하며 약 33.9k 개의 스타를 얻었지만, 2024년에 등장한 uv불과 1년 만에 스타 개수가 36k를 넘어섰다. 나중에 다시 언급하겠지만, 나는 poetry 가 가상환경을 더 불편하게 업데이트 한 것에 좀 불편함이 있다.

2024년 10월 기준으로 PyPI 에서 전체 패키지 다운로드의 약 13.3% 가 uv를 통해 이루어졌고, uv 자체도 월 2천8백만 건 이상의 다운로드 수를 기록 하며 거대한 성장세를 증명하고 있다. 도대체 어떻게 한건가..

ruff

이 정도면 주작아닌가 싶을정도로 ruff 의 성장 기울기는 미쳤다. 불과 2년여 만에 ruff는 GitHub 스타 27k+ 를 돌파해 Black 을 포함한 기존 도구를 넘어섰다. 실제로 ruff는 주당 약 270개의 스타를 얻으며 핫한 프로젝트 1위에 올랐는데, 이는 Black의 스타 증가 속도(주당 약 107개)보다 두 배 이상 빠른 수치라고 한다. - AwesomePython

현재 Flake8, Black 등의 역할을 하나로 통합하면서도 수백만 건의 주간 다운로드를 보여주고 있다. 사실 이미 나도 린팅은 black & flake8 버리고 ruff 로 갈아타버렸다. 사실 python 에서 린팅을 위해 사전 세팅이 꽤 많다. 그리고 이를 위한 써드파티나 플러그인도 필요하니, toml 에 종속적인 것을 위해 ruff 로 갈아타고, 자연스럽게 uv 로 스며들고 있다.

2) uv 는 뭘 해결하고, 왜 빠를까?

아스트랄의 미션과 비전에 얼라인하는 "초고속 패키지/프로젝트 매니저" 오픈소스를 만든 것이다. pip, virtualenv, pipx, pyenv, poetry 등과 같은 도구를 "일원화" 하는게 가장큰 목표이다. 그리고 첫 탄생과 다르게 이제 uv 만으로 라이브러리를 배포하고 버저닝 할 수 있다. (나에게는 이게 가장 큰 요인이었다.) 언급된 도구들 진짜 다 써본 것 같은데 개인적으로 아나콘다가 최악이다..

uv ill like uv 글에 따르면 "Initially Armin Ronacher started rye as a “cargo for python” to unify the fragmented python tooling landscape (for a great overview check out this talk by Anna-Lena Popkes)." 라고 언급되어 있다.

Armin Ronacher가 시작한 실험적 패키징 툴 Rye 가 있는데, Astral의 창업자 Charlie Marsh가 이 아이디어를 이어받아 uv를 통합 후속 프로젝트로 출시한 것 이다.

현재 빠르다고 하는 애들보다 더 빠르다고 한다. (pip 대비 10~100배). 근본적인 빠른 이유는 Rust 기반 컴파일 바이너리파일이기 때문인데, 여러 최적화 스킬이 있다. - uv: Python packaging in Rust

  1. 글로벌 패키지 캐시를 도입하여 동일한 패키지를 반복 설치할 때 다시 다운로드하거나 빌드하지 않는다.
  2. 또한 파일 복사 시 Copy-on-Write와 하드링크를 활용하여 가상환경을 만들 때 디스크 복사 비용을 최소화하고
  3. 이어서 바로 종속성 해석을 같은 프로세스에 처리해서, python 을 중복 실행하는 오버헤드를 없앴다고 한다.

딥 위키에서 uv 를 보면 더 이해하기 쉽다. 시간있을때 https://deepwiki.com/search/-uv-python-installer-lets-thin_90419ea2-54e1-4a0d-81bd-8894df0eedb7 를 따라가면서 한 번 봐보자!

pip은?

일단 pip 은 기본적으로 "순차적인 백트래킹" 방식으로 의존성 체크를 한다. 예로 1버전 시도 -> 충돌 -> 1.1버전 시도 이런식으로 말이다. 그렇기에 순환 참조를 포함한 복잡한 의존성 그래프에서는 최적의 조합을 찾지 못하거나 해결에 실패하는 '의존성 지옥(dependency hell)'에 빠질 수 있다. 게다가 이 과정에서 종종 전체 패키지 파일(wheel)을 다운로드하여 메타데이터를 확인해야 하므로 I/O 비용이 크다!

uv는?

언급한대로 "SAT 해결기 기반 접근법" 을 사용한다. (SAT: Boolean Satisfiability Problem, 부울 충족 가능성 문제) 유사한 문제로 취급하는 PubGrub 알고리즘에서 영감을 받았다고 한다. - Resolver internals

3) uv 장단점, 기존 것과 비교

uv 장점

  1. rust 바이너리기반 훨등한 속도. Poetry Was Good, Uv Is Better: An MLOps Migration Story 에서 poetry 대비 CI/CD 속도가 기하급수적으로 빨라졌다는 (약 10배) 후기도 있다.

  2. "찐 통합 툴". installer, pyenv, virtualenv 심플하게 할 수 있으며 (이상한) pipx 대신 uvx, uv tool 로 대신할 수 있다. uv publishtwine 없이 배포할 수 있다.

  3. "표준 준수, 심플한 세팅". PEP 621/631 에 맞는 toml 하나로 패키지 메타데이터 + 의존성 관리가능! 특히 2024년 말 도입된 PEP 735(dependency groups 표준) 을 빠르게 지원.

  4. uv sync, uv run 이 의존성 동기화를 "알아서 보장" 해줘서 workflow 의 몇 단계를 하나로 함축시킬 수 있음.

사견으로

poetry 대신 uv 로 무조건 모든 프로젝트를 넘어가지는 않았다. 위 4가지 장점은 사실 poetry 도 매우 잘 해준다. 다만 poetry 가 내게 불편 포인트를 줬던건 1) 다중 python 버저닝을 따로 다루는 것, 2) 가상환경을 즈그들 방식대로 한다는 점. 이다.

사실 2번이 매우 열받는 포인트인데, source .venv ... 로 vscode 에게 가상환경 먹여줘야 할 때, 그리고 중앙관리식 가상환경을 쓰면 오히려 사용자인 내가 가끔 까먹는다. 이게 관성에 벗어나는 방식이라...

또, 프로덕션에서는 나는 굳이 uv 또는 poetry 를 사용해서 서버를 운영하지는 않는다. 추가 써드파티나 의존성을 전혀 하고 싶지 않아서 인데, uv 는 오히려 production 에서도 사용해볼까 한다.

uv 단점

  1. CLI 커맨드가 꽤 많다. 그리고 생각보다 마냥 간단하지는 않다. uv lock --upgrade 과 같이 아니 왜 update 가 아닌건데? ㅎㅎ.

  2. uv pip installuv add 는 다르다. uv add 를 하고 uv sync 로 설치하는 흐름. 애매한 경계선에 있는 명령어들 때문에 헷갈릴 수 있다.

  3. poetrypython 으로 만들어졌다. 근데 uvrust 로 만들어졌다. 사실 python 생태계에서는 이게 꽤나 중요할지 모른다. rust 로 만들어진 python 을 위한 생태계 툴이라니,, 제 3의 기여자를 만들기 어렵다고 판단된다.

근데 아스트랄이 소통을 많이 하는 듯 하다. 다양한 장단점을 흡수하고 개선하며 정말 지속적으로 빠르게 발전하고 있다.

4) ruff 는 뭘 해결하고, 왜 빠를까?

아스트랄의 시작이자, 이름을 알린 태초의 라이브러리, ruff는 기존에 Python 코드 품질 관리를 위해 Flake8, isort, pylint, Black 등 여러 도구를 조합해야 했던 불편함을 해결하고자 했다. 위에 언급한대로, 아스트랄은 "파이썬 툴링은 훨씬 더 빨라질 수 있다" 는 가설을 Ruff 로 (Charlie Marsh) 스스로 검증하며 시작됐다.

PEP8 을 따라가기위해 pycodestyle 검사 (사내룰, 구글 스타일 가이드 등) -> flake8 -> isort (import 정렬) -> 포맷팅엔 black 등의 순서로 진행했고, 각 세팅마저 달랐다. 나의 과거글 python - flake8, Black 도입, pre-commit & clean code-style 실천하기 에서도 정말 간단한 세팅에 노고가 좀 필요했다.

근데 ruff 요놈은 한 번 딸각에 해결한다. 더욱이 uv 랑 같이쓰면 세팅이 매우매우 깔끔하고 편해진다. poetry 쓰는 사람들도 ruff 는 바로 써보면 좋겠다. poetry 랑도 합이 잘 맞는다.

왜 빠른가?

단순하게 Rust 기반이라서 빠른 것 보다, 핵심적으로 아래 3가지가 꼽힌다.

  1. ruff는 파일 단위 병렬처리를 통해 다중 코어를 적극 활용.
    • 수백 개 파일을 검사할 때 파일별 토큰화, 파싱을 스레드로 병렬 실행한다.
    • 토큰화 과정 등에서 CPU의 SIMD 명령어를 활용하여 문자열 검색 같은 작업을 한 번에 묶어서 처리한다.
    • Ruff: Extremely Fast Python Linter — Here’s Why
  2. 한번 소스 코드를 파싱하면 그 결과를 기반으로 여러 규칙을 한꺼번에 적용.
    • 토큰화/구문분석을 단 1회만 수행하고 다양한 체크를 실행한다. 즉, 다중 검사일땐 사실 모두 토큰화가 필요했는데, 이를 뛰어넘을 수 있는 것이다!
  3. 저수준 구현에서도 세세한 최적화.
    • 예를 들어 Python의 BigInt 처리에 대응하기 위해 Rust에서는 작은 정수는 64비트로, 매우 큰 정수만 힙 할당하도록 변경하여 정수 토큰화 성능을 약 8% 개선했고
    • 토큰 위치를 내부적으로 “행-열” 대신 바이트 오프셋으로 저장하여 위치 계산을 빠르게 하는 기법으로 전체 처리 시간을 10% 감소시켰다고 한다.

이러한 세심한 튜닝 덕분에 ruff는 Flake8 등의 10~100배 속도로 코드베이스를 토큰화 하고 검사하고 포맷팅할 수 있다!

5) ruff 장단점, 기존 것과 비교

ruff 장점

  1. ruff는 하나의 툴로 800개 이상의 린트 규칙(기존 Flake8 플러그인 다수 포함)을 제공하고, 코드 자동포맷팅 기능도 지원한다.

  2. 린팅, 포맷팅 속도가 거의 실시간이다. 대규모 코드베이스에서도 ruff를 돌리는 데 걸리는 시간은 블링크 수준이라, 실시간 피드백을 받기에 충분하다.

  3. 더 이상 프로젝트에 flake8, black, isort 등을 모두 설치하고 설정할 필요 없이 [tool.ruff] 하나의 설정으로 일관성 있게 관리할 수 있다.

    • 특히 ruff의 포매터는 Black과 >99.9% 동일한 결과를 내도록 설계되어, 기존 Black 사용 프로젝트도 쉽게 ruff로 전환할 수 있다.
    • 예를 들어 Django 전체 코드 포맷 비교에서 ruff와 Black의 결과 차이는 극히 일부분(2772개 파일 중 34파일)뿐이었다고 한다.
    • ruff가 기본적으로 Black의 스타일 가이드를 따르고, 라인 길이 등 기본값도 Black과 동일하게 설정되어 있어서 기존 코드에 최소한의 변경만 주기 때문.
  4. ruff는 IDE/에디터 통합과 CI 연동이 잘 되어 있다! (탄생때부터 이걸 노린 듯하다.)

    • VS Code 확장이나 PyCharm 플러그인 등이 이미 나와 있어 빠른 피드백을 얻을 수 있고
    • GitHub Actions용 액션이나 pre-commit 훅도 공식 지원하여 쉽게 품질 검증 파이프라인에 넣을 수 있다.
  5. ruff는 지금까지도 빠르게 진화하고 있다. 규칙 커버리지도 계속 늘어나고 있고, 향후 static type checker(ty) 까지 개발 중일 정도로 툴체인 확장을 모색하고 있다.

ruff 단점

  1. ruff는 기본적으로 모든 검사를 내장하고 있어, Flake8처럼 사용자가 간편하게 플러그인을 추가하는 방식은 지원하지 않는다.

    • 따라서 혹시 기존 프로젝트에서 매우 특수한 Flake8 플러그인을 쓰고 있었다면, 또는 사내 전용 flake8 룰세팅이 있다면, 직접 개발하지 않는 이상 이를 도입할 수 "없다".
    • ruff에 해당 규칙이 구현되길 기다리거나 직접 기여해야 한다. (그래도 인기 있는 검사들은 거의 다 포함되어 있고 부족한 부분도 빠르게 채워지고 있다고 한다.)
  2. Pylint 처럼 "정적 분석 수준에서 코드의 논리적 버그" 까지 잡아주는 기능은 ruff에는 없다. (ruff는 타입체커가 아니고 주로 스타일, 버그 패턴 위주 검사임).

    • 타입 안정성 검사 등을 위해서는 mypy 같은 도구를 별도로 운용해야 할 수도 있다. (이는 앞서 언급한 장점대로, ty 등과 캐미가 어떻게 되냐에 따라 굉장히 달라질 듯 하다.)
    • 그 외에 ruff 역시 Rust로 만들어졌다는 점에서, Python으로 작성된 flake8/pylint보다 사용자가 룰을 커스터마이징하거나 기여하기 어렵다는 의견이 있다.

2. uv & ruff 설치와 세팅

MacOS 기준

1) uv install & init

$ curl -LsSf https://astral.sh/uv/install.sh | sh

다른 공식자료들, 외부 자료들 때문과 PATH 때문이라도 로 brew 로 하지 않았으면 한다..
RustPython이 없어도 uv 바이너리를 시스템에 설치해준다.

$ mkdir myproject && cd myproject
$ uv init .

타 써드파티와 매우 유사하다. init 을 하면 pyproject.toml 이 만들어지며 poetry 와 유사하게 [project] 메타데이터 및 의존성 섹션을 활용한다. README.md, .python-versiongit 까지 세팅해준다.

uv init --lib mylib       # 라이브러리 템플릿
uv init --package mypkg   # 패키징 가능한 프로젝트(기본 빌드 시스템 부여)

재미있는 포인트는, 라이브러리 템플릿, 패키징 가능한 플젝 템플릿도 바로 제공해준다.

2) uv 파이썬 버저닝과 가상환경

python 버전 변경

uv python pin 3.13 과 같이 하며, 자동으로 관련 값들을 업데이트 해준다.

uv python list 로 내가 지금 설치한 python version 과 path 를 모두 한 눈에 확인할 수 있다. 좋다! 물론 이는 위 사진에서 cpython-3.13.0-... 을 보면 알겠지만 alias 를 포함한 것이다. (정확하게는 심볼릭 링크)

가상환경은 어떻게 함?

오피셜하게는 uv run 을 하면 가상환경과 프로젝트 세팅 & 인터프리터 세팅을 할 수 있다. 즉, 뭔가를 지금 플젝에 세팅한대로 python 을 실행할때, 마치 poetry run ... 처럼, uv run ... 을 하면 된다.

uv add fastapi 와 같이 패키지를 설치하며, 기존 가상환경이 없으면 add 만으로 바로 가상환경을 만든다.

결론적으로 "가상환경 activation은 source .venv/bin/activate 와 같이, 개인적으로 나에게 아주아주 익숙하고 편한 방식으로 관리할 수 있다. 진짜 이름마저 찰떡.

lock file 다루기

만약 패키지매니저가 처음이라면, lock 이 익숙하지 않을 수 있다. 간단하다. "모든 개발자·머신·배포 환경에서 동일한 설치 결과를 재현하기 위해" uv.lock 을 사용한다. 그래서 플랫폼(운영체제/아키텍처/파이썬 버전) 차이까지 고려한 보편(크로스플랫폼)적인 패키지 정보들을 제공한다.

uv lock 으로 lock file 을 만들고 기존 lock 과 uv lock --check 로 최신성만 빠르게 검사할 수 있다. uv run --lockeduv sync --locked 으로 엄격한 모드로 실행하고 싱크를 맞추게 할 수 있다.

그리고 addpip install 핵심 차이는 아래와 같다. (lock file 갱신 여부 차이)

- `uv add`: pyproject/lockfile **갱신** + .venv 설치
- `uv pip install`: lockfile **미갱신**(실험/임시 설치에 유용)

다른 uv 기반 플젝에서 시작하기

uv.lock 이 있다면 uv sync 한 번 으로 가능하다. (사실 이건 poetry 도 그럼). 락파일이 있다면 그 버전 그대로 설치하고, 락파일이 없고 pyproject.toml 만 있다면 uv sync 하기 전에 uv lock 으로 명시적 생성 후 실행하는 것을 추천한다.

그 외 아스트랄 uv 공식 docs의 Features 에서 확인하길 바란다. uv tree, uv build, uv publish 사실 poetry 랑 거의 동일하다고 느껴진다.

3) ruff install & init

uv 를 쓴다고 가정하고, ruff 를 설치할땐 아래와 같이 한다. "--group" 이 앞서 언급한 PEP 735(dependency groups 표준) 이다.

$ uv add --group dev ruff

위 사진과 같이 dependencies.dev 섹션에 추가 된다. poetry add -D ruff 와 동일한 맥락이다. 근데 설치없이 uvuvx 를 통해 바로 실행할 수 있다! (이는 비추천)

uvx ruff check
uvx ruff format

기본 사용법

린팅

ruff check .                 # 재귀적으로 모든 파이썬 파일 검사
ruff check path/to/file.py   # 특정 파일
ruff check . --fix           # 자동 수정 가능한 항목만 적용
ruff check --unsafe-fixes        # 비적용이지만 제안 표시
ruff check --fix --unsafe-fixes  # 실제 적용

포맷팅

ruff format                  # 코드 포맷
ruff format --check --diff   # CI에서 포맷 불일치 검출

기존것들이랑 섞어서 쓸 수 있음?

Migrating to ruff from black and flake8 글과 같이 ruff의 목표는 Black, Flake8, isort 등을 모두 대체하는 것 이므로 굳이 기존 린터/포매터를 함께 쓸 필요는 없다.

  1. Black 대체: 만약 기존에 Black으로 포맷팅해오던 프로젝트라면, ruff 설치 후 ruff check --diff 실행과 black 실행의 차이점 파악. 상호 세팅 차이가 있는 것을 업데이트 하고 (포메팅 규칙) black 을 삭제하면 된다.

  2. Flake8/기타 린터 대체: 맥락은 거의 위와 동일하다. Flake8 설정(.flake8이나 setup.cfg의 [flake8] 섹션 등, 와 이거 진짜 개인적으로 싫다.)

  3. 그 외는 아래 표를 살펴보자!

도구 / 플러그인ruff로 대체 가능?ruff 규칙 접두사참고
Flake8F (Pyflakes), E/W (pycodestyle)대부분의 조건에서 완벽하게 대체 가능합니다.
Black예 (포맷터)ruff format99.9% 이상 호환되지만 일부 의도된 차이점이 있습니다.
isortI임포트 정렬 기능이 내장되어 있습니다.
pyupgradeUP최신 파이썬 문법을 제안합니다.
flake8-bugbearB잠재적인 버그와 설계 문제를 찾아냅니다.
flake8-comprehensionsC4더 관용적인 컴프리헨션 작성을 돕습니다.
pydocstyleD독스트링 컨벤션을 강제합니다.
flake8-banditS일반적인 보안 문제를 확인합니다.
Pylint부분적으로PLruff는 완벽한 대체재가 아니며, 규칙 집합이 다릅니다.
사용자 정의 플러그인아니요해당 없음ruff는 서드파티 플러그인을 지원하지 않으며, 규칙은 핵심 프로젝트에 기여해야 합니다.

4) uv + ruff default conf (with pre-commit hook)

pre-commit hook 이 뭔가요?! 한다면 -> python - flake8, Black 도입, pre-commit & clean code-style 실천하기 글을 참조해 주세요!

.toml 에 세팅하기

# ================================================================
# Ruff (Linter & Formatter) Settings
# ================================================================
[tool.ruff]
# 수정에서 제외할 파일 및 디렉토리 목록
exclude = [
    ".bzr",
    ".direnv",
    ".eggs",
    ".git",
    ".hg",
    ".mypy_cache",
    ".nox",
    ".pants.d",
    ".pytype",
    ".ruff_cache",
    ".svn",
    ".tox",
    ".venv",
    "__pypackages__",
    "_build",
    "buck-out",
    "build",
    "dist",
    "node_modules",
    "venv",
    "*/migrations/*.py",
]
# 한 줄의 최대 글자 수
line-length = 120

# --- Linter (코드 분석기) 설정 ---
[tool.ruff.lint]
# 활성화할 규칙 선택:
# E, W: pycodestyle (에러, 경고)
# F: Pyflakes (논리적 오류)
# I: isort (import 정렬)
select = ["E", "F", "W", "I"]
ignore = []

# 모든 수정 가능한 규칙을 자동으로 고치도록 설정
fixable = ["ALL"]
unfixable = []

# --- Formatter (코드 포맷터) 설정 ---
[tool.ruff.format]
# Black과 유사한 포맷팅 스타일을 따릅니다.
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"

# --- 플러그인별 상세 설정 ---
[tool.ruff.lint.isort]
# 프로젝트에서 사용하는 서드파티 라이브러리 목록
known-third-party = ["django", "rest_framework", "graphene_django"]

# --- 파일/디렉토리별 규칙 무시 설정 ---
[tool.ruff.lint.per-file-ignores]
# settings 파일: 와일드카드 import 허용
"config/settings/*" = ["F403", "F405"]

# __init__.py 파일: 하위 모듈 노출을 위한 미사용/와일드카드 import 허용
"**/__init__.py" = ["F401", "F403"]

# 테스트 파일: 가독성을 위해 긴 줄 허용
"**/test_*.py" = ["E501"]

# 긴 문자열이 많은 파일들, 줄 길이(E501) 등 규칙 무시
"utils/(모자이크)/*" = ["E501", "W291", "W293"]
"batch/*" = ["E501", "W291", "W293"]
"artifacts/*" = ["E501", "W291", "W293"]

# 기타 특정 파일들의 줄 길이(E501) 규칙 무시
"utils/(모자이크).py" = ["E501"]
"config/(모자이크)/(모자이크).py" = ["E501"]
"config/(모자이크).py" = ["E501"]
"apps/(모자이크).py" = ["E501", "W291"]

위는 실제 내가 프로젝트에 적용해서 사용하는 ruff 세팅이다. 통으로 수정을 제외하는 디렉토리, 한 줄 최대 글자 수, 활성화 린팅 그룹 세팅과 포맷팅 세팅, 자체 플러그인, 자체 써드파티, 특정 파일의 특정 규칙만 무시하기!

[tool.ruff.lint] 에서 I 를 추가했기에 import 역시 isort 대로 린팅&포맷팅을 한다.

uv pre-commit

https://github.com/astral-sh/uv-pre-commit 에서 uv 관련된 pre-commit 들을 볼 수 있다. 특히 배포환경에서는 uv 등의 패키지매니저를 굳이 사용하지 않을때 중요한건 requirements.txtlock file 에 준하게 strict 한 버전으로 만들어야 한다는 것! 그래서 아래와 같은 pre-commit 을 사용할 수 있다!

- repo: https://github.com/astral-sh/uv-pre-commit
  # uv version.
  rev: 0.8.22
  hooks:
    - id: uv-export

uv export --format requirements.txt --all-groups --output-file requirements.txt 로 dev 그룹을 포함한, dev를 제외하려면 --no-dev, lock file 기준의 requirements file을 얻을 수 있다.

uv export --format requirements-txt --all-groups > requirements.txt 로도 가능하다.

ruff pre-commit

이 정도 pre-commit 은 세팅하자.

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
  rev: "v0.13.2"  # 사용하고자 하는 ruff 버전 태그
  hooks:
    - id: ruff-check    # 린트 검사 (--fix 없을 경우 검사만)
      args: [--fix]      # 자동수정 활성화 - 권장
    - id: ruff-format   # 포매팅 (Black 대체)

CI/CD, github actions 에서 workflow

- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3

CI/CD 파이프라인에 추가할때 위와 같이 넣으면 된다!

.vscode 에서도 쓰려면

.vscode/settings.json 에 아래 설정값을 추가해보자!

{
  "[python]": {
    // 1. 기본 포맷터를 'ruff'로 지정합니다.
    "editor.defaultFormatter": "charliermarsh.ruff",

    // 2. 파일을 저장할 때마다 자동으로 포맷팅을 실행합니다.
    "editor.formatOnSave": true,

    // 3. 저장 시 추가적인 코드 액션을 실행합니다.
    "editor.codeActionsOnSave": {
      // 3a. 수정 가능한 모든 린트 오류를 자동으로 수정합니다.
      "source.fixAll.ruff": "always",
      // 3b. isort 규칙에 따라 임포트 구문을 자동으로 정렬합니다.
      "source.organizeImports.ruff": "always"
    }
  },

  // 4. ruff가 프로젝트의 가상 환경을 올바르게 인식하도록 인터프리터 경로를 지정합니다.
  //    이렇게 하면 ruff가 설치된 패키지를 정확히 인지하고 'import' 관련 오류를 줄일 수 있습니다.
  "ruff.interpreter": ["${workspaceFolder}/.venv/bin/python"],

  // 5. (선택 사항) ruff 실행 시 추가 인자를 전달할 수 있습니다.
  //    예: 특정 설정 파일을 강제하거나, preview 기능을 활성화할 때 유용합니다.
  "ruff.lint.args": ["--config=pyproject.toml", "--preview"],
  "ruff.format.args": ["--config=pyproject.toml", "--preview"]
}

3. 바쁜분들을 위한 올인원 세팅 커멘드

  1. uv 이니셜라이징하고, ruff 랑 pre-commit 인스톨 후 lock 파일 싱크맞추기
uv init .
uv add --group dev ruff pre-commit
uv lock
uv sync --all-groups

# requirements.txt 만들기
uv export --format requirements-txt --all-groups > requirements.txt
  1. ruff 린팅 포맷팅 세팅과 러닝
# .toml 에 최소한의 ruff 세팅
[tool.ruff]
line-length = 120

[tool.ruff.lint]
select = ["E", "F", "I"]
ignore = []

[tool.ruff.format]
# Black 호환 기본값 권장. 필요 시만 조정.
uv run ruff check  # 린팅

# 자동 수정, 포맷팅
uv run ruff check . --fix
uv run ruff check . --fix --unsafe-fixes   # 위험도가 높은 자동수정까지 허용
  1. 빠르게 pre-commit 에 등록해서 사용하기
# .pre-commit-config.yaml (예: ruff 전용 리포지토리 사용)
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
  rev: "v0.13.2"
  hooks:
    - id: ruff-check
      args: [--fix]
    - id: ruff-format
uv run pre-commit run --all-files
  1. github ci/cd 퀵 구성 (workflow)
name: ci
on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # uv 설치 + 캐시
      - uses: astral-sh/setup-uv@v6
        with:
          enable-cache: true  # 선택사항이지만 명시 권장

      # 프로젝트 환경을 락 기준으로 정확히 동기화 (dev 포함)
      - run: uv sync --locked --all-groups

      # 포맷 확인(자동 수정 금지). 방금 sync 했으므로 run은 no-sync로 빠르게
      - run: uv run --no-sync ruff format . --check

      # 린트(gh 어노테이션). 마찬가지로 no-sync
      - run: uv run --no-sync ruff check . --output-format=github

PS. github CI/CD 에서 testing 때문에 uv 를 사용하지, 진짜 ruff만 쓸꺼면 uv 필요 없다!
PS. .vscode/settings.json 를 쓰는 것은 비추천한다. 무지성 날코드 후 오토세이브로 구원받는 습관을 들이게 된다. 내 얘기가 맞다.


출처

  1. uv 공식 문서 – Astral 제공 (uv 소개 및 사용법)
  2. Astral 블로그: uv (2024) – Charlie Marsh, uv 탄생 배경 및 벤치마크
  3. Poetry vs uv – Kevin Renskers, uv와 Poetry 비교 후기
  4. “Ruff: Extremely Fast Python Linter — Here’s Why”, ruff 성능 기법 분석
  5. “Migrating to ruff from black and flake8”, ruff 마이그레이션 실례
  6. “uv ill like uv” (2025), uv 최신 기능과 통계 정리
  7. Python GitHub 저장소 – python-poetry/poetry (스타 수 등 프로젝트 현황)
  8. 아스트랄 공식 깃헙 레포
  9. python - flake8, Black 도입, pre-commit & clean code-style 실천하기
  10. Python Enhancement Proposal
profile
🔥 도메인 중심의 개발, 깊이의 가치를 이해하고 “문제 해결의 본질” 에 몰두하는 software/product 개발자, 정현우 입니다.

2개의 댓글

comment-user-thumbnail
3일 전

Poetry -> uv로 넘어갔는데 진짜 개인적으로는 너무 편하네요
특히 파이썬 독립적이어서 버전별 테스트도 너무 좋고 uv + pep 723 조합 최고.
uv run만 있으면 비개발자분이 의존성 문제가 훨씬 줄어들고, 하드링크 사용하니 설치 중복도 줄고...

1개의 답글