본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 10. 30. · 37 Views
Pydantic 데이터 검증 완벽 가이드
Python에서 데이터 검증과 설정 관리를 위한 강력한 라이브러리 Pydantic을 마스터해보세요. 타입 힌트 기반의 자동 검증부터 복잡한 비즈니스 로직까지, 실무에서 바로 활용할 수 있는 핵심 개념들을 다룹니다.
목차
- BaseModel 기본 사용법 - Pydantic의 핵심 시작점
- Field를 활용한 세밀한 검증 - 커스텀 규칙 추가하기
- 커스텀 Validator - 복잡한 비즈니스 로직 검증
- 중첩된 모델과 리스트 검증 - 복잡한 데이터 구조 다루기
- Config 클래스로 동작 커스터마이징 - 모델 행동 제어하기
- 타입 변환과 coercion - 유연한 데이터 파싱
- JSON Schema와 OpenAPI 통합 - 자동 문서화
- Settings Management - 환경 설정 관리
1. BaseModel 기본 사용법 - Pydantic의 핵심 시작점
시작하며
여러분이 API를 개발하면서 사용자가 보낸 JSON 데이터를 처리할 때 이런 고민을 해본 적 있나요? "이메일 형식이 맞는지 어떻게 확인하지?", "나이가 음수로 들어오면 어떻게 막지?", "필수 필드가 빠졌는지 일일이 체크해야 하나?" 이런 검증 로직을 직접 작성하면 코드가 길어지고 유지보수가 어려워집니다.
if 문으로 필드를 하나씩 검증하다 보면 코드의 절반 이상이 검증 코드로 채워지고, 새로운 필드가 추가될 때마다 검증 로직도 계속 추가해야 합니다. 바로 이럴 때 필요한 것이 Pydantic의 BaseModel입니다.
클래스를 정의하는 것만으로 자동으로 타입 검증, 데이터 변환, 에러 처리까지 모두 해결할 수 있습니다.
개요
간단히 말해서, BaseModel은 Pydantic의 가장 기본이 되는 클래스로, 이것을 상속받아 데이터 모델을 정의하면 자동으로 검증 기능이 추가됩니다. 실무에서 API 요청 데이터를 받거나, 설정 파일을 읽거나, 데이터베이스 결과를 매핑할 때 매우 유용합니다.
예를 들어, 회원가입 API에서 이메일, 비밀번호, 나이를 받아야 한다면 BaseModel로 정의하는 순간 모든 검증이 자동으로 이루어집니다. 전통적인 방법으로는 딕셔너리로 데이터를 받아서 isinstance(), 정규표현식, 범위 체크 등을 직접 작성했다면, 이제는 클래스 속성에 타입만 명시하면 Pydantic이 알아서 검증합니다.
BaseModel의 핵심 특징은 세 가지입니다. 첫째, Python 타입 힌트를 활용한 자동 검증, 둘째, 데이터 자동 변환(문자열 "123"을 정수 123으로), 셋째, 명확한 에러 메시지 제공입니다.
이러한 특징들이 개발 속도를 높이고 버그를 줄여줍니다.
코드 예제
from pydantic import BaseModel, EmailStr
from typing import Optional
# 사용자 모델 정의
class User(BaseModel):
# 필수 필드: 타입 힌트만으로 검증
username: str
email: EmailStr # 이메일 형식 자동 검증
age: int
# 선택적 필드: 기본값 제공
is_active: bool = True
nickname: Optional[str] = None
# 사용 예시: 딕셔너리에서 자동 생성 및 검증
user_data = {
"username": "john_doe",
"email": "john@example.com",
"age": "25" # 문자열이지만 자동으로 int로 변환
}
user = User(**user_data)
print(user.age) # 25 (정수)
print(user.model_dump()) # 딕셔너리로 변환
설명
이것이 하는 일: BaseModel을 상속받은 클래스는 인스턴스 생성 시 모든 필드의 타입을 검증하고, 가능하면 자동으로 변환하며, 문제가 있으면 ValidationError를 발생시킵니다. 첫 번째로, 클래스 정의 단계에서 각 필드에 타입 힌트를 붙입니다.
username: str은 "이 필드는 문자열이어야 한다"는 규칙이 되고, EmailStr은 Pydantic이 제공하는 특수 타입으로 이메일 형식까지 자동으로 검증합니다. 타입 힌트는 단순한 주석이 아니라 실제로 실행 시점에 검증되는 강력한 규칙입니다.
그 다음으로, User(**user_data)로 인스턴스를 생성할 때 Pydantic이 내부적으로 각 필드를 검사합니다. age가 문자열 "25"로 들어와도 int 타입으로 선언되어 있으므로 자동으로 정수 25로 변환합니다.
만약 "twenty-five"같이 변환 불가능한 값이 들어오면 즉시 에러를 발생시켜 잘못된 데이터가 시스템에 들어오는 것을 막습니다. 세 번째로, Optional[str]과 기본값을 활용해 선택적 필드를 정의할 수 있습니다.
nickname이 없으면 None이 자동으로 할당되고, is_active는 명시하지 않으면 True가 됩니다. 이렇게 필수/선택 필드를 명확히 구분할 수 있습니다.
마지막으로, model_dump() 메서드로 검증된 데이터를 다시 딕셔너리로 변환할 수 있습니다. 이는 JSON 응답을 만들거나 데이터베이스에 저장할 때 매우 유용합니다.
여러분이 이 코드를 사용하면 수십 줄의 검증 코드를 몇 줄의 클래스 정의로 대체할 수 있습니다. 코드가 간결해지고, 타입 안정성이 보장되며, IDE의 자동완성과 타입 체크까지 받을 수 있어 개발 경험이 크게 향상됩니다.
실전 팁
💡 EmailStr 같은 특수 타입을 사용하려면 pip install pydantic[email]로 추가 패키지를 설치해야 합니다. 설치하지 않으면 런타임 에러가 발생하니 주의하세요.
💡 BaseModel은 불변 객체가 아니므로 user.age = 30처럼 값을 변경할 수 있습니다. 불변 객체가 필요하면 Config 클래스에서 frozen=True를 설정하세요.
💡 model_dump() 대신 model_dump_json()을 사용하면 JSON 문자열로 직접 변환되어 API 응답을 만들 때 편리합니다.
💡 FastAPI와 함께 사용하면 엄청난 시너지가 발생합니다. FastAPI가 자동으로 Pydantic 모델을 사용해 요청/응답을 검증하고 문서화까지 생성해줍니다.
💡 from future import annotations를 파일 최상단에 추가하면 순환 참조 문제를 피하고 더 깔끔한 타입 힌트를 작성할 수 있습니다.
2. Field를 활용한 세밀한 검증 - 커스텀 규칙 추가하기
시작하며
여러분이 사용자 나이를 받는데 단순히 "정수여야 한다"만으로는 부족한 상황을 만난 적 있나요? 음수 나이, 200살 같은 비현실적인 값을 막고 싶은데 BaseModel만으로는 한계가 있습니다.
실무에서는 타입 검증만으로는 충분하지 않습니다. "문자열이어야 한다"뿐만 아니라 "최소 3글자, 최대 50글자", "특수문자 금지" 같은 비즈니스 규칙이 필요합니다.
이런 규칙을 일일이 validator 함수로 작성하면 코드가 복잡해집니다. 바로 이럴 때 필요한 것이 Pydantic의 Field입니다.
최솟값, 최댓값, 문자열 길이, 정규표현식 패턴 등을 선언적으로 명시할 수 있어 코드가 자기 문서화됩니다.
개요
간단히 말해서, Field는 각 필드에 추가적인 검증 규칙과 메타데이터를 부여하는 함수입니다. 실무에서 API 파라미터의 범위를 제한하거나, 데이터베이스 제약조건을 코드로 표현하거나, 설명을 추가해 자동 문서화를 개선할 때 사용합니다.
예를 들어, 상품 가격은 0보다 커야 하고, 사용자명은 3-20자 사이여야 한다는 규칙을 Field로 명확히 표현할 수 있습니다. 기존에는 커스텀 validator를 만들어서 if 조건문으로 검증했다면, 이제는 Field의 파라미터로 간단히 선언할 수 있습니다.
ge(greater than or equal), le(less than or equal), min_length, max_length 같은 직관적인 파라미터로 규칙을 명시합니다. Field의 핵심 특징은 선언적 검증 규칙 정의, 기본값과 설명 제공, FastAPI 문서 자동 생성 지원입니다.
이러한 특징들이 코드의 가독성을 높이고 API 문서 품질을 개선합니다.
코드 예제
from pydantic import BaseModel, Field
from typing import Optional
class Product(BaseModel):
# 상품명: 길이 제한과 설명 추가
name: str = Field(
..., # required 표시 (기본값 없음)
min_length=3,
max_length=100,
description="상품명은 3-100자 사이여야 합니다"
)
# 가격: 범위 제한 (0보다 크고 1억 이하)
price: int = Field(gt=0, le=100_000_000, description="원 단위 가격")
# 할인율: 0-100 사이의 값
discount: Optional[float] = Field(default=0, ge=0, le=100)
# 재고: 음수 불가
stock: int = Field(default=0, ge=0, description="재고 수량")
# 사용 예시
product = Product(
name="노트북",
price=1500000,
discount=15.5,
stock=50
)
print(f"{product.name}: {product.price}원 ({product.discount}% 할인)")
설명
이것이 하는 일: Field는 타입 검증을 넘어서 값의 범위, 길이, 패턴 등 구체적인 비즈니스 규칙을 필드에 직접 부여하여 데이터 무결성을 보장합니다. 첫 번째로, Field의 첫 번째 인자로 기본값을 지정합니다.
...(Ellipsis)는 "필수 필드"를 의미하며, 기본값을 제공하지 않으면 반드시 값이 있어야 합니다. default=0처럼 명시하면 선택적 필드가 되어 값이 없을 때 0이 사용됩니다.
그 다음으로, 숫자 필드에는 gt(greater than), ge(greater than or equal), lt(less than), le(less than or equal)를 사용해 범위를 제한합니다. price: int = Field(gt=0, le=100_000_000)은 "1 이상 1억 이하의 정수"라는 명확한 규칙이 됩니다.
만약 0이나 음수, 1억을 초과하는 값이 들어오면 즉시 ValidationError가 발생합니다. 세 번째로, 문자열 필드에는 min_length와 max_length로 길이를 제한합니다.
name 필드는 최소 3글자, 최대 100글자여야 하므로 빈 문자열이나 너무 긴 텍스트를 자동으로 거부합니다. 이는 데이터베이스의 VARCHAR 제약조건과 일치시킬 때 특히 유용합니다.
네 번째로, description 파라미터는 필드에 대한 설명을 추가합니다. 이 설명은 FastAPI에서 자동으로 Swagger 문서에 표시되어 API 사용자에게 명확한 가이드를 제공합니다.
코드만 봐도 각 필드의 의미와 제약조건을 즉시 이해할 수 있습니다. 여러분이 이 코드를 사용하면 검증 로직이 선언적으로 표현되어 가독성이 높아지고, 나중에 규칙을 변경할 때도 Field의 파라미터만 수정하면 됩니다.
또한 FastAPI와 함께 사용하면 자동으로 생성되는 API 문서가 훨씬 풍부해집니다.
실전 팁
💡 정규표현식 검증이 필요하면 Field(pattern=r'^[A-Za-z0-9]+$')처럼 pattern 파라미터를 사용하세요. 복잡한 형식(전화번호, 우편번호 등)을 검증할 때 유용합니다.
💡 Field의 alias 파라미터로 JSON 키 이름을 Python 변수명과 다르게 매핑할 수 있습니다. user_id = Field(alias="userId")처럼 camelCase API와 연동할 때 편리합니다.
💡 exclude=True를 설정하면 model_dump() 결과에서 특정 필드를 제외할 수 있습니다. 비밀번호 같은 민감한 정보를 응답에서 숨길 때 사용하세요.
💡 gt와 ge의 차이를 명확히 하세요. gt=0은 0을 포함하지 않지만(1 이상), ge=0은 0을 포함합니다(0 이상). 비즈니스 요구사항에 맞는 것을 선택하세요.
💡 Field의 examples 파라미터로 예시 값을 제공하면 FastAPI 문서에서 "Try it out" 기능의 기본값으로 사용되어 사용자 경험이 개선됩니다.
3. 커스텀 Validator - 복잡한 비즈니스 로직 검증
시작하며
여러분이 비밀번호 필드를 만드는데 "영문, 숫자, 특수문자를 각각 1개 이상 포함해야 한다"는 복잡한 규칙을 적용해야 한다면 Field만으로는 부족합니다. 실무에서는 단순한 범위 체크를 넘어서 여러 필드 간의 관계를 검증하거나, 외부 API 호출 결과를 검증하거나, 복잡한 비즈니스 로직을 적용해야 하는 경우가 많습니다.
예를 들어, 종료일이 시작일보다 나중이어야 한다거나, 중복된 이메일인지 데이터베이스를 확인해야 하는 경우입니다. 바로 이럴 때 필요한 것이 Pydantic의 커스텀 validator입니다.
field_validator 데코레이터를 사용해 원하는 검증 로직을 Python 함수로 자유롭게 작성할 수 있습니다.
개요
간단히 말해서, field_validator는 특정 필드에 대한 커스텀 검증 함수를 만들 수 있게 해주는 데코레이터입니다. 실무에서 정규표현식으로는 표현하기 어려운 규칙, 여러 조건을 조합한 복잡한 로직, 외부 시스템과의 연동이 필요한 검증에 사용합니다.
예를 들어, 한국 주민등록번호의 체크섬 검증, 신용카드 번호의 Luhn 알고리즘 검증, 사용자명 중복 체크 같은 경우에 필수적입니다. 기존에는 init 메서드를 오버라이드하거나 별도의 validate 함수를 만들어서 호출했다면, 이제는 field_validator로 선언적으로 검증 로직을 필드에 연결할 수 있습니다.
함수 이름은 자유롭게 지을 수 있고, 여러 필드에 대해 하나의 validator를 공유할 수도 있습니다. 커스텀 validator의 핵심 특징은 무제한의 검증 로직 작성 가능, 다른 필드 값 참조 가능, 값 변환과 정규화 동시 수행입니다.
이러한 특징들이 Pydantic을 단순한 타입 체커를 넘어 강력한 데이터 처리 도구로 만듭니다.
코드 예제
from pydantic import BaseModel, field_validator, ValidationError
import re
class UserRegistration(BaseModel):
username: str
password: str
password_confirm: str
@field_validator('username')
@classmethod
def username_alphanumeric(cls, v: str) -> str:
# 사용자명은 영문자와 숫자만 허용
if not re.match(r'^[a-zA-Z0-9]+$', v):
raise ValueError('사용자명은 영문자와 숫자만 사용 가능합니다')
return v.lower() # 소문자로 정규화
@field_validator('password')
@classmethod
def password_strength(cls, v: str) -> str:
# 비밀번호 강도 검증: 최소 8자, 영문/숫자/특수문자 각 1개 이상
if len(v) < 8:
raise ValueError('비밀번호는 최소 8자 이상이어야 합니다')
if not re.search(r'[A-Za-z]', v):
raise ValueError('영문자를 1개 이상 포함해야 합니다')
if not re.search(r'\d', v):
raise ValueError('숫자를 1개 이상 포함해야 합니다')
if not re.search(r'[!@#$%^&*]', v):
raise ValueError('특수문자를 1개 이상 포함해야 합니다')
return v
# 사용 예시
try:
user = UserRegistration(
username="John123",
password="Weak",
password_confirm="Weak"
)
except ValidationError as e:
print(e) # 비밀번호 강도 검증 실패 메시지
설명
이것이 하는 일: field_validator는 지정된 필드의 값이 설정될 때 자동으로 호출되는 검증 함수를 등록하여, Field로는 표현할 수 없는 복잡한 규칙을 적용합니다. 첫 번째로, @field_validator('username') 데코레이터로 어떤 필드를 검증할지 지정합니다.
여러 필드를 검증하려면 @field_validator('field1', 'field2')처럼 여러 개를 나열할 수 있습니다. @classmethod와 함께 사용하며, 첫 번째 인자는 cls(클래스 자체), 두 번째 인자는 v(검증할 값)입니다.
그 다음으로, 함수 내부에서 원하는 검증 로직을 작성합니다. username_alphanumeric 함수는 정규표현식으로 영문자와 숫자만 포함하는지 확인하고, 규칙을 위반하면 ValueError를 발생시킵니다.
Pydantic은 이 ValueError를 자동으로 캐치해서 ValidationError로 변환하고 사용자에게 친절한 에러 메시지를 제공합니다. 세 번째로, 검증을 통과하면 반드시 값을 반환해야 합니다.
return v.lower()처럼 값을 변환해서 반환할 수도 있습니다. 이렇게 하면 검증과 동시에 데이터 정규화(normalization)도 수행할 수 있습니다.
예를 들어 사용자명을 모두 소문자로 통일하거나, 공백을 제거하거나, 특정 형식으로 변환할 수 있습니다. 네 번째로, password_strength 함수처럼 여러 조건을 순차적으로 검사할 수 있습니다.
각 조건마다 명확한 에러 메시지를 제공하면 사용자가 무엇을 고쳐야 하는지 정확히 알 수 있습니다. 첫 번째 실패한 조건에서 즉시 ValueError가 발생하므로 효율적입니다.
여러분이 이 코드를 사용하면 아무리 복잡한 비즈니스 규칙도 명확한 Python 코드로 표현할 수 있습니다. 검증 로직이 모델 정의와 함께 있어 코드 구조가 깔끔하고, 재사용 가능한 검증 함수를 만들 수도 있습니다.
이것이 하는 일: model_validator는 모든 필드의 값이 설정된 후에 실행되어 필드 간의 관계나 전체 데이터의 일관성을 검증합니다. 첫 번째로, @model_validator(mode='after') 데코레이터를 사용합니다.
mode='after'는 모든 개별 필드 검증이 완료된 후에 실행됨을 의미합니다. 이 시점에는 모든 필드가 타입 변환과 개별 검증을 통과한 상태입니다.
그 다음으로, 함수의 첫 번째 인자로 self(모델 인스턴스 자체)를 받습니다. field_validator와 달리 개별 값이 아니라 전체 모델 객체를 받으므로 self.start_date, self.end_date처럼 여러 필드에 접근할 수 있습니다.
이것이 필드 간 관계를 검증할 수 있는 핵심입니다. 세 번째로, 두 필드를 비교하는 로직을 작성합니다.
end_date와 start_date를 비교해서 논리적으로 모순되는 상황을 감지합니다. 종료일이 시작일보다 이전이면 ValueError를 발생시켜 데이터 무결성을 보장합니다.
네 번째로, 검증을 통과하면 반드시 self를 반환해야 합니다. model_validator는 전체 모델 인스턴스를 받아서 검증하고, 수정된(또는 그대로인) 인스턴스를 반환하는 구조입니다.
필요하다면 self.some_field = new_value처럼 필드 값을 수정한 후 반환할 수도 있습니다. 여러분이 이 코드를 사용하면 단일 필드 검증으로는 불가능한 복잡한 비즈니스 규칙을 적용할 수 있습니다.
예를 들어, "할인 가격이 원가보다 높으면 안 된다", "최소 주문 수량은 재고 수량을 초과할 수 없다" 같은 규칙을 명확히 표현할 수 있습니다.
실전 팁
💡 field_validator는 기본적으로 필드 값이 None이 아닐 때만 실행됩니다. Optional 필드에서 None도 검증하려면 mode='before'를 사용하세요.
💡 여러 validator가 같은 필드에 적용되면 정의된 순서대로 실행됩니다. 순서가 중요한 검증(예: 먼저 정규화하고 나서 검증)은 함수 정의 순서를 고려하세요.
💡 model_validator는 필드 간 관계 검증에 사용하고, field_validator는 단일 필드 검증에 사용하세요. 역할을 명확히 분리하면 코드가 더 이해하기 쉽습니다.
💡 ValidationError를 직접 캐치해서 처리하면 사용자에게 친절한 에러 메시지를 제공할 수 있습니다. e.errors()로 모든 에러 정보를 리스트로 받을 수 있습니다.
💡 외부 API 호출이 필요한 검증(예: 이메일 중복 체크)은 성능에 주의하세요. 가능하면 비동기로 처리하거나, 검증 시점을 늦추는 것을 고려하세요.
4. 중첩된 모델과 리스트 검증 - 복잡한 데이터 구조 다루기
시작하며
여러분이 JSON API를 만들 때 사용자 정보 안에 주소 객체가 있고, 주문 정보 안에 여러 개의 상품 리스트가 있는 복잡한 구조를 다룬 적 있나요? 실무에서는 단순한 평면 구조가 아니라 객체 안에 객체가 있고, 배열 안에 객체가 있는 복잡한 데이터를 자주 다룹니다.
전통적인 방법으로는 중첩된 각 레벨마다 별도의 검증 코드를 작성해야 해서 코드가 복잡하고 유지보수가 어렵습니다. 바로 이럴 때 필요한 것이 Pydantic의 중첩 모델 기능입니다.
BaseModel을 다른 BaseModel의 필드 타입으로 사용하면 자동으로 재귀적으로 검증이 이루어집니다.
개요
간단히 말해서, Pydantic 모델을 다른 모델의 필드 타입으로 사용하면 중첩된 구조를 자동으로 검증하고 파싱할 수 있습니다. 실무에서 API 응답이 여러 단계로 중첩된 JSON 구조일 때, 데이터베이스의 관계형 데이터를 ORM으로 가져올 때, 설정 파일이 계층적 구조일 때 매우 유용합니다.
예를 들어, 사용자 프로필에 주소, 결제 정보, 선호 설정이 포함되어 있고 각각이 복잡한 구조를 가진 경우에 빛을 발합니다. 기존에는 딕셔너리를 재귀적으로 순회하며 각 레벨을 수동으로 검증했다면, 이제는 모델 클래스를 조합하는 것만으로 전체 구조가 자동으로 검증됩니다.
List[SomeModel]처럼 타입 힌트를 사용하면 리스트의 각 항목도 자동으로 검증됩니다. 중첩 모델의 핵심 특징은 재귀적 자동 검증, 타입 안정성 보장, 코드 재사용성 향상입니다.
작은 모델을 조합해서 큰 모델을 만들면 각 부분을 독립적으로 테스트하고 재사용할 수 있습니다.
코드 예제
from pydantic import BaseModel, Field
from typing import List, Optional
from datetime import datetime
# 주소 모델 (재사용 가능한 컴포넌트)
class Address(BaseModel):
street: str
city: str
zipcode: str = Field(pattern=r'^\d{5}$') # 5자리 숫자
country: str = "Korea"
# 주문 항목 모델
class OrderItem(BaseModel):
product_id: int
product_name: str
quantity: int = Field(gt=0)
unit_price: int = Field(gt=0)
@property
def total_price(self) -> int:
return self.quantity * self.unit_price
# 주문 모델 (중첩 구조)
class Order(BaseModel):
order_id: str
customer_name: str
shipping_address: Address # 중첩된 모델
items: List[OrderItem] # 리스트 안의 모델
order_date: datetime
notes: Optional[str] = None
@property
def total_amount(self) -> int:
return sum(item.total_price for item in self.items)
# 복잡한 JSON 데이터 자동 파싱 및 검증
order_data = {
"order_id": "ORD-2024-001",
"customer_name": "김철수",
"shipping_address": {
"street": "강남대로 123",
"city": "서울",
"zipcode": "12345"
},
"items": [
{"product_id": 1, "product_name": "노트북", "quantity": 1, "unit_price": 1500000},
{"product_id": 2, "product_name": "마우스", "quantity": 2, "unit_price": 30000}
],
"order_date": "2024-01-15T10:30:00"
}
order = Order(**order_data)
print(f"총 주문 금액: {order.total_amount:,}원")
print(f"배송지: {order.shipping_address.city}")
설명
이것이 하는 일: Pydantic은 중첩된 모델 구조를 만나면 자동으로 재귀적으로 각 레벨을 검증하고, 딕셔너리를 해당 모델 인스턴스로 변환합니다. 첫 번째로, Address처럼 작고 재사용 가능한 모델을 먼저 정의합니다.
이 모델은 독립적으로 사용할 수도 있고, 다른 모델의 일부로 사용할 수도 있습니다. 주소 정보는 사용자 프로필, 주문, 배송 등 여러 곳에서 필요하므로 한 번 정의해두면 계속 재사용할 수 있습니다.
그 다음으로, Order 모델에서 shipping_address: Address처럼 다른 모델을 타입으로 사용합니다. Order 인스턴스를 생성할 때 shipping_address에 딕셔너리를 넘기면 Pydantic이 자동으로 Address 인스턴스로 변환하고, Address의 모든 검증 규칙(zipcode 패턴 등)을 적용합니다.
세 번째로, items: List[OrderItem]처럼 리스트 타입 힌트를 사용하면 리스트의 각 항목이 OrderItem이어야 한다는 규칙이 됩니다. 딕셔너리 리스트를 넘기면 각 딕셔너리가 OrderItem으로 변환되고 검증됩니다.
만약 리스트의 어느 항목이라도 검증에 실패하면 어떤 인덱스에서 실패했는지 명확한 에러 메시지가 나옵니다. 네 번째로, @property 데코레이터로 계산된 속성을 추가할 수 있습니다.
total_price와 total_amount는 저장되는 필드가 아니라 다른 필드들로부터 계산되는 값입니다. 이렇게 하면 데이터 무결성을 유지하면서도 편리하게 파생 값을 사용할 수 있습니다.
여러분이 이 코드를 사용하면 복잡한 JSON API 응답을 타입 안전하게 파싱할 수 있고, 각 모델을 독립적으로 테스트하고 재사용할 수 있습니다. 코드 구조가 실제 데이터 구조를 명확히 반영하여 가독성도 크게 향상됩니다.
실전 팁
💡 중첩이 깊어질수록 성능이 약간 저하될 수 있습니다. 프로파일링을 통해 병목을 확인하고, 필요하면 일부 검증을 생략하는 것도 고려하세요.
💡 순환 참조(모델 A가 B를 참조하고 B가 다시 A를 참조)가 필요하면 from future import annotations와 문자열 타입 힌트('ModelName')를 사용하세요.
💡 model_dump()는 중첩된 모델을 모두 딕셔너리로 변환합니다. exclude_unset=True를 사용하면 명시적으로 설정된 필드만 포함되어 API 응답을 최적화할 수 있습니다.
💡 List 대신 Set, Dict, Tuple 같은 다른 컨테이너 타입도 사용할 수 있습니다. Dict[str, OrderItem]처럼 딕셔너리의 값도 검증할 수 있습니다.
💡 깊은 중첩 구조에서 특정 경로의 에러만 잡고 싶다면 try-except로 ValidationError를 잡고 e.errors()를 순회하며 'loc' 필드를 확인하세요.
5. Config 클래스로 동작 커스터마이징 - 모델 행동 제어하기
시작하며
여러분이 API에서 받은 JSON의 키가 camelCase인데 Python 코드에서는 snake_case를 사용하고 싶거나, 추가 필드가 들어와도 에러를 발생시키지 않고 무시하고 싶은 경우가 있나요? 실무에서는 Pydantic 모델의 기본 동작을 프로젝트 요구사항에 맞게 조정해야 하는 경우가 많습니다.
외부 API와 연동할 때 네이밍 컨벤션이 다르거나, 불변 객체가 필요하거나, 성능을 위해 검증을 최소화해야 할 수 있습니다. 바로 이럴 때 필요한 것이 Pydantic의 Config 클래스입니다.
모델 내부에 Config 클래스를 정의하면 검증 규칙, 직렬화 방식, 네이밍 전략 등 다양한 동작을 세밀하게 제어할 수 있습니다.
개요
간단히 말해서, Config는 Pydantic 모델 내부에 정의하는 특별한 클래스로, 모델의 전반적인 동작 방식을 설정합니다. 실무에서 외부 시스템과 통합할 때 네이밍 변환(camelCase ↔ snake_case), 추가 필드 처리 방식, 불변성 보장, 성능 최적화, ORM 객체 자동 변환 등이 필요할 때 사용합니다.
예를 들어, JavaScript 프론트엔드와 통신하는 백엔드 API에서 자동으로 네이밍을 변환하거나, SQLAlchemy 모델을 Pydantic 모델로 쉽게 변환할 때 유용합니다. 기존에는 필드마다 alias를 일일이 지정하거나, 별도의 변환 함수를 작성했다면, 이제는 Config 클래스에 몇 가지 옵션을 설정하는 것만으로 일괄 적용됩니다.
모든 설정이 한곳에 모여 있어 관리가 쉽습니다. Config의 핵심 특징은 전역 설정 제공, alias 자동 생성, ORM 모드 지원, 불변 객체 옵션입니다.
이러한 특징들이 Pydantic을 다양한 환경과 요구사항에 유연하게 대응할 수 있게 만듭니다.
코드 예제
from pydantic import BaseModel, Field, ConfigDict
from datetime import datetime
class UserProfile(BaseModel):
# Config 설정 (Pydantic v2 스타일)
model_config = ConfigDict(
# camelCase JSON을 snake_case Python으로 자동 변환
alias_generator=lambda field_name: ''.join(
word.capitalize() if i else word
for i, word in enumerate(field_name.split('_'))
),
populate_by_name=True, # 원래 이름과 alias 둘 다 허용
str_strip_whitespace=True, # 문자열 앞뒤 공백 자동 제거
validate_assignment=True, # 값 변경 시에도 검증
frozen=False, # True면 불변 객체 (값 변경 불가)
extra='forbid' # 'allow', 'ignore', 'forbid' - 추가 필드 처리
)
user_id: int
first_name: str
last_name: str
email_address: str
created_at: datetime
is_active: bool = True
# camelCase JSON을 자동으로 snake_case로 변환
json_data = {
"userId": 123,
"firstName": "John",
"lastName": "Doe",
"emailAddress": "john@example.com",
"createdAt": "2024-01-15T10:30:00",
"isActive": True
}
user = UserProfile(**json_data)
print(user.first_name) # John (snake_case로 접근)
print(user.model_dump(by_alias=True)) # camelCase로 출력
설명
이것이 하는 일: Config 클래스는 Pydantic 모델의 메타 설정을 제공하여 파싱, 검증, 직렬화 과정에서 모델이 어떻게 동작할지 결정합니다. 첫 번째로, alias_generator를 사용하면 모든 필드에 자동으로 alias가 생성됩니다.
예제의 람다 함수는 snake_case를 camelCase로 변환하는 로직입니다. first_name은 자동으로 firstName이라는 alias를 갖게 되어, JSON에서 firstName 키를 first_name 필드로 매핑합니다.
필드마다 Field(alias=...)를 쓸 필요가 없어집니다. 그 다음으로, populate_by_name=True는 원래 필드 이름과 alias 둘 다 허용합니다.
{"firstName": "John"} 또는 {"first_name": "John"} 둘 다 작동하므로 API 버전 마이그레이션 시 호환성을 유지할 수 있습니다. 세 번째로, str_strip_whitespace=True는 모든 문자열 필드에서 앞뒤 공백을 자동으로 제거합니다.
사용자 입력에서 실수로 들어온 공백 때문에 검증이 실패하는 것을 방지합니다. validate_assignment=True는 user.first_name = "Jane"처럼 값을 변경할 때도 검증을 수행하여 데이터 무결성을 보장합니다.
네 번째로, extra 옵션은 모델에 정의되지 않은 추가 필드를 어떻게 처리할지 결정합니다. 'forbid'는 추가 필드가 있으면 에러를 발생시키고(엄격한 검증), 'ignore'는 무시하며, 'allow'는 허용하고 저장합니다.
API 스펙을 엄격히 지키고 싶으면 'forbid', 하위 호환성이 필요하면 'ignore'를 사용하세요. 다섯 번째로, frozen=True를 설정하면 불변 객체가 됩니다.
인스턴스 생성 후 어떤 필드도 변경할 수 없어서 함수형 프로그래밍 스타일이나 캐싱에 유용합니다. model_dump(by_alias=True)를 사용하면 alias 이름(camelCase)으로 딕셔너리를 출력합니다.
여러분이 이 코드를 사용하면 외부 시스템과의 네이밍 불일치 문제를 자동으로 해결하고, 일관된 검증 정책을 모델 전체에 적용할 수 있습니다. 특히 프론트엔드와 백엔드의 네이밍 컨벤션이 다를 때 Config만 설정하면 자동 변환되어 개발 생산성이 크게 향상됩니다.
실전 팁
💡 Pydantic v2에서는 model_config = ConfigDict(...)를 사용합니다. v1의 class Config 스타일은 deprecated 되었으니 최신 문법을 사용하세요.
💡 from_attributes=True를 설정하면 SQLAlchemy 같은 ORM 객체를 Pydantic 모델로 직접 변환할 수 있습니다. user_pydantic = UserProfile.model_validate(user_orm)처럼 사용합니다.
💡 alias_generator 대신 Field(alias=...)를 사용하면 특정 필드만 개별적으로 alias를 지정할 수 있습니다. 대부분 자동, 일부는 수동으로 조합할 수 있습니다.
💡 validate_assignment=True는 편리하지만 성능 오버헤드가 있습니다. 값을 자주 변경하는 모델에서는 성능을 측정해보고 필요한 경우에만 사용하세요.
💡 json_schema_extra를 사용하면 JSON Schema에 추가 메타데이터(예시, 설명)를 넣을 수 있어 FastAPI 문서가 더 풍부해집니다.
6. 타입 변환과 coercion - 유연한 데이터 파싱
시작하며
여러분이 API에서 숫자를 받아야 하는데 클라이언트가 실수로 문자열 "123"을 보내거나, 불리언 필드에 "true"(문자열) 또는 1(숫자)이 들어오는 상황을 만난 적 있나요? 실무에서는 완벽한 데이터를 받는 경우가 드뭅니다.
URL 쿼리 파라미터는 모두 문자열이고, 레거시 시스템에서는 불리언을 0/1로 표현하며, 엑셀에서 가져온 데이터는 타입이 뒤섞여 있습니다. 이런 상황에서 엄격한 타입 검증만 하면 정상적인 데이터도 거부하게 됩니다.
바로 이럴 때 필요한 것이 Pydantic의 자동 타입 변환(type coercion)입니다. 합리적인 범위 내에서 타입을 자동으로 변환하여 사용자 편의성과 안정성의 균형을 맞춥니다.
개요
간단히 말해서, Pydantic은 타입이 정확히 일치하지 않아도 안전하게 변환 가능한 경우 자동으로 타입을 변환합니다. 실무에서 폼 데이터 처리(모든 값이 문자열), URL 쿼리 파라미터 파싱, CSV/엑셀 데이터 가져오기, 다양한 클라이언트와의 호환성 유지 등에 필수적입니다.
예를 들어, 페이지네이션 API에서 ?page=5&limit=20처럼 문자열로 들어온 쿼리 파라미터를 정수로 자동 변환하여 사용할 수 있습니다. 기존에는 int(value), bool(value) 같은 변환 코드를 직접 작성하고 예외 처리를 해야 했다면, 이제는 타입 힌트만 올바르게 지정하면 Pydantic이 알아서 변환을 시도합니다.
변환이 불가능한 경우에만 에러를 발생시킵니다. 타입 변환의 핵심 특징은 스마트한 자동 변환, 엄격 모드 옵션, 명확한 실패 메시지입니다.
문자열 "123"은 정수로, "true"는 불리언으로, ISO 형식 문자열은 datetime으로 자동 변환되어 개발자의 수고를 덜어줍니다.
코드 예제
from pydantic import BaseModel, Field, field_validator
from datetime import datetime
from typing import List
class SearchQuery(BaseModel):
# 문자열 "123" → 정수 123 자동 변환
page: int = Field(default=1, ge=1)
limit: int = Field(default=20, ge=1, le=100)
# 다양한 불리언 표현 자동 인식
# "true", "1", "yes", "on" → True
# "false", "0", "no", "off" → False
include_archived: bool = False
# ISO 형식 문자열 → datetime 객체 변환
# "2024-01-15T10:30:00" → datetime 객체
created_after: datetime | None = None
# 쉼표로 구분된 문자열 → 리스트 변환 (커스텀)
tags: str = ""
@field_validator('tags')
@classmethod
def parse_tags(cls, v: str) -> List[str]:
# "python,django,api" → ["python", "django", "api"]
if not v:
return []
return [tag.strip() for tag in v.split(',') if tag.strip()]
# URL 쿼리 파라미터 시뮬레이션 (모두 문자열)
query_params = {
"page": "3", # 문자열 → 정수
"limit": "50",
"include_archived": "true", # 문자열 → 불리언
"created_after": "2024-01-15T10:30:00", # 문자열 → datetime
"tags": "python, pydantic, validation"
}
query = SearchQuery(**query_params)
print(f"Page {query.page}, Limit {query.limit}")
print(f"Archived: {query.include_archived}")
print(f"Tags: {query.parse_tags(query.tags)}")
print(f"Type of page: {type(query.page)}") # <class 'int'>
설명
이것이 하는 일: Pydantic은 각 필드의 타입 힌트를 보고, 입력 값이 정확히 일치하지 않아도 손실 없이 변환 가능한 경우 자동으로 타입을 변환하여 개발자 편의성을 높입니다. 첫 번째로, 숫자 타입 변환을 살펴봅시다.
page: int로 선언했는데 "3"(문자열)이 들어오면 Pydantic은 int("3")을 시도합니다. 성공하면 정수 3으로 변환하고, 실패하면(예: "three") ValidationError를 발생시킵니다.
소수점이 있는 "3.14"도 정수 필드에서는 거부되어 데이터 무결성을 보장합니다. 그 다음으로, 불리언 변환은 매우 스마트합니다.
"true", "True", "TRUE", "1", "yes", "on" 등 다양한 표현을 모두 True로 인식합니다. 반대로 "false", "0", "no", "off"는 False로 변환됩니다.
이는 HTML 폼 데이터나 다양한 API와의 호환성을 크게 향상시킵니다. 세 번째로, datetime 변환은 ISO 8601 형식 문자열을 자동으로 인식합니다.
"2024-01-15T10:30:00"처럼 표준 형식이면 datetime 객체로 변환되고, 잘못된 형식이면 에러가 발생합니다. 타임존 정보도 자동으로 처리되어 국제화된 애플리케이션에 유용합니다.
네 번째로, 커스텀 변환이 필요한 경우(예: 쉼표 구분 문자열 → 리스트) field_validator를 사용합니다. parse_tags 함수는 "python, pydantic, validation" 같은 문자열을 받아서 공백을 제거하고 리스트로 변환합니다.
이렇게 Pydantic의 자동 변환과 커스텀 로직을 조합할 수 있습니다. 다섯 번째로, Union 타입(또는 Python 3.10+의 | 연산자)을 사용하면 여러 타입을 허용할 수 있습니다.
created_after: datetime | None은 datetime 객체나 None 둘 다 허용하며, 문자열이 들어오면 먼저 datetime으로 변환을 시도합니다. 여러분이 이 코드를 사용하면 다양한 소스에서 들어오는 데이터를 일관된 타입으로 정규화할 수 있습니다.
URL 쿼리 파라미터, 폼 데이터, CSV 파일 등 어디서 오든 같은 Pydantic 모델로 안전하게 처리할 수 있어 코드 재사용성이 높아집니다.
실전 팁
💡 엄격한 타입 검증이 필요하면 StrictInt, StrictBool, StrictStr 같은 strict 타입을 사용하세요. 자동 변환 없이 정확한 타입만 허용합니다.
💡 float 필드에 정수를 넣으면 자동으로 float로 변환되지만, 반대(int 필드에 float)는 거부됩니다. 데이터 손실 가능성 때문입니다.
💡 리스트나 딕셔너리도 자동 변환이 일어납니다. {"tags": "python"}처럼 단일 값이 들어와도 tags: List[str]이면 ["python"]으로 자동 변환될 수 있습니다(설정에 따라).
💡 커스텀 타입 변환이 자주 필요하면 get_validators 메서드를 가진 커스텀 타입 클래스를 만들어 재사용하세요.
💡 변환 실패 시 에러 메시지가 명확하므로 사용자에게 "정수를 입력하세요" 같은 친절한 안내를 제공할 수 있습니다. e.errors()로 에러 정보를 추출하세요.
7. JSON Schema와 OpenAPI 통합 - 자동 문서화
시작하며
여러분이 API를 만들고 나서 Swagger 문서를 별도로 작성해야 하는 번거로움을 겪은 적 있나요? 코드가 변경될 때마다 문서를 수동으로 업데이트하다 보면 코드와 문서가 불일치하는 문제가 발생합니다.
실무에서는 API 문서화가 필수이지만, 수동으로 관리하면 항상 최신 상태를 유지하기 어렵습니다. 개발자는 코드를 수정하고 문서 업데이트를 잊어버리거나, 문서 작성 자체가 귀찮아서 미루게 됩니다.
바로 이럴 때 필요한 것이 Pydantic의 JSON Schema 자동 생성 기능입니다. 모델 정의만으로 자동으로 OpenAPI 스펙 호환 스키마가 생성되어 Swagger 문서가 코드와 항상 동기화됩니다.
개요
간단히 말해서, Pydantic 모델은 자동으로 JSON Schema를 생성할 수 있고, FastAPI 같은 프레임워크와 함께 사용하면 OpenAPI(Swagger) 문서가 자동으로 만들어집니다. 실무에서 RESTful API 문서화, 프론트엔드 팀과의 API 스펙 공유, 자동 테스트 케이스 생성, 클라이언트 SDK 자동 생성 등에 활용됩니다.
예를 들어, FastAPI는 Pydantic 모델을 기반으로 /docs 엔드포인트에서 인터랙티브한 API 문서를 자동으로 제공합니다. 기존에는 Swagger YAML/JSON을 수동으로 작성하거나, docstring에 복잡한 포맷으로 문서를 작성했다면, 이제는 Pydantic 모델에 description, example 같은 메타데이터만 추가하면 자동으로 문서가 생성됩니다.
코드가 곧 문서가 되는 "Documentation as Code"를 실현합니다. JSON Schema 통합의 핵심 특징은 자동 스키마 생성, FastAPI와의 완벽한 통합, 예시와 설명 포함 가능입니다.
이러한 특징들이 API 개발 속도를 높이고 문서 품질을 보장합니다.
코드 예제
from pydantic import BaseModel, Field, ConfigDict
from typing import List, Optional
from datetime import datetime
class UserCreate(BaseModel):
"""사용자 생성 요청 모델"""
model_config = ConfigDict(
json_schema_extra={
"examples": [
{
"username": "john_doe",
"email": "john@example.com",
"age": 25,
"interests": ["coding", "music"]
}
]
}
)
username: str = Field(
...,
min_length=3,
max_length=20,
description="사용자 고유 아이디",
examples=["john_doe", "alice123"]
)
email: str = Field(
...,
description="이메일 주소 (인증용)",
examples=["user@example.com"]
)
age: int = Field(
...,
ge=0,
le=150,
description="사용자 나이",
examples=[25, 30, 40]
)
interests: Optional[List[str]] = Field(
default=None,
description="관심사 목록",
examples=[["coding", "music"], ["sports", "travel"]]
)
# JSON Schema 생성 (OpenAPI 스펙 호환)
schema = UserCreate.model_json_schema()
# 스키마 출력 (일부)
import json
print(json.dumps(schema, indent=2))
# FastAPI 예시 (실제 사용)
"""
from fastapi import FastAPI
app = FastAPI()
@app.post("/users/", response_model=UserCreate)
def create_user(user: UserCreate):
# Pydantic 모델이 자동으로 요청 검증 + 문서 생성
return user
# 이제 http://localhost:8000/docs 에서
# 자동 생성된 Swagger UI를 볼 수 있습니다!
"""
설명
이것이 하는 일: Pydantic은 모델 정의를 분석해서 필드 타입, 제약조건, 설명, 예시를 포함한 표준 JSON Schema를 생성하고, 이를 OpenAPI 문서화에 활용합니다. 첫 번째로, Field의 description 파라미터로 각 필드에 대한 설명을 추가합니다.
이 설명은 JSON Schema의 description 속성으로 변환되어 API 문서에 표시됩니다. "이메일 주소 (인증용)"처럼 구체적으로 작성하면 API 사용자가 필드의 목적을 명확히 이해할 수 있습니다.
그 다음으로, examples 파라미터로 예시 값을 제공합니다. API 문서의 "Try it out" 기능에서 이 예시 값이 기본값으로 채워져 사용자가 즉시 테스트해볼 수 있습니다.
여러 개의 예시를 리스트로 제공하면 다양한 사용 사례를 보여줄 수 있습니다. 세 번째로, model_config의 json_schema_extra로 스키마 전체에 대한 추가 정보를 제공합니다.
examples 키에 전체 객체의 예시를 배열로 제공하면 OpenAPI 문서에서 완전한 요청 예시로 표시됩니다. 실제 사용 사례를 보여주는 가장 효과적인 방법입니다.
네 번째로, model_json_schema() 메서드를 호출하면 모델의 전체 JSON Schema가 딕셔너리로 반환됩니다. 이 스키마는 OpenAPI 3.0 스펙과 호환되므로 다양한 도구와 통합할 수 있습니다.
스키마에는 타입, 제약조건(minLength, maximum 등), 필수 필드 목록, 설명, 예시가 모두 포함됩니다. 다섯 번째로, FastAPI와 함께 사용하면 완전히 자동화됩니다.
함수 파라미터로 Pydantic 모델을 선언하면 FastAPI가 자동으로 요청 body 검증, 에러 응답 생성, OpenAPI 스키마 생성, Swagger UI 제공을 모두 처리합니다. /docs 엔드포인트에 접속하면 즉시 인터랙티브한 API 문서를 볼 수 있습니다.
여러분이 이 코드를 사용하면 API 문서 작성에 드는 시간을 거의 제로로 만들 수 있습니다. 코드를 수정하면 문서가 자동으로 업데이트되므로 항상 최신 상태를 유지하고, 프론트엔드 개발자나 외부 사용자에게 명확한 API 가이드를 제공할 수 있습니다.
실전 팁
💡 docstring(""" """)은 클래스 자체의 설명이 되어 스키마의 title과 description으로 사용됩니다. 모델의 전반적인 목적을 docstring에 작성하세요.
💡 deprecated=True를 Field에 설정하면 OpenAPI 문서에서 해당 필드가 deprecated로 표시되어 점진적 마이그레이션에 유용합니다.
💡 model_json_schema()의 결과를 파일로 저장하면 프론트엔드 팀과 스펙을 공유하거나, 코드 생성 도구의 입력으로 사용할 수 있습니다.
💡 FastAPI의 @app.post("/users/", tags=["users"], summary="Create a user")처럼 추가 메타데이터를 엔드포인트에 붙이면 문서가 더 체계적으로 구성됩니다.
💡 response_model을 설정하면 응답 스키마도 자동 생성되어 클라이언트가 어떤 데이터를 받을지 명확히 알 수 있습니다.
8. Settings Management - 환경 설정 관리
시작하며
여러분이 데이터베이스 URL, API 키, 디버그 모드 같은 설정을 관리할 때 환경 변수를 직접 os.getenv()로 읽고, 타입 변환하고, 기본값을 처리하는 반복적인 코드를 작성한 적 있나요? 실무에서는 개발/스테이징/프로덕션 환경마다 다른 설정을 관리해야 하고, 환경 변수와 .env 파일을 혼합해서 사용하며, 민감한 정보를 안전하게 다뤄야 합니다.
설정 관리 코드가 여기저기 흩어져 있으면 유지보수가 어렵고 실수하기 쉽습니다. 바로 이럴 때 필요한 것이 Pydantic의 Settings Management 기능입니다.
BaseSettings를 상속받으면 환경 변수를 자동으로 읽어서 타입 검증까지 해주는 설정 클래스를 만들 수 있습니다.
개요
간단히 말해서, BaseSettings는 환경 변수와 .env 파일에서 자동으로 값을 읽어와 검증된 설정 객체를 만들어주는 특별한 BaseModel입니다. 실무에서 애플리케이션 설정 중앙 관리, 환경별 설정 분리, 시크릿 정보 안전한 처리, 12-Factor App 원칙 구현에 사용합니다.
예를 들어, DATABASE_URL, SECRET_KEY, DEBUG 같은 설정을 타입 안전하게 관리하고, 필수 설정이 빠졌을 때 즉시 에러를 발생시킬 수 있습니다. 기존에는 os.getenv('DEBUG', 'false').lower() == 'true' 같은 장황한 코드를 반복해서 작성했다면, 이제는 debug: bool 필드 하나만 선언하면 자동으로 환경 변수를 읽고 불리언으로 변환합니다.
.env 파일도 자동으로 읽어와 로컬 개발 환경 설정이 간편합니다. Settings Management의 핵심 특징은 자동 환경 변수 로딩, .env 파일 지원, 타입 변환과 검증, 계층적 설정 지원입니다.
이러한 특징들이 설정 관리를 단순화하고 배포 환경 간의 차이를 명확히 관리할 수 있게 합니다.
코드 예제
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field, SecretStr
from typing import Optional
class AppSettings(BaseSettings):
"""애플리케이션 전역 설정"""
model_config = SettingsConfigDict(
env_file='.env', # .env 파일 자동 로드
env_file_encoding='utf-8',
env_prefix='APP_', # 환경 변수 접두사: APP_DEBUG, APP_PORT 등
case_sensitive=False # 대소문자 구분 안 함
)
# 환경 변수 APP_DEBUG를 자동으로 읽음
debug: bool = Field(default=False, description="디버그 모드")
# 환경 변수 APP_PORT를 자동으로 읽고 정수로 변환
port: int = Field(default=8000, ge=1, le=65535)
# 민감한 정보는 SecretStr로 관리 (로그에 출력 안 됨)
database_url: SecretStr = Field(
..., # 필수 필드
description="데이터베이스 연결 URL"
)
secret_key: SecretStr = Field(
...,
min_length=32,
description="JWT 시크릿 키"
)
# 선택적 설정
log_level: str = Field(default="INFO")
redis_url: Optional[str] = None
# .env 파일 예시:
"""
APP_DEBUG=true
APP_PORT=3000
APP_DATABASE_URL=postgresql://user:pass@localhost/db
APP_SECRET_KEY=your-super-secret-key-at-least-32-chars-long
APP_LOG_LEVEL=DEBUG
"""
# 사용 예시
settings = AppSettings()
print(f"Debug mode: {settings.debug}") # True
print(f"Port: {settings.port}") # 3000
print(f"Database: {settings.database_url.get_secret_value()}")
# 주의: 실제로는 시크릿 값을 출력하지 마세요!
# 싱글톤 패턴으로 사용 권장
def get_settings() -> AppSettings:
return AppSettings()
설명
이것이 하는 일: BaseSettings는 환경 변수, .env 파일, 기본값을 우선순위에 따라 읽어와서 타입 변환과 검증을 수행하고, 애플리케이션 전체에서 사용할 수 있는 설정 객체를 생성합니다. 첫 번째로, 클래스 정의만으로 설정 필드를 선언합니다.
debug: bool처럼 타입 힌트를 붙이면 Pydantic이 자동으로 환경 변수를 해당 타입으로 변환합니다. APP_DEBUG=true(문자열)가 self.debug=True(불리언)로 자동 변환되는 마법이 일어납니다.
그 다음으로, model_config에서 env_prefix='APP_'를 설정하면 모든 필드가 APP_ 접두사를 가진 환경 변수를 찾습니다. debug 필드는 APP_DEBUG 환경 변수를 읽고, port는 APP_PORT를 읽습니다.
이렇게 하면 환경 변수 이름 충돌을 방지하고 애플리케이션 설정을 명확히 구분할 수 있습니다. 세 번째로, env_file='.env'로 .env 파일 경로를 지정하면 자동으로 파일을 읽어옵니다.
우선순위는 "실제 환경 변수 > .env 파일 > 기본값" 순서입니다. 로컬 개발에서는 .env 파일을, 프로덕션에서는 실제 환경 변수를 사용하는 것이 일반적입니다.
네 번째로, SecretStr 타입은 민감한 정보를 다룰 때 필수입니다. database_url: SecretStr로 선언하면 객체를 출력할 때 **********로 마스킹되어 실수로 로그에 비밀번호가 노출되는 것을 방지합니다.
실제 값은 get_secret_value() 메서드로만 접근 가능합니다. 다섯 번째로, Field에 필수 여부(...)와 검증 규칙을 추가할 수 있습니다.
database_url처럼 ...로 표시된 필드는 필수이므로, 환경 변수가 없으면 애플리케이션 시작 시 즉시 에러가 발생합니다. 이는 잘못된 설정으로 배포되는 것을 방지합니다.
여러분이 이 코드를 사용하면 설정 관리 코드가 한 곳에 집중되고, 타입 안정성이 보장되며, 환경별 배포가 간편해집니다. 새로운 설정을 추가할 때도 필드 하나만 추가하면 되므로 유지보수가 쉽습니다.
실전 팁
💡 pydantic-settings는 별도 패키지이므로 pip install pydantic-settings로 설치해야 합니다. pydantic 패키지에 포함되어 있지 않습니다.
💡 @lru_cache 데코레이터로 get_settings() 함수를 감싸면 싱글톤 패턴이 되어 .env 파일을 한 번만 읽고 재사용합니다. 성능이 향상됩니다.
💡 env_nested_delimiter='__'를 설정하면 DATABASE__HOST, DATABASE__PORT처럼 중첩된 설정을 표현할 수 있습니다.
💡 .env 파일은 절대 git에 커밋하지 마세요! .gitignore에 추가하고, .env.example 파일로 템플릿만 공유하세요.
💡 Kubernetes Secrets, AWS Secrets Manager 같은 시크릿 관리 도구와 통합하려면 커스텀 settings source를 구현할 수 있습니다. 이상으로 Pydantic 데이터 검증 완벽 가이드를 마칩니다. 이 8가지 핵심 개념을 마스터하면 Python에서 안전하고 유지보수 가능한 데이터 처리 코드를 작성할 수 있습니다!
이 카드뉴스가 포함된 코스
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.