SQLite와 Python dataclass로 이해하는 Entity, DTO, VO (260531)

WonTerry·2026년 5월 31일

Python

목록 보기
12/20

SQLite와 Python dataclass로 이해하는 Entity, DTO, VO

파이썬을 공부하다 보면 Entity, DTO, VO라는 용어를 자주 만나게 됩니다. 특히 웹 개발, 데이터베이스 프로그래밍, DDD(Domain Driven Design)를 학습하기 시작하면 거의 반드시 등장하는 개념입니다.

하지만 처음 접하면 이런 생각이 들 수 있습니다.

"어차피 데이터를 담는 객체 아닌가?"

"dataclass 하나만 사용하면 되는 것 아닌가?"

"Entity와 DTO는 뭐가 다른가?"

이번 글에서는 SQLite를 사용하는 간단한 쇼핑몰 프로젝트를 예로 들어 Entity, DTO, VO의 차이를 알아보겠습니다.


프로젝트 소개

가상의 쇼핑몰 주문 시스템을 만든다고 가정해 보겠습니다.

데이터베이스에는 다음 두 개의 테이블이 존재합니다.

customers

CREATE TABLE customers (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL
);

orders

CREATE TABLE orders (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    customer_id INTEGER NOT NULL,
    product_name TEXT NOT NULL,
    total_amount INTEGER NOT NULL
);

예시 데이터는 다음과 같습니다.

고객 정보

idnameemail
1홍길동hong@test.com

주문 정보

idcustomer_idproduct_nametotal_amount
11MacBook Pro3500000

dataclass는 무엇인가?

먼저 중요한 사실부터 알아야 합니다.

dataclass는 Entity, DTO, VO가 아닙니다.

dataclass는 객체를 쉽게 만들기 위한 도구입니다.

예를 들어 일반 클래스는 다음과 같이 작성해야 합니다.

class Customer:

    def __init__(self, id, name):
        self.id = id
        self.name = name

하지만 dataclass를 사용하면

from dataclasses import dataclass

@dataclass
class Customer:
    id: int
    name: str

만 작성해도 자동으로 생성자와 출력 메서드가 만들어집니다.

즉,

dataclass
=
객체를 편하게 만드는 도구

입니다.


Entity란 무엇인가?

Entity는 데이터베이스 테이블과 연결되는 객체입니다.

우리 프로젝트의 CustomerEntity를 보겠습니다.

from dataclasses import dataclass

@dataclass
class CustomerEntity:
    id: int | None
    name: str
    email: str

데이터베이스의 한 행(row)은 다음과 같습니다.

customers

id | name   | email
-------------------------
1  | 홍길동 | hong@test.com

이를 파이썬 객체로 표현하면

customer = CustomerEntity(
    id=1,
    name="홍길동",
    email="hong@test.com"
)

이 됩니다.

즉,

데이터베이스 레코드
        ↓
     Entity

관계가 성립합니다.


Entity의 핵심 특징

Entity는 식별자(ID)를 가집니다.

예를 들어

customer1 = CustomerEntity(
    id=1,
    name="홍길동",
    email="hong@test.com"
)

customer2 = CustomerEntity(
    id=1,
    name="홍길동",
    email="hong@test.com"
)

두 객체는 같은 고객을 의미합니다.

왜냐하면 ID가 동일하기 때문입니다.

Entity는 "누구인가?"가 중요합니다.

Entity
=
식별자(ID)가 중요

VO(Value Object)란 무엇인가?

VO는 값 자체를 표현하는 객체입니다.

우리 프로젝트에서는 Email과 Money를 VO로 만들었습니다.

Email VO

from dataclasses import dataclass

@dataclass(frozen=True)
class Email:
    value: str

사용 예

email1 = Email("hong@test.com")
email2 = Email("hong@test.com")

비교

print(email1 == email2)

결과

True

VO는 값이 같으면 같은 객체로 취급합니다.


Money VO

@dataclass(frozen=True)
class Money:
    amount: int

사용 예

price = Money(3500000)

VO는 다음과 같은 특징이 있습니다.

Money(1000)

Money(1000)

은 같은 값입니다.


VO를 사용하는 이유

다음 코드를 보겠습니다.

price = -1000

음수 금액이 들어갈 수 있습니다.

하지만 Money VO를 사용하면

@dataclass(frozen=True)
class Money:

    amount: int

    def __post_init__(self):
        if self.amount < 0:
            raise ValueError(
                "금액은 음수가 될 수 없습니다."
            )

이런 검증을 넣을 수 있습니다.

따라서

VO
=
값 + 검증 규칙

이라고 생각하면 이해하기 쉽습니다.


DTO란 무엇인가?

DTO는 Data Transfer Object의 약자입니다.

말 그대로 데이터를 전달하는 객체입니다.

주문 생성 요청을 예로 들어보겠습니다.

사용자가 주문을 생성합니다.

{
    "customer_id": 1,
    "product_name": "MacBook Pro",
    "total_amount": 3500000
}

이를 받아주는 객체가 DTO입니다.

@dataclass
class CreateOrderDTO:
    customer_id: int
    product_name: str
    total_amount: int

DTO가 필요한 이유

사용자 입력은 항상 불완전합니다.

예를 들어 웹 API에서는

{
    "customer_id": 1,
    "product_name": "MacBook Pro"
}

처럼 금액이 빠질 수도 있습니다.

DTO를 사용하면

외부 입력
      ↓
DTO
      ↓
서비스 로직

구조를 만들 수 있습니다.


Entity와 DTO의 차이

많은 초급자가 가장 헷갈리는 부분입니다.

CustomerEntity

@dataclass
class CustomerEntity:
    id: int
    name: str
    email: str

CustomerResponseDTO

@dataclass
class CustomerResponseDTO:
    name: str
    email: str

겉보기에는 비슷합니다.

하지만 역할이 다릅니다.

Entity는 DB용입니다.

DTO는 데이터 전달용입니다.

예를 들어 고객 비밀번호가 저장되어 있다면

@dataclass
class CustomerEntity:
    id: int
    name: str
    email: str
    password_hash: str

API 응답에서는 비밀번호를 보내면 안 됩니다.

그래서 DTO를 따로 만듭니다.

@dataclass
class CustomerResponseDTO:
    name: str
    email: str

전체 흐름

우리 프로젝트의 흐름은 다음과 같습니다.

사용자 입력
      ↓
CreateOrderDTO
      ↓
OrderService
      ↓
OrderEntity
      ↓
OrderRepository
      ↓
SQLite


SQLite
      ↓
OrderRepository
      ↓
OrderEntity
      ↓
OrderResponseDTO
      ↓
사용자 화면

한눈에 정리

Entity

데이터베이스 레코드 표현

예:
CustomerEntity
OrderEntity

특징

  • ID가 중요
  • DB 테이블과 연결
  • 영속성(Persistence) 담당

VO

값 자체를 표현

예:
Email
Money
Address

특징

  • 값이 중요
  • 보통 immutable
  • 검증 로직 포함

DTO

데이터 전달용

예:
CreateOrderDTO
OrderResponseDTO

특징

  • API 요청/응답
  • 계층 간 데이터 전달
  • DB와 직접 연결되지 않음

마무리

초급자 입장에서는 다음 한 문장만 기억해도 충분합니다.

Entity는 데이터베이스를 표현한다.

VO는 의미 있는 값을 표현한다.

DTO는 데이터를 전달한다.

dataclass는 이들을 쉽게 만드는 도구이다.

처음에는 모두 비슷해 보이지만 프로젝트 규모가 커질수록 역할을 분리했을 때 코드의 가독성, 유지보수성, 확장성이 크게 향상됩니다.

실무에서 FastAPI, Django, Flask, SQLAlchemy 등을 사용할 때도 결국은 같은 개념을 다양한 형태로 적용하게 됩니다.

profile
Hello, I'm Terry! 👋 Enjoy every moment of your life! 🌱 My current interests are Signal processing, Machine learning, Python, Database, LLM & RAG, MCP & ADK, Multi-Agents, Physical AI, ROS2...

0개의 댓글