FastAPI에서 의존성 주입을 사용하다 보면 Ruff 또는 flake8-bugbear에서 B008 경고가 발생할 수 있습니다.
예를 들어 다음과 같이 Depends()를 기본 인자로 사용하는 코드는 FastAPI에서 매우 일반적인 패턴입니다.
from fastapi import Depends
async def get_user(db=Depends(get_db)):
...
하지만 Ruff는 이 코드를 보고 다음과 같은 경고를 표시할 수 있습니다.
B008 Do not perform function call `Depends` in argument defaults
처음에는 코드가 잘못 작성된 것처럼 보였지만, 확인해보니 FastAPI의 Depends() 사용 방식에서는 일반적인 버그가 아니라 Ruff의 오탐(false positive)에 가까운 케이스였습니다.
참고한 글은 다음 Stack Overflow 답변입니다.
B008 경고가 발생하는 이유
Ruff의 B008 규칙은 함수의 기본 인자에서 함수 호출을 사용하는 패턴을 경고합니다.
def my_func(x=some_function()):
...
Python에서 기본 인자는 함수가 호출될 때마다 새로 평가되는 것이 아니라, 함수가 정의되는 시점에 한 번만 평가됩니다. Ruff 공식 문서에서도 기본 인자의 함수 호출은 정의 시점에 한 번만 실행되고, 이후 호출에서 같은 값이 재사용될 수 있다고 설명합니다.
이 특성 때문에 다음과 같은 코드는 의도치 않은 버그를 만들 수 있습니다.
def append_to(element, to=list()):
to.append(element)
return to
위 코드에서 list()는 함수가 호출될 때마다 새로 실행되지 않습니다. 함수 정의 시점에 한 번만 실행되고, 이후 모든 호출에서 같은 리스트 객체를 공유합니다.
즉, Ruff의 B008은 이런 실수를 방지하기 위한 규칙입니다.
FastAPI의 Depends()는 왜 예외인가?
FastAPI에서는 의존성 주입을 위해 Depends()를 기본 인자에 사용하는 것이 공식적으로 널리 사용되는 패턴입니다.
from fastapi import Depends
@app.get("/items")
async def read_items(db=Depends(get_db)):
...
일반적인 Python 함수라면 기본 인자에서 함수 호출을 사용하는 것이 위험할 수 있습니다. 하지만 FastAPI의 Depends()는 실제 의존성 결과를 기본 인자 값으로 직접 저장하려는 목적이 아닙니다.
Depends(get_db)는 FastAPI에게 다음과 같은 정보를 전달하는 역할을 합니다.
이 엔드포인트를 실행할 때 get_db 의존성을 사용해야 한다.
즉, Depends() 호출 자체는 함수 정의 시점에 한 번 실행될 수 있지만, FastAPI는 이 정보를 바탕으로 요청 처리 과정에서 의존성을 해결합니다. 따라서 일반적인 mutable default argument 문제와는 성격이 다릅니다.
Ruff는 FastAPI의 프레임워크 동작 방식까지 알 수 없기 때문에 Depends() 사용을 일반적인 기본 인자 함수 호출로 판단하고 B008을 발생시킬 수 있습니다.
해결 방법: Ruff 설정에서 FastAPI 호출을 예외 처리하기
이 경우 B008 규칙 전체를 끄기보다는, FastAPI에서 기본 인자로 사용하는 함수만 예외로 등록하는 것이 좋습니다.
pyproject.toml에 다음 설정을 추가합니다.
# pyproject.toml
[tool.ruff]
target-version = "py312"
line-length = 100
[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "UP"]
[tool.ruff.lint.flake8-bugbear]
# B008 예외:
# FastAPI의 Depends, Query, Body 등은 기본 인자로 사용하는 것이 일반적인 패턴이다.
extend-immutable-calls = [
"fastapi.Depends",
"fastapi.Query",
"fastapi.Body",
"fastapi.Header",
"fastapi.Path",
"fastapi.Cookie",
"fastapi.File",
"fastapi.Form",
"fastapi.Security",
]
Ruff 공식 문서에서도 extend-immutable-calls 설정을 통해 fastapi.Depends, fastapi.Query 같은 호출을 예외로 등록하는 예시를 제공하고 있습니다.
이 설정은 B008 또는 RUF009 같은 규칙을 평가할 때 특정 호출을 immutable한 호출로 간주하도록 지정합니다.
extend-immutable-calls의 의미
extend-immutable-calls는 Ruff에게 다음과 같이 알려주는 설정입니다.
이 함수 호출은 기본 인자에서 사용해도 문제가 없는 호출로 간주해도 된다.
즉, 아래와 같은 FastAPI 코드를 B008 대상으로 경고하지 않도록 만드는 설정입니다.
from fastapi import Depends, Query
@app.get("/items")
async def read_items(
db=Depends(get_db),
limit: int = Query(10),
):
...
주의할 점은, 이 설정이 모든 기본 인자 함수 호출을 허용하는 것은 아니라는 점입니다. 예외로 등록한 호출만 허용합니다.
따라서 다음과 같은 실제 위험한 코드는 여전히 B008 검사 대상이 됩니다.
def append_to(element, to=list()):
to.append(element)
return to
이 방식은 B008 규칙 자체를 비활성화하지 않고, FastAPI에서 의도적으로 사용하는 패턴만 예외 처리할 수 있다는 장점이 있습니다.
정리
Ruff의 B008은 기본 인자에서 함수 호출을 사용하는 위험한 패턴을 잡기 위한 규칙입니다.
Python의 기본 인자는 함수 호출 시점이 아니라 함수 정의 시점에 한 번만 평가되기 때문에, 의도치 않게 값이 공유되는 버그가 발생할 수 있습니다.
하지만 FastAPI의 Depends(), Query(), Body() 등은 프레임워크가 의존성 주입과 요청 파라미터 처리를 위해 사용하는 패턴입니다. 따라서 일반적인 mutable default argument 문제와는 다르게 봐야 합니다.
이 경우 B008 전체를 끄기보다는 pyproject.toml에서 extend-immutable-calls를 사용해 FastAPI 관련 호출만 예외로 등록하는 것이 좋습니다.
'Language > Python' 카테고리의 다른 글
| Ruff E712와 SQLAlchemy Boolean 조건식 처리하기 (0) | 2026.05.20 |
|---|---|
| Python 타입 힌트: Optional, Union, T | None 차이 정리 (0) | 2026.05.20 |