파이썬 데이터 클래스 툴벨트 선택하기
Ethan Miller
Product Engineer · Leapcell

소개
현대 소프트웨어 개발에서 데이터를 효과적으로 관리하고 표현하는 것은 매우 중요합니다. 웹 API를 구축하든, 복잡한 데이터셋을 처리하든, 또는 단순히 구성 매개변수를 구성하든, 파이썬은 데이터 구조를 정의하는 몇 가지 강력한 방법을 제공합니다. 일반 클래스로도 충분히 작업을 수행할 수 있지만, 보일러플레이트 코드가 많고 강력한 애플리케이션에 중요한 내장 기능이 부족한 경우가 많습니다. 바로 이때 전문 데이터 클래스 라이브러리가 중요한 역할을 합니다. 이 글에서는 dataclasses
, Pydantic
, attrs
를 살펴보고 각 라이브러리의 강점, 약점, 이상적인 사용 사례를 탐색하여 파이썬 프로젝트에 가장 적합한 도구를 선택하는 데 도움을 드리겠습니다.
핵심 개념
각 라이브러리의 구체적인 내용을 살펴보기 전에, 논의 전반에 걸쳐 관련될 몇 가지 일반 용어를 정의해 보겠습니다.
- 데이터 클래스(Data Class): 주로 데이터를 보유하기 위해 설계된 클래스로, 해당 데이터를 관리하는 데 필요한 것 이상의 최소한의 또는 전혀 없는 동작을 가집니다.
- 보일러플레이트 코드(Boilerplate Code):
__init__
,__repr__
,__eq__
등과 같이 거의 또는 전혀 변형 없이 많은 곳에 작성해야 하는 반복적인 코드입니다. - 타입 힌트(Type Hinting): 파이썬 기능(PEP 484)으로, 개발자가 변수, 함수 인자 및 반환 값의 예상 유형을 나타낼 수 있어 코드 가독성을 향상시키고 정적 분석을 가능하게 합니다.
- 불변성(Immutability): 생성 후 상태를 수정할 수 없는 객체의 속성입니다. 이는 더 예측 가능하고 스레드 안전한 코드로 이어질 수 있습니다.
- 유효성 검사(Validation): 사용하기 전에 데이터가 특정 규칙 또는 제약 조건을 준수하는지 확인하는 프로세스입니다. 이는 데이터 무결성을 유지하는 데 중요합니다.
- 직렬화/역직렬화(Serialization/Deserialization): 객체의 상태를 저장하거나 전송할 수 있는 형식(예: JSON, YAML)으로 변환한 다음 해당 형식에서 객체를 다시 구성하는 프로세스입니다.
dataclasses
: 파이썬의 내장 솔루션
파이썬 3.7(PEP 557)에 도입된 dataclasses
는 표준 라이브러리의 일부로, 데이터 클래스를 만드는 데 데코레이터 기반 접근 방식을 제공합니다. 이는 타입 힌트를 기반으로 __init__
, __repr__
, __eq__
, __hash__
와 같은 일반적인 메서드를 자동으로 생성하여 보일러플레이트를 줄이는 것을 목표로 합니다.
구현 및 사용법
dataclasses
를 사용하려면 클래스를 @dataclass
로 장식하기만 하면 됩니다. 필드는 타입 힌트를 사용하여 정의됩니다.
from dataclasses import dataclass, field @dataclass class User: user_id: int name: str = "Anonymous" # 기본값 email: str | None = None is_active: bool = True friends: list[int] = field(default_factory=list) # 가변 기본값은 factory로 처리 # 인스턴스화 user1 = User(user_id=123, name="Alice", email="alice@example.com") user2 = User(user_id=456, name="Bob") print(user1) # 출력: User(user_id=123, name='Alice', email='alice@example.com', is_active=True, friends=[]) print(user1 == User(user_id=123, name="Alice", email="alice@example.com")) # 출력: True # 불변성 @dataclass(frozen=True) class ImmutablePoint: x: int y: int # point = ImmutablePoint(10, 20) # point.x = 5 # FrozenInstanceError 발생
주요 기능 및 사용 사례
- 보일러플레이트 감소:
__init__
,__repr__
,__eq__
,__hash__
등을 자동으로 생성합니다. - 타입 힌트 기반: 필드 정의를 위해 표준 파이썬 타입 힌트를 활용합니다.
- 불변성: 불변 데이터 클래스를 만드는 데
frozen=True
를 지원합니다. - 기본값: 필드에 기본값을 쉽게 할당할 수 있습니다.
- 작은 설치 공간: 표준 라이브러리이므로 외부 종속성이 추가되지 않습니다.
- 사용 사례: 간단한 데이터 구조, 구성, 기본 데이터 표현 및 동일성 검사만으로 충분한 내부 데이터 모델에 이상적이며 외부 종속성을 피하고 싶을 때 적합합니다.
attrs
: 성숙하고 기능이 풍부한 대안
attrs
(종종 "에이터스"로 발음됨)는 dataclasses
보다 앞선 2015년에 출시된 타사 라이브러리입니다. 이는 반복적인 __init__
, __repr__
, __eq__
등을 작성할 필요성을 제거하는 데 중점을 둔, 더 성숙하고 기능이 풍부한 클래스 정의 방법을 제공합니다. dataclasses
는 attrs
에서 많은 영감을 받았습니다.
구현 및 사용법
dataclasses
와 유사하게 attrs
는 데코레이터와 특수 attr.ib
함수를 사용하여 필드를 정의합니다.
from attrs import define, field # 새 버전에서는 @attr.s 대신 @define 사용 @define class Product: product_id: str name: str price: float = field(validator=lambda instance, attribute, value: value > 0) # 기본 유효성 검사 description: str | None = None tags: list[str] = field(factory=list) # default_factory와 유사한 가변 기본값 # 인스턴스화 product1 = Product(product_id="P001", name="Laptop", price=1200.0) product2 = Product(product_id="P002", name="Mouse", price=25.0, tags=["electronics"]) print(product1) # 출력: Product(product_id='P001', name='Laptop', price=1200.0, description=None, tags=[]) # 기본 유효성 검사 작동 # try: # Product(product_id="P003", name="Invalid", price=-10.0) # except ValueError as e: # print(e) # 출력: 'price' must be > 0 (got -10.0) # kw_only 또는 frozen을 통한 불변성 @define(frozen=True) class Coordinate: x: int y: int
주요 기능 및 사용 사례
- 고도로 구성 가능: 생성된 메서드 및 필드 동작에 대한 더 세부적인 제어를 제공합니다.
- 유효성 검사 후크: 필드에 유효성 검사를 정의하는 내장 지원을 통해 조기 오류 감지가 가능합니다.
- 변환기: 입력 값을 원하는 유형으로 자동 변환할 수 있습니다.
- 불변성: 불변 클래스에 대해
frozen=True
를 지원합니다. - 상호 운용성: 다른 라이브러리와 잘 작동합니다.
- 사용 사례:
dataclasses
가 제공하는 것보다 더 고급 기능(예: 유효성 검사, 변환, 클래스 생성에 대한 더 많은 제어)이 필요한 경우, 특히 대규모 애플리케이션이나 라이브러리에서 유용합니다. 복잡한 데이터 모델에 대한 강력한 선택입니다.
Pydantic
: 데이터 파싱 및 유효성 검사의 강화
Pydantic
은 데이터 파싱과 유효성 검사에 중점을 두어 데이터 정의를 한 단계 더 발전시키는 타사 라이브러리입니다. 파이썬 타입 힌트를 사용하여 데이터 스키마를 정의하고 해당 스키마에 대해 데이터를 자동으로 유효성 검사하여 유효성 검사에 실패하면 명확하고 간결한 오류를 발생시킵니다. 또한 직렬화 및 역직렬화 기능과도 원활하게 통합됩니다.
구현 및 사용법
Pydantic
모델은 BaseModel
을 상속합니다. 필드는 dataclasses
와 유사하게 타입 힌트를 사용하여 정의됩니다.
from pydantic import BaseModel, ValidationError, Field from typing import List, Optional class Address(BaseModel): street: str city: str zip_code: str class Person(BaseModel): name: str = Field(min_length=2, max_length=50) # 추가 유효성 검사를 위한 Pydantic Field age: int = Field(gt=0, lt=150) # 나이는 0보다 크고 150보다 작아야 함 email: Optional[str] = None is_admin: bool = False addresses: List[Address] = [] # 중첩 모델 # 인스턴스화 및 유효성 검사 try: person1 = Person(name="Charlie", age=30, email="charlie@example.com", addresses=[Address(street="123 Main St", city="Anytown", zip_code="12345")]) print(person1) # 출력: name='Charlie' age=30 email='charlie@example.com' is_admin=False addresses=[Address(street='123 Main St', city='Anytown', zip_code='12345')] # 자동 JSON 직렬화 print(person1.json()) # 출력: {"name": "Charlie", "age": 30, "email": "charlie@example.com", "is_admin": false, "addresses": [{"street": "123 Main St", "city": "Anytown", "zip_code": "12345"}]} # 데이터 유효성 검사 실패 # Person(name="A", age=-5) # ValidationError 발생 except ValidationError as e: print(e.json()) # 상세한 오류 메시지 출력 # Pydantic은 불변성 및 기타 설정을 위해 Config를 지원하기도 합니다. class Item(BaseModel): name: str price: float class Config: allow_mutation = False # 인스턴스를 불변으로 만듦 # item = Item(name="Book", price=25.0) # item.price = 30.0 # TypeError 발생
주요 기능 및 사용 사례
- 강력한 데이터 유효성 검사: 타입 힌트를 강력하게 강제하고 풍부한 유효성 검사 기본 요소(정규식, 최소/최대 길이, 범위 등)를 제공합니다.
- 자동 유형 변환: Pydantic은 입력 데이터를 예상 유형으로 변환하려고 시도합니다(예: "123"을
123
으로). - 직렬화/역직렬화: 모델을 JSON, 사전 등으로 쉽게 변환하고 그 반대로 변환할 수 있습니다.
- 중첩 모델: 중첩된 Pydantic 모델을 사용하여 복잡한 데이터 구조를 원활하게 정의합니다.
- 오류 보고: 유효성 검사 실패 시 명확하고 자세한 오류 메시지를 생성합니다.
- 설정 관리: 유형 안전한 액세스로 애플리케이션 설정 및 구성을 정의하는 데 탁월합니다.
- 통합: FastAPI와 같은 웹 프레임워크에서 API 요청/응답 본문 유효성 검사에 널리 사용됩니다.
- 사용 사례: 특히 외부 데이터(API, 구성 파일, 사용자 입력)에 대한 강력한 데이터 유효성 검사, 파싱 및 직렬화가 중요한 경우에 사용됩니다. API 개발(예: FastAPI 사용), 데이터 수집 및 데이터 무결성과 유형 안전성이 가장 중요한 모든 시나리오에서 우선적으로 선택됩니다.
올바른 도구 선택하기
dataclasses
, attrs
, Pydantic
중에서 선택하는 것은 주로 데이터 유효성 검사, 직렬화 및 외부 종속성에 대한 프로젝트의 특정 요구 사항에 따라 달라집니다.
-
dataclasses
를 선택하는 경우:- 최소한의 오버헤드로 간단한 데이터 구조가 필요한 경우.
- 프로젝트에 외부 종속성에 대한 강력한 제약이 있는 경우.
- 기본적인
__init__
,__repr__
,__eq__
가 충분한 경우. - 신뢰할 수 없는 입력을 받지 않는 내부 데이터 모델에서 작업하는 경우.
-
attrs
를 선택하는 경우:dataclasses
보다 더 많은 제어 및 기능(예: 사용자 정의 유효성 검사기, 변환기)이 필요한 경우.dataclasses
가 안정화되기 전에 프로젝트를 시작했거나 이전 파이썬 버전에 대한 하위 호환성이 필요한 경우.- 클래스 정의를 위한 유연성과 광범위한 API를 높이 평가하는 경우.
- 외부 데이터 파싱 또는 직렬화가 각 인스턴스화에 필요하지 않은 복잡한 데이터 모델을 가진 경우.
-
Pydantic
을 선택하는 경우:- 데이터 유효성 검사가 주요 관심사인 경우, 특히 외부 데이터(API, 구성 파일, 사용자 입력)에 대해.
- 자동 유형 변환 및 자세한 오류 메시지가 필요한 경우.
- JSON 또는 사전으로의 직렬화 및 역직렬화가 빈번한 요구 사항인 경우.
- 웹 API(예: FastAPI 사용)를 구축하거나 외부 데이터 피드를 처리하는 경우.
- 최소한의 종속성보다 강력한 스키마 정의 및 데이터 무결성을 우선시하는 경우.
결론
파이썬은 간단한 클래스를 넘어 더 효율적이고 강력한 솔루션을 제공하는 풍부한 데이터 구조 정의 에코시스템을 제공합니다. dataclasses
는 기본 데이터 컨테이너에 편리한 내장 옵션을 제공하고, attrs
는 더 뛰어난 구성 기능을 갖춘 강력하고 성숙한 대안을 제공하며, Pydantic
은 탁월한 데이터 유효성 검사, 파싱 및 직렬화 기능으로 두각을 나타냅니다. 각 라이브러리의 고유한 강점을 이해함으로써 개발자는 더 안정적이고 유지 관리하기 쉬운 파이썬 애플리케이션을 구축하기 위한 가장 적합한 도구를 자신 있게 선택할 수 있습니다. 귀하의 선택은 진정으로 데이터 모델이 요구하는 엄격성, 유효성 검사 및 파싱 수준에 따라 달라집니다.