fastapi의 열띤 토론 현장을 훔처보자

9hb_y·2025년 10월 24일

Back과사전

목록 보기
4/6
post-thumbnail

💬 FastAPI 내부 성능 측정 토론 분석: 프레임워크의 진화와 커뮤니티 협업




1. FastAPI: 현대적 Python 웹 프레임워크의 혁신

FastAPI란?

FastAPI는 2018년 Sebastián Ramírez에 의해 개발된 현대적인 Python 웹 프레임워크로, API 개발에 특화되어 있습니다. Starlette(웹 처리)와 Pydantic(데이터 검증)을 기반으로 구축되어, Python의 타입 힌트를 적극 활용한 고성능 비동기 API 서버 개발을 지원합니다.

🚀 주요 특징

뛰어난 성능: FastAPI는 Node.js나 Go와 비견될 만한 속도를 자랑하며, Flask 대비 3배 이상 빠른 성능을 보여줍니다. 비동기(async/await) 처리를 기본적으로 지원하여 높은 동시성 처리가 가능합니다.

자동 문서화: Swagger UI와 ReDoc을 사용한 자동 API 문서 생성 기능을 제공하여, 별도의 문서화 작업 없이도 상세한 API 문서를 확보할 수 있습니다.

타입 기반 검증: Python의 타입 힌트를 활용해 자동으로 데이터를 검증하고, 개발 단계에서 버그를 사전에 방지할 수 있습니다.

⭐ Python 웹 프레임워크 시장에 미친 영향

FastAPI는 기존 Django(풀스택 프레임워크)와 Flask(마이크로 프레임워크)가 지배하던 Python 웹 개발 생태계에 새로운 패러다임을 제시했습니다. 특히 RESTful API, 마이크로서비스, 머신러닝 모델 배포 등에서 압도적인 성능과 개발 생산성을 보여주며, Netflix, Microsoft를 비롯한 글로벌 기업들이 도입하는 등 빠르게 성장하고 있습니다.



2. GitHub Discussions: 커뮤니티 소통의 장

💬 Discussions의 역할

GitHub Discussions는 오픈소스 프로젝트를 중심으로 커뮤니티를 위한 공동 커뮤니케이션 포럼입니다. 포럼 형식으로 참여자들 간의 자유로운 의견 교환, 질의응답, 아이디어 논의를 할 수 있는 공간으로, 프로젝트의 소통과 정보 공유를 위한 중앙 집중식 영역을 제공합니다.

⁉️ Issues와의 차이점

Issues: 버그 보고, 새로운 기능 요청, 개선 사항 협의 등 코드와 직접 관련된 구체적인 문제를 추적하고 해결하는 공식적인 채널입니다.

Discussions: 특정 주제에 대한 장기적인 논의, 사용법 질문, 베스트 프랙티스 공유, 아이디어 교류 등 투명하고 접근 가능한 대화를 위한 비공식적 공간입니다.

활용 방식

리포지토리 관리자는 Settings에서 Discussions 기능을 활성화하고, Announcement, Q&A, Ideas 등 다양한 카테고리로 구성하여 커뮤니티의 다양한 요구를 수용할 수 있습니다.



3. 토론의 주제와 생성 배경

💬 토론 주제: "사용자 코드 외부에서 FastAPI가 수행하는 작업의 타이밍 데이터 제공"

이 토론은 2019년 11월 10일 sm-Fifteen이라는 개발자에 의해 시작되었습니다. 대용량 JSON 데이터를 반환하는 데이터베이스 프론트엔드 API를 구축하면서, API 응답 지연의 정확한 원인을 파악하기 어렵다는 실무적 문제를 제기했습니다.

🔎 문제의 핵심

FastAPI 애플리케이션에서 성능 저하를 일으키는 주요 요인은 4가지입니다.

  1. 엔드포인트 코드 실행 (사용자 코드)
  2. 데이터베이스 대기 (I/O, 사용자 코드)
  3. 데이터 검증 (Pydantic, FastAPI 내부)
  4. 데이터 직렬화 (JSON 변환, FastAPI 내부)

문제는 사용자가 time.time()으로 측정할 수 있는 것은 1, 2번뿐이며, FastAPI가 내부적으로 수행하는 3, 4번의 성능 병목을 파악할 방법이 없다는 점이었습니다. 특히 50MB 크기의 JSON을 반환하는 경우, json, ujson, orjson 중 어느 라이브러리가 유리한지, 또는 Pydantic 검증에서 병목이 발생하는지 판단하기 어려웠습니다.

제안된 솔루션

토론 작성자는 FastAPI가 내부 동작의 타이밍 정보를 노출해야 한다고 제안하며, 특히 최근 브라우저 개발자 도구에서 지원하는 Server-Timing HTTP 헤더를 활용한 프로파일링 정보 제공을 언급했습니다.



4. 토론의 주요 내용과 기술적 고민

4.1 직렬화 과정의 복잡성 이해

FastAPI 협력자 dmontagu는 직렬화가 실제로 2단계로 이루어진다는 중요한 통찰을 제공했습니다.

  1. response_model 지정 시, 반환 데이터를 Pydantic 모델로 파싱
  2. fastapi.encoders.jsonable_encoder를 사용해 JSON으로 변환

대용량 데이터에서 jsonable_encoder의 비용이 json.dumps보다 훨씬 크며, 성능 최적화를 위해서는 BaseModel.construct를 사용하면 일반 __init__ 대비 30-40배 빠른 속도를 낼 수 있다고 조언했습니다.

4.2 프로파일링 도구 탐색: yappi의 발견

dmontagu는 yappi라는 프로파일러를 제안했습니다. yappi는 ASGI 미들웨어 내에서 함수별 통계를 수집하고, Server-Timing 헤더로 반환할 수 있는 가능성을 보여줬습니다.

4.3 비동기 환경의 도전과제

sm-Fifteen이 yappi로 미들웨어를 구현했으나, 심각한 문제가 발생했습니다.

class BenchMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next) -> Response:
        yappi.start()
        response = await call_next(request)
        yappi.stop()
        # 프로파일링 데이터 수집
        yappi.clear_stats()
        response.headers["Server-Timing"] = ','.join(server_timing)
        return response

문제점: yappi의 프로파일링이 전역적이어서, 동시에 여러 요청이 들어오면 프로파일링 데이터가 뒤섞이거나 누락되었습니다.

4.4 ContextVar를 활용한 획기적 해결책

dmontagu는 Python 3.7의 contextvars와 yappi의 컨텍스트 ID 콜백 기능을 결합하는 해결책을 제시했습니다.

from contextvars import ContextVar
import yappi

yappi_request_id = ContextVar('yappi_request_id')

def get_context_id() -> int:
    try:
        return yappi_request_id.get()
    except LookupError:
        return -2

yappi.set_context_id_callback(get_context_id)

class BenchMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next) -> Response:
        ctx_id = id(request)
        yappi_request_id.set(ctx_id)
        response = await call_next(request)
        
        tracked_stats = yappi.get_func_stats({"name": func_name, "ctx_id": ctx_id})
        # Server-Timing 헤더 생성

이 방식으로 요청별로 독립적인 프로파일링이 가능해졌습니다.

4.5 yappi 개발자의 적극적 참여

놀랍게도 yappi의 개발자 sumerc가 직접 토론에 참여했습니다. FastAPI 커뮤니티의 요구사항을 듣고, 비동기 코루틴의 wall-time 프로파일링을 지원하는 새로운 기능을 개발하기 시작했습니다.

주요 개선사항

  • 컨텍스트 전환 시 _pause_ctx()_resume_ctx() 메커니즘 구현
  • C 레벨에서 필터링 구현 및 해시테이블 최적화로 10배 성능 개선
  • 2019년 12월 12일, 코루틴 프로파일링을 지원하는 yappi 1.2.1 릴리스

sm-Fifteen은 "Python에서 코루틴 프로파일링이 불가능하다고 생각했는데, 단 한 달 만에 가능해졌다"며 감격했습니다.

4.6 실무 적용 사례

2020년 1월, sm-Fifteen은 실제 작동하는 미들웨어 설정을 공유했습니다.

app.add_middleware(ServerTimingMiddleware, calls_to_track={
    "1deps": fastapi.routing.solve_dependencies.__qualname__,  # Depends 실행
    "3valid": fastapi.routing.ModelField.validate.__qualname__,  # Pydantic 검증
    "4encode": fastapi.encoders.jsonable_encoder.__qualname__,  # JSON 인코딩
    "db_exec": sqlalchemy.engine.base.Engine.execute.__qualname__,  # DB 실행
})

브라우저 개발자 도구에서 각 단계별 타이밍을 시각적으로 확인할 수 있게 되었습니다.

4.7 남은 기술적 과제

토론에서 논의된 주요 한계점들을 정리했습니다.

  1. await 시간 측정: 비동기 I/O 대기 시간을 정확히 측정하기 어려움
  2. 엔드포인트 실행 시간: get_request_handler의 클로저로 실행되어 직접 프로파일링이 불가능
  3. 메모리 관리: 장기 실행 서버에서 프로파일링 데이터 누적 문제
  4. 함수 매칭: 함수 이름으로 프로파일링 대상을 식별하는 방식의 한계


5. 토론의 결론과 향후 방향

최종 합의

2020년 4월, FastAPI 메인테이너 tiangolo는 다음과 같이 정리했습니다.

"FastAPI 측에서는 모든 준비가 완료되었습니다. 필요한 것은 제3자 미들웨어입니다."

핵심 기능을 FastAPI에 직접 통합하기보다는, 독립적인 ASGI 미들웨어 패키지로 개발하여 필요한 개발자들이 선택적으로 사용하도록 하는 방향으로 결정되었습니다.

📆 패키지화 계획

sm-Fifteen은 정식 미들웨어 패키지 출시를 약속했으나, yappi의 함수 디스크립터 매칭 기능(sumerc/yappi#41) 완료를 기다리기로 했습니다. 이 기능이 추가되면 FastAPI 공식 문서에 PR을 제출할 계획이었습니다.

현재 상태 (2024년 기준)

2024년 4월, 다시 질문이 올라왔을 때 sm-Fifteen은 다음과 같이 답했습니다.

"FastAPI에 공식 성능 훅이 추가되지는 않았습니다. GitHub의 미들웨어는 여전히 작동하지만, Pydantic V2와 orjson 네이티브 함수를 인식하도록 약간의 수정이 필요합니다."

🌟 토론의 의의

이 토론은 여러 측면에서 오픈소스 커뮤니티의 모범 사례를 보여주고 있습니다.

기술적 협업: FastAPI 개발자, yappi 개발자, 일반 사용자가 함께 문제를 해결했습니다.

실용적 솔루션: 이론적 논의에 그치지 않고, 실제 작동하는 코드와 프로토타입을 공유했습니다.

라이브러리 진화: yappi가 FastAPI 커뮤니티의 요구에 맞춰 코루틴 프로파일링 기능을 추가하는 등, 생태계 전체가 발전했습니다.

명확한 범위 설정: 모든 기능을 프레임워크에 통합하지 않고, 제3자 패키지로 분리하여 유지보수성을 확보했습니다.


마치며

"Provide timing data for things FastAPI does outside of user code" 토론은 단순한 기능 요청을 넘어, 커뮤니티 주도 개발의 힘을 보여주는 사례입니다. 실무 문제 제기, 기술적 토론, 라이브러리 개선, 프로토타입 공유, 그리고 명확한 방향 설정까지 - 오픈소스 프로젝트가 어떻게 진화하는지를 생생하게 보여줍니다.

FastAPI는 빠른 성능과 개발 편의성으로 Python 웹 프레임워크 시장에 혁신을 가져왔지만, 진정한 강점은 이러한 활발한 커뮤니티 참여와 협업 문화에 있슴을 깨닫게 되었습니다.





참고 자료

profile
Face the fear, Build the future

0개의 댓글