pydantic 검증과 Field 설정¶
한 줄 요약: pydantic(파이단틱)은 타입 힌트로 들어온 값을 실제로 검사·변환하는 파이썬 라이브러리다.
Field에는 검사하는 설정과 설명만 하는 설정이 섞여 있다. "값을 검사하는 것"과 "스키마에 설명을 적는 것"은 다른 일이다.
📍 만난 상황¶
- 2026-10-27 — 로컬 매니저
models.py의payload에Field(json_schema_extra=...)로 스키마 힌트를 넣어 본 직후였다. 테스트 761개가 그대로 통과했다는 결과를 들었다. 5번 물음 → 그날 일지 - 15:17 "payload 타입 자체를 바꾸는 것과 Field에 추가하는 게 뭐가 다르냐" → 타입은 검문소,
json_schema_extra는 안내문이다. - 15:19 "Field가 원래 그런 역할이냐" → 정정:
Field에는 검사 설정과 설명 설정이 함께 들어간다. - 16:33 "Field는 어떤 라이브러리 함수냐" → pydantic이다. 이름은 py + pedantic(깐깐한).
- 16:34 "Field에 쓰면 검사 없이 타입 힌트를 줄 수 있냐" →
json_schema_extra하나만 그렇다. 용어는 "타입 힌트"가 아니라 "스키마 힌트"다. - 17:03 "이슈의 B안(검증 전 값 변환)은 타입 자체를 바꾸는 거냐" → 아니다. 타입은 그대로 두고 검사 직전에 데이터를 고친다(
model_validator(mode="before")).
🧩 쉽게 말하면¶
건물 입구를 떠올리자. 검문소는 출입증을 실제로 확인하고, 틀리면 돌려보낸다. 안내문은 벽에 "출입증은 파란색입니다"라고 붙어 있을 뿐, 아무도 막지 않는다.
pydantic의 타입 힌트는 검문소다. Field의 description·json_schema_extra는 안내문이다. model_validator(mode="before")는 검문소 앞에서 서류를 고쳐 써 주는 직원이다.
이게 없으면 API로 들어온 값을 손으로 하나하나 확인해야 한다. "숫자여야 하는데 글자가 왔다" 같은 검사를 매번 직접 쓴다.
📖 제대로 알기¶
pydantic이 하는 일¶
| 이름 | 역할 | 오늘 본 곳 |
|---|---|---|
BaseModel |
데이터 모양을 클래스로 정의 | class ResourcePlanItem(BaseModel) |
Field |
칸 하나에 기본값·제약·설명을 덧붙임 | payload: ... = Field(...) |
model_validate() |
들어온 값을 검사 | 매니저 코어가 "true"를 거절한 곳 |
model_json_schema() |
모델을 JSON Schema로 자동 변환 | Gemma에게 보내는 스키마 |
FastAPI(웹 서버), LangChain(args_schema), 매니저 공용 코어가 모두 pydantic을 쓴다. 오늘 어시스턴트의 버전은 오류 링크로 보아 2.13이었다.
타입 힌트 = 실제 검사¶
타입 힌트(type hint: 이름: 타입 형태로 적는 표기)는 보통 파이썬에선 설명일 뿐이다. 하지만 pydantic은 이걸 검사 규칙으로 쓴다.
- nullable: bool → 참/거짓이 아니면 거절한다.
- payload: dict[str, Any] → 키가 글자인 묶음이면 값은 아무거나 통과한다.
너그러운 모드와 엄격한 모드¶
- lax 모드(기본): 바꿀 수 있으면 바꿔 준다.
"true"→True,"10"→10. - strict 모드(
strict=True): 바꾸지 않고 거절한다."true"가 오면Input should be a valid boolean. - 매니저의 데이터셋 스키마 검증은 #2727에서 일부러 strict로 막아 두었다. 그래서 문자열 참거짓이 드러났다.
Field에 넣는 설정 종류¶
| 종류 | 예 | 값 검사 |
|---|---|---|
| 기본값 | default="", default_factory=dict |
값이 없을 때 채움 |
| 제약 조건 | min_length, max_length, pattern, ge=0 |
함(어기면 거절) |
| 설명 | description, title, examples |
안 함 |
| 스키마에 그대로 추가 | json_schema_extra={...} |
안 함 |
네 가지 모두 JSON Schema에는 반영된다. 검사에 영향을 주는 건 타입과 제약 조건뿐이다.
검사 전·후에 끼우는 함수¶
field_validator("칸", mode="before"): 칸 하나를 검사 전에 고친다.model_validator(mode="before"): 모델 전체 데이터를 검사 전에 받아 고친다.mode="after": 검사가 끝난 뒤 추가 규칙을 확인한다.
⚙️ 어떻게 동작하나¶
- 클래스를 정의하면 pydantic이 타입 힌트와
Field설정으로 검사기와 스키마 생성기를 함께 만든다. model_json_schema()를 부르면 타입에서 스키마를 만들고,description·json_schema_extra를 끼워 넣는다.model_validate(데이터)를 부르면 순서대로 실행한다.- ①
mode="before"함수가 원본 데이터를 고친다. - ② 타입·제약 조건으로 검사하고, lax면 변환한다.
- ③
mode="after"함수가 결과를 확인한다. - 검사와 스키마는 같은 클래스에서 나오지만 서로 다른 길이다. 그래서 스키마만 바꾸고 검사는 그대로 둘 수 있다.
🧪 예시¶
-
예시 1 · 일상에 빗대어: 출입증 칸에
"true"라고 적힌 서류 3장이 온다. ① 안내문만 고친 건물: 벽보는 바뀌었지만 검문소는 3장 모두 돌려보낸다. ② 검문소 규칙을 바꾼 건물: 3장 모두 통과하지만, 다른 서류의 규칙까지 같이 바뀔 위험이 있다. ③ 앞에서 고쳐 주는 직원을 둔 건물: 정해진 칸의"true"만true로 고쳐서, 검문소는 그대로인데 3장 모두 통과한다. -
예시 2 · 실제로는: 오늘 로컬에서 시험한 두 방식의 모양이다.
①class ResourcePlanItem(BaseModel): payload: dict[str, Any] = Field( # ① 타입 그대로 default_factory=dict, json_schema_extra={"properties": _payload_schema_hints()}, # ② 스키마에만 추가 ) @model_validator(mode="before") # ③ 검사 "전에" 실행 @classmethod def _normalize_payload_scalars(cls, data): # schema.fields[].nullable "true"/"false" → true/false # widgets[].layout.x, y, w, h "4" → 4 # "yes"처럼 애매한 값은 건드리지 않음 → 원래대로 검사에서 걸림 return datadict[str, Any]는 그대로라 검사는 바뀌지 않는다. ②json_schema_extra는 Gemma에게 가는 스키마에만 "nullable은 boolean"을 적는다. vLLM이 그걸 보고 타입을 되돌리길 기대하는 방식이다. ③ before 함수는 정해진 위치의 문자열만 고친다. strict 검사는 그대로 둔다.
같은 파일의 id 칸은 검사 설정의 예다.
id: str = Field(
min_length=1, max_length=64, # 검사: 1~64글자
pattern=r"^[A-Za-z0-9_.:-]+$", # 검사: 허용 문자만
description="Stable item id …", # 설명만
)
id에 100글자를 보내면 max_length 때문에 거절된다. description은 아무것도 막지 않는다.
⚖️ 비교 · 흔한 오해¶
| 방법 | 값 검사·변환 | 스키마 |
|---|---|---|
| 타입 자체를 바꿈 | 바뀜(lax면 "true"→true) |
타입에서 자동 반영 |
Field(json_schema_extra) |
안 바뀜 | 적은 그대로 추가 |
model_validator(before) |
검사 전에 데이터를 고침 | 안 바뀜 |
내가 헷갈린 점
- "Field는 설명만 한다" → 틀렸다. min_length, pattern처럼 검사하는 설정도 Field에 들어간다. json_schema_extra가 설명 전용이다.
- "검사 없이 타입 힌트를 준다" → 용어가 틀렸다. 파이썬의 타입 힌트는 dict[str, Any] 부분이고, pydantic은 그걸로 검사한다. 추가한 건 스키마 힌트다.
- "B안은 타입을 바꾼다" → 아니다. 타입과 strict 규칙은 그대로고, 들어온 데이터를 고친다.
왜 헷갈리기 쉬운가: 셋 다 같은 클래스 안, 같은 칸 옆에 적힌다. 그리고 셋 다 결과 스키마나 결과 값이 "비슷하게" 달라진다. 무엇이 검사 경로에 있고 무엇이 스키마 경로에 있는지 나눠 봐야 보인다.
왜 타입 자체를 바꾸지 않았나: payload는 데이터셋·대시보드·파이프라인 등 15가지 모양이다. 하나의 타입으로 묶으면 공통 위치만 적을 수 있다. 잘못 적으면 정상 계획까지 거절된다.
🛠️ 실무에서는¶
- 외부 모델·사용자에게 보여 줄 설명만 바꾸려면
description·json_schema_extra를 쓴다. 기존 동작이 바뀌지 않아 안전하다. - 실제로 값을 막거나 바꿔야 하면 타입·제약 조건·validator를 쓴다. 테스트 결과가 달라질 수 있다.
- before 함수를 모델에 달면 그 모델이 쓰이는 모든 곳에서 실행된다. 오늘 경우 ① LangChain 인자 검사, ② 코어 propose 검사, ③ apply 해시 계산이 모두 같은 값을 본다.
- 값 변환은 범위를 좁게 잡는다.
"true"·"false"·정수 모양만 바꾸고,"001"·"yes"처럼 애매한 값은 그대로 둔다. - 문제가 생기면 먼저 볼 것: 오류가 어느 모델의 어느 칸에서 났나 → 그 칸이 strict인가 →
model_json_schema()결과에 기대한 내용이 들어갔나.
❓ 자주 묻는 질문¶
- Q.
json_schema_extra만 넣었는데 테스트가 왜 하나도 안 깨졌나? A. 검사 경로를 건드리지 않았기 때문이다. 스키마 출력만 달라졌다. - Q. lax 모드면
"true"를 알아서 바꿔 주는데, 왜 strict로 막아 두나? A. 조용한 변환이 다른 버그를 숨길 수 있어서다. 예를 들어 데이터셋 스키마의 형식을 정확히 지키게 하려는 목적이다. - Q.
field_validator와model_validator는 언제 무엇을 쓰나? A. 칸 하나만 고치면field_validator, 여러 칸이나 깊은 경로를 한 번에 보면model_validator가 편하다.
🔭 더 깊이 보기¶
- 판별 유니온(discriminated union: 어떤 칸 값에 따라 다른 모델로 검사)은 무엇인가 — 15가지
payload모양을 타입으로 표현하는 정석이라서. model_json_schema()가$defs·$ref를 만드는 방식 — 스키마 힌트가 실제로 어디에 붙는지 알려면 필요해서.- 관련 주제: JSON Schema 규약과 해석, LLM 도구 호출 파서와 타입 복원
📚 참고 자료¶
- Fields (pydantic 공식 문서, 영문) —
Field의 기본값·제약·json_schema_extra· 링크 - Validators (pydantic 공식 문서, 영문) —
field_validator,model_validator의 before·after 순서 · 링크 - Strict Mode (pydantic 공식 문서, 영문) — lax와 strict의 변환 차이 · 링크
🗓️ 이력¶
- 2026-10-27: 타입 변경과
Field추가의 차이,Field의 역할, B안(검증 전 값 변환)이 무엇인지 5번 물었다 → 일지