안동민 개발노트 아이콘

안동민 개발노트

7장 : 데이터 모델링과 ERD

ERD 표기법과 실습

ER 모델을 그림으로 표현하는 방식이 ERD(Entity-Relationship Diagram)입니다. 표기법에 따라 같은 모델도 다르게 보이므로, 대표적인 표기법들을 알아두면 어디서든 ERD를 읽고 그릴 수 있습니다. 이 절에서는 Chen, IE(Crow's Foot), UML 표기법을 중심으로 비교한 뒤, Oracle/Barker 계열 표기처럼 도구별로 조금 다른 Crow's Foot 변형은 함께 구분해서 봅니다.


첸(Chen) 표기법

피터 첸(Peter Chen)이 1976년에 제안한 원래의 ER 다이어그램 표기법입니다. 학술적인 자료와 시험 문제에서 자주 등장하며, 개념적 모델링에 적합합니다.

첸 표기법은 개체·관계·속성이 명확하게 분리되므로 학습과 시험에 유용합니다. 하지만 속성이 많아지면 다이어그램이 매우 복잡해지는 단점이 있어, 대규모 논리·물리 ERD에서는 공간 효율 때문에 덜 쓰이는 편입니다.


까마귀 발(Crow's Foot / IE) 표기법

실무에서 가장 널리 사용되는 표기법입니다. IE(Information Engineering) 표기법이라고도 합니다. 관계의 카디널리티와 선택/필수 참여도를 선의 끝 모양으로 표현하며, 속성을 개체 박스 안에 넣어 공간을 절약합니다.

Barker 표기법은 Oracle 계열 모델링 도구에서 자주 보이는 Crow's Foot 계열 표기입니다. IE와 큰 방향은 같지만 필수/선택 참여도, 식별 관계, 속성 표시 방식이 도구마다 다르게 보일 수 있으므로, 다이어그램을 읽을 때는 범례와 선 끝 기호를 먼저 확인합니다.

Crow's Foot 기호 — 완전 정리
끝 기호(Notation)
  ──│    1 (정확히 하나)      세로선
  ──○    0 (없을 수도 있음)   동그라미
  ──<    N (다수, Many)       까마귀 발

조합 — 최소..최대
  ──│──│   1..1   정확히 하나 (필수, 단일)
  ──○──│   0..1   없거나 하나 (선택, 단일)
  ──│──<   1..N   하나 이상 (필수, 다수)
  ──○──<   0..N   없거나 여러 개 (선택, 다수)

Crow's Foot 표기법은 테이블 구조에 가까운 형태이므로, 논리적·물리적 모델링에 적합합니다.

Crow's Foot은 선의 한쪽 끝만 읽는 것이 아니라 양쪽 끝을 함께 읽어야 합니다. 예를 들어 User 1 ──< Order는 "한 주문은 정확히 한 회원에 속하고, 한 회원은 여러 주문을 가질 수 있다"처럼 양방향 최소·최대 참여도를 모두 확인해야 합니다.


식별 / 비식별 관계

Crow's Foot 표기법에서는 관계선의 종류로 식별/비식별 관계를 구분합니다. 핵심 기준은 부모 테이블의 PK가 자식 테이블의 PK 일부가 되는지입니다.

식별 관계는 약한 개체나 교차 테이블처럼 부모 키가 자식의 식별자 일부가 될 때 사용합니다. 비식별 관계는 부모 키가 자식의 일반 FK로만 들어가며, 자식은 별도의 PK를 가질 수 있습니다. 다만 대리키를 도입했다고 해서 업무적 의존성이 사라지는 것은 아니므로, ERD에는 생명주기와 삭제 규칙도 함께 검토합니다.


UML 클래스 다이어그램 표기법

UML(Unified Modeling Language)의 클래스 다이어그램도 ER 모델 표현에 사용됩니다. 객체지향 설계에서 ER 모델을 겸할 때 유용합니다.


3대 표기법 비교


ERD 도구

도구의 비용과 지원 기능은 바뀔 수 있으므로, 아래 표는 선택지를 이해하기 위한 예시로 봅니다. 실제 도입 시에는 라이선스, 협업 방식, 포워드/리버스 엔지니어링 지원 범위를 다시 확인해야 합니다.

도구특징비용
ERDCloud웹 기반, 한국어, 공유·협업 기능무료
dbdiagram.io코드로 ERD 작성, 깔끔한 UI무료/유료
MySQL WorkbenchMySQL 리버스 엔지니어링 지원무료
Oracle SQL Developer Data ModelerOracle 특화, 포워드/리버스 엔지니어링무료
draw.io (diagrams.net)범용 다이어그램, ERD 템플릿 제공무료
Lucidchart협업 기능 강력, 클라우드 기반유료
DataGrip (JetBrains)DB IDE에 내장된 ERD 시각화유료
DBeaver오픈소스 DB 도구, ERD 자동 생성무료/유료

dbdiagram.io 문법

dbdiagram.io에서는 코드로 테이블을 정의하면 자동으로 ERD가 생성됩니다. DBML(Database Markup Language)이라는 DSL을 사용합니다.

DBML 문법 예시
Table users {
  id int [pk, increment]
  name varchar [not null]
  email varchar [unique, not null]
  created_at timestamp [default: `now()`]
}

Table orders {
  id int [pk, increment]
  user_id int [ref: > users.id]   // Many-to-One
  total_amount decimal
  status enum('pending','paid','shipped')
  order_date date
}

Table order_items {
  id int [pk, increment]
  order_id int [ref: > orders.id]
  product_id int [ref: > products.id]
  quantity int [not null]
  unit_price decimal [not null]
}

Table products {
  id int [pk, increment]
  name varchar [not null]
  price decimal [not null]
  category_id int [ref: > categories.id]
  stock int [default: 0]
}

Table categories {
  id int [pk, increment]
  name varchar [not null]
  parent_id int [ref: > categories.id]  // Self-reference
}

// DBML 관계 표기
//   >   Many-to-One
//   <   One-to-Many
//   -   One-to-One
//   <>  Many-to-Many

ERDCloud 활용

ERDCloud는 한국어를 지원하는 웹 기반 ERD 도구의 한 예입니다. 팀 공유와 협업에는 편하지만, 도구가 만든 그림을 그대로 믿기보다 PK/FK, 카디널리티, 선택/필수 참여도가 요구사항과 맞는지 직접 검토해야 합니다.


ERD 개체·속성·관계 매핑 규칙

ERD에서 릴레이션으로 변환할 때의 기본 규칙을 미리 정리합니다.


실습 1 — 온라인 쇼핑몰 ERD

쇼핑몰의 요구사항을 분석하고 ERD를 단계별로 설계합니다.

요구사항
1. 회원은 이름, 이메일, 전화번호, 주소를 가진다
2. 상품은 이름, 가격, 재고를 가지며 카테고리에 속한다
3. 회원은 여러 주문을 할 수 있다
4. 하나의 주문에 여러 상품이 포함된다 (수량, 단가 기록)
5. 카테고리는 계층 구조이다 (대분류 > 중분류 > 소분류)
6. 회원은 상품에 리뷰를 남길 수 있다 (별점, 내용)
7. 배송 정보를 기록한다 (배송상태, 운송장번호, 배송업체)
Step 1: 개체 식별
개체(Entity)
  * 회원(User)         — 서비스 이용 주체
  * 상품(Product)      — 판매 대상
  * 카테고리(Category) — 상품 분류
  * 주문(Order)        — 구매 행위
  * 주문상세(OrderItem)— 주문-상품 교차
  * 리뷰(Review)       — 상품 평가
  * 배송(Shipping)     — 배송 정보
Step 2: 관계 분석
관계
  회원 ──(1:N)── 주문         : 한 회원이 여러 주문
  주문 ──(1:N)── 주문상세     : 한 주문에 여러 상품
  상품 ──(1:N)── 주문상세     : 한 상품이 여러 주문에 포함
  카테고리 ──(1:N)── 상품     : 한 카테고리에 여러 상품
  카테고리 ──(자기참조)── 카테고리: 상위-하위 계층
  회원 ──(1:N)── 리뷰         : 한 회원이 여러 리뷰
  상품 ──(1:N)── 리뷰         : 한 상품에 여러 리뷰
  주문 ──(1:0..1)── 배송      : 주문 생성 직후에는 배송이 없을 수 있음

Category 테이블의 parent_id는 자기 자신을 참조하는 자기 참조(Self-Referencing) 관계입니다. 이를 통해 계층 구조를 하나의 테이블로 표현합니다.

배송은 주문 이후에 생성될 수 있으므로 ERD에서는 주문 1건에 배송 0 또는 1건으로 읽고, DDL에서는 shipping.order_idUNIQUE 제약을 두어 1:1 범위를 보장합니다.

리뷰는 요구사항에 따라 회원 + 상품당 하나만 허용할 수도 있고, 구매한 주문상세에 대해서만 허용할 수도 있습니다. 후자의 경우 revieworder_item을 참조하도록 모델을 조정해야 하며, 단순히 회원과 상품 FK만 두면 "구매하지 않은 상품 리뷰"를 막기 어렵습니다.


실습 2 — 학사 관리 시스템 ERD

요구사항
1. 학생은 학번, 이름, 학과에 속한다
2. 교수는 교번, 이름, 학과에 속한다
3. 교수는 여러 과목을 개설한다
4. 학생은 여러 과목을 수강하고 성적을 받는다
5. 학과 정보를 별도로 관리한다 (학과장, 위치)

실습 3 — SNS 시스템 ERD

요구사항
1. 사용자는 프로필(아이디, 닉네임, 자기소개)을 가진다
2. 사용자는 게시글을 작성한다 (텍스트, 이미지 URL)
3. 사용자는 다른 사용자를 팔로우한다 (M:N 자기참조)
4. 사용자는 게시글에 좋아요를 누를 수 있다
5. 사용자는 게시글에 댓글을 달 수 있다

ERD 설계 시 흔한 실수


ERD 품질 점검 체크리스트

ERD 품질 점검 10항목
□ 1. 모든 개체에 PK가 존재하는가?
□ 2. FK가 참조하는 PK가 존재하는가?
□ 3. M:N 관계가 교차 테이블로 해소되었는가?
□ 4. 카디널리티와 참여 제약이 정확한가?
□ 5. 속성명이 명확하고 일관적인가?
     (snake_case 통일 등)
□ 6. 자기참조 관계가 올바르게 표현되었는가?
□ 7. 약한 개체의 식별 관계가 표시되었는가?
□ 8. 다중값 속성이 별도 테이블로 분리되었는가?
□ 9. 정규화 수준과 반정규화 근거가 명확한가?
□ 10. 업무 규칙이 ERD에 충실히 반영되었는가?

ERD에서 DDL 변환

완성된 ERD에서 SQL DDL을 도출하는 과정입니다.

쇼핑몰 ERD → DDL 예시
-- MySQL 중심 예시
-- 카테고리 (자기참조)
CREATE TABLE category (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    parent_id INT,
    FOREIGN KEY (parent_id) REFERENCES category(id)
);

-- 회원
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    phone VARCHAR(20),
    address VARCHAR(200)
);

-- 상품
CREATE TABLE product (
    id INT PRIMARY KEY AUTO_INCREMENT,
    category_id INT NOT NULL,
    name VARCHAR(200) NOT NULL,
    price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
    stock INT DEFAULT 0 CHECK (stock >= 0),
    FOREIGN KEY (category_id) REFERENCES category(id)
);

-- 주문
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    order_date DATETIME DEFAULT CURRENT_TIMESTAMP,
    status VARCHAR(20) DEFAULT 'pending',
    total_amount DECIMAL(12,2) CHECK (total_amount >= 0),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

-- 주문상세 (교차 테이블)
CREATE TABLE order_item (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL CHECK (quantity > 0),
    unit_price DECIMAL(10,2) NOT NULL CHECK (unit_price >= 0),
    UNIQUE (order_id, product_id),
    FOREIGN KEY (order_id) REFERENCES orders(id),
    FOREIGN KEY (product_id) REFERENCES product(id)
);

-- 배송
CREATE TABLE shipping (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT NOT NULL UNIQUE,
    status VARCHAR(20) DEFAULT 'preparing',
    tracking_no VARCHAR(100),
    carrier VARCHAR(50),
    shipped_at DATETIME,
    delivered_at DATETIME,
    FOREIGN KEY (order_id) REFERENCES orders(id)
);

-- 리뷰
CREATE TABLE review (
    id INT PRIMARY KEY AUTO_INCREMENT,
    product_id INT NOT NULL,
    user_id INT NOT NULL,
    rating TINYINT CHECK (rating BETWEEN 1 AND 5),
    content TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    UNIQUE (product_id, user_id),
    FOREIGN KEY (product_id) REFERENCES product(id),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

CHECK, AUTO_INCREMENT, 시간 기본값 문법은 DBMS마다 차이가 있습니다. MySQL에서도 CHECK 제약은 8.0.16 이후부터 실질적으로 검사됩니다. PostgreSQL, Oracle, SQL Server에서는 identity 컬럼, 날짜 타입, 현재 시각 기본값 표현을 해당 DBMS에 맞게 바꿔야 합니다.

order_itemUNIQUE (order_id, product_id)는 한 주문에 같은 상품 라인을 한 번만 허용한다는 정책입니다. 옵션, 할인, 출고 단위 때문에 같은 상품이 여러 라인으로 나뉠 수 있다면 이 제약은 맞지 않을 수 있습니다. total_amount는 주문상세에서 계산 가능한 유도 값이므로 저장한다면 동기화 규칙도 함께 설계해야 합니다.

리뷰의 UNIQUE (product_id, user_id)는 “회원은 상품당 하나의 리뷰만 남긴다”는 업무 규칙을 선택한 예시이며, 여러 번 리뷰를 허용하는 정책이면 제거해야 합니다.


ERD 표기법 종합 정리

다음 절에서는 이 ER 모델을 실제 릴레이션(테이블)으로 변환하는 규칙을 다루겠습니다.