시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.
이전 내용
★ Pydantic 관련 상세 내용
Pydantic 필드 제약 조건
Field는 Pydantic 모델에서 필드에 추가적인 정보나 제약 조건을 지정할 때 사용하는 함수이다.
다양한 인자를 통해 세부 설정을 할 수 있으며, 주요 옵션은 아래와 같다.
- default: 필드의 기본값을 지정. 만약 기본값이 없다면 필수 입력 필드가 된다.
- alias: JSON 필드의 이름을 파이썬 변수와 다르게 지정할 때 사용
- title: 스키마에서 볼 수 있는 추가적인 정보로, 주로 문서화에 사용
- description: 필드에 대한 설명 추가. 주로 API 문서에서 확인
- min_length & max_length: 문자열 길이의 최솟값과 최댓값 지정
- gt(greater than), lt(less than): 숫자의 크기 제약 추가
- regex: 정규 표현식을 통한 패턴 매칭
[Field를 사용한 예제 코드]
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import List
app = FastAPI()
class Item(BaseModel):
# 'name'은 최소 2자, 최대 50자를 가져아 하며 필수 필드
name: str = Field(..., title="Item Name", min_length=2, max_length=50)
# 'description'은 선택 필드이며, 최대 300자 까지 가능
description: str = Field(None, description="The description of the item", max_length=300)
# 'price'는 0보다 커야 하며 필수 필드
price: float = Field(..., gt=0, description="The price must be greater than zero")
# 'tag' 필드는 선택이며, 기본값으로 빈 리스트를 가짐. JSON에서는 'item-tags'로 나타냄
tag: List[str] = Field(default=[], alias="item-tags")
@app.post("/items/")
async def create_item(item: Item):
# 아이템 생성을 위한 엔드포인트로, 모델 인스턴스의 딕셔너리 표현 반환
return {"item": item.dict()}
Field 테스트
curl로 테스트할 수 있는 다양한 케이스와 그 결과를 확인하며, Field 사용법을 이해해본다.
케이스 1. 정상적인 요청
curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"name\":\"MyItem\", \"price\": 35.4}"
▶ 서버로부터 200 OK 응답을 받는다. (JSON 바디에 정의된 name과 price 필드가 Pydantic 모델의 필드 제약 조건 충족)
케이스 2. name 필드의 길이가 짧은 경우
curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"name\":\"A\", \"price\": 35.4}"
{"detail":[{"type":"string_too_short","loc"["body","name"],"msg":"String should have at least 2 characters","input":"A","ctx":{"min_length":2},"url":"https://errors.pydantic.dev/2.8/v/string_too_short"}]}
▶ 422 Unprocessable Entity 오류 발생 (name 필드의 길이가 Pydantic 모델에서 설정한 min_length 조건 불만족 - 조건: 최소 2글자)
케이스3. price 필드가 0보다 작거나 같은 경우
curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"name\":\"MyItem\", \"price\": -1}"
▶ 422 Unprocessable Entity 오류 발생 (price 필드의 값이 Pydantic 모델에서 설정한 gt(greater than) 조건 불만족 - 조건: 0이상)
케이스4. 필수 필드를 누락한 경우(name 누락)
curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"price\": 35.4}"
▶ 422 Unprocessable Entity 오류 발생 (필드가 누락되었다는 메시지 반환)
케이스5. 여러 제약 조건을 위반한 경우
curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"name\":\"A\", \"price\": -1}"
▶ 422 Unprocessable Entity 오류 발생(name 2글자 이상 조건 불만족, price 1 이상 조건 불만족)
중첩된 모델
중첩된 모델이라는 것은
하나의 모델이 다른 모델을 포함하는 구조를 의미한다.
이러한 구조는 복잡한 데이터 형태를 모델링할 때 유용한데, 예를 들어 여기서 Item 클래스는 Image 클래스를 포함하고 있다.
Item 모델을 보면 image: image로 정의되어 있는데, 이는 Item 모델이 Image 타입의 image 필드를 가진다는 것을 의미한다.
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: str
image: Image
@app.post("/items/")
def create_item(item: Item):
return {"item": item.dict()}
- class Image(BaseModel): 이미지에 대한 정보를 담는 Pydantic 모델 정의 (위 코드에서는 url과 name이라는 두 가지 필드 존재)
- class Item(BaseModel): 아이템에 대한 정보를 담는 Pydantic 모델 정의 (위 코드에서는 name, description, image 라는 필드를 가지며, 특히 image 필드의 타입은 앞서 정의한 Image 클래스)
- def create_item(item: Item): FastAPI 라우터에서 이 Item 모델 사용. 클라이언트로부터 전달받은 JSON 데이터가 Item 모델과 일치하는 지 자동으로 검사
▶ 이 예에서는 name, description, 그리고 중첩된 image 객체를 포함한 JSON 데이터를 전달한다. 이렇게 하면 FastAPI는 Item 모델을 기반으로 들어온 데이터의 유효성을 자동으로 검사하고, 응답으로 그 데이터를 반환한다.
List와 Union
List와 Union은 복잡한 데이터 구조와 다형성을 모델링할 때 유용한 타입 힌트이다.
- List: List[<type>] 형식을 사용하여 지정된 <type>의 여러 값을 갖는 배열이나 리스트를 나타낸다. (예. List[int]: 정수들의 리스트 / List[str]: 문자열들의 리스트) ▶ Pydantic 모델에서 리스트를 사용하면 리스트 내 각 아이템에 대해 정의된 타입의 유효성이 검사된다.
- Union: Union[<type1>, <type2>, ...] 형식으로 여러 타입 중 하나를 허용하는 변수를 정의할 수 있다. (예. Union[int, str]: 해당 필드가 정수 또는 문자열 일 수 있음) ▶ Pydantic 모델은 제공된 값이 Union에 지정된 타입 중 하나와 일치하는 지 검사한다.
제네릭 타입
제네릭 타입은 일종의 '타입 템플릿'이다. 그래서 List[T] 같은 형태로 사용한다.
▶ T는 타입 변수(Type variable)라고 부르며, 여기서는 '아무 타입이나' 올 수 있음을 의미하여 여러 다른 타입에 대해 동일한 로직을 적용할 수 있다.
★ T는 단순한 변수일 뿐 의미 자체는 없어 대신에 다른 알파벳을 사용해도 되지만, 관례적으로 T를 많이 사용한다.
만약 두 개의 다른 타입 변수가 필요하다면 U = TypeVar('U') 등과 같이 다른 문자를 사용하여 선언할 수 있다.
FastAPI와 Pydantic 에서 제네릭 타입을 사용하려면 typing 모듈의 TypeVar와 Generic 클래스를 이용한다.
- TypeVar: 타입 변수를 생성하며, 제네릭 크래스나 함수가 사용할 수 있는 타입 매개변수를 정의한다. 동적 타이핑 언어인 파이썬에서 TypeVar는 정적 타입 검사 도구가 타입 정보를 이해하고 검사할 수 있게 만드는 역할을 한다. 제네릭 타입을 사용하려면 먼저 타입 변수를 명시적으로 선언해야 하며, 그것이 바로 TypeVar의 역할이다. TypeVar를 선언할 때는 일반적으로 변수 이름과 같은 문자열을 인자로 전달한다.
T = TypeVar('T')
- 왼쪽의 T: 타입 변수의 이름으로, 코드 내에서 타입 힌트로 사용
- 오른쪽의 T: TypeVar 함수에 전달되는 문자열 리터럴. TypeVar 객체를 생성할 때 내부적으로 사용되는 식별자. 파이썬의 타입 시스템과 관련된 도구들(예. linters, IDEs) ▶ 문서화와 가독성을 위함.
- Generic[T]: T를 타입 매개변수로 가지는 제네릭 클래스를 정의할 때 사용한다.
[Type Var와 Generic 타입에 대한 코드 예시]
from typing import TypeVar, Generic
from pydantic import BaseModel
# 타입 변수 T 선언 (커스텀 제네릭 타입을 만들기 위한 첫 단계)
# 'T'는 임의의 타입을 나타내는 타입 변수
T = TypeVar('T')
# Generic[T]를 상속받는 클래스를 정의함으로써, GenericItem은 어떤 타입 T도 받을 수 있는 제네릭 클래스가 된다
# 이 클래스는 이름(name)과 내용(content)을 필드로 가지며, content의 타입은 동적으로 결정됨.
class GenericItem(BaseModel, Generic[T]):
name: str # 아이템의 이름 필드, 문자열 타입
content: T # 아이템의 내용 필드, T 타입
▶ Pydantic과 FastAPI에서 제네릭 타입을 사용하면 데이터 모델의 유연성을 높이면서도 타입 안정성을 유지할 수 있다. 또, 다양한 데이터 타입에 대해 같은 로직을 적용할 수 있는 모델을 만들 수 있다.
[Pydantic 제네릭 타입 예시]
from typing import TypeVar, Generic
from pydantic import BaseModel
from fastapi import FastAPI
app = FastAPI()
# T는 제네릭 타입에서 사용될 변수
T = TypeVar('T')
# GenericItem 클래스는 제네릭 타입 T를 사용하는 모델
# 이 모델은 다양한 타입의 'content' 필드를 가질 수 있음
class GenericItem(BaseModel, Generic[T]):
name: str # 아이템의 이름 필드
content: T # 제네릭 타입을 사용하는 아이템의 내용 필드
# 이 엔드포인트는 정수 타입의 'content'를 가진 GenericItem 객체를 생성
@app.post("/generic_items/")
def create_item(item: GenericItem[int]):
reutrn {"item": item.dict()}
◆ curl 테스트
curl -X POST "http://127.0.0.1:8000/generic_items/" -H "accept:application/json" -H "Content-Type: application/json" -d "{\"name\": \"Generic\", \"content\": 42}"
- POST 메서드를 사용하여 /generic_items/ 엔드포인트에 데이터 전송
- HTTP 헤더는 요청이 JSON을 받아들이고(JSON에 대한 accept)
- JSON 형식의 데이터를 전송한다는 것을 나타냄(Content-Type: JSON)
- 요청 바디(-d)에는 name과 content가 포함된 JSON 객체 전달
- 서버에서는 GenericItem[int] 타입에 따라 content가 정수인지 검사하고 타입이 일치하면 요청된 데이터를 그대로 반환
- 만약 정수가 아닌 다른 타입의 데이터를 보내면, Pydantic은 타입 에러를 반환하여 요청이 유효하지 않음을 알려줌
다음 내용
'[파이썬 Projects] > <파이썬 웹개발>' 카테고리의 다른 글
[파이썬] FastAPI - 응답 클래스 (0) | 2024.08.19 |
---|---|
[파이썬] FastAPI - 응답 모델 (0) | 2024.08.18 |
[파이썬] Pydantic: 데이터 검증 라이브러리 (0) | 2024.08.17 |
[파이썬] FastAPI - HTTP 메서드, Pydantic (0) | 2024.08.17 |
[파이썬] FastAPI - 타입 힌트(Type hint) (0) | 2024.08.17 |