EAV로 이해하는 동적 데이터 모델링

Fe·2026년 8월 21일

Data

목록 보기
1/1
post-thumbnail

최근 새로 알게 된 데이터 모델에 대한 이야기를 해보려 한다.
바로 EAV(Entity-Attribute-Value) 모델이다. 이름 그대로 Entity, Attribute, Value를 가지고 데이터를 표현한다.

이런 식의 구조는 이전에 어디선가 본 적이 있지만 그 이름은 처음 알았다.

왜 굳이 이렇게 저장하는지가 잘 와닿지 않아서, 요구사항을 하나씩 추가해보면서 EAV 모델이 필요한 상황을 만들어보기로 했다.


TV를 판매하는 쇼핑몰

TV를 판매하는 작은 쇼핑몰이 있다고 해보자.
TV의 상품 정보에는 다음과 같은 값들이 필요하다.

  • 상품명
  • 가격
  • 제조사
  • 화면 크기
  • 해상도
  • 패널 종류

테이블을 만들면 다음과 같다.

TV
tv_id
name
price
manufacturer
screen_size
resolution
panel_type

일반적인 관계형 데이터베이스에서 볼 수 있는 형태다.
데이터 타입도 screen_size INTEGER, resolution VARCHAR(20)과 같이 명확하게 정의할 수 있다.

테이블만 봐도 데이터의 의미를 알기 쉽다.

냉장고도 판매해보자

쇼핑몰이 성장하면서 냉장고도 판매하게 되었다. 냉장고는 TV와는 다른 속성들이 필요하다.

  • 용량 (ex: 800L)
  • 도어 수
  • 에너지 등급

기본적인 id 값, 이름, 가격 등은 TV와 동일하게 필요하다.

만약 TV와 냉장고를 "상품"이라는 하나의 테이블에서 관리한다면 다음과 같이 만들 수도 있다.

Product
product_id
type
name
price
manufacturer
screen_size
resolution
panel_type
capacity
door_count
energy_grade

실제 데이터가 들어가면 다음과 같은 모습일 것이다.

product_idtypescreen_sizeresolutionpanel_typecapacitydoor_countenergy_grade
1TV604KOLEDNULLNULLNULL
2냉장고NULLNULLNULL80042

TV에는 냉장고의 속성들이 필요하지 않고, 냉장고도 TV에만 적용되는 속성들이 필요하지 않다. 상품 종류가 많아지고, 하나의 테이블로 계속 관리한다면 컬럼은 계속 늘어나지만 각 상품이 사용하는 컬럼은 그중 일부일 것이다.

결국 테이블은 점점 wide하고 sparse해진다. 즉, 컬럼이 많아지고 대부분이 NULL인 테이블이 될 것이다.

그냥 테이블을 나누면 되지 않을까?

이런 경우에는 사실 하나의 테이블로 관리하는 것이 더 어색하다. 상품 종류별로 테이블을 나누면 된다.

공통적으로 사용하는 값은 Product에 두고, TV와 냉장고에서만 사용하는 속성은 각각의 테이블로 분리해보자.

Product
product_id (PK)
nam
price
manufacturer
TV
product_id (PK, FK)
screen_size
resolution
panel_type
Refrigerator
product_id (PK, FK)
capacity
door_count
energy_grade

Product가 슈퍼타입, TV와 냉장고가 서브타입이 된다. 상품의 공통 속성은 상위 테이블에, 특정 상품에만 필요한 속성은 하위 테이블에 있다.
이렇게 하면 테이블 하나로 관리할 때 발생했던 불필요한 NULL 컬럼 문제도 해결된다.

상품 종류가 많아진다면?

이번에는 판매하는 상품을 크게 늘려보자. 전자제품을 다양하게 취급할 수 있게 되어 노트북, 카메라도 팔게 되었고, 옷도 판매하게 되었다. 또, 소형 전자제품을 담을 가방도 판매한다고 한다.

Product 
│ 
├── Electronic 
│ ├── TV 
│ ├── Refrigerator
│ ├── Laptop
│ └── Camera 
│
├── Clothing 
│ ├── Top 
│ └── Pants 
│
├── Bag
│ ├── Backpack
│ ├── Tote
│ ├── Crossbody
│ └── Handbag
│
└── ...

각 상품마다 필요한 속성도 크게 달라질 것이다. 노트북에는 CPU, RAM 등의 속성이 필요하다. 옷은 사이즈, 원료와 세탁 가능 여부 등이 들어갈 수 있고, 가방에는 가로/세로/너비와 재질 등이 필요할 것이다.

전체 시스템에서 관리해야 하는 속성의 종류는 굉장히 많아지지만 상품 하나가 사용하는 것은 그중 극히 일부가 된다.

EAV 모델에서 이야기하는 sparse attributes와 비슷한 상황이 될 수도 있지만, 그럴 필요는 없다고 생각한다. 상품 종류별로 테이블을 계속 나누면 된다.

테이블은 물론 많아지겠지만, 상품의 구조가 안정적이라면 불가능한 방법은 아니다.

오픈마켓으로 확장해보자

상품이 충분히 많아졌다고 가정하고, 요구사항을 바꿔보자.
지금까지는 쇼핑몰에서 판매하는 상품의 구조를 개발자가 알고 있었지만, 서비스가 오픈마켓으로 확장되면서 여러 판매자가 직접 상품을 등록할 수도 있게 되었다.

플랫폼에서 TV에 기본적으로 다음과 같은 속성들을 제공하고 있다.

  • 화면 크기
  • 해상도
  • 패널 종류

어느 날 운영을 하다가 새로운 속성을 추가해달라는 요청이 들어왔다.

  • 주사율
  • HDR 지원 여부

기존 구조대로라면 TV 테이블을 변경해야 한다.

ALTER TABLE TV
ADD COLUMN refresh_rate INTEGER;

ALTER TABLE TV
ADD COLUMN hdr_supported BOOLEAN;

그런데 얼마 뒤에 또 새로운 속성을 추가해달라고 한다.

  • 리모콘 여부
  • AI 기능

개발자는 그때마다 스키마를 변경해야 한다.
지금까지의 상황과는 조금 다르다. 상품의 속성이 개발 시점에 고정되어 있지 않고, 운영 과정에서 계속 추가되거나 변경되고 있다.

판매자가 직접 속성을 만든다면?

이제는 플랫폼이 확장되어 전세계에서 사용하게 되었다. 다른 국가에서도 TV, 냉장고 등을 판매자가 등록할 수 있다.

플랫폼에서 상품의 기본 속성은 제공하지만, 국가마다 정책과 환경이 다르기 때문에 판매자가 자신의 상품을 설명하기 위한 속성을 직접 추가할 수도 있게 되었다.

판매자 A가 판매하는 TV는 다음과 같은 속성이 필요하다.

  • 게임 모드 지원 여부

다른 국가의 판매자 B는 다른 속성이 필요하다.

  • 호텔 모드 지원 여부
  • 상업용 여부

이제는 개발자도 어떤 속성이 생길지 사전에 알 수 없다.
이런 경우 같은 TV라도 판매자마다 사용 가능한 속성 목록이 달라질 것이다. 어떻게 모델링할지 간단하게 생각해보자.

속성 목록을 관리하는 테이블을 두고, 판매자마다 어떤 속성을 사용할 수 있는지도 별도의 관계로 관리할 수 있을 것 같다.

여기까지만으로는 EAV라고 보기는 어렵다. 속성의 정의와 사용 가능 여부를 row로 관리하고 있을 뿐, 아직 특정 상품에 대한 속성과 실제 값을 하나의 row로 저장하고 있지는 않기 때문이다.

판매자가 정의한 속성을 실제 상품에 적용하기 시작하면 이야기가 달라진다.

상품 001 - 게임 모드 지원 여부 - Y

와 같은 정보를 저장해야 하고, 이때 EAV 구조가 자연스럽게 등장한다.

지금까지 시스템의 특징을 다시 정리해보자면,

1. 전체 Attribute의 종류가 매우 많다.
2. 하나의 상품이 사용하는 Attribute는 전체 중 일부다.
3. Attribute가 운영 과정에서 계속 추가되고 변경된다.
4. 사용자가 새로운 Attribute를 직접 정의할 수도 있다.

고정된 스키마만으로 모든 속성을 표현하는 것이 점점 부담스러워진다.

Attribute를 데이터로 만들기

EAV 모델은 이름 그대로 Entity, Attribute, Value 세 가지 요소로 데이터를 표현한다.

  • Entity: 설명하려는 대상
  • Attribute: 대상의 속성
  • Value: 해당 속성의 값

위의 TV 테이블에서 사용되는 각 속성들을 하나의 데이터로 바꾸어 생각하면 다음과 같다.

EntityAttributeValue
TV 001화면 크기60
TV 001해상도4K
TV 001패널 종류OLED
TV 001HDR 지원 여부Y
.........

이처럼 EAV에서는 하나의 row가 하나의 Entity를 설명하는 속성-값 쌍이 된다.

실제 테이블은 다음과 같이 구성해볼 수 있겠다.

Product
product_id
name
price
Attribute
attribute_id
name
Product_attribute_value
product_id
attribute_id
value

Attribute 테이블에는 다음과 같은 값이 있다.

attribute_idname
1화면 크기
2해상도
3패널 종류
4HDR 지원 여부

실제 상품의 값 테이블은 다음과 같이 정의된다.

product_idattribute_idvalue
001160
00124K
0013OLED
0014Y

기존에는 screen_size, resolution 같은 속성 정의가 스키마의 컬럼으로 존재했다.

EAV에서는 속성 정의가 Row로 존재한다.
결국, 속성 정의의 일부를 Column에서 Row로 옮긴 것으로 생각할 수 있다.

EAV - 새로운 속성 추가하기

판매자로부터 주사율 속성을 추가해달라는 요청이 들어왔을 때를 다시 생각해보자.

기존 방식이었다면 TV 테이블에 컬럼을 추가해야 한다.

ALTER TABLE TV
ADD COLUMN refresh_rate INTEGER;

EAV에서는 먼저 Attribute 테이블에 새로운 속성을 등록한다.

attribute_idname
5주사율

그리고 해당 속성을 사용하는 상품에만 값 row를 추가한다.

product_idattribute_idvalue
001560

즉, EAV가 관리하는 동적인 속성에 한해서는 새로운 속성의 추가 = 스키마 변경에서 데이터 변경으로 바뀐다.

EAV - 판매자별 속성 커스텀하기

판매자가 직접 속성을 추가하는 경우를 다시 생각해보자.

EAV 모델에서는 판매자 A가 게임 모드 지원 여부 속성을 직접 만든다면 다음 데이터가 추가될 것이다.

<Attribute>

attribute_idseller_idname
61게임 모드 지원 여부

그리고 상품 001에서 그 값을 사용한다면

<Product_attribute_value>

product_idattribute_idvalue
0016Y

와 같이 추가될 것이다.

EAV는 문제가 없을까?

특정 판매자만 사용하는 속성이 추가되어도, EAV 모델에서는 스키마를 변경하지 않고 유연하게 처리할 수 있음을 확인했다. 이렇듯 스키마를 유연하게 다룰 수 있다는 점은 EAV의 장점이다.

지금까지 다룬 요구사항 외에도 실제 상황에서의 복잡한 규칙들을 녹인다면 더욱 장점이 부각될 것이다.

하지만 그만큼 단점도 뚜렷하게 보였다.

기존에는 DB 스키마만 봐도 어느 정도 알 수 있는 정보가 많았다.
어떤 값이 어떤 타입으로 정의되었는지 명확하다.

screen_size INTEGER
hdr_supported BOOLEAN

그런데 EAV에서는 예를 들어,

attribute_idvalue
16Y
3350

이렇게 되어 있다면 16번 속성이 뭔지 알 수 없고, Y가 어떤 의미인지도 모른다. Y가 boolean인지, "Y"라는 코드인지 스키마만 봐서는 알기 어렵다.
33번 속성도 마찬가지다. 50이라는 값이 뭘 의미하는지 모르겠다.

EAV를 도입하면서 스키마가 유연해진 대신, 기존 스키마가 가지고 있던 정보와 값 검증 책임의 일부를 스키마만으로 표현하기 어려워졌다

따라서 책임의 일부를 속성 메타데이터와 애플리케이션 로직에서 관리하게 된다.

속성에 대한 메타데이터 테이블을 보면,

attribute_idnamerequired...
5주사율N...
6HDR 지원 여부N...

이제는 속성 id만으로는 알 수 없었던 정보들을 확인할 수 있다.
서비스 로직에서는 attribute_id = 5의 메타데이터를 조회하고, 해당 정보를 기반으로 실제 값을 해석하거나 검증할 수 있다.

Hybrid EAV

EAV는 스키마의 유연성을 확보할 수 있으니, 모든 속성을 EAV로 옮기는 것이 좋을까?

생각해보면, 상품에 딸린 속성 중에서도 거의 모든 상품이 가지고 있고 운영 중에 잘 바뀌지 않는 값들도 있다.

id, 이름, 가격, 판매자 id 같은 정보들 말이다. 이런 값들까지 굳이 EAV로 관리할 필요는 없다.

반면 화면 크기, RAM 같이 상품마다 크게 다를 수 있는 상세 속성들도 있다.

따라서 고정적이고 대부분의 Entity가 사용하는 속성은 일반 컬럼으로 관리하고, 동적이고 sparse한 속성만 EAV로 관리하는 방식을 사용할 수 있다. 이런 방식을 Hybrid EAV 형태로 볼 수 있다.

EAV 모델은 기존의 일반적인 관계형 모델을 완전히 대체하기보다는, 데이터의 성격에 따라 선택적으로 적용하는 것이 자연스러운 것 같다.

  • 속성의 종류가 많고 테이블이 sparse한가?
  • 운영 중에 자주 바뀌는가?
  • 개발 시점에 예측 가능한가?

와 같은 항목들을 먼저 따져보면 좋을 것 같다.


마무리

EAV 모델에서 스키마의 유연성을 얻은 대신 포기한 것들을 얼마나 잘 관리할 수 있을까? 를 고민해 보니 흥미로웠다.

메타데이터와 애플리케이션 로직 안에서 데이터를 안전하게 사용하고, 의미를 명확하게 전달하기 위해 신경써야 한다.

요즘 RDBMS에서는 JSON, JSONB 형식으로 동적인 데이터를 저장할 수 있다. 동적인 데이터를 다룰 때 EAV가 최선이 아닐 수도 있다고 한다. 이 부분도 더 알아보면 좋을 것 같다.

profile
하고 싶은 게 많은 사람

0개의 댓글