topic★★★★★난이도 · 약 20분
정규화 · 관계(1:N·N:M) · ERD
1NF/2NF/3NF · 외래키 · 1:N과 N:M · 반정규화 트레이드오프 — 스키마 설계의 뼈대.
#정규화#1NF#2NF#3NF#ERD#N:M#외래키
왜 배우는가
AI에게 "DB 스키마 짜줘"라고 하면 잘못된 정규화로 중복·이상(anomaly)이 생기기 쉽다. 1:N·N:M을 구분할 줄 알면 엉성한 스키마를 바로잡을 수 있다.
정규화는 중복을 제거해 이상(anomaly)을 막는 설계 원칙. 삽입·갱신·삭제 시 모순이 나지 않게 테이블을 적절히 쪼갠다.
| 단계 | 규칙 | 어기면? |
|---|---|---|
| 1NF | 한 칸에 값 하나 (원자성) | `tags = "js,ts"` 같은 CSV 지옥 |
| 2NF | 1NF + 부분 함수 종속 제거 | 복합키 일부에만 의존 |
| 3NF | 2NF + 이행 종속 제거 | 비키 속성이 다른 비키에 의존 |
| 실무 | 3NF까지만 기본 | 그 이상은 보통 과도함 |
sql
-- ❌ 비정규화 (나쁜 예)
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_name TEXT, -- 고객 정보 중복 저장
customer_email TEXT, -- 고객이 이메일 바꾸면 모든 주문 업데이트
items TEXT -- "책|펜|노트" CSV 지옥
);
-- ✅ 정규화된 설계
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer_id INT NOT NULL REFERENCES customers(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE order_items ( -- 1:N (주문 1개 → 아이템 N개)
id SERIAL PRIMARY KEY,
order_id INT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
product_id INT NOT NULL REFERENCES products(id),
quantity INT NOT NULL
);ON DELETE CASCADE — 주문 삭제 시 관련 아이템도 자동 삭제. 참조 무결성 유지의 표준 도구.
| 관계 | 예시 | 스키마 표현 |
|---|---|---|
| 1:1 | 사용자-프로필 | `user_profiles.user_id UNIQUE REFERENCES users(id)` |
| 1:N | 유저-주문 | `orders.user_id REFERENCES users(id)` |
| N:M | 학생-수업 | 조인 테이블 (`enrollments(student_id, course_id)`) |
sql
-- N:M 관계 (학생 ↔ 수업) — 조인 테이블 필수
CREATE TABLE students (id SERIAL PK, name TEXT);
CREATE TABLE courses (id SERIAL PK, title TEXT);
CREATE TABLE enrollments (
student_id INT REFERENCES students(id),
course_id INT REFERENCES courses(id),
enrolled_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (student_id, course_id) -- 복합키 = 중복 수강 방지
);
-- "학생 A가 듣는 모든 수업"
SELECT c.title
FROM students s
JOIN enrollments e ON s.id = e.student_id
JOIN courses c ON c.id = e.course_id
WHERE s.id = ?;"한 학생은 여러 수업, 한 수업은 여러 학생" — 이 둘을 합친 게 N:M. 조인 테이블 없이 CSV로 저장하면 확장 시 지옥.
반정규화(Denormalization) — 조회 성능을 위해 일부러 중복 저장. 예: `users.post_count`를 미리 저장. 장점: JOIN·COUNT 불필요. 단점: 업데이트 시 동기화 책임. 초반엔 정규화, 느려지면 선택적 반정규화.
ERD(Entity-Relationship Diagram)는 테이블·관계를 그림으로. Claude에게 "이 요구사항의 ERD 그려줘" → Mermaid나 ASCII로 받을 수 있다. 초기 설계 리뷰에 필수.
text
# Mermaid ERD (Claude가 자주 뽑는 포맷)
erDiagram
users ||--o{ orders : "places"
orders ||--|{ order_items : "contains"
products ||--o{ order_items : "included in"
users { int id PK; text name; text email }
orders { int id PK; int user_id FK; timestamp created_at }
order_items { int id PK; int order_id FK; int product_id FK; int qty }
products { int id PK; text name; numeric price }`||--o{` = 1:N, `}o--o{` = N:M, PK = 기본키, FK = 외래키. GitHub·Notion에서 그대로 렌더링.
실기 드릴 2문항
edit실기 드릴 · 단답형
학생과 수업처럼 '다대다' 관계를 표현할 때 필요한 별도 테이블의 이름은?
check_circle실기 드릴 · OX
반정규화는 항상 나쁜 설계다.