입력·출력용 OpenAPI 스키마 분리 여부
입력·출력용 OpenAPI 스키마 분리 여부 (Separate OpenAPI Schemas for Input and Output or Not)
Pydantic v2가 출시된 이후로, 생성되는 OpenAPI는 예전보다 조금 더 정확하고 올바르게 됐어요. 😎
사실 어떤 경우에는 OpenAPI 안에 같은 Pydantic 모델에 대한 JSON Schema가 두 개 생길 수도 있어요. 입력용과 출력용이죠. **기본값(default values)**이 있는지에 따라 달라요.
이게 어떻게 동작하는지, 그리고 필요하다면 어떻게 바꾸는지 볼게요.
출처: 공식문서
입력과 출력을 위한 Pydantic 모델
기본값이 있는 Pydantic 모델이 있다고 해 볼게요. 이렇게요:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
app = FastAPI()
@app.post("/items/")
def create_item(item: Item):
return item
@app.get("/items/")
def read_items() -> list[Item]:
return [
Item(
name="Portal Gun",
description="Device to travel through the multi-rick-verse",
),
Item(name="Plumbus"),
]
입력용 모델
이 모델을 여기처럼 입력으로 쓴다면:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
app = FastAPI()
@app.post("/items/")
def create_item(item: Item):
return item
@app.get("/items/")
def read_items() -> list[Item]:
return [
Item(
name="Portal Gun",
description="Device to travel through the multi-rick-verse",
),
Item(name="Plumbus"),
]
...그러면 description 필드는 필수가 아니게 돼요. 기본값이 None이기 때문이에요.
문서에서의 입력 모델
문서에서 확인해 보면 description 필드에 **빨간 별표(red asterisk)**가 없어요. 필수로 표시되지 않죠.
출력용 모델
하지만 같은 모델을 여기처럼 출력으로 쓴다면:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
app = FastAPI()
@app.post("/items/")
def create_item(item: Item):
return item
@app.get("/items/")
def read_items() -> list[Item]:
return [
Item(
name="Portal Gun",
description="Device to travel through the multi-rick-verse",
),
Item(name="Plumbus"),
]
...description에 기본값이 있기 때문에, 그 필드에 아무것도 반환하지 않아도 여전히 그 기본값을 갖게 돼요.
출력 응답 데이터용 모델
문서와 상호작용해서 응답을 확인해 보면, 코드가 description 필드 중 하나에 아무것도 추가하지 않았는데도 JSON 응답에 기본값(null)이 들어 있어요.
즉 항상 값이 있다는 뜻이에요. 값이 None(JSON에선 null)일 수 있을 뿐이죠.
그 말은 여러분 API를 쓰는 클라이언트가 값이 있는지 없는지 확인할 필요가 없다는 거예요. 그 필드가 항상 거기 있을 거라고 가정할 수 있지만, 어떤 경우에는 기본값인 None을 가질 뿐이에요.
OpenAPI에서 이걸 설명하는 방법은 그 필드를 **required(필수)**로 표시하는 거예요. 항상 있으니까요.
그래서 모델의 JSON Schema는 입력으로 쓰이느냐 출력으로 쓰이느냐에 따라 달라질 수 있어요:
- 입력에서는
description이 필수가 아님 - 출력에서는 필수(그리고
None, 즉 JSON 용어로null일 수 있음)
문서에서의 출력 모델
문서에서 출력 모델도 확인할 수 있어요. name과 description 둘 다 빨간 별표로 required로 표시돼요.
문서에서의 입력·출력 모델
그리고 OpenAPI에서 쓸 수 있는 모든 Schema(JSON Schemas)를 확인하면 두 개가 있다는 걸 볼 수 있어요. 하나는 Item-Input, 하나는 Item-Output이에요.
Item-Input에서 description은 필수가 아니에요. 빨간 별표가 없죠.
하지만 Item-Output에서 description은 필수예요. 빨간 별표가 있어요.
Pydantic v2의 이 기능 덕분에 여러분의 API 문서가 더 정밀해지고, 자동 생성된 클라이언트와 SDK가 있다면 그것들도 더 정밀해져서 더 나은 개발자 경험과 일관성을 얻게 돼요. 🎉
스키마 분리하지 않기
이제 입력과 출력에 같은 스키마를 쓰고 싶은 경우도 있어요.
아마 가장 큰 사용 사례는 이미 자동 생성된 클라이언트 코드/SDK가 있고, 아직 모든 자동 생성 코드/SDK를 업데이트하고 싶지 않을 때예요. 언젠가는 하고 싶겠지만, 지금 당장은 아닐 수 있죠.
그런 경우 FastAPI에서 separate_input_output_schemas=False 파라미터로 이 기능을 끌 수 있어요.
참고 —
separate_input_output_schemas지원은 FastAPI0.102.0에서 추가됐어요. 🤓
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
app = FastAPI(separate_input_output_schemas=False)
@app.post("/items/")
def create_item(item: Item):
return item
@app.get("/items/")
def read_items() -> list[Item]:
return [
Item(
name="Portal Gun",
description="Device to travel through the multi-rick-verse",
),
Item(name="Plumbus"),
]
문서에서의 입력·출력 모델용 단일 스키마
이제 그 모델의 입력과 출력에 대해 단 하나의 스키마만 존재하고, Item 하나뿐이며, description이 필수가 아님으로 표시돼요.