LLM 엔지니어링13장 · REST API 와 FastAPI8 / 22← 이전목차다음 →MetaContents

13장. REST API 와 FastAPI

Pydantic 으로 입력 검사

은행 창구의 신청서에는 칸마다 규칙이 있습니다. 생년월일은 숫자 여덟 자리, 성별은 고르는 칸입니다. 칸이 비거나 규칙에 어긋나면 창구에서 바로 돌려보냅니다. Pydantic 모델은 요청 본문의 신청서입니다. 칸 이름과 타입, 범위, 고를 수 있는 값을 정해 두면, 어긋난 요청은 모델에 닿기 전에 422 로 돌려보냅니다.

요청 JSON 이 Pydantic 모델의 타입, 범위, 고르는 값 검사를 거쳐 통과하면 경로 함수로, 어긋나면 422 로 돌아가는 흐름도

1번에서 JSON 본문이 옵니다. 2번에서 칸마다 타입(정수, 실수, 글)이 맞는지, 3번에서 범위(0~23시)와 고를 수 있는 값(서울, 경기, 인천, 지방)에 드는지 봅니다. 4번에서 모두 통과하면 경로 함수가 검사된 객체를 받고, 하나라도 어긋나면 어느 칸이 왜 틀렸는지 적힌 422 가 돌아갑니다. 여기서 신청서의 칸 규칙이 Pydantic 의 필드 정의입니다.

칸 규칙 적는 법

하고 싶은 것적는 법어긋나면
타입거리km: float"숫자가 아니다"
범위주문시각: int = Field(ge=0, le=23)"23 보다 작거나 같아야 한다"
고르는 값지역: Literal["서울", "경기", "인천", "지방"]"다음 가운데 하나여야 한다"
글 길이질문: str = Field(min_length=2, max_length=200)"적어도 2글자"
목록 길이주문들: list[Order] = Field(max_length=500)"500개를 넘을 수 없다"
선택 칸날씨: str = "맑음"빠지면 기본값

검사해 보기

Python
from typing import Literal
from pydantic import BaseModel, Field, ValidationError

class Order(BaseModel):
    주문시각: int = Field(ge=0, le=23)
    지역: Literal["서울", "경기", "인천", "지방"]

print(Order(주문시각=9, 지역="서울"))
try:
    Order(주문시각=25, 지역="부산")
except ValidationError as e:
    for err in e.errors():                    # 어긋난 칸마다 위치(loc)와 까닭(msg)
        print(err["loc"], err["msg"])

코드에 나오는 함수

코드쉬운 뜻
class Order(BaseModel)BaseModel(바탕 모델)을 물려받아 요청 본문의 칸을 정하는 클래스다
Field(ge=0, le=23)칸의 규칙이다. ge 는 greater than or equal(크거나 같다), le 는 less than or equal(작거나 같다)이다
Literal["서울", "경기", "인천", "지방"]literal(글자 그대로의)이라는 뜻이다. 적어 둔 값 가운데 하나만 받는다
ValidationError검사에 어긋나면 나는 예외다. validation 은 "검증"이라는 뜻이다
e.errors()어긋난 칸마다의 정보를 목록으로 준다
err["loc"], err["msg"]loc 은 location(위치), 곧 어느 칸인지이고, msg 는 message(까닭을 적은 글)다

이 코드가 뜻하는 것

은행 창구 직원은 신청서를 받으면 칸마다 규칙에 맞는지 한꺼번에 보고, 틀린 칸에 모두 표시해서 돌려줍니다. 하나 고치고 다시 오고, 또 하나 고치고 다시 오게 하지 않습니다. 여기서 신청서 양식이 Order 클래스, 칸 규칙이 Field 와 Literal, 틀린 칸 표시가 e.errors() 입니다.

줄하는 일쉽게 말하면
class Order(BaseModel): ...주문시각과 지역 두 칸의 규칙을 정한다신청서 양식
Order(주문시각=9, 지역="서울")규칙에 맞는 값으로 만든다바르게 쓴 신청서
Order(주문시각=25, 지역="부산")두 칸이 모두 어긋난 값으로 만들어 본다두 칸을 틀리게 쓴 신청서
except ValidationError as e:검사 실패를 잡는다창구에서 돌려받음
for err in e.errors(): ...틀린 칸마다 위치와 까닭을 보인다빨간 펜 표시 읽기

실행하면 첫 줄에 주문시각=9 지역='서울' 이 나오고, 이어서 ('주문시각',) Input should be less than or equal to 23 과 ('지역',) Input should be '서울', '경기', '인천' or '지방' 이 나옵니다. "23 보다 작거나 같아야 한다", "넷 가운데 하나여야 한다"는 뜻이고, 틀린 두 칸이 한 번에 모두 알려졌습니다.

작게 줄여서 눈으로 보기

기본값이 있는 칸과, 숫자처럼 생긴 글이 오는 경우를 봅니다.

Python
from typing import Literal
from pydantic import BaseModel, Field

class Order(BaseModel):
    주문시각: int = Field(ge=0, le=23)
    지역: Literal["서울", "경기", "인천", "지방"]
    날씨: str = "맑음"

print(Order(주문시각=9, 지역="서울"))
print(Order(주문시각="9", 지역="경기", 날씨="비"))
print(Order.model_validate({"주문시각": 23, "지역": "지방"}).model_dump())
출력
주문시각=9 지역='서울' 날씨='맑음'
주문시각=9 지역='경기' 날씨='비'
{'주문시각': 23, '지역': '지방', '날씨': '맑음'}
결과읽는 법
첫 줄날씨를 주지 않아 기본값 "맑음"이 들어갔다
둘째 줄글 "9" 를 주었지만 정수 9 로 바뀌었다. 숫자로 읽을 수 있는 글은 Pydantic 이 기본으로 바꿔 준다
셋째 줄model_validate 는 사전을 받아 검사하고, model_dump 는 검사한 객체를 다시 사전으로 돌려준다. JSON 본문이 들어오고 나갈 때 FastAPI 가 하는 일과 같다

주의할 점

  • 입력 검사는 모델이 학습한 범위를 지키는 장치이기도 합니다. 학습 데이터에 없던 "부산"이 오면 모델은 그럴듯한 숫자를 내놓지만 믿을 수 없습니다.
  • 칸 이름을 한글로 쓰면 파이썬 이름과 JSON 칸 이름이 같아져 읽기 쉽습니다. 다른 팀이나 외부와 주고받는 API 라면 영어 칸 이름을 쓰는 것이 흔합니다.
← 이전 절13장 · FastAPI 의 기본다음 절 →13장 · 예측 API 의 짜임