o2o-castad-backend/docs/reference/ConfigDict.md

383 lines
11 KiB
Markdown

# Pydantic ConfigDict 사용 매뉴얼
## 개요
Pydantic v2에서 `ConfigDict`는 모델의 유효성 검사, 직렬화, JSON 스키마 생성 등의 동작을 제어하는 설정을 정의하는 TypedDict입니다.
> Pydantic v1의 `class Config`는 더 이상 권장되지 않으며, `ConfigDict`를 사용해야 합니다.
## 기본 사용법
```python
from pydantic import BaseModel, ConfigDict
class MyModel(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
strict=True
)
name: str
age: int
```
## 설정 옵션 전체 목록
### 문자열 처리
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `str_to_lower` | `bool` | `False` | 문자열을 소문자로 변환 |
| `str_to_upper` | `bool` | `False` | 문자열을 대문자로 변환 |
| `str_strip_whitespace` | `bool` | `False` | 문자열 앞뒤 공백 제거 |
| `str_min_length` | `int \| None` | `None` | 문자열 최소 길이 |
| `str_max_length` | `int \| None` | `None` | 문자열 최대 길이 |
**예시:**
```python
class UserInput(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
str_to_lower=True,
str_min_length=1,
str_max_length=100
)
username: str
user = UserInput(username=" HELLO ")
print(user.username) # "hello"
```
### 유효성 검사
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `strict` | `bool` | `False` | 엄격한 타입 검사 활성화 (타입 강제 변환 비활성화) |
| `validate_assignment` | `bool` | `False` | 속성 할당 시 유효성 검사 수행 |
| `validate_default` | `bool` | `False` | 기본값도 유효성 검사 수행 |
| `validate_return` | `bool` | `False` | 반환값 유효성 검사 |
| `revalidate_instances` | `Literal['always', 'never', 'subclass-instances']` | `'never'` | 모델 인스턴스 재검증 시점 |
| `arbitrary_types_allowed` | `bool` | `False` | Pydantic이 지원하지 않는 타입 허용 |
**예시 - strict 모드:**
```python
class StrictModel(BaseModel):
model_config = ConfigDict(strict=True)
count: int
# strict=False (기본값): "123" -> 123 자동 변환
# strict=True: "123" 입력 시 ValidationError 발생
```
**예시 - validate_assignment:**
```python
class User(BaseModel):
model_config = ConfigDict(validate_assignment=True)
age: int
user = User(age=25)
user.age = "invalid" # ValidationError 발생
```
### Extra 필드 처리
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `extra` | `'allow' \| 'ignore' \| 'forbid'` | `'ignore'` | 추가 필드 처리 방식 |
**값 설명:**
- `'ignore'`: 추가 필드 무시 (기본값)
- `'allow'`: 추가 필드 허용, `__pydantic_extra__`에 저장
- `'forbid'`: 추가 필드 입력 시 에러 발생
**예시:**
```python
class AllowExtra(BaseModel):
model_config = ConfigDict(extra='allow')
name: str
data = AllowExtra(name="John", unknown_field="value")
print(data.__pydantic_extra__) # {'unknown_field': 'value'}
class ForbidExtra(BaseModel):
model_config = ConfigDict(extra='forbid')
name: str
ForbidExtra(name="John", unknown="value") # ValidationError 발생
```
### 불변성 (Immutability)
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `frozen` | `bool` | `False` | 모델을 불변(immutable)으로 만듦, `__hash__()` 구현 |
**예시:**
```python
class ImmutableUser(BaseModel):
model_config = ConfigDict(frozen=True)
name: str
age: int
user = ImmutableUser(name="John", age=30)
user.age = 31 # 에러 발생: Instance is frozen
# frozen=True이면 해시 가능
users_set = {user} # 정상 작동
```
### 별칭 (Alias) 설정
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `populate_by_name` | `bool` | `False` | 필드명과 별칭 모두로 값 설정 허용 (deprecated) |
| `validate_by_alias` | `bool` | `True` | 별칭으로 필드 값 설정 허용 |
| `validate_by_name` | `bool` | `False` | 별칭이 있어도 필드명으로 값 설정 허용 |
| `serialize_by_alias` | `bool` | `False` | 직렬화 시 별칭 사용 |
| `alias_generator` | `Callable[[str], str] \| None` | `None` | 별칭 자동 생성 함수 |
| `loc_by_alias` | `bool` | `True` | 에러 위치에 별칭 사용 |
**예시:**
```python
from pydantic import Field
class APIResponse(BaseModel):
model_config = ConfigDict(
validate_by_alias=True,
validate_by_name=True,
serialize_by_alias=True
)
user_name: str = Field(alias="userName")
# 둘 다 가능
response1 = APIResponse(userName="John")
response2 = APIResponse(user_name="John")
print(response1.model_dump(by_alias=True)) # {"userName": "John"}
```
**예시 - alias_generator:**
```python
def to_camel(name: str) -> str:
parts = name.split('_')
return parts[0] + ''.join(word.capitalize() for word in parts[1:])
class CamelModel(BaseModel):
model_config = ConfigDict(
alias_generator=to_camel,
serialize_by_alias=True
)
first_name: str
last_name: str
data = CamelModel(firstName="John", lastName="Doe")
print(data.model_dump(by_alias=True))
# {"firstName": "John", "lastName": "Doe"}
```
### JSON 스키마
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `title` | `str \| None` | `None` | JSON 스키마 타이틀 |
| `json_schema_extra` | `dict \| Callable \| None` | `None` | JSON 스키마에 추가할 정보 |
| `json_schema_serialization_defaults_required` | `bool` | `False` | 직렬화 스키마에서 기본값이 있는 필드도 required로 표시 |
| `json_schema_mode_override` | `Literal['validation', 'serialization', None]` | `None` | JSON 스키마 모드 강제 지정 |
**예시 - json_schema_extra:**
```python
class Product(BaseModel):
model_config = ConfigDict(
title="상품 정보",
json_schema_extra={
"example": {
"name": "노트북",
"price": 1500000
},
"description": "상품 데이터를 나타내는 모델"
}
)
name: str
price: int
# OpenAPI/Swagger 문서에 예시가 표시됨
```
**예시 - Callable json_schema_extra:**
```python
def add_examples(schema: dict) -> dict:
schema["examples"] = [
{"name": "예시1", "value": 100},
{"name": "예시2", "value": 200}
]
return schema
class DynamicSchema(BaseModel):
model_config = ConfigDict(json_schema_extra=add_examples)
name: str
value: int
```
### ORM/속성 모드
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `from_attributes` | `bool` | `False` | 객체 속성에서 모델 생성 허용 (SQLAlchemy 등) |
**예시:**
```python
class UserORM:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
class UserModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
name: str
age: int
orm_user = UserORM(name="John", age=30)
pydantic_user = UserModel.model_validate(orm_user)
print(pydantic_user) # name='John' age=30
```
### Enum 처리
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `use_enum_values` | `bool` | `False` | Enum 대신 값(value)으로 저장 |
**예시:**
```python
from enum import Enum
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
class User(BaseModel):
model_config = ConfigDict(use_enum_values=True)
status: Status
user = User(status=Status.ACTIVE)
print(user.status) # "active" (문자열)
print(type(user.status)) # <class 'str'>
# use_enum_values=False (기본값)이면
# user.status는 Status.ACTIVE (Enum 객체)
```
### 직렬화 설정
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `ser_json_timedelta` | `'iso8601' \| 'float'` | `'iso8601'` | timedelta JSON 직렬화 형식 |
| `ser_json_bytes` | `'utf8' \| 'base64' \| 'hex'` | `'utf8'` | bytes JSON 직렬화 인코딩 |
| `ser_json_inf_nan` | `'null' \| 'constants' \| 'strings'` | `'null'` | 무한대/NaN JSON 직렬화 형식 |
### 숫자/Float 설정
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `allow_inf_nan` | `bool` | `True` | float에서 무한대/NaN 허용 |
| `coerce_numbers_to_str` | `bool` | `False` | 숫자를 문자열로 강제 변환 허용 |
### 기타 설정
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `protected_namespaces` | `tuple[str, ...]` | `('model_',)` | 보호할 필드명 접두사 |
| `hide_input_in_errors` | `bool` | `False` | 에러 메시지에서 입력값 숨김 |
| `defer_build` | `bool` | `False` | validator/serializer 빌드 지연 |
| `use_attribute_docstrings` | `bool` | `False` | 속성 docstring을 필드 설명으로 사용 |
| `regex_engine` | `'rust-regex' \| 'python-re'` | `'rust-regex'` | 정규식 엔진 선택 |
| `validation_error_cause` | `bool` | `False` | Python 예외를 에러 원인에 포함 |
## 설정 상속
자식 모델은 부모 모델의 `model_config`를 상속받습니다.
```python
class ParentModel(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
extra='allow'
)
name: str
class ChildModel(ParentModel):
model_config = ConfigDict(
frozen=True # 부모 설정 + frozen=True
)
age: int
# ChildModel은 str_strip_whitespace=True, extra='allow', frozen=True
```
## FastAPI와 함께 사용
FastAPI에서 요청/응답 스키마로 사용할 때 특히 유용합니다.
```python
from fastapi import FastAPI
from pydantic import BaseModel, ConfigDict, Field
app = FastAPI()
class CreateUserRequest(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
json_schema_extra={
"example": {
"username": "johndoe",
"email": "john@example.com"
}
}
)
username: str = Field(..., min_length=3, max_length=50)
email: str
class UserResponse(BaseModel):
model_config = ConfigDict(
from_attributes=True, # ORM 객체에서 변환 가능
serialize_by_alias=True
)
id: int
user_name: str = Field(alias="userName")
@app.post("/users", response_model=UserResponse)
async def create_user(user: CreateUserRequest):
# user.username은 자동으로 공백이 제거됨
...
```
## 주의사항
1. **v1에서 마이그레이션**: `class Config`는 deprecated입니다. `model_config = ConfigDict(...)`를 사용하세요.
2. **populate_by_name은 deprecated**: `validate_by_alias``validate_by_name`을 함께 사용하세요.
3. **json_encoders는 deprecated**: 커스텀 직렬화가 필요하면 `@field_serializer` 데코레이터를 사용하세요.
## 참고 자료
- [Pydantic Configuration API 공식 문서](https://docs.pydantic.dev/latest/api/config/)
- [Pydantic Models 개념](https://docs.pydantic.dev/latest/concepts/models/)
- [Pydantic Migration Guide](https://docs.pydantic.dev/latest/migration/)