# 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)) # # 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/)