FastAPI 구조 살펴보기 4 - API Documentation

Dev Smile·2025년 5월 6일
1

FastAPI

목록 보기
11/12
post-thumbnail

지난 3편의 FastAPI 구조 살펴보기 글을 통해서 ASGI 서버가 어떻게 동작하고, FastAPI의 기반이 되는 Starlette가 요청을 어떻게 처리하는지 알아보았습니다. 이번에는 FastAPIStarlette를 어떻게 확장하여 추가적인 기능들을 제공하는지 간단하게 정리해보겠습니다.

지난 3편의 FastAPI 구조 살펴보기 글을 통해서 ASGI 서버가 어떻게 동작하고, FastAPI의 기반이 되는 Starlette가 요청을 어떻게 처리하는지 알아보았습니다. 이번 글에서는 FastAPI의 OpenAPI 기반 API 문서가 언제 생성되고, 어떤 구조로 동작하는지 코드를 기반으로 구체적으로 살펴보겠습니다.

시작하기에 앞서

GitDiagram이라는 github repo를 빠르게 시각화하여 이해하는데 도움을 주는 도구가 있습니다.

FastAPI Diagram

위 그림은 GitDiagram로 확인한 FastAPI의 Diagram 입니다.

구조 파악을 위해서 diagram 형식으로 보면 파악하기 좋은 것 같아서 가져와 보았습니다.

FastAPI 구조 살펴보기 1에서 설명드렸던 것 처럼 FastAPI는 ASGI 표준 위에서 동작합니다.
Diagram을 확인하여 보면 대표적인 ASGI 서버인 Uvicorn이 요청을 받고 FastAPI 애플리케이션에 전달함을 확인할 수 있습니다.

그리고 diagram에 표시되어 있지는 않지만, FastAPI 구조 살펴보기 2, 3에서 설명드렸던 것 처럼 Uvicorn이 전달하는 FastAPI Application은 Starlette 클래스를 상속받아 생성되어 있습니다.

API Documentation은 Documentation 단락에서 OpenAPI Schema와 Swagger UI, ReDoc을 사용함을 파악할 수 있습니다.

이제 Swagger UI, ReDoc 같은 기능이 어디서 어떻게 생성되는지 하나씩 짚어보겠습니다.


1. FastAPI는 언제 문서를 생성하는가?

FastAPI 인스턴스를 생성할 때, 내부적으로 자동으로 문서 생성 경로를 설정합니다. 다음은 FastAPI의 생성자 내부를 살펴본 코드입니다.

fastapi/application.py : __init__

class FastAPI(Starlette):
    def __init__(
      self, 
      ..., 
      openapi_url="/openapi.json",  #L205 ~ 227
      docs_url="/docs",  #L399 ~ 422
      redoc_url="/redoc",  #L423 ~ 446
      ...) -> None:
          ...
          self.openapi_url = openapi_url  #L831
          self.docs_url = docs_url  #L834
          self.redoc_url = redoc_url  #L835
          ...
          self.setup()
  • openapi_url: OpenAPI JSON 정의를 제공하는 경로 (/openapi.json)
  • docs_url: Swagger UI 경로 (/docs)
  • redoc_url: ReDoc 경로 (/redoc)

즉, 앱 인스턴스를 만드는 시점에 문서 경로들이 이미 등록되고 있습니다.


2. FastAPI 문서 관련 라우트는 어디서 추가될까?

문서 관련 경로들을 실제로 추가하는 코드는 setup 함수 안에 있습니다.

fastapi/application.py : setup()

def setup(self) -> None:
    ...
    if self.openapi_url:
        self.add_route(self.openapi_url, openapi, include_in_schema=False)
    if self.openapi_url and self.docs_url:
        self.add_route(self.docs_url, swagger_ui_html, include_in_schema=False)
    if self.openapi_url and self.redoc_url:
        self.add_route(self.redoc_url, redoc_html, include_in_schema=False)
  • /openapi.jsonself.openapi_json 핸들러
  • /docs → Swagger UI HTML
  • /redoc → ReDoc HTML

이처럼 FastAPI는 문서 관련 핸들러를 기본 라우터에 등록하여, 별도 설정 없이도 자동으로 문서가 제공됩니다.


3. 문서의 핵심: get_openapi

FastAPI의 OpenAPI 스펙(JSON)은 다음 함수에서 동적으로 생성됩니다.

fastapi/openapi/utils.py : get_openapi()

def get_openapi(
    title: str,
    version: str,
    routes: Sequence[BaseRoute],
    ... # 기타 설정들
) -> Dict[str, Any]:
    ...
    info: Dict[str, Any] = {"title": title, "version": version}
    output: Dict[str, Any] = {"openapi": openapi_version, "info": info}
    ...
    for route in routes or []:
        if isinstance(route, routing.APIRoute):
            result = get_openapi_path(
                route=route,
                operation_ids=operation_ids,
                schema_generator=schema_generator,
                model_name_map=model_name_map,
                field_mapping=field_mapping,
                separate_input_output_schemas=separate_input_output_schemas,
            )
            if result:
                path, security_schemes, path_definitions = result
                if path:
                    paths.setdefault(route.path_format, {}).update(path)
                if security_schemes:
                    components.setdefault("securitySchemes", {}).update(
                        security_schemes
                    )
                if path_definitions:
                    definitions.update(path_definitions)
  • FastAPI는 모든 APIRoute를 탐색하여 OpenAPI의 paths에 자동으로 추가합니다.
  • response_model, status_code, dependencies, summary 등도 이 과정에서 함께 문서화됩니다.

결과적으로 get_openapi()는 FastAPI 라우터에서 정의된 모든 정보를 수집해 OpenAPI 표준 JSON으로 변환합니다.


4. Swagger / ReDoc UI는 어떻게 동작하나?

Swagger와 ReDoc은 각각 swagger_ui_html, redoc_html 메서드를 통해 HTML이 제공됩니다.

fastapi/application.py : swagger_ui_html()

async def swagger_ui_html(req: Request) -> HTMLResponse:
    return get_swagger_ui_html(
        openapi_url=openapi_url,
        title=f"{self.title} - Swagger UI",
        ...
    )

fastapi/openapi/utils.py : get_swagger_ui_html()

def get_swagger_ui_html(...) -> HTMLResponse:
    html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        ...
        <<script src="{swagger_js_url}"></script>
        ...
    </head>
    <body>
        <div id="swagger-ui"></div>
        <script>
        const ui = SwaggerUIBundle({{
            url: "{openapi_url}",
            ...
        }})
        </script>
    </body>
    </html>
    """
    return HTMLResponse(html)

이 코드를 통해 FastAPI는 Swagger UI와 ReDoc의 CDN을 포함한 HTML을 동적으로 렌더링하여 /docs, /redoc에서 제공하게 됩니다.


5. 커스터마이징 가능한가?

물론 가능합니다. FastAPI의 생성자에서 다음과 같은 인자들을 조정할 수 있습니다:

app = FastAPI(
    docs_url="/swagger",
    redoc_url=None,
    openapi_url="/custom-openapi.json"
)

이렇게 하면:

  • Swagger UI: /swagger에서 접근 가능
  • ReDoc: 비활성화
  • OpenAPI JSON: /custom-openapi.json에서 제공

또한, app.openapi_schema에 접근하거나 app.openapi = custom_openapi로 오버라이딩하면 문서 스펙 자체도 직접 커스터마이징할 수 있습니다.


마치며

이번 글에서는 FastAPI가 API 문서를 어떻게 처리하고 자동으로 생성하는지를 GitHub 코드를 바탕으로 살펴보았습니다. 정리하면 다음과 같습니다.

  • 문서는 FastAPI 인스턴스 생성 시 자동으로 등록
  • /docs, /redoc, /openapi.json은 기본 경로로 설정됨
  • get_openapi() 함수가 핵심 로직
  • Swagger UI, ReDoc은 각각 HTML을 렌더링하여 UI 제공

오늘은 아래 글을 참고하여 작성했습니다:

0개의 댓글