시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.
이전 내용
FastAPI 응답 모델
FastAPI 응답 모델은 클라이언트에 반환되는 데이터의 구조를 정의하는 데 사용되는 강력한 기능이다.
응답 모델을 정의함으로써 API는 반환되는 데이터의 유효성을 보장하고, OpenAPI 스키마(자동 문서화)를 생성하여 API 사용자에게 명확한 정보를 제공한다.
FastAPI의 경로 연산에서 response_model 매개변수를 사용하여 응답 모델을 지정할 수 있다.
이 매개변수는 경로 연산 함수에 의해 반환되는 데이터의 형태를 Pydantic 모델로 정의하게 해준다. 이 모델은 반환된 데이터가 클라이언트로 전송되기 전에 시리얼라이즈되는 방식을 결정한다.
★ 시리얼라이즈(serialize): 데이터를 일련의 비트로 변환하여 파일, 메모리, 네트워크를 통해 저장하거나 전송할 수 있는 형식으로 만드는 과정.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
def get_item_from_db(id):
# 매우 간단한 아이템 반환
return {
"name": "Simple Item",
"description": "A simple item description",
"price": 50.0,
"dis_price": 45.0
}
@app.get("/items/{item_id}", response_model=Item)
def read_item(item_id: int):
# 데이터베이스에서 item_id에 해당하는 아이템을 검색 후 반환
item = get_item_from_db(item_id)
return item
▶ read_item() 함수는 response_model로 Item을 사용한다. 이는 함수가 Item 인스턴스를 반환하거나 Item 모델로 시리얼라이즈할 수 있는 데이터(예.dict)를 반환한다는 의미이다.
[response_model의 장점]
- 데이터 검증: 반환되는 데이터가 response_model에 정의된 모델의 필드 및 타입과 일치하는지 FastAPI에 의해 자동으로 검증된다.
- 자동 문서 생성: FastAPI는 response_model을 사용하여 API 문서에 정확한 응답 형식을 표시한다. 이는 API 사용자가 기대할 수 있는 응답의 구조를 이해하는 데 도움이 된다.
- 보안: response_model은 경로 연산이 노출할 데이터를 제한하는 데 사용할 수 있다. (예. 모델에서 반환하지 않아야 하는 내부 정보 숨김)
response_model을 지정하지 않으면 FastAPI는 반환된 객체를 그대로 JSON으로 변환하여 클라이언트에 반환한다. 이 경우 모든 데이터가 노출될 수 있으며, 자동 문서화 기능을 온전히 활용하지 못할 수도 있다.
[주요 응답 모델의 종류]
- 기본 응답 모델: 가장 일반적인 형태. Pydantic 클래스를 이용해 모델을 정의할 수 있다.
- Generic 응답 모델: 제네릭 타입을 활용하여 다양한 타입의 응답을 동일한 엔드포인트에서 다룰 수 있다.
- Union 응답 모델: 여러 가능한 모델 중 하나가 될 수 있는 경우에 유용하다.
- List 응답 모델: 리스트 형태의 데이터를 반환활 때 사용한다.
◆ 기본 응답 모델
Pydantic의 BaseModel을 상속하여 API 응답으로 사용할 데이터 모델을 정의한다.
FastAPI 경로 연산에서 response_model 매개변수를 이용해 이 모델을 지정하면, 해당 경로 연산은 지정된 모델에 따라 응답 데이터를 검증하고 시리얼라이즈 한다.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# Pydantic 모델 정의. (응답 데이터의 구조 나타냄)
class Item(BaseModel):
name: str # 아이템의 이름 필드
price: float # 아이템의 가격 필드
# FastAPI 경로 연산 정의. 이 연산은 GET 요청을 처리하고
# 'response_model'을 'Item'으로 지정하여 반환할 데이터의 구조를 정의
@app.get("/item/", response_model=Item)
def get_item():
# 데이터베이스나 다른 데이터 소스에서 아이템을 가져와 반환
# 예시를 위해 고정된 값 반환
return {"name": "milk", "price": 3.5}
curl 명령어
curl -X GET "http://127.0.0.1:8000/item/"
▶ GET 메서드를 사용하여 /item/ 엔드포인트에 요청을 보낸다.
◆ Generic 응답 모델
Generic 응답 모델은 FastAPI에서 타입 매개변수를 이용하여 유연한 응답 타입을 정의한다. 이는 다양한 데이터 타입에 대해 재사용 가능한 응답 모델을 만들고자할 때 유용하다.
from typing import TypeVar, Generic
from fastapi import FastAPI
from pydantic.generics import GenericModel
app = FastAPI()
# 제네릭 타입 매개변수 T 선언
T = TypeVar('T')
# GenericModel을 상속받아 제네릭 응답 모델을 생성
# 이 모델은 다양한 타입의 'data' 필드를 포함
class GenericItem(GenericModel, Generic[T]):
data: T # 'data' 필드의 타입은 제네릭 타입 매개변수 T로 선언
# 경로 연산에서 'response_model'을 GenericItem[str]로 지정하여
# 반한되는 'data' 필드가 문자열 타입임을 명시
@app.get("/generic_item/", response_model=GenericItem[str])
async def get_generic_item():
# 응답 모델에 맞춰 'data' 필드에 문자열 값 반환
return {"data": "generic item"}
curl 명령어
curl -X GET "http://127.0.0.1:8000/generic_item/"
▶ GET 메서드를 사용하여 /generic_item/ 엔드포인트에 요청을 보내고, 서버는 GenericItem[str] 모델에 정의된 대로 문자열 타입의 data 필드글 갖는 JSON 응답을 반환한다.
응답은 {"data": "generic_item"} 처럼 data 필드에 "generic_item" 문자열 값을 포함하는 JSON 객체이다.
◆ Union 응답 모델
Union 응답 모델은 파이썬의 typing 모듈에 있는 Union 타입을 사용하여 하나의 경로 연산에서 여러 다른 모델 중 하나를 반환할 수 있도록 한다. 이는 API가 다양한 가능성 중 하나를 선택해서 반환해야 할 때 유용하다.
Union은 타입 힌트로 사용되며, 여기에 지정된 모델 중 하나가 응답 데이터로 사용될 수 있음을 나타낸다.
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# 각각의 동물을 나타내는 Pydantic 모델 정의
class Cat(BaseModel):
name: str # 고양이의 이름 필드
class Dog(BaseModel):
name: str # 강아지의 이름 필드
# 경로 연산 정의. 여기에서 'response_model'은 Union[Cat, Dog]로 지정되어 있음
# 이는 반환되는 응답이 Cat 혹은 Dog 모델 중 하나의 형태를 띄게 됨.
@app.get("/animal/", response_model=Union[Cat, Dog])
async def get_animal(animal: str):
# 쿼리 매개변수로 'animal'을 받아서 그에 맞는 동물 데이터 반환
if animal == "cat":
return Cat(name="Whiskers")
else:
return Dog(name="Fido")
curl 명령어
- 고양이 데이터 요청
curl -X GET "http://127.0.0.1:8000/animal/?animal=cat"
- 강아지 데이터 요청
curl -X GET "http://127.0.0.1:8000/animal/?animal=dog"
▶ /animal/ 엔드포인트로 GET 요청을 보내고, 쿼리 매개변수 animal의 값에 다라 서버는 Cat 또는 Dog 모델에 정의된 형태의 JSON 응답을 반환한다. 응답은 쿼리 매개변수 animal의 값이 "cat" 인 경우 {"name": "Whiskers"}, "dog"인 경우 {"name": "Fido"}처럼 해당 동물 이름을 포함하는 JSON 객체이다.
이는 Union[Cat, Dog] 응답 모델에 의해 정의된 데이터 구조에 따라 반환된 것이다.
◆ List 응답 모델
List 응답 모델은 FastAPI 리스트 형태의 데이터를 반환할 때 사용한다. 이 모델은 List 타입 힌트와 함께 사용되며, 반환되는 데이터가 리스트의 각 항목이 특정 모델을 준수하는지를 검증한다.
이를 통해 API 사용자는 반환된 데이터가 일정한 구조를 가지는 배열임을 기대할 수 있다.
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# Pydantic 모델 정의. (응답 데이터의 구조 나타냄)
class Item(BaseModel):
name: str # 아이템의 이름 필드
# 경로 연산 정의. 여기서 response_model'은 List[Item]으로 지정되어 있음
# 이는 반환되는 응답이 Item 인스턴스들의 리스트 임을 명시
@app.get("/items/", response_model=List[Item])
async def get_items():
# 데이터베이스나 다른 데이터 소스에서 아이템을 가져와 반환
# 예시를 위해 고정된 값 반환
return [{"name": "Item 1"}, {"name": "Item 2"}]
curl 명령어
curl -X GET "http://127.0.0.1:8000/items/"
▶ GET 메서드를 사용하여 /items/ 엔드포인트에 요청을 보내고, 서버는 List[Item] 모델에 정의된 대로 Item 객체의 리스트를 갖는 JSON 응답을 반환한다. 응답은 Item 모델의 구조에 맞춰진 [{"name": "Item 1"}, {"name": "Item 2"}]와 같은 JSON 데이터 배열이다.
이는 FastAPI에서 response_model 을 통해 정의된 리스트 응답 모델의 구조에 따라 반환된 데이터이다.
def vs async def
Python을 사용하다 보면 함수와 비동기 함수에 대해 많이 들어보셨을 것이다. 특히, def와 async def의 차이점은 Python 개발자라면 꼭 알아야 하는 중요한 개념 중 하나이다.
def와 async def의 차이는 동기와 비동기 실행 방식의 차이이다. 동기 함수는 하나의 작업이 완료될 때까지 다른 작업을 멈추게 되지만, 비동기 함수는 여러 작업을 동시에 처리할 수 있다. 따라서, 작업의 성격에 따라 적절한 함수를 선택하는 것이 중요하다.
1. def란 무엇인가?
def는 Python에서 함수를 정의할 때 사용하는 키워드이다. 일반적으로 함수는 입력 값을 받아 특정 작업을 수행한 후 결과를 반환하는 코드 블록을 의미한다. 예를 들어, 다음과 같은 코드를 통해 간단한 함수를 정의할 수 있다.
def greet(name):
return f"Hello, {name}!"
위 함수는 name이라는 매개변수를 받아서 "Hello, name!"이라는 문자열을 반환한다. 이 함수는 호출되면 바로 실행되고, 작업이 끝날 때까지 프로그램의 흐름을 잠시 멈춘다. 이와 같은 함수를 동기 함수라고 한다.
2. async def란 무엇인가?
async def는 Python에서 비동기 함수를 정의할 때 사용하는 키워드이다. 비동기 함수는 작업이 완료될 때까지 기다리지 않고, 다른 작업을 동시에 수행할 수 있는 함수이다. 예를 들어, 다음과 같은 비동기 함수를 정의할 수 있다.
import asyncio
async def greet(name):
await asyncio.sleep(1)
return f"Hello, {name}!"
위 함수는 await 키워드와 함께 asyncio.sleep(1)을 사용하여 1초 동안 대기한 후 결과를 반환한다. 비동기 함수는 await 키워드를 사용해 다른 비동기 작업이 완료될 때까지 기다릴 수 있다. 이러한 기능 덕분에, 비동기 함수는 I/O 작업(예: 파일 읽기/쓰기, 네트워크 요청 등)과 같이 시간이 오래 걸리는 작업에 적합하다.
3. def와 async def의 차이점
특징 | def | async def |
실행 방식 | 동기적 실행 (완료될 때까지 대기) | 비동기적 실행 (다른 작업과 병렬 수행) |
반환 값 | 일반적인 값 (예: int, str 등) | coroutine 객체를 반환 |
사용 상황 | 일반적인 작업에 적합 | I/O 바운드 작업에 적합 |
키워드 | return | await, return |
4. 언제 async def를 사용해야 할까?
비동기 함수는 주로 다음과 같은 상황에서 유용하다.
- 네트워크 요청: 예를 들어, 웹 서버에서 여러 클라이언트의 요청을 동시에 처리해야 할 때 비동기 함수는 매우 효과적이다.
- 파일 입출력: 대용량 파일을 처리할 때 비동기 함수를 사용하면, 파일을 읽거나 쓰는 동안 다른 작업을 수행할 수 있다.
- 데이터베이스 작업: 비동기 함수는 데이터베이스에서 데이터를 가져오거나 업데이트할 때도 유용하게 사용할 수 있다.
다음 내용
'[파이썬 Projects] > <파이썬 웹개발>' 카테고리의 다른 글
[파이썬] FastAPI - 요청 (0) | 2024.08.20 |
---|---|
[파이썬] FastAPI - 응답 클래스 (0) | 2024.08.19 |
[파이썬] FastAPI - Pydantic (2) (0) | 2024.08.17 |
[파이썬] Pydantic: 데이터 검증 라이브러리 (0) | 2024.08.17 |
[파이썬] FastAPI - HTTP 메서드, Pydantic (0) | 2024.08.17 |