본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] @dataclass와 NamedTuple, 일반 클래스의 용도 차이 해결 방법과 7가지 실무 사례

by Papa Martino V 2026. 3. 29.
728x90

@dataclass와 NamedTuple
@dataclass와 NamedTuple

 

파이썬 데이터 아키텍처의 핵심 도구를 심층 분석하고, 실무에서 마주치는 "언제 무엇을 써야 할까?"라는 질문에 대한 명확한 해답을 제안합니다.


1. 개요: 파이썬 데이터 모델링의 세 가지 갈림길

현대 파이썬 프로그래밍에서 데이터를 체계적으로 관리하는 것은 애플리케이션의 성능과 유지보수성에 직결되는 핵심 요소입니다. 과거에는 단순히 데이터를 저장하기 위해 복잡한 일반 클래스(Plain Old Class)를 정의하여 __init__, __repr__, __eq__ 같은 'Boilerplate' 코드를 양산해야 했습니다. 파이썬 3.7부터 도입된 @dataclass와 그 이전부터 가독성 도구로 사랑받았던 NamedTuple은 이러한 불편을 해소하고 '순수 데이터 저장'이라는 본연의 기능에 집중할 수 있도록 도와줍니다. 하지만 이 세 가지 도구는 근본적인 동작 원리와 성능, 그리고 확장성 면에서 명확한 용도 차이를 보입니다. 본 가이드에서는 세 도구의 특징을 해결하기 위한 7가지 구체적인 실무 예제와 상세한 비교를 통해, 더 독창적이고 효율적인 파이썬 코드를 작성하는 방법을 제안합니다.

2. 핵심 비교: 데이터, 성능, 그리고 가변성

세 가지 데이터 구조의 핵심 차이를 명확한 표로 정리하였습니다. 이 비교를 통해 자신의 프로젝트 상황에 최적화된 도구를 선택할 수 있습니다.

비교 항목 NamedTuple (collections) @dataclass (dataclasses) 일반 클래스 (Boilerplate)
근본 특징 튜플(tuple)의 확장 (튜플처럼 동작) 데이터 중심의 클래스 데코레이터 순수 객체지향 아키텍처 (행위 중심)
가변성 (Mutability) 불변 (Immutable) 가변 (Default), 불변 설정 가능 가변 (Mutable)
Boilerplate 코드 매우 낮음 (한 줄 정의) 낮음 (자동 생성) 높음 (직접 작성 필요)
타이핑 (Type Hints) 강력 지원 (typing.NamedTuple 사용 시) 강력 지원 (필수 요건) 지원 (Boilerplate 작성 시)
메모리 및 속도 최고 (경량 튜플 기반) 우수 (__slots__ 활용 시 최고) 우수 (하지만 오버헤드 존재)
기본 기능 (Repr, Eq) 자동 생성 자동 생성 (옵션 설정 가능) 직접 구현 필요
상속 가능 여부 지원 (하지만 복잡함) 매우 강력 지원 매우 강력 지원

3. 실무를 위한 7가지 Sample Examples

실제 개발 현장에서 세 도구를 어떻게 활용하고 용도 차이를 해결하는지 보여주는 7가지 사례입니다.

Example 1: 간단한 좌표 표현 (경량 불변 데이터) - NamedTuple

설정 데이터나 좌표처럼 생성 후 변경되지 않는 경량 데이터에 최적입니다.


from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)

# 가독성: 인덱스와 이름 모두 접근 가능
assert p.x == p[0]
assert p.y == p[1]
# 불변성: 변경 시도 시 에러 발생
# p.x = 30 # AttributeError
        

Example 2: JSON 응답 파싱 (타이핑이 있는 불변 데이터) - NamedTuple

API 응답 데이터를 안정적으로 다루기 위해 `typing.NamedTuple`을 사용합니다.


from typing import NamedTuple

class UserResponse(NamedTuple):
    id: int
    name: str
    email: str

def parse_api_response(data):
    return UserResponse(**data)
        

Example 3: 사용자 프로필 관리 (가변 데이터) - @dataclass

Boilerplate 코드 없이 데이터를 쉽게 표현하고 수정할 수 있습니다.


from dataclasses import dataclass

@dataclass
class UserProfile:
    id: int
    name: str
    status: str = "active" # 기본값 지원

u = UserProfile(1, "Kim")
u.status = "inactive" # 가변성 활용
        

Example 4: 복잡한 기본값과 Teardown 로직 - @dataclass

리스트나 딕셔너리 같은 가변 기본값을 안전하게 처리하거나, 생성 후 로직을 실행합니다.


from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class LogEntry:
    message: str
    tags: list[str] = field(default_factory=list) # 리스트 기본값
    timestamp: datetime = field(init=False) # 직접 입력받지 않음

    def __post_init__(self):
        # 객체 생성 후 실행되는 Teardown 로직
        self.timestamp = datetime.now()
        

Example 5: 성능 최적화가 필요한 데이터 - @dataclass (with slots)

데이터가 수만 개 생성되어 메모리 사용량이 중요할 때 사용합니다.


@dataclass(slots=True) # 파이썬 3.10+
class HighPerformanceData:
    x: int
    y: int
        

Example 6: 상속을 통한 데이터 계층 구조 - 일반 클래스 (or @dataclass)

순수 데이터 저장보다는 행위(Behavior)가 중요하거나 복잡한 계층 구조가 필요할 때 일반 클래스를 사용합니다.


class Connection:
    def connect(self): raise NotImplementedError

class DBConnection(Connection):
    def connect(self): return "DB Connected"
        

Example 7: 딕셔너리처럼 동작하는 가변 데이터 - @dataclass (with astuple)

가변적이면서도 필요시 튜플이나 딕셔너리로 쉽게 변환해야 할 때 사용합니다.


from dataclasses import dataclass, astuple, asdict

@dataclass
class Settings:
    theme: str
    volume: int

s = Settings("dark", 80)
s.volume = 90
assert astuple(s) == ("dark", 90)
assert asdict(s) == {"theme": "dark", "volume": 90}
        

4. 결론 및 용도 해결 전략

어떤 도구를 선택할지는 성능, 가변성, Boilerplate 코드량에 따라 달라집니다.

  • 순수 경량 데이터: 불변성이 보장되고 메모리/속도가 가장 중요하며, 튜플처럼 인덱스로도 접근해야 한다면 NamedTuple이 최적의 해결 방법입니다.
  • 구조화된 가변 데이터: 데이터 변경이 필요하고, 강력한 타입 힌트와 기본값 지원이 중요하며, 클래스 상속 계층이 필요하다면 @dataclass를 선택하십시오.
  • 행위 중심의 객체: 데이터를 저장하는 것보다 메서드를 통한 복잡한 행위나 로직이 더 중요하고, 전통적인 OOP 디자인 패턴을 구현해야 한다면 일반 클래스를 사용해야 합니다.

실무에서는 데이터 모델링의 시작은 @dataclass로 하되, 불변성이 확실하거나 성능 최적화가 필요할 때 `NamedTuple`로 전환하는 전략이 독창적이고 가치 있는 접근입니다.

참고 및 출처:

  • Python 공식 문서 (dataclasses, collections.namedtuple, types.NamedTuple)
  • PEP 557: Data Classes
  • Fluent Python (Brett Slatkin) - Chapter 5: Data Structures
  • Real Python: "@dataclass vs NamedTuple vs Class" (CommunityReview)
728x90