콘텐츠로 이동

API 문서 (하)#


2024 iThome 철인 대회

이 글은 Django Ninja 시리즈 튜토리얼의 18번째 글이에요.

이전 글에서는 Django Ninja가 API 문서의 표현에 영향을 미치는 몇 가지 중요한 설정을 탐구했어요. 이것들은 자동화 API 문서의 기본기이므로 무시할 수 없죠.

하지만 이것만으로는 부족해요! 우리는 이 문서를 더욱 생생하게 만들고, 읽고 이해하기 쉽게 만들고 싶어요.

그 핵심은 API 문서에 있는 데이터 예시에 있어요. 좋은 예시는 한 번 읽고 바로 이해할 수 있게 해주어 이해하고 생각하는 시간을 효과적으로 단축시켜 줘요.

이 글에서는 Pydantic의 Field 설정을 활용하여 API 문서의 명확성과 가독성을 전방위적으로 향상시키는 방법을 소개할게요. 자동 생성된 문서에 생생한 예시를 추가하여 문서가 실제 상황에 더 가깝게 느껴지도록 만드는 방법을 탐구할 거예요.

이 글의 모든 코드 변경 사항은 이 PR을 참고해 주세요.

GitHub 예제 프로젝트#

👉 Django-Ninja-Tutorial


Django Ninja에서 Pydantic의 역할#

Pydantic은 데이터 검증, 직렬화를 구현하는 패키지로, FastAPI 및 Django Ninja와 같은 프레임워크에 널리 응용되고 있어요.

Django Ninja에서 Pydantic은 Schema를 정의하는 데 사용되며, 이 Schema는 API가 HTTP 요청 및 응답에서 데이터를 처리하는 방법을 결정하고 OpenAPI 표준에 부합하는 문서로 자동 변환해요.

Pydantic의 강력한 점은 데이터를 검증할 수 있을 뿐만 아니라, Field 설정을 통해 문서 필드에 추가적인 설명, 예시 및 기본값을 제공할 수 있다는 거예요.

이러한 세부 설정은 변환된 API 문서에 자동으로 반영되어 개발자가 API의 동작과 내포된 의미를 더 잘 이해할 수 있도록 도와줘요.

Pydantic Field#

Pydantic의 Field는 매우 강력한 도구로, 제목, 설명, 예시, 기본값 등과 같은 더 많은 세부 정보를 각 데이터 필드에 제공하는 데 사용할 수 있어요.

이러한 설정은 데이터 검증에 도움이 될 뿐만 아니라 API 문서의 가독성을 크게 높여줘요. 다음은 몇 가지 일반적인 Field 매개변수예요:

  • title: 필드의 제목을 설정하여 개발자가 해당 필드의 역할을 빠르게 이해할 수 있도록 도와줘요.
  • description: 필드에 대한 설명을 제공하여 이 필드의 용도와 제한 사항을 더 명확하게 이해할 수 있게 해줘요.
  • examples: 예시 값을 설정하여 개발자가 API의 입력, 출력 형식을 직관적으로 이해할 수 있도록 도와줘요.
  • default: 첫 번째 위치 매개변수로, 필드의 기본값을 제공해요. Input에서 해당 필드 값을 제공하지 않은 경우, 자동으로 기본값이 사용돼요.

이러한 매개변수를 잘 활용하면 고품질의 API 문서를 생성할 수 있어요.

코드와 문서의 균형점#

하지만! 우리는 현실을 조금 타협해야 해요. 만약 모든 API에 이렇게 많은 내용을 작성해야 한다면, 개발자에게 부담이 너무 클 수 있어요.

또한, 대량의 매개변수를 사용하면 문서는 확실히 보기 좋아지지만, 문서를 생성하는 코드는 피할 수 없이 길고 장황해질 거예요!

우리는 충분한 정보를 제공하면서도 코드가 너무 길어지지 않는 균형점을 찾아야 해요.

이러한 관점에서 볼 때, 제가 생각하는 가장 중요한 두 가지 매개변수defaultexamples——특히 후자예요!

그래서 이 글에서는 이 두 가지에 집중해서 소개할게요. 이렇게 하면 학습의 초점도 맞출 수 있고, 제 일상적인 개발 방식과도 잘 부합하거든요.


공식 문서와 소스 코드#

Pydantic Field의 매개변수와 사용법을 더 자세히 알고 싶다면, Django Ninja가 아닌 Pydantic의 공식 문서를 봐야 해요.

Django Ninja의 문서에는 Field 사용법을 전문적으로 소개하는 장이 없어요. 왜냐하면 Field는 실제로 Pydantic의 기능이지 Django Ninja에 특화된 것이 아니기 때문이에요.

하지만 이 문서를 직접 본다면, Field의 모든 매개변수에 대한 설명이 아주 상세하지는 않다는 것을 발견할 수도 있어요.

사용 가능한 모든 매개변수를 알고 싶다면, 소스 코드를 보는 것이 가장 빠르다고 생각해요. 그런 다음 함수 시그니처 (맞아요, Field는 함수예요)의 type hints를 보고 그 사용법을 유추해 보는 것도 좋은 방법이에요.


이제, Pydantic Field의 examplesdefault 매개변수를 사용하여 API 문서를 더욱 생생하고 엄격하게 만드는 방법을 설명하기 시작할게요.

API 문서에 「예시」 추가하기#

이전 글에서 현재 API 문서의 부족한 점을 언급했는데, 그 중 「실제 예시 부족」이라는 문제는 아직 해결되지 않았어요.

다음은 「단일 게시글 정보 가져오기」의 문서 내 응답 예시예요:

{
    "id": 0,
    "title": "string",
    "content": "string",
    "author": {
        "id": 0,
        "username": "string",
        "email": "string"
    },
    "created_at": "2024-09-22T08:58:55.960Z",
    "updated_at": "2024-09-22T08:58:55.960Z"
}

0이나 "string" 모두 좋은 문서 예시라고 부를 수 없어요——둘 다 충분히 현실적이지 않거든요.

이제, 응답 Schema에 예시를 추가할 거예요. 코드는 다음과 같아요:

class _AuthorInfo(Schema):
    id: int = Field(examples=[1])
    username: str = Field(examples=['Alice'])
    email: str = Field(examples=['[email protected]'])

class PostResponse(Schema):
    id: int = Field(examples=[1])
    title: str = Field(examples=['Ninja is awesome!'])
    content: str = Field(examples=['This is my first post.'])
    author: _AuthorInfo
    created_at: datetime = Field(examples=['2021-01-01T00:00:00Z'])
    updated_at: datetime = Field(examples=['2021-01-01T00:00:00Z'])
    ...

여기에는 두 가지 핵심 포인트가 있어요.

핵심 포인트 1: examples 매개변수#

저는 examples 매개변수만 사용했어요. 이 방법이 가장 간단하고, 예시는 문서에서 상당히 중요한 부분이기 때문이에요.

게다가, 이 examples는 사실 큰 의미가 있어요. 만약 example이라고 적는다면, 예를 들어:

class PostResponse(Schema):
    id: int = Field(example=1)

실제로는 정상적으로 작동하지만, Mypy가 다음과 같이 경고할 거예요:

Unexpected keyword argument "example" for "Field"; did you mean "examples"?

맞아요, 현재의 Pydantic v2에서는 Fieldexamples 매개변수 있기 때문이에요. example은 Pydantic v1의 방식이었고, Django Ninja는 여전히 두 가지 모두에 대한 호환성을 유지하고 있는 것이죠.

미래를 고려한다면 여전히 examples를 사용하는 것을 권장해요. Mypy의 경고를 피할 수 있을 뿐만 아니라, 최신 버전의 Pydantic과 일관성을 유지할 수 있으니까요.

핵심 포인트 2: 중첩된 Schema의 예시#

중첩된(Nested) Schema의 예시는 가장 아래 계층의 Schema에 Field를 추가하기만 하면 돼요. 참조하는 계층에서는 선언할 필요가 없어요:

class _AuthorInfo(Schema):  # 이것은 중첩된 최하위 계층이므로 Field를 작성해야 해요
    id: int = Field(examples=[1])
    username: str = Field(examples=['Alice'])
    email: str = Field(examples=['[email protected]'])

class PostResponse(Schema):
    ...
    author: _AuthorInfo  # Field를 다시 작성할 필요가 없어요
    ...

실제 효과#

실제 API 문서 응답을 살펴보세요. 페이지에서 JSON 값을 직접 캡처했어요:

{
    "id": 1,
    "title": "Ninja is awesome!",
    "content": "This is my first post.",
    "author": {
        "id": 1,
        "username": "Alice",
        "email": "[email protected]"
    },
    "created_at": "2021-01-01T00:00:00Z",
    "updated_at": "2021-01-01T00:00:00Z"
}

이전의 0, "string"과 비교하면, 훨씬 더 생생하고 읽기 쉽지 않나요?


default 매개변수의 올바른 사용 시기#

제가 보기에 대부분의 경우, 우리는 기본값을 정의할 필요가 없어요.

다음과 같이 작성하는 것도 추천하지 않아요:

class PostResponse(Schema):
    id: int = 1
    ...

문서상에도 예시 값이 1로 표시되겠지만, 사실 이 작성법은 아래의 작성법과 동일해요:

class PostResponse(Schema):
    id: int = Field(default=1)
    ...

이것은 실제로 기본값을 정의하는 거예요. 앞서 언급했듯이 Schema가 HTTP 요청에 사용되고 클라이언트가 해당 필드의 값을 제공하지 않은 경우, Django Ninja는 기본값을 자동으로 사용할 거예요.

이것은 예상치 못한 결과를 초래할 가능성이 매우 커요.

올바른 흐름은: 프론트엔드에서 값을 제공하지 않았을 때, Django Ninja가 422 응답을 반환해야 한다는 거예요.

그래서 아예 기본값을 정의할 필요도 (그리고 해서도 안) 없어요——다음 상황을 제외하고는요.

선택적(Optional) 필드에 기본값 None 사용하기#

개인적으로는 요청 필드가 「선택적(optional)」일 때만 default 매개변수를 사용하는 것을 추천해요.

그리고 이때의 기본값은 None이어야 해요.

설명을 위해, 새로운 API——「사용자 추가」 (즉, 사용자 등록)를 만들어 볼게요. 이 API는 이후 튜토리얼에서도 반복적으로 언급되고 개선될 거예요.

우리의 User 모델에서 bio 필드가 선택적이었던 것 기억하시나요?

class User(AbstractUser):
    email = models.EmailField(unique=True)  # 고유한 email을 강제함
    bio = models.TextField(null=True)  # 개인 소개 필드 (선택적)
    ...

그래서, 우리의 API 요청 Schema는 다음과 같아요——직접 bio 필드 설정을 보세요:

class CreateUserRequest(Schema):
    ...
    bio: str | None = Field(
        default=None,
        examples=['Hello, I am Alice.']
    )

Field의 default=None 설정은 클라이언트가 값을 채우지 않았을 때 API도 오류를 발생시키지 않도록 해줘요.

또한 bio: str | None 이라는 type hint에 유의하세요. 절대 None을 빼먹지 마세요. 문서의 렌더링 결과에 영향을 미칠 수 있어요: (이것은 None이 있을 때의 결과예요)

None이 있어야, API 문서는 필드 값이 선택적(string | null)이라고 표시해요.


소결 및 다음 단계#

이번 장의 학습과 개선을 통해, 우리의 API 문서는 이미 80점 수준에 도달했어요! 대부분의 개발 프로젝트에서 이 정도의 문서 품질이라면 꽤 훌륭하다고 할 수 있죠.

다음으로 우리는 제5장——데이터 검증오류 처리에 들어갈 거예요.

이 장에서는 Django Ninja에서 효과적인 데이터 검증을 구현하는 방법과, 발생할 수 있는 다양한 오류 상황을 우아하게 처리하고 응답하는 방법을 다룰 거예요.

이러한 기술들을 통해, 우리는 더 견고하고 안정적인 API를 구축할 수 있을 거예요.