ERD 설계 (정규화, 외래키, 무결성)
데이터베이스의 모든 사고는 첫 단추인 ERD 설계에서 시작된다. 키를 잘못 잡으면 정규화가 깨지고, 외래 키 관계가 흩어지면 무결성이 무너지며, 결국 운영 단계에서 끝없는 데이터 정합성 문제로 이어진다. 이 글은 정보처리기사 필기에서 다룬 "DB 설계 3단계"의 다음 단계로, ERD 설계의 실전 기술 — 키의 종류, 관계의 모델링, 식별 관계 vs 비식별 관계, 무결성 제약, 그리고 정규화 심화까지 — 를 세심하게 정리한다(출처: 위키백과 — Entity-Relationship Model). 제가 학교 캡스톤에서 사용자 ID를 char(20)로 잡고 외래 키를 7개 테이블에 박았다가 한 달 뒤 ID 정책이 바뀌어 7개 테이블을 한꺼번에 마이그레이션하는 사고를 겪고 나서야 "키 설계 한 줄의 결정이 6개월 뒤 운영 비용을 결정한다"는 사실을 손끝으로 받아들였다.

데이터베이스 키의 종류와 선택 기준
키(Key)는 한 테이블의 행을 식별하거나 다른 테이블과 연결하는 핵심 컬럼이다. 시험과 실무 모두에서 자주 출제되는 키의 종류를 한 표로 정리하면 다음과 같다.
| 키 | 의미 | 특징 |
|---|---|---|
| 슈퍼키 (Super Key) | 행을 유일하게 식별하는 컬럼들의 집합 | 후보키의 상위 개념 |
| 후보키 (Candidate Key) | 슈퍼키 중 최소 단위 | 여러 개 존재 가능 |
| 기본키 (Primary Key, PK) | 후보키 중 선택된 대표 | NULL 불가, 테이블당 1개 |
| 대체키 (Alternate Key) | PK로 선택되지 않은 나머지 후보키 | UNIQUE 인덱스 |
| 외래키 (Foreign Key, FK) | 다른 테이블의 PK를 참조 | 무결성 제약의 핵심 |
| 자연키 (Natural Key) | 비즈니스 의미가 있는 키 (주민번호·ISBN) | 의미는 있으나 변경 위험 |
| 대리키 (Surrogate Key) | 시스템이 부여한 의미 없는 키 | AUTO_INCREMENT·UUID |
여기서 자연키 vs 대리키의 선택이 ERD 설계에서 가장 자주 마주치는 결정이며, 한 번 잘못 결정하면 되돌리기 어려운 영구적인 비용으로 이어진다. 자연키는 의미가 직관적이지만 비즈니스 정책 변경(예: 주민번호 → 외국인등록번호 변경)에 취약하고, 대리키는 의미가 없지만 변경 불변성이 보장된다. 솔직히 제 경험상 학교 동아리 프로젝트에서 학번을 PK로 잡았다가 졸업생 처리 정책이 바뀌어 한참을 헤맨 후로는, 거의 모든 PK는 대리키로 두고 자연키는 UNIQUE 인덱스로 별도 보호하는 패턴을 자동으로 택하게 되었다.
-- 자연키만 사용 (비추천)
CREATE TABLE student_v1 (
student_id VARCHAR(20) PRIMARY KEY, -- 학번을 PK로 (정책 변경 위험)
name VARCHAR(30) NOT NULL
);
-- 대리키 + 자연키 UNIQUE (권장)
CREATE TABLE student_v2 (
id BIGINT PRIMARY KEY AUTO_INCREMENT, -- 대리키 (변경 없음)
student_no VARCHAR(20) NOT NULL UNIQUE, -- 자연키는 UNIQUE 인덱스
name VARCHAR(30) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
관계의 모델링 — 1:1, 1:N, M:N과 식별·비식별
ERD의 관계(Relationship)는 두 엔티티 사이의 연결 양식을 표현하며, 카디널리티(cardinality)에 따라 세 가지로 분류된다. 여기서 카디널리티란 한 엔티티의 한 인스턴스가 다른 엔티티의 몇 개 인스턴스와 연결되는지를 나타내는 수치 관계를 가리킨다.
| 관계 | 표현 방법 | 예시 |
|---|---|---|
| 1:1 | 한쪽 테이블에 FK + UNIQUE | 사람-여권 |
| 1:N | N 쪽 테이블에 FK | 부서-직원 |
| M:N | 연결 테이블(Junction Table) 필수 | 학생-수강과목 |
M:N 관계는 관계형 모델에서 직접 표현할 수 없으므로, 두 PK를 합친 새 테이블을 만들어 두 개의 1:N 관계로 분해해야 한다. 이 연결 테이블에 추가 속성(수강 학기·성적 등)을 둘 수 있다는 점이 정규화 측면에서 자주 활용된다.
-- M:N 분해 — 학생-수강과목 예시
CREATE TABLE student (
id BIGINT PRIMARY KEY,
name VARCHAR(30)
);
CREATE TABLE course (
id BIGINT PRIMARY KEY,
title VARCHAR(50)
);
CREATE TABLE enrollment ( -- 연결 테이블
student_id BIGINT,
course_id BIGINT,
semester VARCHAR(10),
grade CHAR(2),
PRIMARY KEY (student_id, course_id, semester), -- 합성키
FOREIGN KEY (student_id) REFERENCES student(id),
FOREIGN KEY (course_id) REFERENCES course(id)
);
또 한 가지 시험에서 자주 출제되는 분류가 식별 관계(Identifying Relationship)와 비식별 관계(Non-Identifying Relationship)이다.
| 분류 | 자식의 PK 구성 | 의미 | 표기 |
|---|---|---|---|
| 식별 관계 | 부모 PK가 자식 PK의 일부 | 자식은 부모 없이 존재 불가 | 실선 |
| 비식별 관계 | 부모 PK를 자식의 일반 FK로 보유 | 자식은 부모 없이도 식별 가능 | 점선 |
여기서 식별 관계란 자식 엔티티가 자기 자신만으로는 식별할 수 없고 부모 엔티티의 PK를 자기 PK의 일부로 포함해야 식별되는 관계를 가리키며, 주문-주문상세, 게시글-댓글처럼 부모와 자식이 "강한 종속"을 가질 때 자연스럽다. 대부분의 단순한 1:N(부서-직원)은 비식별 관계로 모델링되며, 식별 관계는 합성키가 깊어지는 부담이 있어 신중히 선택해야 한다.
무결성 제약과 정규화 심화
데이터의 일관성을 보장하기 위한 무결성 제약(Integrity Constraint)은 네 가지로 분류된다. 시험에서 매회 출제되는 핵심이다.
| 무결성 | 의미 | DBMS 강제 도구 |
|---|---|---|
| 개체 무결성 (Entity) | PK는 NULL일 수 없음 | PRIMARY KEY 제약 |
| 참조 무결성 (Referential) | FK 값은 부모 PK에 존재해야 함 | FOREIGN KEY 제약 |
| 도메인 무결성 (Domain) | 컬럼 값이 정의된 도메인 안 | CHECK·NOT NULL·DEFAULT |
| 사용자 정의 무결성 | 비즈니스 규칙 | TRIGGER·STORED PROCEDURE |
특히 참조 무결성은 삭제·갱신 시 어떻게 처리할지를 명시할 수 있으며, 다섯 가지 옵션이 시험에서 자주 출제된다. CASCADE(연쇄 삭제/갱신), SET NULL(NULL로 변경), SET DEFAULT(기본값으로 변경), NO ACTION(거부, 기본값), RESTRICT(즉시 거부)이다.
CREATE TABLE order_item (
id BIGINT PRIMARY KEY,
order_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT CHECK (quantity > 0), -- 도메인 무결성
FOREIGN KEY (order_id) REFERENCES orders(id)
ON DELETE CASCADE, -- 주문 지우면 항목도 함께
FOREIGN KEY (product_id) REFERENCES product(id)
ON DELETE RESTRICT -- 상품 삭제 시 항목 있으면 거부
);
정규화 심화에서 시험과 실무 모두에서 자주 출제되는 것이 반정규화(Denormalization)의 의도적 적용이다(출처: 위키백과 — Database normalization). 모든 테이블을 BCNF로 정규화하는 것이 데이터 무결성 측면에서는 이상적이지만, 조인 비용이 폭증하면 운영 환경에서 성능이 무너진다. 따라서 다음 세 가지 신호가 보이면 반정규화를 검토한다.
- 빈번한 조회 + 드문 갱신 — 사용자 프로필 화면처럼 매번 5~6개 테이블을 조인해야 한다면 핵심 컬럼을 한 테이블로 통합
- 집계 결과의 반복 계산 — 매번 SUM·COUNT를 돌리지 말고 별도 집계 테이블에 미리 저장
- 읽기 전용 영역 — 보고서·대시보드처럼 데이터를 변경하지 않는 영역에는 비정규화된 분석용 테이블을 별도 운영
솔직히 이건 예상 밖이었는데, 학교에서는 "BCNF가 정답"으로 배웠다가 인턴십에서 본 실제 운영 DB의 핵심 테이블 대부분이 의도적으로 비정규화되어 있다는 사실을 알고 나서는, "정규화는 출발점이고 반정규화는 측정 기반의 보완"이라는 한 줄을 손끝으로 받아들였다. 마지막으로 시험 답안에서 자주 쓰이는 정형 표현을 정리하면, "ERD 설계의 핵심은 키 선택(자연키 vs 대리키)·관계 모델링(1:1·1:N·M:N의 연결 테이블 분해)·무결성 제약(개체·참조·도메인·사용자 정의) 세 가지이며, 모든 PK는 가능한 한 대리키로 두고 자연키는 UNIQUE 인덱스로 별도 보호하는 패턴이 표준이다"는 두 문장이 표준 답안 표현으로 통한다.
메타 디스크립션: ERD 설계의 핵심인 키의 종류(슈퍼·후보·기본·외래·자연·대리키) 선택 기준, 관계 모델링(1:1·1:N·M:N과 식별·비식별), 무결성 제약 4종, 그리고 정규화 심화와 반정규화 트레이드오프를 DB 입문자 관점에서 세심하게 정리합니다.