Pydantic이란 무엇인가?
Pydnatic은 Python에서 데이터 검증, 설정 및 직렬화/역직렬화 작업을 간편하게 처맇라 수 있도록 돕는 라이브러리이다. Pydantic의 주요 목적은 정확하고 안전하게 데이터를 다룰 수 있도록 해주는 것으로, 주로 JSON, Dict, Model 형태의 데이터를 효율적으로 처리하고 검증하는데 사용된다.
Pydantic은 BaseModel을 통해 명시적인 데이터 모델을 정의하고, 해당 모델의 필드가 특정 조건을 충족하는지 자동으로 검증해준다. 예를 들어, str, int, float, datetime과 같은 데이터 타입에 맞는 값을 설정하고, 타입이 맞지 않거나 필수 값이 빠졌을 경우 자동으로 에러를 발생시킨다.
비공개 속성이 필요한 이유
python에서 클래스의 속성은 기본적으로 공개되어, 외부에서 접근하고 수정할 수 있다. 그러나 클래스 외부에서 접근하거나 수정하지 말아야 할 데이터가 필요하고 이러한 데이터는 내부 로직을 처리하는 데만 사용되며, 외부와의 데이터 교환에서 제외되어야 한다.
비공개 속성의 필요성
1. 보안
- 민감한 데이터(예: 비밀번호, API키, 토큰 등)는 외부에 노출 되지 않아야 하고, 이를 보호하기 위해 해당 데이터를 비공개 속성으로 관리해야한다.
from pydantic import BaseModel, PrivateAttr
class User(BaseModel):
username: str
password: str
_session_token: str = PrivateAttr() # 세션 토큰, 외부에 노출될 필요 없음
def authenticate(self, token: str):
# 세션 토큰 검증 로직
if self._session_token == token:
return True
return False
def generate_token(self):
# 임의의 세션 토큰 생성 로직
self._session_token = "secret_session_token"
return self._session_token
2. 내부 로직 캡슐화
- 특정 속성은 외부에서 수정될 필요가 없고, 낸부적인 계산이나 상태 추적을 위해 사용될 수 있다. 예를들어, _internal_value와 같은 속성은 외부와의 상호작용에 필요하지 않지만 클래스 내부에서 중요한 역할을 할 수 있다.
- 데이터 은닉(Encapsulation)은 객체 지향 프로그래밍에서 중요한 개념으로, 클래스 외부에서 불필요한 접근을 제한하고 내부 구현을 캡슐화하는데 사용된다.
from pydantic import BaseModel, PrivateAttr
class MyModel(BaseModel):
name: str
_internal_value: int = PrivateAttr(42) # 기본값을 PrivateAttr로 설정
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._internal_value += 1 # 내부값을 초기화하면서 변경
# 사용 예
model = MyModel(name="Test")
print(model.name) # 'Test' (직렬화가 되므로 name은 접근 가능)
print(model._internal_value) # '42' (직접 접근할 수 있지만, 일반적으로 권장되지 않음)
3. 데이터 무결성 유지
- 일부 속성은 클래스 외부에서 잘못 수정되면 프로그램의 로직이 깨질 수 있는 중요한 데이터일 수 있다. 이러한 솏어은 외부에서 수정되지 않도록 보호하고, 내부에서만 제어될 필요가 있다.
from pydantic import BaseModel, PrivateAttr
class Product(BaseModel):
name: str
price: float
_price_history: list[float] = PrivateAttr(default_factory=list)
def apply_discount(self, discount_percentage: float):
# 할인율을 적용한 가격을 계산하여 가격 변경
self._price_history.append(self.price)
self.price = self.price * (1 - discount_percentage / 100)
def revert_price(self):
# 이전 가격으로 되돌리기
if self._price_history:
self.price = self._price_history.pop()
# 사용 예시
product = Product(name="Laptop", price=1000)
product.apply_discount(10)
print(product.price) # 900.0
product.revert_price()
print(product.price) # 1000.0
print(product.dict()) # {'name': 'Laptop', 'price': 1000.0}
print(product._price_history) # 접근 불가, 직렬화에서 제외됨
4. 상속 관계에서의 속성 관리
- 상속을 사용할 때, 부모 클래스에서 정의된 속성이 자식 클래스에서 사용되거나 수정되지 않도록 비공개 속성으로 관리할 수 있다. 자식 클래스에서 부모 클래스의 속성에 직접 접근할 수 없게 되므로, 상속 구조에서의 안정성을 높여준다.
from pydantic import BaseModel, PrivateAttr
class Vehicle(BaseModel):
brand: str
_engine_status: str = PrivateAttr("OFF") # 부모 클래스에서 비공개로 처리
def start_engine(self):
self._engine_status = "ON"
def stop_engine(self):
self._engine_status = "OFF"
class Car(Vehicle):
model: str
def get_engine_status(self):
return self._engine_status
# 사용 예시
car = Car(brand="Toyota", model="Corolla")
car.start_engine()
print(car.get_engine_status()) # "ON"
print(car.dict()) # {'brand': 'Toyota', 'model': 'Corolla'}
print(car._engine_status) # 접근 불가, 직렬화에서 제외됨
PrivateAttr의 필요성과 목적 설명
Pydantic의 PrivateAttr은 비공개 속성을 정의하기 위한 기능이다. PrivateAttr을 사용하면 Pydantic 모델에서 직렬화 및 외부 접근에서 제외되는 속성을 설정할 수 있다. PrivateAttr은 단순히 속성을 숨기거나 보호하는 목적 외에도, 모델 내에서 중요한 데이터를 안전하게 관리하고, 외부와의 데이터 처리에서 발생할 수 있는 실수를 방지하는 역할을 한다.
PrivateAttr의 목적
1. 직렬화에서 제외
- PrivateAttr로 정의된 속성은 모델을 dict()나 json()으로 직렬화할 때 자동으로 제외된다. 이는 외부 API나 데이터베이스에 불필요한 정보를 노출하지 않게 해준다.
from pydantic import BaseModel, PrivateAttr
class SensitiveData(BaseModel):
username: str
_password_hash: str = PrivateAttr() # 비밀번호 해시, 직렬화에서 제외됨
def set_password(self, password: str):
self._password_hash = hash(password) # 비밀번호를 해시로 저장
# 사용 예시
user = SensitiveData(username="alice", _password_hash="hashed_password")
print(user.dict()) # {'username': 'alice'}
# _password_hash는 dict에 포함되지 않음
2. 내부 상태 관리
- 모델의 외부 인터페이스에 포함되지 않지만, 모델의 내부 상태를 추적하거나, 비즈니스 로직을 구현하는데 필요한 데이터를 저장하는데 사용된다.
- 예를 들어, 모델이 특정 값을 계산하거나, 일시적인 데이털르 추절해야 할 때 유용
from pydantic import BaseModel, PrivateAttr
class Order(BaseModel):
order_id: str
total_amount: float
_processing_status: str = PrivateAttr("pending") # 주문 처리 상태, 외부에서 수정되지 않음
def process_order(self):
self._processing_status = "processed" # 상태 변경
def get_processing_status(self):
return self._processing_status
# 사용 예시
order = Order(order_id="ORD123", total_amount=100)
order.process_order()
print(order.get_processing_status()) # "processed"
print(order.dict()) # {'order_id': 'ORD123', 'total_amount': 100}
3. 객체 불변성 유지
- PrivateAttr을 사용하여 외부에서 속성 값을 수정할 수 없게 만들어, 객체의 불변성을 유지할 수 있다. 이는 특히 클래스 내부에서만 사용되며 외부와는 관련 없는 속성을 다룰 때 중요하다.
from pydantic import BaseModel, PrivateAttr
class Rectangle(BaseModel):
width: float
height: float
_area: float = PrivateAttr()
def __init__(self, width: float, height: float):
super().__init__(width=width, height=height)
self._area = self.calculate_area()
def calculate_area(self) -> float:
return self.width * self.height
@property
def area(self):
return self._area
# 사용 예시
rect = Rectangle(width=10, height=5)
print(rect.area) # 50
# 외부에서 _area를 수정하려고 할 수 없고, 직렬화 시에도 제외됨
4. 보안성 강화
- 민감함 데이터나 외부에 노출되지 않아야 하는 값들(예: 비밀번호, 인증 키 등)을 PrivateAttr로 설정하면 보안을 강화할 수 있다. 이렇게 설정된 값은 외부에 노출되지 않으며, 클래스 외부에서 접근할 수 없다.
from pydantic import BaseModel, PrivateAttr
class APIClient(BaseModel):
client_id: str
_api_key: str = PrivateAttr() # API 키, 외부에서 접근할 수 없음
def set_api_key(self, api_key: str):
self._api_key = api_key
def get_api_key(self) -> str:
return self._api_key
# 사용 예시
client = APIClient(client_id="my_client_id")
client.set_api_key("my_secret_api_key")
print(client.get_api_key()) # "my_secret_api_key"
print(client.dict()) # {'client_id': 'my_client_id'}
# _api_key는 직렬화에서 제외됨
결론
- Pydnatic은 데이터 모델링 및 검증을 위한 도구로, 데이터의 유효성 검사와 직렬화/역직렬화를 쉽게 처리할 수 있게 해준다. 이를 통해 안전하고 효율적인 데이터 관리가 가능하다.
(직렬화: 데이터를 저장하거나 전송하기 위해 객체를 특정 형식 (Json, XML)으로 변환하는 과정)
(역직렬화: 직렬화된 데이터를 다시 원래 객체나 데이터 구조로 복원하는 과정)
- 비공개 속성은 보안과 내부 로직 캡슐화, 데이터 무결성 유지 등의 이유로 필요하다. 특히, 클래스 내부에서마 사용되며 외부에 노출될 필요가 없는 데이터를 다룰 때 유용하다.
- PrivateAttr은 비공개 속성을 모델 내에서 정의하고 관리할 수 있도록 도와주는 기능으로, 직렬화에서 제외되고 외부에서 접근 불가능한 속성을 안전하게 관리할 수 있게 해준다.