FastAPI 프로젝트에 Ruff 포맷팅을 적용해보자

2025. 2. 3. 21:41·💻 개발

1. 하려는 것

 저는 현재 티끌이라는 프로젝트에 서버 파트를 맡아서 진행하고 있습니다. Lambda 환경에서 FastAPI를 이용해 프로젝트를 진행하고 있는데, 좀 특이하지만 그래도 새로운 것을 접하고 익숙해지면서 재미있게 개발을 하고 있습니다.


 개발을 진행하면서 이번에는 코드 스타일 통일에 대해서 고민하게 되었습니다. 저는 현재 혼자서 서버 개발을 모두 하고 있기 때문에 우선순위에서는 좀 떨어졌는데, 원래 협업 과정에서는 최우선으로 두고 진행해야하는 사안이긴 합니다. 물론 혼자 개발하더라도 가독성이나 일관성 측면에서 보면 사용하는데 훨씬 좋은게 코드 스타일을 정해두는 것이긴 하죠.


 원래는 Pycharm에서 기본 제공하는 포매팅 기능을 사용했었는데요. 개발 과정에서 자꾸 거슬리는 부분이 패키지를 추가/삭제할 때 이 포매팅 기능에서는 자동으로 정리해주지 않는다는 점이었습니다.(IntelliJ에서는 다 해주는데..) 물론 제가 설정을 세세하게 만지지 않고 기본 설정만을 사용해서 발생한 문제이며, 세부 설정을 통해서 처리가 가능할 수도 있지만(찾아보지 않았어요..ㅎ) 이 기회에 코드 스타일도 함께 처리하려고 자주 사용되는 포매터를 도입해보기로 했습니다.

2. 필요성

  • 협업 시 충돌 최소화: 코드 스타일이 통일되지 않는다면 사소한 차이(공백, 따음표, import 순서 등)에 따라 PR에서 Conflict가 많이 발생할 수 있습니다.
  • 가독성 향상: 검증된 코드 스타일을 사용한다면 가독성은 자동으로 따라옵니다.
  • 생산성 향상: 포매터와 린터를 활용하면 잠재적으로 발생할 수 있는 에러도 체크하기 때문에 좀 더 안정적으로 개발이 가능합니다.

3. Black / Isort

처음에는 Black과 Isort 조합을 적용해보고자 했습니다.

1) Black(Link!)

Black은 상당히 오래 전부터 많이 사용된 코드 포매터라고 합니다. 기본적으로 설정된 코드 스타일이 존재해서 단순히 적용만 해도 꽤나 깔끔한 코드가 나옵니다.

아래 명령어를 통해서 간단하게 설치할 수 있고, Pycharm의 File Watchers에 등록해서 파일을 저장할 때마다 실행하도록 할 수 있습니다. 자세한 방법은 이 주소를 참고해주세요.

pip install black

파일이 저장될 때마다 실행된다면 자동 저장 기능을 켜놓았을 때 코드가 이리저리 휙휙 바뀌게 됩니다. 저는 저장할 때마다 실행되는 방식 말고 포맷팅 단축키를 눌렀을 때 자동으로 실행되도록 하고 싶어서 방법을 찾았습니다.

Black 패키지를 설치하고 좀 기다리면 Pycharm의 Preference - Tools에 Black에 대한 옵션이 나타납니다.

Black 옵션

위 사진에서 코드 서식 다시 지정 시 옵션을 체크해주면 코드 포맷팅 단축키(Option + Command + L)을 사용해서 포맷팅을 설정할 수 있습니다.

2) Isort

Isort는 import 부분을 정리해주는 도구입니다. 실행하게 되면 사용하지 않는 import를 자동으로 제거해주며, 알파벳 순서대로 정렬까지 해주는 어마무시한 기능을 가지고 있습니다.

마찬가지로 아래 명령어를 통해서 설치가 가능하며, File Watchers에 추가해서 사용도 가능합니다.

pip install isort

Isort는 위의 Black처럼 바로 Tools에 인식되어 사용할 수 없습니다. 따라서 포맷팅 단축키를 통해서 실행하기 위해서는 External Tools에 등록해야합니다.

Preference - Tools - External Tools 탭에 들어가서 추가만 해주면 되는데요.

+버튼을 눌러서 추가를 해줍니다. 옵션은 아래처럼 해주면 되는데요. Program에 넣는 값은 패키지가 설치된 위치를 찾아주시면 됩니다. 저는 venv를 사용해서 패키지를 관리하기 때문에 해당 경로를 입력해줬어요.

isort 명령어는 아래처럼 사용합니다.

isort $FilePath$

그렇기 때문에 인수는 $FilePath$를 넣어주면 되고, 작업 디렉토리는 해당 프로젝트에서 경로를 설정할 것이기 때문에 $ProjectFileDir$을 넣어주면 됩니다.

이렇게 External Tools를 추가하면 KeyMap에서 해당 외부 도구 실행에 단축키를 할당할 수 있습니다. 아래처럼 존재하니 찾아서 단축키를 추가해주시면 됩니다.

이렇게까지 하면 단축키를 통해서 isort 명령어를 실행하여, 현재 작업중인 파일의 import를 정리할 수 있게 됩니다.

3) Black + Isort 조합의 문제점

이 두가지를 함께 사용하면서 거슬리는 점이 생겼습니다. 바로 import 구문에 대한 정렬 방식이 서로 다르다는건데요. 이 때문에 어떤 포맷팅을 쓰는지에 따라서 계속 import 부분의 탭이 늘어났다가 줄었다가... 를 반복하게 됩니다. 이를 해결하기 위해서 코드 스타일을 추가로 커스텀 할 수도 있지만, 좀 번거로움을 느꼈습니다.

또한 Isort를 적용하기 위해서 포맷팅 단축키를 눌렀을 때 동작이 일관되지 않았습니다. 커서가 코드에 있을 때에는 동작하지 않고, 터미널에 커서가 있어야지만 해당 단축키로 코드의 import 구문이 포맷팅 되는것을 확인했습니다. 이 때문에 코드를 수정한 이후, Isort 포맷팅을 적용하기 위해 터미널을 클릭하고 단축키를 눌러야하는 번거로움이 또 생겼습니다.

Black Formatter를 적용했을 때
Isort Formatter를 적용했을 때

이러한 불편함 때문에 이를 통합할 수 있고 더 빠른 동작이 가능하다는 Ruff 도입을 진행하게 되었습니다.

4. Ruff란(Link!)

1) Ruff 설명

Ruff란 Rust로 개발된 Python 린터 & 포맷터입니다. 포맷팅과 린팅을 한번에 처리하도록 Black, Isort, Flake 등의 여러 도구들이 모두 포함되어있다는 점에서 우선 마음에 들었는데요. 여러개를 설치할 필요없이 하나로 다 처리할 수 있어서 깔끔함도 챙길 수 있습니다.

또 Rust로 개발되었기 때문에 매우 빠른 속도를 자랑한다는 것도 좋았습니다. 물론 기존의 Black, Isort도 빠르긴 했지만, 이것들보다 최대100배 더 빠르다고 하니 기분상으로도 그 빠름이 느껴지는 것 같습니다.

이런 장점들을 가지고 있기 때문에 Github Star의 수도 급격한 증가세를 보였습니다. 이 수가 늘어날수록 좀 더 믿을만한 도구라는 생각이 들어서 부담없이 쓸 수 있었습니다.

Ruff 사이트에서 자랑하는 속도

2) Ruff 설치

아래 명령어로 ruff를 설치하고 사용할 수 있습니다.

pip install ruff

그리고 아래 명령어로 프로젝트 내의 포맷팅이 필요한 부분을 확인할 수 있습니다.

ruff check

권장하는 변경사항으로 바로 반영할 수 있도록 하기 위해서는 --fix 옵션을 추가하면 됩니다.

ruff check --fix

아래처럼 All checks passed!가 뜬다면 모든 코드가 ruff의 포맷에 맞추어져있다는 것을 의미합니다.

만약 변경 권장사항이 존재한다면 해당 문구 대신 어느 부분에서 수정을 필요로 하는지를 알려줍니다.

 

또 Pycharm에는 Ruff Plugin도 존재합니다.

Ruff Plugin

기존 패키지보다 더 다양한 기능을 제공하는데요. 기존 코드에서 수정을 필요로 하는 부분에 밑줄을 그어주고, Fix message도 알려주게 됩니다.

Ruff Tools

Plugin을 설치하면 이렇게 Tools에서 Ruff에 대한 설정을 할 수 있습니다. 저는 포맷팅 과정에서 자동으로 Ruff가 적용되도록, Run ruff when Reformat Code 옵션을 활성화했습니다.

그리고 맨 아래에 보면 Ruff config file을 지정할 수도 있습니다. 기본 설정도 충분히 사용할만 하지만, 추가적인 설정을 하면 더 본인의 프로젝트에 맞게 설정할 수 있습니다.

3) Ruff Config File

제가 이번에 설정하면서 추가한 옵션들과 기본 옵션을 함께 보면 아래처럼 구성됩니다.

# Exclude a variety of commonly ignored directories.
exclude = [
    ".bzr",
    ".direnv",
    ".eggs",
    ".git",
    ".git-rewrite",
    ".hg",
    ".ipynb_checkpoints",
    ".mypy_cache",
    ".nox",
    ".pants.d",
    ".pyenv",
    ".pytest_cache",
    ".pytype",
    ".ruff_cache",
    ".svn",
    ".tox",
    ".venv",
    ".vscode",
    "__pypackages__",
    "_build",
    "buck-out",
    "build",
    "dist",
    "node_modules",
    "site-packages",
    "venv",
    "test" # 테스트코드 제외
]

# Black과 같은 옵션을 사용
line-length = 88
indent-width = 4

# Python 3.11버전을 사용함
target-version = "py311"

[lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`)  codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = [
    # Pycodestyle - 코드 스타일 가이드
    "E4", # import 관련 규칙 체커
    "E7", # Syntax Error 체커
    "E9", # Runtime Error 체커
    "F", # Pyflakes - 코드 검증, 에러 체커
    "B", # flake8-bugbear - 엄격한 버그 체커
    "I", # Isort - import 정렬
    "FAST" # FastAPI 관련 검사
]
ignore = [
    "E501", # 줄 길이 제한 경고 무시
    "B008", # flake8-bugbear에서 Dependency Injection 경고 무시
]

# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = [
    "B" # flake8-bugbear로 인한 경고 자동 수정 불가
]

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[format]
# Like Black, use double quotes for strings.
quote-style = "double"

# Like Black, indent with spaces, rather than tabs.
indent-style = "space"

# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false

# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"

# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false

# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"

기본적으로 config toml 파일의 구성은 Ruff의 공식 사이트에서 확인할 수 있습니다. 

저는 그 코드를 그대로 가져와서 필요한 것들을 더 추가하고, 수정하는 과정을 거쳤는데요. 기본 구성으로 사용하고 싶다면 그냥 toml 파일 없이 Ruff를 사용해도 무방합니다.

  • exclude: Formatting & Linting을 제외할 파일이나 디렉터리입니다.
  • line-length: 한 줄에 입력 가능한 최대 문자 길이를 지정합니다.
  • indent-width: 탭을 한번 눌렀을 때 몇 칸이 들어갈지 지정합니다.
  • target-version: Python 버전을 지정합니다.
  • [lint]
  • select: 어떤 도구를 사용하여 Lint할지 정합니다. select에 들어갈 수 있는 옵션은 여기에서 각 Tool에 괄호로 적힌 Code를 사용하면 됩니다. 여기서 저는 flake8-bugbear, Isort, FastAPI 관련 도구를 추가했습니다.
  • ignore: 제외할 Lint 규칙을 정합니다. select에서 추가된 Tool의 목록 중 제외할 Rule의 Code를 사용하면 됩니다.
  • fixable: 자동 수정할 Lint 규칙을 정합니다. ignore와 사용 방식을 동일합니다.
  • unfixable: 자동 수정하지 않을 Lint 규칙을 정합니다. 동일합니다.
  • dummy-variable-rgx: 무시할 더미 변수(사용하지 않는 변수) 패턴입니다. 현재는 "_"로 시작되는 변수명을 가진 변수가 사용되지 않더라도 경고하지 않도록 설정되어있습니다.
  • [format]
  • quote-style: 기본적으로 사용할 따음표 스타일입니다. double - "문자열", single - '문자열' 입니다.
  • indent-style: 들여쓰기 스타일을 지정합니다. space - 공백을 통해 들여쓰기, tab - \t를 통해 들여쓰기 입니다.
  • skip-magic-trailing-comma: magic-trailing-comma 적용 여부를 결정합니다. 리스트나 딕셔너리 맨 뒤의 항목에 쉼표를 쓸지 여부입니다. true일 시에는 skip이기 때문에 쓰지 않고, false일 때에는 자동으로 추가합니다.
  • line-ending: 개행 스타일입니다. 따로 수정할 필요 없습니다.
  • docstring-code-format: Document String("""문자열""")에 대해 포맷팅을 적용할지 여부를 설정합니다.
  • docstring-code-line-length: Document String의 한 줄당 최대 길이를 지정합니다.


6. 결과

이렇게 적용하면서 Ruff를 사용하게 되었는데요. 일단 import를 자동으로 정리해주는 Isort가 있다는 것에 정말 만족감을 느끼고 있습니다. 이 기능이 정말 필요하다고 느꼈는데, 이번 기회에 추가하게 되어서 다행이고 또 포맷팅 툴을 다양하게 접할 수 있는 기회여서 재미있었습니다. 역시 새로운 도구를 사용하는 것은 언제나 즐거운 경험인 것 같습니다. 

앞으로도 Python을 사용할 때 Ruff를 애용할 것 같네요!

'💻 개발' 카테고리의 다른 글

블루-그린 무중단 배포에서 Downtime을 측정해보자  (0) 2025.05.12
프리티어에서 블루-그린 무중단 배포를 해보자  (0) 2025.03.16
Spring Boot에서 DB에 초기 데이터를 넣어보자  (0) 2025.02.02
@OneToOne 연관에서 발생한 N+1 문제를 해결하자  (0) 2025.01.17
Spring Boot에서 Sentry가 쿼리를 추적하게 해보자  (1) 2025.01.13
'💻 개발' 카테고리의 다른 글
  • 블루-그린 무중단 배포에서 Downtime을 측정해보자
  • 프리티어에서 블루-그린 무중단 배포를 해보자
  • Spring Boot에서 DB에 초기 데이터를 넣어보자
  • @OneToOne 연관에서 발생한 N+1 문제를 해결하자
가디(Gadi)
가디(Gadi)
dev-gadi 님의 블로그 입니다.
  • 가디(Gadi)
    진짜 개발용 블로그
    가디(Gadi)
  • 전체
    오늘
    어제
    • 분류 전체보기 (9)
      • 💻 개발 (6)
      • 🤔 회고 (3)
  • 태그

    마무리
    측정
    downtime
    티끌
    무중단 배포
    CLF
    쉘 스크립트
    Python
    AWS
    Sentry
    N+1
    CI/CD
    jmeter
    springboot
    FastAPI
    formatting
    글또
    isort
    Ruff
    2025
    SQL
    log
    BLACK
    Query
    코지메이트
    자격증
    트러블슈팅
    회고
    블루-그린
  • 공지사항

  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
가디(Gadi)
FastAPI 프로젝트에 Ruff 포맷팅을 적용해보자
상단으로

티스토리툴바