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의 콘텐츠 팀입니다.
관련 포스트
비즈니스
탄탄한 DB 아키텍처로 시리즈 B 200억 투자 유치 성공
VC들이 가장 주목한 기술 경쟁력, 확장 가능한 DB 설계로 기업 가치 3배 인정받은 스타트업 사례입니다.
•3분 읽기
비즈니스
DB 설계로 스타트업 스케일링 성공: 유저 10배 증가 무장애 운영
처음부터 제대로 된 DB 설계로 유저 100만 돌파까지 리팩토링 없이 성공한 스타트업 사례입니다.
•4분 읽기
비즈니스
DB 최적화로 연간 서버 비용 12억 절감: 클라우드 비용 80% 감소
올바른 DB 설계와 최적화로 AWS 비용을 80% 절감하고 연간 12억을 아낀 중견 기업 사례입니다.
•4분 읽기