관계형 데이터베이스를 설계하다 보면 Player, Guild, Item처럼 여러 테이블이 만들어집니다.
문제는 테이블이 많아질수록 테이블끼리 어떤 관계를 가지고 있는지 파악하기 어려워진다는 것입니다.
예를 들어 RPG 게임의 데이터베이스에 다음 테이블이 있다고 해봅시다.
Player
Guild
Item
Inventory
Quest
Monster
테이블 이름만 봐도 대략적인 의미는 알 수 있지만,
Player와 Guild는 어떤 관계인가? Player와 Item은 어떻게 연결되는가? 한 Guild에는 Player가 몇 명까지 들어갈 수 있는가? Player 한 명은 Item을 몇 개 가질 수 있는가?
같은 구조를 한눈에 파악하기는 어렵습니다.
이러한 데이터베이스의 구조를 그림으로 표현한 것이 **ERD(Entity Relationship Diagram)**입니다.
ERD란?
**ERD(Entity Relationship Diagram)**는 데이터베이스에 존재하는 Entity와 Entity 사이의 관계를 그림으로 표현한 다이어그램입니다.
쉽게 말하면
데이터베이스의 설계도
라고 볼 수 있습니다.
건물을 만들기 전에 설계도를 그리는 것처럼 데이터베이스도 실제로 테이블을 만들기 전에 ERD를 통해 구조를 설계할 수 있습니다.
게임 데이터베이스를 아주 간단하게 표현하면 다음과 같습니다.
[Guild]
guild_id PK
guild_name
│
│ 1
│
N
[Player]
player_id PK
nickname
level
guild_id FK
이 그림만 보더라도
Guild 1 : N Player
관계라는 것을 알 수 있습니다.
즉 하나의 길드에는 여러 명의 플레이어가 가입할 수 있다는 뜻입니다.
ERD를 구성하는 기본 요소
ERD를 이해하려면 크게 세 가지를 보면 됩니다.
Entity
Attribute
Relationship
각각 살펴보겠습니다.
Entity — 무엇을 저장하는가?
**Entity(엔티티)**는 데이터베이스에서 관리하고 싶은 대상을 의미합니다.
게임이라면 다음과 같은 것들이 Entity가 될 수 있습니다.
Entity | 의미 |
|---|---|
Player | 플레이어 |
Guild | 길드 |
Item | 아이템 |
Monster | 몬스터 |
Quest | 퀘스트 |
Skill | 스킬 |
실제 관계형 데이터베이스에서는 Entity가 보통 **테이블(Table)**이 됩니다.
예를 들어 Player라는 Entity를 다음과 같이 표현할 수 있습니다.
Attribute — Entity의 정보
Entity가 가지고 있는 각각의 정보를 **Attribute(속성)**라고 합니다.
Player를 예로 들면 다음과 같습니다.
Attribute | 의미 |
|---|---|
player_id | 플레이어 고유 번호 |
nickname | 닉네임 |
level | 레벨 |
gold | 보유 골드 |
guild_id | 가입한 길드 |
즉
Player
↓
player_id
nickname
level
gold
guild_id
에서 Player는 Entity이고 아래에 있는 값들은 Attribute입니다.
관계형 데이터베이스에서는 보통 Attribute가 **컬럼(Column)**으로 만들어집니다.
PK와 FK도 ERD에 표시한다
ERD에서는 단순히 컬럼 이름만 표시하는 것이 아니라 PK와 FK도 함께 표현하는 경우가 많습니다.
예를 들어 Player 테이블이 다음과 같다고 해봅시다.
player_id | nickname | level | guild_id |
|---|---|---|---|
1 | 천기사 | 35 | 10 |
2 | 흑마법사 | 42 | 20 |
3 | 궁수왕 | 28 | 10 |
ERD에서는 다음처럼 표현할 수 있습니다.
여기서
PK = Primary Key
FK = Foreign Key
입니다.
player_id는 Player 테이블의 데이터를 구분하고,
guild_id는 Guild 테이블을 참조합니다.
Relationship — Entity 사이의 관계
ERD의 핵심은 **Relationship(관계)**입니다.
게임 데이터를 보면 Entity들이 독립적으로 존재하지 않습니다.
예를 들어
플레이어는 길드에 가입한다.
플레이어는 아이템을 소유한다.
플레이어는 퀘스트를 수행한다.
몬스터는 아이템을 드롭한다.
처럼 서로 관계를 가지고 있습니다.
이를 ERD에서는 Entity 사이에 선을 연결해서 표현합니다.
예를 들어
라고만 표시하면 두 Entity가 연결되어 있다는 것은 알 수 있습니다.
하지만 이것만으로는 관계의 수를 정확히 알 수 없습니다.
그래서 **Cardinality(관계의 수)**를 함께 표시합니다.
Cardinality란?
Cardinality는
한 Entity의 데이터 하나가 상대 Entity의 데이터 몇 개와 연결될 수 있는가
를 의미합니다.
대표적으로 다음 세 가지 관계가 있습니다.
관계 | 의미 |
|---|---|
1 : 1 | 하나와 하나 |
1 : N | 하나와 여러 개 |
N : M | 여러 개와 여러 개 |
1:1 관계
플레이어와 플레이어 상세 설정을 생각해봅시다.
플레이어마다 하나의 환경설정 데이터가 존재한다고 가정하겠습니다.
Player
player_id | nickname |
|---|---|
1 | 천기사 |
2 | 흑마법사 |
PlayerSetting
setting_id | player_id | sound_volume |
|---|---|---|
101 | 1 | 80 |
102 | 2 | 50 |
한 플레이어는 하나의 설정을 가지고,
하나의 설정 역시 한 플레이어에게만 속합니다.
따라서 1:1 관계입니다.
1:N 관계
게임에서 가장 흔하게 볼 수 있는 관계입니다.
하나의 Guild에는 여러 Player가 가입할 수 있습니다.
Guild
guild_id | guild_name |
|---|---|
10 | 용기사단 |
20 | 마법협회 |
Player
player_id | nickname | guild_id |
|---|---|---|
1 | 천기사 | 10 |
2 | 궁수왕 | 10 |
3 | 성기사 | 10 |
4 | 흑마법사 | 20 |
데이터를 관계로 보면
입니다.
즉
관계입니다.
N:M 관계
이번에는 Player와 Item을 생각해봅시다.
한 플레이어는 여러 아이템을 가지고 있습니다.
그런데 포션은 천기사만 가지고 있는 것이 아닙니다.
따라서
관계입니다.
이것을 다대다 관계(N**)**라고 합니다.
N:M 관계는 중간 테이블을 사용한다
관계형 데이터베이스에서는 N 관계를 일반적으로 중간 테이블을 이용하여 해결합니다.
Player와 Item 사이에 Inventory를 만들어보겠습니다.
Player
player_id | nickname |
|---|---|
1 | 천기사 |
2 | 흑마법사 |
Item
item_id | item_name |
|---|---|
101 | 철검 |
102 | 포션 |
103 | 지팡이 |
Inventory
player_id | item_id | quantity |
|---|---|---|
1 | 101 | 1 |
1 | 102 | 5 |
2 | 102 | 10 |
2 | 103 | 1 |
원래
Player N : M Item
이었던 관계가
Player 1 : N Inventory
Item 1 : N Inventory
두 개의 1 관계로 바뀝니다.
ERD로 표현하면 다음과 같습니다.
[Player]
player_id PK
nickname
│
│ 1
│
N
[Inventory]
player_id PK, FK
item_id PK, FK
quantity
N
│
│ 1
[Item]
item_id PK
item_name
ERD 표기법은 하나만 있는 것이 아니다
ERD라고 해서 모든 그림이 똑같이 생기는 것은 아닙니다.
Entity와 Relationship을 표현하는 여러 표기 방식이 존재합니다.
대표적으로 다음과 같은 방식이 있습니다.
표기법 | 특징 |
|---|---|
Chen 표기법 | Entity와 Relationship을 도형으로 명확히 표현 |
IE 표기법 | 실무에서 많이 사용하는 테이블 중심 표현 |
Crow's Foot | 관계의 수를 새 발 모양으로 표시 |
UML | 객체지향 모델링에서도 사용하는 표기법 |
데이터베이스 설계 도구에서 자주 볼 수 있는 방식이 IE 표기법입니다.
IE 표기법이란?
IE(Information Engineering) 표기법은 ERD에서 Entity 사이의 관계와 Cardinality를 선 끝의 기호로 표현하는 방식입니다.
특히 관계의 끝부분에
|
O
<
같은 기호를 사용합니다.
그중 여러 개를 의미하는 기호가 새의 발처럼 생겨서 흔히 Crow's Foot(까마귀발) 표기법이라고도 부릅니다.
예를 들어 하나의 길드에 여러 플레이어가 존재한다면 대략 다음과 같은 형태가 됩니다.
오른쪽의 < 모양이 실제 ERD에서는 세 갈래로 펼쳐진 까마귀발 모양으로 표시됩니다.
IE 표기법의 기본 기호
IE 표기법에서는 관계의 최소 개수와 최대 개수를 함께 표현합니다.
기본적으로 다음 기호를 기억하면 됩니다.
기호 | 의미 | |
|---|---|---|
| 0개, 없어도 됨 | |
| 1개 | |
까마귀발 | 여러 개 |
이를 조합하면 관계를 구체적으로 나타낼 수 있습니다.
0 또는 1 — Optional One
다음 기호를 생각해봅시다.
O|
의미는
최소 = 0
최대 = 1
입니다.
즉
없을 수도 있고 하나 있을 수도 있다.
게임에서 플레이어가 탈것을 하나까지만 장착할 수 있지만, 탈것을 장착하지 않아도 된다고 생각해봅시다.
플레이어 입장에서는
탈것 없음 → 가능
탈것 1개 → 가능
탈것 2개 → 불가능
입니다.
정확히 1 — Mandatory One
||
처럼 표시하면
최소 = 1
최대 = 1
즉 반드시 하나라는 의미입니다.
예를 들어 캐릭터 하나는 반드시 하나의 계정에 소속되어야 한다고 해봅시다.
Player가 존재한다면 Account 역시 반드시 존재해야 합니다.
0개 이상 — Optional Many
이번에는
O<
형태입니다.
실제 IE 표기에서는 < 대신 까마귀발 모양이 사용됩니다.
의미는
최소 = 0
최대 = 여러 개
입니다.
즉
하나도 없을 수도 있고 여러 개가 있을 수도 있다.
게임에서 플레이어가 퀘스트를 아직 하나도 받지 않았을 수도 있고 여러 개를 가지고 있을 수도 있습니다.
예를 들어
player | 보유 퀘스트 |
|---|---|
천기사 | 없음 |
흑마법사 | 마왕성 조사 |
궁수왕 | 약초 수집, 늑대 사냥, 마을 방어 |
모두 허용됩니다.
1개 이상 — Mandatory Many
|<
는
최소 = 1
최대 = 여러 개
를 의미합니다.
즉 반드시 하나 이상 존재해야 합니다.
게임에서 레이드 파티를 생성하려면 최소 한 명 이상의 플레이어가 있어야 한다고 가정할 수 있습니다.
이 경우
0명 → 불가능
1명 → 가능
5명 → 가능
20명 → 가능
입니다.
IE 표기법 네 가지 조합 정리
이 부분은 IE 표기법에서 가장 중요합니다.
표현 | 최소 | 최대 | 의미 | ||
|---|---|---|---|---|---|
| 0 | 1 | 없거나 하나 | ||
| 1 | 1 | 정확히 하나 | ||
| 0 | N | 없거나 여러 개 | ||
| 1 | N | 하나 이상 |
쉽게 기억하면
O = 0
| = 1
< = 여러 개
입니다.
그리고 앞쪽 기호는 최소값, 바깥쪽 기호는 최대값이라고 보면 이해하기 쉽습니다.
실제 게임 ERD로 IE 표기법 읽기
다음 게임 데이터베이스가 있다고 해봅시다.
관계를 말로 표현하면 다음과 같습니다.
Guild 1 : N Player
Player 1 : N Inventory
Item 1 : N Inventory
조금 더 정확한 규칙을 추가해봅시다.
길드는 플레이어가 한 명도 없을 수 있다.
플레이어는 길드에 가입하지 않을 수도 있다.
플레이어는 아이템을 하나도 가지고 있지 않을 수 있다.
Inventory 한 행은 반드시 Player 하나를 가리킨다.
Inventory 한 행은 반드시 Item 하나를 가리킨다.
그러면 IE 표기법에서는 단순히 1:N보다 훨씬 많은 정보를 표현할 수 있습니다.
1만으로는 부족한 이유
다음 두 게임 시스템을 비교해봅시다.
게임 A
길드를 만들자마자 플레이어가 0명일 수 있습니다.
용기사단 → 가입자 없음
게임 B
길드를 생성하려면 길드장이 반드시 있어야 합니다.
용기사단 → 최소 1명 필요
둘 다 최대 관계만 보면
Guild 1 : N Player
입니다.
하지만 최소 관계는 다릅니다.
게임 A
Guild → Player
0명 이상
게임 B
Guild → Player
1명 이상
IE 표기법은 바로 이런 차이까지 표현할 수 있습니다.
Optional과 Mandatory
IE 표기법을 볼 때 자주 등장하는 단어가 있습니다.
Optional
필수가 아니다라는 의미입니다.
최소 개수가 0입니다.
O|
O<
가 이에 해당합니다.
예를 들어
Player는 Guild에 가입하지 않아도 된다.
라면 Guild 관계는 Optional입니다.
Mandatory
반드시 존재해야 한다는 의미입니다.
최소 개수가 1입니다.
||
|<
가 이에 해당합니다.
예를 들어
Inventory 데이터는 반드시 Item 하나를 가리켜야 한다.
라면 Item은 Mandatory 관계입니다.
데이터로 직접 비교하기
Player가 Guild에 가입하지 않아도 된다고 해봅시다.
Player
player_id | nickname | guild_id |
|---|---|---|
1 | 천기사 | 10 |
2 | 흑마법사 | NULL |
3 | 궁수왕 | 10 |
guild_id에 NULL을 허용하고 있습니다.
따라서 Player 한 명의 입장에서 Guild는
0 또는 1
입니다.
흑마법사 → Guild 없음
천기사 → Guild 하나
반대로 한 Guild에는 여러 Player가 들어갈 수 있습니다.
Guild
guild_id | guild_name |
|---|---|
10 | 용기사단 |
20 | 마법협회 |
30 | 암흑기사단 |
암흑기사단에는 아직 플레이어가 한 명도 없을 수 있습니다.
그러므로 Guild의 입장에서 Player는
0개 이상
입니다.
이처럼 IE 표기법에서는 단순한 1:N 관계뿐 아니라 0을 허용하는지 여부까지 표시할 수 있습니다.
FK와 관계를 함께 보면 이해하기 쉽다
ERD를 읽을 때는 관계선만 보지 말고 FK가 어디에 존재하는지도 함께 보는 것이 좋습니다.
예를 들어
에서 FK는 Player에 있습니다.
따라서 일반적으로
Guild = 부모
Player = 자식
관계로 생각할 수 있습니다.
데이터를 보면 더욱 명확합니다.
Guild
guild_id | guild_name |
|---|---|
10 | 용기사단 |
Player
player_id | nickname | guild_id |
|---|---|---|
1 | 천기사 | 10 |
2 | 궁수왕 | 10 |
3 | 성기사 | 10 |
Player의 여러 행이 동일한 guild_id = 10을 참조하고 있습니다.
따라서
Guild 1 : N Player
입니다.
식별 관계와 비식별 관계
ERD를 공부하다 보면 관계선이 실선과 점선으로 다르게 표시되는 경우도 있습니다.
이는 보통 **식별 관계(Identifying Relationship)**와 **비식별 관계(Non-identifying Relationship)**를 구분하기 위한 것입니다.
사용하는 ERD 도구에 따라 표현 방식에는 차이가 있을 수 있습니다.
비식별 관계
Player와 Guild를 보겠습니다.
Player의 PK는
player_id
입니다.
Guild의 guild_id가 Player에 FK로 들어왔지만 Player의 PK에는 포함되지 않습니다.
player_id → PK
guild_id → FK
이러한 관계를 비식별 관계라고 합니다.
즉
부모의 PK를 FK로 가져오지만 자식의 PK에는 포함하지 않는 관계
입니다.
식별 관계
Inventory를 살펴보겠습니다.
Inventory의 PK가
(player_id, item_id)
라고 하면 Player에서 가져온 player_id가 Inventory의 FK이면서 동시에 PK의 일부입니다.
Item의 item_id 역시 마찬가지입니다.
따라서 이러한 관계를 식별 관계라고 할 수 있습니다.
부모의 PK를 가져와 자식의 PK 구성에 사용하는 관계
입니다.
식별 관계와 비식별 관계 비교
구분 | 부모 PK가 자식에 | 자식 PK에도 포함? |
|---|---|---|
식별 관계 | FK로 들어옴 | O |
비식별 관계 | FK로 들어옴 | X |
게임 예시로 비교하면
Guild → Player
Player PK = player_id
Guild의 guild_id는 FK만 됨
→ 비식별 관계
반면
Player → Inventory
Inventory PK =
(player_id, item_id)
Player의 player_id가
PK + FK 역할
→ 식별 관계
입니다.
ERD와 실제 CREATE TABLE 비교
ERD가 실제 데이터베이스 코드로 어떻게 연결되는지 살펴보겠습니다.
ERD가 다음과 같다고 해봅시다.
실제 SQL에서는 다음과 같이 만들 수 있습니다.
CREATE TABLE guild (
guild_id INTEGER PRIMARY KEY,
guild_name VARCHAR(50) NOT NULL
);
CREATE TABLE player (
player_id INTEGER PRIMARY KEY,
nickname VARCHAR(50) NOT NULL,
guild_id INTEGER,
FOREIGN KEY (guild_id)
REFERENCES guild(guild_id)
);
ERD에서
Player.guild_id FK
↓
Guild.guild_id PK
로 표현했던 관계가 SQL의
FOREIGN KEY (guild_id)
REFERENCES guild(guild_id)
로 구현된 것입니다.
복합키가 있는 ERD
Inventory처럼 중간 테이블에서는 복합키가 사용될 수 있습니다.
실제 데이터는 다음과 같습니다.
player_id | item_id | quantity |
|---|---|---|
1 | 101 | 1 |
1 | 102 | 5 |
2 | 101 | 2 |
player_id = 1만으로는 하나의 행을 구별할 수 없습니다.
item_id = 101만으로도 구별할 수 없습니다.
하지만
(player_id = 1, item_id = 101)
두 값을 합치면 하나의 행을 특정할 수 있습니다.
SQL에서는 다음과 같이 표현할 수 있습니다.
CREATE TABLE inventory (
player_id INTEGER,
item_id INTEGER,
quantity INTEGER NOT NULL,
PRIMARY KEY (player_id, item_id),
FOREIGN KEY (player_id)
REFERENCES player(player_id),
FOREIGN KEY (item_id)
REFERENCES item(item_id)
);
ERD를 제대로 이해하면 이러한 테이블 생성 구조도 훨씬 쉽게 읽을 수 있습니다.
게임 데이터베이스 전체 ERD 예시
간단한 MMORPG 데이터베이스를 만들어보겠습니다.
Guild
컬럼 | 역할 |
|---|---|
guild_id | PK |
guild_name | 길드 이름 |
Player
컬럼 | 역할 |
|---|---|
player_id | PK |
nickname | 닉네임 |
level | 레벨 |
guild_id | Guild FK |
Item
컬럼 | 역할 |
|---|---|
item_id | PK |
item_name | 아이템 이름 |
price | 가격 |
Inventory
컬럼 | 역할 |
|---|---|
player_id | PK + FK |
item_id | PK + FK |
quantity | 수량 |
관계를 연결하면 다음과 같습니다.
ERD를 읽는 순서
복잡한 ERD를 처음부터 모든 선을 이해하려고 하면 어렵습니다.
다음 순서로 읽으면 비교적 쉽습니다.
① Entity 확인
↓
② PK 확인
↓
③ FK 확인
↓
④ FK가 어떤 PK를 가리키는지 확인
↓
⑤ 1:1 / 1:N / N:M 확인
↓
⑥ Optional / Mandatory 확인
↓
⑦ 식별 / 비식별 관계 확인
예를 들어
를 발견했다면
먼저 player_id가 PK라는 것을 확인합니다.
그다음 guild_id가 FK라는 것을 확인합니다.
그리고
Player.guild_id
↓
Guild.guild_id
를 찾습니다.
그러면
Player → Guild와 관계가 있구나.
를 알 수 있고 관계선의 Crow's Foot 기호를 확인해서 1 여부를 판단하면 됩니다.
IE 표기법 읽는 가장 쉬운 방법
IE 표기법이 처음에는 기호 때문에 어렵게 보일 수 있습니다.
하지만 세 가지만 기억하면 됩니다.
O = Zero
| = One
까마귀발 = Many
그리고 이것들을 조합합니다.
O| = 0 또는 1
|| = 정확히 1
O< = 0개 이상
|< = 1개 이상
예를 들어
Player → Guild
관계에서 플레이어가 길드에 가입하지 않아도 된다면
0 또는 1
이고,
길드 하나에 플레이어가 없거나 여러 명 존재할 수 있다면
0개 이상
입니다.
IE 표기법은 이러한 규칙을 그림 하나에 표현하는 방식입니다.
ERD가 필요한 이유
ERD의 가장 큰 장점은 데이터베이스 구조를 SQL보다 훨씬 빠르게 파악할 수 있다는 것입니다.
예를 들어 다음 SQL을 처음 보는 것과
CREATE TABLE inventory (
player_id INTEGER,
item_id INTEGER,
quantity INTEGER,
PRIMARY KEY (player_id, item_id),
FOREIGN KEY (player_id)
REFERENCES player(player_id),
FOREIGN KEY (item_id)
REFERENCES item(item_id)
);
다음 ERD를 보는 것을 비교해봅시다.
Player
│
│ 1:N
▼
Inventory
▲
│ N:1
│
Item
ERD를 보면 바로
플레이어와 아이템 사이를 Inventory가 연결하고 있구나.
라고 이해할 수 있습니다.
특히 수십 개, 수백 개의 테이블이 존재하는 시스템에서는 ERD가 데이터베이스 구조를 이해하는 데 매우 중요합니다.
정리
**ERD(Entity Relationship Diagram)**는 데이터베이스의 Entity, Attribute, Relationship을 그림으로 표현한 데이터베이스 설계도입니다.
게임 데이터베이스를 예로 들면
Guild
Player
Item
Inventory
가 Entity이고,
player_id
nickname
level
guild_id
등이 Attribute입니다.
그리고
Guild 1 : N Player
Player 1 : N Inventory
Item 1 : N Inventory
처럼 Entity 사이의 관계를 표현합니다.
IE 표기법에서는 이러한 관계를 조금 더 자세하게 표현하기 위해 최소 Cardinality와 최대 Cardinality를 함께 표시합니다.
기호 | 의미 | |
|---|---|---|
| 0 | |
| 1 | |
Crow's Foot | 여러 개 |
따라서
표현 | 의미 | ||
|---|---|---|---|
| 0 또는 1 | ||
| 정확히 1 | ||
| 0개 이상 | ||
| 1개 이상 |
으로 읽을 수 있습니다.
결국 ERD를 볼 때 가장 중요한 것은 선의 모양 자체를 외우는 것이 아니라 다음 질문을 하는 것입니다.
이 테이블 하나가 상대 테이블의 데이터 몇 개와 연결될 수 있는가?
그리고
그 관계는 반드시 존재해야 하는가, 없어도 되는가?
이 두 가지를 이해하면 IE 표기법의 대부분을 읽을 수 있습니다.
ERD
= 데이터베이스의 전체 구조
Entity
= 테이블이 될 대상
Attribute
= 컬럼이 될 정보
Relationship
= 테이블 사이의 연결
Cardinality
= 몇 개와 연결되는지
IE 표기법
= 관계의 최소·최대 개수를 기호로 표현하는 방법
관계형 데이터 모델링 → 정규화 → ERD → IE 표기법까지 연결해서 보면, 지금까지 나누어 배운 개념들이 결국 실제 데이터베이스를 설계하기 위한 하나의 과정이라는 것을 알 수 있습니다.
댓글 0
댓글을 불러오는 중…