icon

안동민 개발노트

7장 : 데이터 모델링과 ERD

데이터 모델링의 개념


코드를 먼저 짜고 테이블을 나중에 만들면, 결국 전부 다시 짜게 됩니다. 좋은 설계가 좋은 애플리케이션의 출발점이고, 좋은 설계는 좋은 데이터 모델에서 시작합니다. 데이터 모델링(Data Modeling)은 현실 세계의 데이터를 데이터베이스의 구조로 변환하는 체계적인 과정입니다. 이 과정 없이 무작정 테이블을 만들면 중복 데이터, 비정상적인 갱신, 조회 성능 저하 등 수많은 문제에 직면하게 됩니다.


데이터 모델링이란

데이터 모델링을 한마디로 정의하면, 현실 세계의 업무 프로세스와 데이터를 추상화하여 데이터베이스로 표현하는 설계 활동입니다. 건물을 짓기 전에 설계도를 그리는 것처럼, 데이터베이스를 구축하기 전에 어떤 데이터를 어떤 구조로 저장할지 미리 계획하는 것입니다.

데이터 모델링이 왜 중요한지 구체적으로 살펴보겠습니다. 첫째, 의사소통의 도구입니다. 개발자, 기획자, DBA가 동일한 다이어그램을 보며 업무를 논의할 수 있습니다. 회원이 상품을 주문한다라는 문장보다 ERD 한 장이 오해를 훨씬 줄여줍니다. 둘째, 설계 오류의 조기 발견입니다. 코드를 작성한 후에 테이블 구조를 바꾸는 것은 비용이 매우 큽니다. 모델링 단계에서 구조적 문제를 발견하면 수정 비용은 0에 가깝습니다. 셋째, 데이터 품질 보장입니다. 적절한 제약 조건과 정규화를 통해 중복 없는 일관된 데이터를 유지할 수 있습니다. 넷째, 유지보수 용이성입니다. 체계적으로 설계된 데이터베이스는 새로운 요구사항이 추가되어도 최소한의 변경으로 대응할 수 있습니다.


데이터 모델의 구성 요소

데이터 모델(Data Model)은 데이터의 구조를 기술하기 위한 개념적 도구의 집합입니다. 모든 데이터 모델은 세 가지 요소로 구성됩니다.

데이터 모델의 3요소
┌────────────────────────────────────────────┐
│           데이터 모델 (Data Model)         │
│                                            │
│  ┌───────────┐  ┌──────────┐  ┌──────────┐ │
│  │ 구조(S)   │  │ 연산(O)  │  │ 제약(C)  │ │
│  │ Structure │  │ Operation│  │Constraint│ │
│  │           │  │          │  │          │ │
│  │ 데이터가  │  │ 데이터를 │  │ 데이터의 │ │
│  │ 어떤 형태 │  │ 어떻게   │  │ 논리적   │ │
│  │ 로 저장   │  │ 조작     │  │ 규칙     │ │
│  │ 되는가    │  │ 하는가   │  │          │ │
│  └───────────┘  └──────────┘  └──────────┘ │
└────────────────────────────────────────────┘

구조(Structure)는 데이터가 어떤 형태로 조직되는지를 정의합니다. 관계형 모델에서는 릴레이션(테이블)이 기본 구조이고, 계층형 모델에서는 트리, 네트워크형 모델에서는 그래프가 기본 구조입니다. 구조는 정적인 성질을 나타내며, 데이터 타입과 데이터 간의 관계를 규정합니다.

연산(Operation)은 데이터를 조작하는 방법을 정의합니다. 관계형 모델에서는 관계 대수(Relational Algebra)의 SELECT(σ), PROJECT(π), JOIN(⋈), UNION(∪) 등이 기본 연산이며, 이것이 SQL로 구현됩니다. 연산은 동적인 성질을 나타냅니다. 조회, 삽입, 수정, 삭제(CRUD)뿐 아니라 집계, 정렬, 그룹화 등 다양한 연산이 포함됩니다.

제약 조건(Constraint)은 데이터가 지켜야 하는 논리적 규칙입니다. 기본키는 NULL이 될 수 없고(개체 무결성), 외래키가 참조하는 값은 반드시 존재해야 하며(참조 무결성), 나이는 0 이상이어야 한다(도메인 무결성)는 등의 규칙이 제약 조건에 해당합니다.

이 세 가지 요소가 함께 작동해야 비로소 완전한 데이터 모델이 됩니다. 구조만 있고 제약이 없으면 잘못된 데이터가 들어가고, 연산이 없으면 데이터를 활용할 수 없습니다.


데이터 모델의 종류

역사적으로 여러 데이터 모델이 등장했습니다. 각 모델은 데이터의 논리적 구조를 서로 다른 방식으로 표현합니다.

모델구조대표 DBMS시대특징
계층형트리(Tree)IBM IMS1960년대부모-자식 관계, 1:N만 표현
네트워크형그래프(Graph)IDMS1970년대M:N 관계 표현 가능, 복잡한 포인터
관계형테이블(Relation)Oracle, MySQL1970년~현재수학적 기반, SQL, 가장 널리 사용
객체지향형객체(Object)ObjectDB1990년대상속, 캡슐화, 복합 데이터
객체관계형테이블+객체PostgreSQL, Oracle2000년대~관계형 + 객체지향 혼합
NoSQL다양함MongoDB, Redis2010년대~스키마 유연, 수평 확장

계층형 모델(Hierarchical Model)은 데이터를 트리 구조로 표현합니다. 하나의 루트 노드 아래에 여러 자식 노드가 달리는 형태입니다. IBM의 IMS가 대표적인데, 항공 예약 시스템에서 오랜 기간 사용되었습니다. 장점은 특정 경로의 데이터를 빠르게 조회할 수 있다는 것이지만, 단점은 M:N 관계를 직접 표현할 수 없고, 데이터 중복이 발생하며, 구조 변경이 매우 어렵다는 것입니다.

계층형 모델 예시
          대학교
         /      \
      공과대학   인문대학
      /    \        \
   컴공학과 전자공학과  국문학과
   /    \
 학생1  학생2

계층형 모델에서 한 학생이 두 학과에 소속되려면 데이터를 복제해야 합니다. 이것이 바로 계층형 모델의 근본적인 한계입니다.

네트워크형 모델(Network Model)은 계층형의 한계를 극복하기 위해 등장했습니다. CODASYL(Conference on Data Systems Languages) 위원회에서 제안했으며, 데이터를 그래프 구조로 표현합니다. 하나의 자식이 여러 부모를 가질 수 있어 M:N 관계를 직접 표현할 수 있습니다. 그러나 레코드 간의 관계가 포인터로 연결되어 있어, 프로그래머가 데이터 접근 경로를 직접 제어해야 합니다. 이는 프로그래밍이 복잡해지고 데이터 독립성이 떨어지는 원인이 됩니다.

관계형 모델(Relational Model)은 1970년 IBM의 에드거 커드(Edgar F. Codd)가 발표한 논문에서 시작되었습니다. 데이터를 수학의 릴레이션(집합론의 관계)으로 표현하며, 테이블이라는 직관적인 구조를 사용합니다. 포인터 없이 값(Value)만으로 테이블 간의 관계를 표현하기 때문에 데이터 독립성이 높고, SQL이라는 비절차적 언어로 데이터를 조작할 수 있습니다. 현재 시장에서 가장 압도적인 점유율을 차지하고 있습니다.

객체지향형 모델(Object-Oriented Model)은 프로그래밍 언어의 객체 개념을 데이터베이스에 도입한 것입니다. 상속, 캡슐화, 다형성을 지원하며, 복잡한 데이터 타입(이미지, 멀티미디어 등)을 자연스럽게 다룰 수 있습니다. 그러나 관계형 모델에 비해 성숙도가 낮고 표준화가 부족하여 범용적으로 사용되지는 않습니다.


데이터 모델링 단계

데이터 모델링은 추상화 수준에 따라 세 단계로 나뉩니다. 각 단계는 이전 단계의 결과를 입력으로 받아 더 구체적인 모델을 만들어냅니다.

3단계 모델링
현실 세계의 요구사항


┌───────────────┐   "무엇을 저장할 것인가?"
│ 개념적 모델링 │   개체(Entity)와 관계(Relationship) 도출
│               │   ERD (개체-관계 다이어그램)
└──────┬────────┘


┌───────────────┐   "어떤 구조로 저장할 것인가?"
│ 논리적 모델링 │   릴레이션(테이블) 변환, 정규화
│               │   속성, 키, 제약 조건 정의
└──────┬────────┘


┌───────────────┐   "실제로 어떻게 저장할 것인가?"
│ 물리적 모델링 │   테이블 생성 SQL, 데이터 타입
│               │   인덱스, 파티셔닝, 저장 공간
└───────────────┘

개념적 모델링

개념적 모델링(Conceptual Modeling)은 가장 높은 추상화 수준의 모델링입니다. DBMS의 종류나 구현 기술에 독립적이며, 업무의 본질인 "무엇(What)"에 초점을 맞춥니다. 이 단계의 핵심 산출물은 ERD(Entity-Relationship Diagram, 개체-관계 다이어그램)입니다.

개념적 모델링에서 수행하는 작업은 다음과 같습니다.

  • 업무에서 관리해야 하는 개체(Entity)를 식별합니다. 회원, 상품, 주문처럼 독립적으로 존재하며 식별 가능한 대상입니다.
  • 개체의 속성(Attribute)을 파악합니다. 회원의 이름, 이메일, 가입일 같은 특성입니다.
  • 개체 간의 관계(Relationship)를 정의합니다. 회원이 상품을 주문한다에서 주문한다가 관계입니다.
  • 관계의 카디널리티(Cardinality)를 결정합니다. 1:1, 1:N, M:N 중 어떤 유형인지 파악합니다.
  • 개체의 식별자(Identifier)를 결정합니다. 각 개체를 유일하게 구분할 수 있는 속성입니다.

개념적 모델링은 기술적인 용어보다 업무 용어를 사용합니다. 기획자나 현업 담당자도 이해할 수 있어야 하기 때문입니다. PRIMARY KEY, VARCHAR(100) 같은 기술 용어는 이 단계에서는 등장하지 않습니다.

개념적 모델 예시 - 온라인 서점
┌───────────┐         ┌───────────┐         ┌─────────┐
│  회  원   │──주문──▶│  주  문   │◀──포함──│ 도  서  │
│           │  (1:N)  │           │  (M:N)  │         │
│ #회원번호 │         │ #주문번호 │         │ #ISBN   │
│  이름     │         │  주문일   │         │  제목   │
│  이메일   │         │  총금액   │         │  저자   │
│  연락처   │         │  상태     │         │  가격   │
└───────────┘         └───────────┘         └─────────┘

                      배송 (1:1)

                    ┌──────────┐
                    │  배  송  │
                    │ #배송번호│
                    │  주소    │
                    │  상태    │
                    └──────────┘

논리적 모델링

논리적 모델링(Logical Modeling)은 개념적 모델을 특정 데이터 모델(관계형, 계층형 등)의 구조로 변환하는 단계입니다. 관계형 데이터베이스를 사용한다면, 이 단계에서 개체는 릴레이션(테이블)으로, 속성은 컬럼으로, 관계는 외래키로 변환됩니다. 아직 특정 DBMS에 종속되지 않습니다.

논리적 모델링에서 수행하는 주요 작업은 다음과 같습니다.

  • 개체→테이블 변환: 각 개체를 하나의 릴레이션으로 변환합니다.
  • 속성→컬럼 변환: 개체의 속성을 릴레이션의 속성(컬럼)으로 정의하고, 데이터 타입의 논리적 범주(문자, 숫자, 날짜 등)를 결정합니다.
  • 관계→외래키 변환: 1:N 관계에서 N쪽 테이블에 외래키를 추가합니다. M:N 관계는 중간 테이블을 생성하여 두 개의 1:N 관계로 분해합니다.
  • 기본키 결정: 각 테이블의 기본키를 확정합니다.
  • 정규화 수행: 데이터 중복을 제거하고 갱신 이상을 방지하기 위해 정규화를 적용합니다.
  • 무결성 제약 정의: 도메인 제약, 개체 무결성, 참조 무결성 등을 명시합니다.
논리적 모델 - 테이블 구조 (DBMS 독립적)
-- 회원 테이블
회원 (
    회원번호    정수, PRIMARY KEY,
    이름       문자열(50), NOT NULL,
    이메일      문자열(100), UNIQUE, NOT NULL,
    연락처      문자열(20),
    가입일      날짜, DEFAULT 현재날짜
)

-- 주문 테이블
주문 (
    주문번호    정수, PRIMARY KEY,
    회원번호    정수, FOREIGN KEY → 회원(회원번호),
    주문일      날짜, NOT NULL,
    총금액      실수, CHECK(총금액 >= 0),
    상태       문자열(20), DEFAULT '주문완료'
)

-- M:N 관계 → 중간 테이블
주문상세 (
    주문번호    정수, FOREIGN KEY → 주문(주문번호),
    ISBN       문자열(13), FOREIGN KEY → 도서(ISBN),
    수량       정수, CHECK(수량 > 0),
    단가       실수,
    PRIMARY KEY (주문번호, ISBN)
)

논리적 모델링에서 가장 중요한 활동이 정규화(Normalization)입니다. 정규화를 거치지 않은 테이블은 삽입 이상, 삭제 이상, 갱신 이상이 발생할 수 있습니다. 예를 들어, 주문 테이블에 회원의 주소를 직접 저장하면, 회원이 주소를 변경했을 때 과거 모든 주문 레코드의 주소를 일일이 수정해야 합니다. 이것이 갱신 이상입니다.

물리적 모델링

물리적 모델링(Physical Modeling)은 논리적 모델을 실제 DBMS에 맞게 구현하는 단계입니다. 이 단계에서 비로소 Oracle, MySQL, PostgreSQL 같은 특정 DBMS의 문법과 특성이 반영됩니다. 성능을 고려한 물리적 설계가 이루어지는 단계이기도 합니다.

물리적 모델링에서 수행하는 주요 작업은 다음과 같습니다.

  • 데이터 타입 확정: 논리적 모델의 문자열을 MySQL의 VARCHAR(100)으로, Oracle의 VARCHAR2(100)으로 구체화합니다.
  • 인덱스 설계: 조회 빈도가 높은 컬럼, WHERE절에 자주 등장하는 컬럼, JOIN에 사용되는 컬럼에 인덱스를 생성합니다.
  • 파티셔닝 전략: 대용량 테이블을 날짜, 지역 등의 기준으로 분할하여 조회 성능을 향상시킵니다.
  • 반정규화(Denormalization): 정규화로 인한 JOIN 과다 발생 시, 의도적으로 중복을 허용하여 조회 성능을 개선합니다.
  • 저장 공간 설계: 테이블스페이스, 파일 그룹, 스토리지 파라미터를 설정합니다.
  • 뷰(View) 정의: 복잡한 조회를 단순화하거나 보안을 위해 뷰를 생성합니다.
물리적 모델 - MySQL 구현
CREATE TABLE members (
    member_id    INT          AUTO_INCREMENT PRIMARY KEY,
    name         VARCHAR(50)  NOT NULL,
    email        VARCHAR(100) NOT NULL UNIQUE,
    phone        VARCHAR(20),
    created_at   DATETIME     DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_email (email),
    INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE orders (
    order_id     INT          AUTO_INCREMENT PRIMARY KEY,
    member_id    INT          NOT NULL,
    ordered_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    total_amount DECIMAL(12,2) CHECK (total_amount >= 0),
    status       VARCHAR(20)  DEFAULT '주문완료',
    FOREIGN KEY (member_id) REFERENCES members(member_id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE,
    INDEX idx_member_id (member_id),
    INDEX idx_ordered_at (ordered_at)
) ENGINE=InnoDB;

CREATE TABLE order_items (
    order_id     INT          NOT NULL,
    isbn         CHAR(13)     NOT NULL,
    quantity     INT          NOT NULL CHECK (quantity > 0),
    unit_price   DECIMAL(10,2) NOT NULL,
    PRIMARY KEY (order_id, isbn),
    FOREIGN KEY (order_id) REFERENCES orders(order_id)
        ON DELETE CASCADE,
    FOREIGN KEY (isbn) REFERENCES books(isbn)
) ENGINE=InnoDB;

물리적 모델링에서는 성능과 정규화 사이의 균형을 잡는 것이 핵심입니다. 정규화를 극단적으로 적용하면 JOIN이 많아져 조회 성능이 떨어지고, 반정규화를 과도하게 적용하면 데이터 불일치가 발생합니다.


3단계 비교 정리

구분개념적 모델링논리적 모델링물리적 모델링
추상화 수준높음중간낮음
대상 독자기획자, 현업설계자, 개발자DBA, 개발자
DBMS 종속독립적데이터 모델 종속특정 DBMS 종속
핵심 산출물ERD릴레이션 스키마DDL 스크립트
핵심 활동개체/관계 도출정규화, 키 결정인덱스, 파티셔닝
사용 용어개체, 관계릴레이션, 속성, 키테이블, 컬럼, 인덱스
관심사무엇을 저장하는가어떤 구조로 저장하는가어떻게 저장하는가

세 단계는 서로 독립적이지 않고 연속적입니다. 개념적 모델링의 결과가 논리적 모델링의 입력이 되고, 논리적 모델링의 결과가 물리적 모델링의 입력이 됩니다. 실무에서는 이 과정이 반드시 순차적으로 진행되지는 않습니다. 물리적 모델링 단계에서 성능 문제를 발견하면 논리적 모델을 수정하기도 하고, 요구사항이 변경되면 개념적 모델부터 다시 검토하기도 합니다.


요구사항에서 모델로

실제 프로젝트에서 모델링이 어떻게 진행되는지 구체적인 예시를 통해 살펴보겠습니다.

쇼핑몰 요구사항 분석
요구사항
  "회원이 여러 상품을 주문할 수 있다.
   하나의 주문에 여러 상품이 포함될 수 있다.
   각 상품의 수량과 가격이 기록되어야 한다."

개체 추출
  회원(User), 상품(Product), 주문(Order)

관계 분석
  회원 ──1:N──→ 주문  (한 회원이 여러 주문)
  주문 ──M:N──→ 상품  (주문과 상품은 다대다)
   └→ 주문상세(OrderItem) 중간 테이블 필요

이 요구사항을 분석하는 과정을 단계별로 풀어보겠습니다.

1단계: 개체 식별입니다. 요구사항에서 명사를 추출합니다. "회원", "상품", "주문"이 핵심 개체로 식별됩니다. "수량"과 "가격"은 개체가 아니라 속성입니다. 독립적으로 존재할 수 없고, 주문-상품 관계에 종속되기 때문입니다.

2단계: 관계 분석입니다. 요구사항의 동사를 분석합니다. "주문할 수 있다"에서 회원과 주문 사이의 관계가 도출됩니다. "포함될 수 있다"에서 주문과 상품 사이의 관계가 도출됩니다. "여러"라는 단어에서 카디널리티를 파악합니다. 한 회원이 여러 주문을 할 수 있으므로 1:N, 하나의 주문에 여러 상품이 포함되고 하나의 상품이 여러 주문에 포함될 수 있으므로 M:N입니다.

3단계: M:N 관계 해소입니다. 관계형 데이터베이스에서 M:N 관계는 직접 구현할 수 없습니다. 중간 테이블(연결 테이블, 교차 테이블)을 생성하여 두 개의 1:N 관계로 분해합니다. 이 중간 테이블이 "주문상세(OrderItem)"입니다. 수량과 가격은 이 중간 테이블의 속성이 됩니다.

4단계: 속성 정의입니다. 각 개체의 속성을 구체적으로 정의합니다.

속성 정의
회원: 회원번호(PK), 이름, 이메일(UK), 연락처, 주소, 가입일
상품: 상품번호(PK), 상품명, 가격, 재고수량, 카테고리
주문: 주문번호(PK), 회원번호(FK), 주문일, 총금액, 주문상태
주문상세: 주문번호(FK,PK), 상품번호(FK,PK), 수량, 단가

데이터 독립성

데이터 모델링과 밀접하게 관련된 개념이 데이터 독립성(Data Independence)입니다. ANSI/SPARC 3계층 아키텍처에서 제안된 개념으로, 데이터의 논리적 구조와 물리적 저장 방식을 분리하여 한쪽의 변경이 다른 쪽에 영향을 미치지 않도록 하는 것입니다.

ANSI/SPARC 3계층 아키텍처
┌──────────────────────┐
│     외부 스키마      │   사용자 뷰 (View)
│  (External Schema)   │   각 사용자/응용 프로그램마다 다름
└──────────┬───────────┘
           │ ← 논리적 데이터 독립성
┌──────────┴───────────┐
│     개념 스키마      │   전체 데이터의 논리적 구조
│  (Conceptual Schema) │   테이블, 관계, 제약 조건
└──────────┬───────────┘
           │ ← 물리적 데이터 독립성
┌──────────┴───────────┐
│     내부 스키마      │   물리적 저장 구조
│  (Internal Schema)   │   파일, 인덱스, 저장 경로
└──────────────────────┘

논리적 데이터 독립성(Logical Data Independence)은 개념 스키마가 변경되어도 외부 스키마(응용 프로그램)가 영향을 받지 않는 것입니다. 예를 들어, 회원 테이블에 새로운 컬럼(포인트)을 추가해도, 기존 애플리케이션은 해당 컬럼을 사용하지 않으므로 영향이 없습니다. 뷰(View)를 통해 논리적 독립성을 확보할 수 있습니다.

물리적 데이터 독립성(Physical Data Independence)은 내부 스키마가 변경되어도 개념 스키마가 영향을 받지 않는 것입니다. 예를 들어, 테이블의 저장 위치를 SSD에서 HDD로 옮기거나, 인덱스를 새로 추가해도 논리적 테이블 구조는 변하지 않습니다. 관계형 데이터베이스가 물리적 독립성을 잘 지원하는 이유는 SQL이 "무엇(What)"을 기술하지 "어떻게(How)"를 기술하지 않기 때문입니다.


좋은 데이터 모델의 조건

데이터 모델링의 결과물이 좋은 모델인지 판단하는 기준이 있습니다.

완전성(Completeness)은 업무에서 필요한 모든 데이터가 빠짐없이 포함되어 있는지를 의미합니다. 나중에 "이 데이터는 어디에 저장하지?"라는 질문이 나오면 모델이 불완전한 것입니다.

비중복성(Non-Redundancy)은 동일한 데이터가 여러 곳에 중복 저장되지 않는 것입니다. 정규화를 통해 달성할 수 있습니다. 회원의 주소가 회원 테이블과 주문 테이블에 모두 있으면 중복이고, 갱신 이상의 원인이 됩니다.

명확성(Clarity)은 모델을 보는 사람이 쉽게 이해할 수 있어야 한다는 것입니다. 테이블명, 컬럼명이 직관적이어야 하고, 관계가 명확해야 합니다. "TB_01"보다 "members"가, "col_a"보다 "email"이 훨씬 명확합니다.

확장성(Extensibility)은 새로운 요구사항이 추가되었을 때 기존 구조를 크게 변경하지 않고도 수용할 수 있는 유연한 구조입니다. 예를 들어, 결제 방법이 추가된다면 결제 테이블만 새로 만들면 되는 구조가 좋은 구조입니다.

일관성(Consistency)은 데이터 모델 전반에 걸쳐 동일한 규칙이 일관되게 적용되는 것입니다. 어떤 테이블은 대리키를 쓰고 어떤 테이블은 자연키를 쓰는 등 규칙이 일관되지 않으면 혼란을 초래합니다.


모델링 시 흔한 실수

실무에서 데이터 모델링 시 자주 발생하는 실수를 정리합니다.

첫째, 요구사항 분석을 건너뛰고 바로 테이블을 만드는 것입니다. "일단 빨리 만들자"는 마인드로 시작하면, 결국 대규모 마이그레이션이 필요해집니다. 요구사항이 불명확할수록 개념적 모델링에 더 많은 시간을 투자해야 합니다.

둘째, 모든 것을 하나의 테이블에 넣는 것입니다. 주문 테이블에 회원 정보, 상품 정보, 배송 정보를 모두 넣으면 컬럼이 수십 개가 되고 중복이 넘쳐납니다. 이것은 정규화가 필요하다는 명확한 신호입니다.

나쁜 설계 - 모든 것을 하나의 테이블에
-- 이렇게 하면 안 됩니다
CREATE TABLE orders_bad (
    order_id     INT PRIMARY KEY,
    member_name  VARCHAR(50),   -- 회원 정보 중복
    member_email VARCHAR(100),  -- 회원 정보 중복
    member_phone VARCHAR(20),   -- 회원 정보 중복
    product_name VARCHAR(100),  -- 상품 정보 중복
    product_price DECIMAL(10,2),-- 상품 정보 중복
    quantity     INT,
    ordered_at   DATETIME
);
-- 같은 회원이 10번 주문하면 이름,이메일,전화번호가 10번 중복 저장

셋째, 과도한 정규화입니다. 정규화는 중요하지만, 지나치면 단순한 조회에도 5~6개 테이블을 JOIN해야 하는 상황이 벌어집니다. 성능 요구사항과의 균형이 필요합니다.

넷째, 명명 규칙 부재입니다. user_name, userName, UserName, usr_nm이 혼재되면 유지보수가 어려워집니다. 프로젝트 초기에 명명 규칙(Naming Convention)을 정하고 일관되게 적용해야 합니다.

다섯째, NULL의 남용입니다. 의미가 불분명한 NULL을 허용하면 쿼리가 복잡해지고 버그의 원인이 됩니다. 가능하면 NOT NULL 제약을 적용하고, 기본값(DEFAULT)을 설정하는 것이 좋습니다.


실무에서의 모델링 도구

데이터 모델링은 손으로 그릴 수도 있지만, 전문 도구를 사용하면 ERD 작성, DDL 생성, 역공학(Reverse Engineering)을 효율적으로 수행할 수 있습니다.

도구특징용도
ERWin업계 표준, 대규모 프로젝트대기업, SI 프로젝트
DA# (다샵)한국형, 가격 경쟁력공공기관, 국내 SI
MySQL WorkbenchMySQL 전용, 무료소규모 프로젝트, 학습
pgModelerPostgreSQL 전용, 오픈소스PostgreSQL 프로젝트
dbdiagram.io웹 기반, 코드로 ERD 작성빠른 프로토타이핑
draw.io범용 다이어그램, 무료문서화, 설계 공유

정방향 공학(Forward Engineering)은 ERD에서 DDL(CREATE TABLE 문)을 자동 생성하는 것이고, 역공학(Reverse Engineering)은 이미 존재하는 데이터베이스에서 ERD를 추출하는 것입니다. 기존 시스템을 분석하거나 문서화할 때 역공학이 유용합니다.


모델링과 개발 프로세스

데이터 모델링은 소프트웨어 개발 프로세스의 어느 단계에서 이루어질까요? 전통적인 폭포수 모델에서는 분석→설계 단계에서 모델링이 집중적으로 이루어지지만, 애자일 환경에서는 점진적으로 모델링이 진행됩니다.

폭포수 vs 애자일 모델링
폭포수 모델
  요구분석 → [전체 ERD 작성] → 설계 → 구현 → 테스트 → 운영
  - 모든 테이블을 한 번에 설계
  - 변경에 대한 비용이 높음
  - 대규모, 안정적인 시스템에 적합

애자일 모델
  스프린트1 → [핵심 테이블 설계] → 구현
  스프린트2 → [추가 테이블 설계] → 구현
  스프린트3 → [리팩터링/확장]   → 구현
  - 점진적으로 테이블을 추가/수정
  - 마이그레이션 전략이 중요
  - 변화가 잦은 스타트업에 적합

어떤 방법론을 사용하든, 데이터 모델링을 완전히 건너뛰는 것은 위험합니다. 규모가 작은 프로젝트라면 간단한 ERD라도 그려두는 것이 나중에 큰 도움이 됩니다. 데이터 모델은 코드보다 오래 살아남는 경우가 많기 때문입니다. 코드는 프레임워크가 바뀌면 다시 작성하지만, 데이터는 마이그레이션하여 계속 사용합니다.


데이터 모델링 절차 상세

모델링을 실무에서 수행할 때의 구체적인 절차를 정리합니다.

1. 요구사항 수집 및 분석: 업무 담당자 인터뷰, 기존 문서 분석, 업무 프로세스 관찰을 통해 데이터 요구사항을 파악합니다. "어떤 데이터가 필요한가", "데이터 간의 관계는 무엇인가", "데이터의 양은 얼마나 되는가", "어떤 조회가 빈번한가"를 중심으로 분석합니다.

2. 개체 및 속성 도출: 요구사항에서 명사를 추출하여 개체 후보를 나열합니다. 각 개체의 속성을 식별하고, 식별자(유일하게 구분하는 속성)를 결정합니다. 파생 속성(다른 속성에서 계산 가능한 값)의 저장 여부를 결정합니다.

3. 관계 정의: 개체 간의 업무적 관계를 정의합니다. 관계의 유형(1:1, 1:N, M:N), 필수/선택 여부(한 회원이 주문을 반드시 해야 하는가?), 관계 속성(관계 자체에 속하는 속성)을 결정합니다.

4. ERD 작성: 개체, 속성, 관계를 ERD로 표현합니다. 표기법은 Peter Chen, IE(Information Engineering), Barker 등 여러 가지가 있지만, 현업에서는 IE 표기법이 가장 널리 사용됩니다.

5. 릴레이션 변환: ER 모델을 관계형 모델로 변환합니다. 개체→테이블, 속성→컬럼, 관계→외래키로 변환하고, M:N 관계는 중간 테이블을 생성합니다.

6. 정규화: 함수적 종속성을 분석하여 테이블을 분해합니다. 1NF→2NF→3NF→BCNF 순서로 진행하며, 실무에서는 보통 3NF 또는 BCNF까지 적용합니다.

7. 물리적 설계: DBMS를 선정하고, 데이터 타입, 인덱스, 파티셔닝, 테이블스페이스 등을 결정합니다. 성능 테스트를 거쳐 필요 시 반정규화를 적용합니다.

8. 검증 및 리뷰: 완성된 모델을 업무 담당자, DBA, 개발자가 함께 검토합니다. 요구사항이 빠짐없이 반영되었는지, 구조적 문제가 없는지, 성능 이슈가 예상되지 않는지를 확인합니다.


개념적 모델링 심화 - 개체의 분류

개체는 그 성격에 따라 여러 유형으로 분류됩니다.

강한 개체(Strong Entity)는 다른 개체에 의존하지 않고 독립적으로 존재할 수 있는 개체입니다. 자체적인 기본키를 가집니다. 회원, 상품, 학과 등이 강한 개체입니다.

약한 개체(Weak Entity)는 단독으로는 식별할 수 없고, 다른 개체(소유 개체)에 의존하여 식별되는 개체입니다. 예를 들어, "강의실"은 "건물"에 종속됩니다. "101호"만으로는 유일하지 않고, "공학관 101호"처럼 건물과 결합해야 유일하게 식별됩니다.

강한 개체와 약한 개체
강한 개체              약한 개체
┌──────────┐           ╔═══════════╗
│  건  물  │           ║  강의실   ║  (이중선으로 표기)
│ #건물코드│───소속───▶║ #호실번호 ║
│  건물명  │           ║  수용인원 ║
└──────────┘           ╚═══════════╝

강의실의 PK = 건물코드(FK) + 호실번호(부분키)

연관 개체(Associative Entity)는 M:N 관계를 해소하기 위해 생성되는 개체입니다. 주문상세(OrderItem), 수강(Enrollment) 등이 대표적입니다. 연관 개체는 두 개체의 기본키를 외래키로 가지며, 관계 자체의 속성(수량, 성적 등)을 포함합니다.


속성의 분류

개체의 속성도 여러 기준으로 분류할 수 있습니다.

단순 속성(Simple Attribute)은 더 이상 분해할 수 없는 원자적 속성입니다. 나이, 이름 등이 해당합니다.

복합 속성(Composite Attribute)은 여러 하위 속성으로 분해할 수 있는 속성입니다. 주소를 시, 구, 동, 상세주소로 분해할 수 있다면 복합 속성입니다. 검색이나 정렬이 필요하면 분해하여 저장하는 것이 유리합니다.

유도 속성(Derived Attribute)은 다른 속성에서 계산할 수 있는 속성입니다. 생년월일에서 나이를 계산할 수 있으므로, 나이는 유도 속성입니다. 유도 속성은 저장하지 않는 것이 원칙이지만, 계산 비용이 높거나 조회 빈도가 높으면 저장하고 동기화하는 전략을 사용하기도 합니다.

다중값 속성(Multi-valued Attribute)은 하나의 개체에 대해 여러 값을 가질 수 있는 속성입니다. 한 사람이 여러 전화번호를 가질 수 있는 경우가 이에 해당합니다. 관계형 모델에서는 1NF를 만족시키기 위해 별도의 테이블로 분리합니다.

속성의 분류
속성 (Attribute)
├── 단순 속성: 이름, 나이, 학번
├── 복합 속성: 주소 → (시, 구, 동, 상세)
├── 유도 속성: 나이 (생년월일에서 계산)
├── 다중값 속성: 전화번호 (여러 개 가능)
└── 키 속성: 학번 (유일 식별)

실전 예제: 도서관 관리 시스템

좀 더 복잡한 예제를 통해 모델링 전 과정을 살펴보겠습니다.

도서관 관리 시스템 요구사항
1. 도서관에는 여러 도서가 소장되어 있다.
2. 같은 도서라도 여러 권이 있을 수 있다 (복본).
3. 회원은 도서를 대출하고 반납할 수 있다.
4. 한 회원이 동시에 대출할 수 있는 권수는 최대 5권이다.
5. 대출 기간은 14일이며, 1회 연장 가능하다.
6. 연체 중인 회원은 대출이 불가하다.
7. 도서는 분류코드(DDC)로 분류된다.
8. 회원은 도서에 대해 예약을 걸어둘 수 있다.
개체 식별
도서정보(BookInfo): #ISBN, 제목, 저자, 출판사, 출판연도, 분류코드
소장도서(BookCopy): #소장번호, 상태(대출가능/대출중/분실)
회원(Member):       #회원번호, 이름, 연락처, 가입일
대출(Loan):         #대출번호, 대출일, 반납예정일, 실반납일, 연장여부
예약(Reservation):  #예약번호, 예약일, 상태(대기중/취소/완료)
관계 분석
도서정보 ──1:N──→ 소장도서  (한 ISBN에 여러 복본)
회원    ──1:N──→ 대출     (한 회원이 여러 건 대출)
소장도서 ──1:N──→ 대출     (한 권의 책이 여러 번 대출됨)
회원    ──1:N──→ 예약     (한 회원이 여러 건 예약)
도서정보 ──1:N──→ 예약     (한 도서에 여러 예약)
논리적 모델 → 물리적 모델 (SQL)
CREATE TABLE book_info (
    isbn         CHAR(13)     PRIMARY KEY,
    title        VARCHAR(200) NOT NULL,
    author       VARCHAR(100) NOT NULL,
    publisher    VARCHAR(100),
    pub_year     YEAR,
    ddc_code     VARCHAR(20)
);

CREATE TABLE book_copies (
    copy_id      INT          AUTO_INCREMENT PRIMARY KEY,
    isbn         CHAR(13)     NOT NULL,
    status       ENUM('대출가능','대출중','분실') DEFAULT '대출가능',
    FOREIGN KEY (isbn) REFERENCES book_info(isbn)
);

CREATE TABLE members (
    member_id    INT          AUTO_INCREMENT PRIMARY KEY,
    name         VARCHAR(50)  NOT NULL,
    phone        VARCHAR(20),
    joined_at    DATE         DEFAULT (CURRENT_DATE)
);

CREATE TABLE loans (
    loan_id      INT          AUTO_INCREMENT PRIMARY KEY,
    member_id    INT          NOT NULL,
    copy_id      INT          NOT NULL,
    loaned_at    DATE         NOT NULL DEFAULT (CURRENT_DATE),
    due_date     DATE         NOT NULL,
    returned_at  DATE,
    extended     BOOLEAN      DEFAULT FALSE,
    FOREIGN KEY (member_id) REFERENCES members(member_id),
    FOREIGN KEY (copy_id) REFERENCES book_copies(copy_id)
);

CREATE TABLE reservations (
    reservation_id INT       AUTO_INCREMENT PRIMARY KEY,
    member_id      INT       NOT NULL,
    isbn           CHAR(13)  NOT NULL,
    reserved_at    DATETIME  DEFAULT CURRENT_TIMESTAMP,
    status         ENUM('대기중','취소','완료') DEFAULT '대기중',
    FOREIGN KEY (member_id) REFERENCES members(member_id),
    FOREIGN KEY (isbn) REFERENCES book_info(isbn)
);

이 예제에서 주목할 점은 도서정보(BookInfo)와 소장도서(BookCopy)를 분리한 것입니다. 같은 책을 5권 소장하고 있다면, 도서 정보(제목, 저자, ISBN)는 한 번만 저장하고, 각 권의 상태(대출 가능, 대출 중)는 소장도서 테이블에서 개별 관리합니다. 이것이 정규화의 효과입니다.


데이터 모델링은 단순히 테이블을 배치하는 작업이 아닙니다. 현실 세계의 복잡한 업무를 체계적으로 분석하고, 데이터의 의미와 관계를 명확히 정의하며, 미래의 변화에 유연하게 대응할 수 있는 구조를 만드는 지적 활동입니다. 좋은 데이터 모델은 수십 년간 안정적으로 비즈니스를 지탱하는 기반이 됩니다. 다음 절에서는 개체와 관계를 도식화하는 ER 모델을 다루겠습니다.

목차