FastAPI는 Python의 기본 자료형(str
, int
, float
, bool
) 외에도 다양한 데이터 타입을 지원하여 API 개발을 더욱 유연하게 해준다. 이번 글에서는 FastAPI에서 UUID
, datetime
, date
, time
, timedelta
, frozenset
, bytes
, decimal
등과 같은 다양한 데이터 타입을 처리하는 방법에 대해 알아보자.
1. UUID
UUID
(Universally Unique Identifier)는 고유 식별자이다. FastAPI는 UUID
타입을 직접 지원하며, Pydantic을 사용하여 모델에서 쉽게 처리할 수 있다.
from fastapi import FastAPI
from pydantic import BaseModel
from uuid import UUID
app = FastAPI()
class Item(BaseModel):
id: UUID
name: str
@app.post("/items/")
async def create_item(item: Item):
return item
위 코드에서 id
필드는 UUID
타입으로 지정되어 있다. 클라이언트는 다음과 같은 형식의 UUID 데이터를 전송할 수 있다.
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Sample Item"
}
만일 id가 32자리
가 아니라면 아래와 422 Unprocessable Entity 오류가 발생한다.
{
"detail": [
{
"type": "uuid_parsing",
"loc": [
"body",
"id"
],
"msg": "Input should be a valid UUID, invalid group length in group 4: expected 12, found 11",
"input": "123e4567-e89b-12d3-a456-42661417400",
"ctx": {
"error": "invalid group length in group 4: expected 12, found 11"
}
}
]
}
2. datetime
datetime
은 날짜와 시간을 포함한 데이터 타입이다. FastAPI는 datetime
타입을 지원하며, 이를 통해 날짜와 시간 정보를 처리할 수 있다.
from fastapi import FastAPI
from pydantic import BaseModel
from datetime import datetime
app = FastAPI()
class Event(BaseModel):
name: str
event_datetime: datetime
@app.post("/events/")
async def create_event(event: Event):
return event
클라이언트는 다음과 같은 형식으로 datetime
데이터를 전송할 수 있습니다.
{
"name": "Conference",
"event_datetime": "2024-08-16T10:00:00"
}
또는 아래와 같이 전송해도 된다.
{
"name": "Conference",
"event_datetime": "2024-08-16 10:00:00"
}
3. date
date
타입은 날짜만을 포함하며, 시간 정보는 포함하지 않는다. date
타입은 종종 생년월일, 이벤트 날짜 등의 데이터를 처리할 때 사용된다.
from fastapi import FastAPI
from pydantic import BaseModel
from datetime import date
app = FastAPI()
class Event(BaseModel):
name: str
event_date: date
@app.post("/events/")
async def create_event(event: Event):
return event
이 경우 클라이언트는 다음과 같은 데이터를 전송할 수 있다.
{
"name": "Birthday Party",
"event_date": "2024-08-16"
}
4. time
time
타입은 시간만을 포함하며, 날짜 정보는 포함하지 않는다. 특정 시간대에 대한 데이터 처리에 유용하다.
from fastapi import FastAPI
from pydantic import BaseModel
from datetime import time
app = FastAPI()
class Schedule(BaseModel):
name: str
start_time: time
@app.post("/schedules/")
async def create_schedule(schedule: Schedule):
return schedule
클라이언트는 다음과 같은 형식으로 time
데이터를 전송할 수 있다.
{
"name": "Morning Run",
"start_time": "06:30:00"
}
5. timedelta
timedelta
는 두 날짜 또는 시간 간의 차이를 나타낸다. FastAPI는 이를 지원하여 시간 간격 데이터를 처리할 수 있다.
from fastapi import FastAPI
from pydantic import BaseModel
from datetime import timedelta
app = FastAPI()
class Task(BaseModel):
name: str
duration: timedelta
@app.post("/tasks/")
async def create_task(task: Task):
return task
클라이언트는 다음과 같은 데이터를 전송할 수 있다.
{
"name": "Workout",
"duration": "1:30:00"
}
6. frozenset
frozenset
은 불변 집합으로, FastAPI에서 다루기 위해 Pydantic의 지원을 받는다. frozenset
은 고유한 값을 유지하면서도 변하지 않도록 할 때 유용하다.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class ItemCollection(BaseModel):
tags: frozenset[str]
@app.post("/collections/")
async def create_collection(collection: ItemCollection):
return collection
클라이언트는 다음과 같은 형식으로 frozenset
데이터를 전송할 수 있다.
{
"tags": ["red", "blue", "green"]
}
7. bytes
bytes
는 이진 데이터(예: 파일의 내용)를 처리하는 데 사용된다. FastAPI는 바이트 데이터 처리를 기본적으로 지원한다.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class FileData(BaseModel):
file_name: str
data: bytes
@app.post("/files/")
async def upload_file(file_data: FileData):
return {"file_name": file_data.file_name, "data_length": len(file_data.data)}
클라이언트는 이진 데이터를 Base64로 인코딩하여 전송할 수 있다.
{
"file_name": "example.txt",
"data": "SGVsbG8gV29ybGQh" # "Hello World!"의 Base64 인코딩
}
8. decimal
decimal.Decimal
은 고정 소수점과 부동 소수점 간의 차이로 인해 발생할 수 있는 부정확성을 피하기 위해 정확한 소수점 계산이 필요한 경우 사용된다.
from fastapi import FastAPI
from pydantic import BaseModel
from decimal import Decimal
app = FastAPI()
class Product(BaseModel):
name: str
price: Decimal
@app.post("/products/")
async def create_product(product: Product):
return product
클라이언트는 다음과 같은 데이터를 전송할 수 있다.
{
"name": "Laptop",
"price": "1999.99"
}
9. 모델 생성하지 않고 body로 전송
모델을 만들지 않고 Annotated
를 사용하여 아래와 같이 한번에 전송할 수도 있다.
@app.post("/items/{item_id}")
async def read_items(
item_id: UUID,
start_datetime: Annotated[datetime, Body()],
end_datetime: Annotated[datetime, Body()],
process_after: Annotated[timedelta, Body()],
repeat_at: Annotated[time | None, Body()] = None,
):
start_process = start_datetime + process_after
duration = end_datetime - start_process
return {
"item_id": item_id,
"start_datetime": start_datetime,
"end_datetime": end_datetime,
"process_after": process_after,
"repeat_at": repeat_at,
"start_process": start_process,
"duration": duration,
}
또는 Annotated를 사용하지 않고 만드는 방법은 아래와 같다.
@app.post("/items/{item_id}")
async def read_items(
item_id: UUID,
start_datetime: datetime = Body(),
end_datetime: datetime = Body(),
process_after: timedelta = Body(),
repeat_at: time | None = Body(),
):
start_process = start_datetime + process_after
duration = end_datetime - start_process
return {
"item_id": item_id,
"start_datetime": start_datetime,
"end_datetime": end_datetime,
"process_after": process_after,
"repeat_at": repeat_at,
"start_process": start_process,
"duration": duration,
}
클라이언트에서는 다음과 같이 데이터를 전송할 수 있다.
{
"start_datetime": "2024-08-16T10:00:00",
"end_datetime": "2024-08-16T12:00:00",
"process_after": "0:20:00",
"repeat_at": "06:30:00"
}