이 글은 Open source contribution with python 시리즈의 첫 게시글 입니다. 다음으로 작성할 내용은 GitHub Actions
, pytest
에 해당합니다.
Why should we use pre-commit?
Pre-commit에 대해서 이해하려면, 우선 "이걸 왜 사용해야 하는가?"를 이야기할 필요가 있다. 가령 대규모 프로젝트를 한다고 생각해보자. (e.g. Poetry, Docker
)

그런데 만약 "일관된 규칙"이 없다면, 개발자(contributor)들은 각자 다른 방식으로 코드를 작성할 것이며, 따라서 줄 간격이 불규칙해지므로 가독성이 저하된다는 등의 문제가 발생할 수 있다.
이를 해결하기 위해 등장한 개념이 일관된 규칙을 적용하는, Pre-commit이다. Rust, C, Ruby, Swift
처럼 다양한 언어에 대해 그들만의 규칙이 있지만, 오늘은 Python
에서 가장 많이 사용되는 Ruff에 대해서 알아보고자 한다.
What is Ruff?
Ruff
의 가장 큰 특징은 Rust로 빌드되었다는 점이다. 그 덕분에, Rust 고유의 memory-efficient & Fast 장점을 가진다. (c.f. Black, flake8 은 python으로 빌드된 pre-commit이다.)
심지어 Black, flake8
의 기능을 그대로 사용하기 때문에, "더 가벼우면서 기능은 다양한" 도구라고 볼 수 있다. 이러한 장점 덕분에, Ruff
는 python 생태계에서 표준 pre-commit
으로 자리를 잡아가는 추세이다.
Features in Ruff
Formatting
Formatting은 "줄 간격을 정리함으로써 가독성을 향상시킨다"고 볼 수 있다. 간단한 예제를 살펴보자.
# Input
def _make_ssl_transport(
rawsock, protocol, sslcontext, waiter=None,
*, server_side=False, server_hostname=None):
'''Make an SSL transport.''' # Markdown is wrtten with '
if waiter is None:
waiter = Future(loop=loop)
if extra is None:
extra = {}
...
# After Ruff
def _make_ssl_transport(
rawsock,
protocol,
sslcontext,
waiter=None,
*,
server_side=False,
server_hostname=None,
):
"""Make an SSL transport."""
if waiter is None:
waiter = Future(loop=loop)
if extra is None:
extra = {}
위의 예제에서 변화는 다음과 같이 정리해볼 수 있다. :
Function
에서augments (인자)
의 줄 간격을 조절함으로써 가독성 향상Markdown 부분을
'''
에서"""
으로 변경 (주석에'
가 들어가는 문제 방지)2줄 이상 불필요하게 줄 간격이 된 부분을 조절
Linting
Linting은 "사용되지 않는 인자를 감지해 안전하게 제거한다."고 볼 수 있다. 간단한 예제를 살펴보자.
# Input
import json # 사용되지 않음
def add(x, y):
z = 10
return x + y
# After Ruff
def add(x, y):
return x + y
위의 예제에서 변화는 다음과 같이 정리해볼 수 있다. :
Unused package (json)
이 삭제되어 코드가 간결해졌다.add(x, y)
에서 사용되지 않는assignment (z)
를 삭제해 코드가 간결해졌다.
How to use Ruff?
가장 간단한 방법은 ruff check --fix
이지만, 대규모 오픈소스는 고유의 규칙을 .pre-commit-config.yaml
파일에 지정함으로써 관리한다. (e.g. Airflow, Poetry) 따라서 만약 당신이 오픈소스에 기여하고 싶다면, 이 규칙을 준수하는 것이 요구된다.
이를 지키는 방법은 pre-commit install
을 통해서, 규칙을 로컬에 설치하는 방법이다. 이렇게 하면 commit
을 하기 전에, 자동으로 pre-commit
을 통과했는지 여부를 검사한다.

그 뒤에는 pre-commit run --all-files
를 통해서, pre-commit
을 적용시킨다. (이 과정에서 다양한 오류가 발견될 것이고, 직접 해결해야 한다.)
만약 모든 내용을 Pass
했다는 알림을 얻었다면, 이제 contribution을 할 시간이다. git commit -m "~"
이 실행 가능해지며, 드디어 commit
할 준비가 되었다고 볼 수 있다!!
Conclusion
지금까지 내용을 정리해보면, Ruff
는 코드를 더 간결하고 체계적으로 검사함으로써 높은 품질의 코드를 위해 쓰이는 도구라고 정리해볼 수 있다. 가장 중요한 점은, 모든 contributor가 일관된 규칙 아래에서 개발하는 규칙을 준수함으로써 보다 완성도 높은 프로젝트를 경험할 수 있다는 점이다.
만약 여러분이 어떤 open source project에 기여를 해보고 싶지만, pre-commit
을 통과하지 않은 상태로 PR (Pull Request)
를 작성한다면, 심한 잔소리 (또는 차단당할 수도 있다..)를 들을 수 있다. 따라서 이를 방지하기 위해서는, pre-commit
에 대해서 익숙해져야할 필요가 있다고 볼 수 있다.
See also
[1] Code Review with Ruff : Ruff 수정사항에 대한 자세한 설명이 있습니다.
[2] Sample pre-commit-config.yaml
: 연습을 위한 간단한 파일입니다.
sexclude: |
(?x)(
# Documents
^.*\.md$|
^docs/|
# Dataset & Checkpoints
^data/|
^datasets/|
^checkpoints/|
^weights/|
^.*\.pth$|
^.*\.ckpt$|
# Config files
^\.env|
^\.venv/|
^venv/|
^\.idea/|
^\.vscode/
)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-toml
- id: debug-statements
- id: check-added-large-files
args: ['--maxkb=1024']
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.9
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]