DB 정규화 완벽 가이드: 1NF부터 3NF까지 실전 예제로 배우기

데이터베이스 정규화를 실전 예제로 단계별로 배웁니다. 1NF, 2NF, 3NF를 적용해 데이터 중복을 제거하고 무결성을 보장하는 방법을 알아봅니다.

럿지 AI 팀
6분 읽기

DB 정규화 완벽 가이드



정규화란?



**정의:**
데이터 중복을 최소화하고 무결성을 보장하기 위해 테이블을 분해하는 과정

**목적:**
- 데이터 중복 제거
- 이상 현상(Anomaly) 방지
- 데이터 무결성 유지

정규화 전 문제 사례



비정규화 테이블



**주문 테이블:**
``
| 주문ID | 고객명 | 고객주소 | 상품명 | 상품가격 | 수량 |
|--------|--------|----------|--------|----------|------|
| 1 | 홍길동 | 서울 | 노트북,마우스 | 1000000,50000 | 1,2 |
| 2 | 김철수 | 부산 | 키보드 | 80000 | 1 |
| 3 | 홍길동 | 서울 | 모니터 | 300000 | 1 |
`

발생하는 문제



**1. 삽입 이상:**
- 상품만 등록하려면? 주문 없이는 불가능

**2. 갱신 이상:**
- 홍길동의 주소가 바뀌면? 모든 행을 수정해야 함

**3. 삭제 이상:**
- 주문 1을 삭제하면? 노트북/마우스 정보도 사라짐

**4. 다중값:**
- 한 셀에 여러 값 (노트북,마우스)

1단계: 제1정규형 (1NF)



규칙



**원자값(Atomic Value):**
- 모든 속성은 원자값만 가져야 함
- 반복되는 그룹 제거

적용



**Before:**
`
| 주문ID | 고객명 | 고객주소 | 상품명 | 상품가격 | 수량 |
|--------|--------|----------|--------|----------|------|
| 1 | 홍길동 | 서울 | 노트북,마우스 | 1000000,50000 | 1,2 |
`

**After (1NF):**
`
| 주문ID | 고객명 | 고객주소 | 상품명 | 상품가격 | 수량 |
|--------|--------|----------|--------|----------|------|
| 1 | 홍길동 | 서울 | 노트북 | 1000000 | 1 |
| 1 | 홍길동 | 서울 | 마우스 | 50000 | 2 |
| 2 | 김철수 | 부산 | 키보드 | 80000 | 1 |
| 3 | 홍길동 | 서울 | 모니터 | 300000 | 1 |
`

MySQL 구현



`sql
CREATE TABLE orders_1nf (
order_id INT,
customer_name VARCHAR(50),
customer_address VARCHAR(100),
product_name VARCHAR(100),
product_price DECIMAL(10,2),
quantity INT,
PRIMARY KEY (order_id, product_name)
);
`

여전히 남은 문제



- 고객 정보 중복 (홍길동의 주소가 3번 반복)
- 갱신 이상 여전히 존재

2단계: 제2정규형 (2NF)



규칙



**부분 함수 종속 제거:**
- 기본키의 일부에만 종속되는 속성 제거
- 완전 함수 종속만 허용

함수 종속성 분석



**1NF 테이블의 종속성:**
- (주문ID, 상품명) → 수량 ✓ (완전 함수 종속)
- (주문ID, 상품명) → 고객명 ✗ (부분 함수 종속, 주문ID만으로 결정)
- (주문ID, 상품명) → 고객주소 ✗
- (주문ID, 상품명) → 상품가격 ✗ (부분 함수 종속, 상품명만으로 결정)

적용



**분해:**
테이블을 3개로 분리

**테이블 1: 주문 (orders)**
`
| 주문ID | 고객명 | 고객주소 |
|--------|--------|----------|
| 1 | 홍길동 | 서울 |
| 2 | 김철수 | 부산 |
| 3 | 홍길동 | 서울 |
`

**테이블 2: 상품 (products)**
`
| 상품명 | 상품가격 |
|--------|----------|
| 노트북 | 1000000 |
| 마우스 | 50000 |
| 키보드 | 80000 |
| 모니터 | 300000 |
`

**테이블 3: 주문상세 (order_items)**
`
| 주문ID | 상품명 | 수량 |
|--------|--------|------|
| 1 | 노트북 | 1 |
| 1 | 마우스 | 2 |
| 2 | 키보드 | 1 |
| 3 | 모니터 | 1 |
`

MySQL 구현



`sql
-- 주문
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_name VARCHAR(50),
customer_address VARCHAR(100)
);

-- 상품
CREATE TABLE products (
product_name VARCHAR(100) PRIMARY KEY,
product_price DECIMAL(10,2)
);

-- 주문 상세
CREATE TABLE order_items (
order_id INT,
product_name VARCHAR(100),
quantity INT,
PRIMARY KEY (order_id, product_name),
FOREIGN KEY (order_id) REFERENCES orders(order_id),
FOREIGN KEY (product_name) REFERENCES products(product_name)
);
`

개선된 점



- 상품 정보 중복 제거
- 상품 가격 변경 시 한 곳만 수정

여전히 남은 문제



- 고객 정보 중복 (홍길동이 여러 주문)
- 고객 주소 변경 시 여러 행 수정 필요

3단계: 제3정규형 (3NF)



규칙



**이행 함수 종속 제거:**
- A → B, B → C이면 A → C (이행 종속)
- 모든 속성이 기본키에만 종속

함수 종속성 분석



**orders 테이블:**
- 주문ID → 고객명 ✓
- 주문ID → 고객주소 ✗ (이행 종속: 주문ID → 고객명 → 고객주소)

적용



**테이블 1: 주문 (orders)**
`
| 주문ID | 고객ID |
|--------|--------|
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
`

**테이블 2: 고객 (customers)**
`
| 고객ID | 고객명 | 고객주소 |
|--------|--------|----------|
| 1 | 홍길동 | 서울 |
| 2 | 김철수 | 부산 |
`

**테이블 3: 상품 (products)**
`
| 상품ID | 상품명 | 상품가격 |
|--------|--------|----------|
| 1 | 노트북 | 1000000 |
| 2 | 마우스 | 50000 |
| 3 | 키보드 | 80000 |
| 4 | 모니터 | 300000 |
`

**테이블 4: 주문상세 (order_items)**
`
| 주문ID | 상품ID | 수량 |
|--------|--------|------|
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 2 | 3 | 1 |
| 3 | 4 | 1 |
`

MySQL 구현 (최종)



`sql
-- 고객
CREATE TABLE customers (
customer_id INT AUTO_INCREMENT PRIMARY KEY,
customer_name VARCHAR(50) NOT NULL,
customer_address VARCHAR(100)
);

-- 주문
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

-- 상품
CREATE TABLE products (
product_id INT AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
product_price DECIMAL(10,2) NOT NULL CHECK (product_price >= 0)
);

-- 주문 상세
CREATE TABLE order_items (
order_item_id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL CHECK (quantity > 0),
FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(product_id),
UNIQUE KEY unique_order_product (order_id, product_id)
);
`

정규화 전후 비교



Before (비정규화)



**문제:**
- 데이터 중복 심각
- 고객 주소 변경 시 모든 주문 수정
- 상품 가격 변경 시 모든 주문 상세 수정

After (3NF)



**개선:**
- 데이터 중복 제거
- 고객 주소 변경: customers 테이블 1행만 수정
- 상품 가격 변경: products 테이블 1행만 수정

실전 쿼리



주문 내역 조회



`sql
SELECT
o.order_id,
c.customer_name,
c.customer_address,
p.product_name,
p.product_price,
oi.quantity,
(p.product_price * oi.quantity) AS subtotal
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.order_id = 1;
`

고객별 총 주문 금액



`sql
SELECT
c.customer_name,
SUM(p.product_price * oi.quantity) AS total_amount
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
GROUP BY c.customer_id, c.customer_name;
``

핵심 포인트



1. 정규화 목적



**중복 제거:**
데이터 일관성 유지

**이상 현상 방지:**
삽입/갱신/삭제 이상 제거

2. 실무 적용



**3NF까지:**
대부분의 경우 3NF면 충분

**BCNF 이상:**
특수한 경우에만 필요

3. 반정규화



**성능 vs 정규화:**
필요 시 선택적 반정규화

**예:**
통계 테이블 (집계 데이터)

더 배우기



이 가이드는 기본 정규화를 다룹니다.

**심화 학습:**
김영한의 실전 데이터베이스
- BCNF, 4NF
- 반정규화 전략
- 실전 프로젝트

---

**태그**: #DB정규화 #1NF #2NF #3NF #튜토리얼 #데이터베이스

L

럿지 AI 팀

AI 기술과 비즈니스 혁신을 선도하는 럿지 AI의 콘텐츠 팀입니다.