안녕하세요, IT 개발을 공부하고 계신 여러분!
오늘은 데이터베이스(DB) 설계에서 꼭 알아야 할 ‘정규화(Normalization)와 역정규화(Denormalization)’에 대해 깊이 있게 다뤄보려고 해요.
이 개념들은 데이터베이스 성능과 유지보수성에 큰 영향을 미치기 때문에, 백엔드 개발자뿐만 아니라 데이터 엔지니어, DBA, 소프트웨어 엔지니어라면 반드시 이해하고 있어야 해요.
그럼, 하나씩 차근차근 살펴볼까요?
📌 데이터 정규화란?
정규화(Normalization)란 데이터 중복을 최소화하고, 데이터 무결성을 보장하는 과정이에요.
즉, 데이터를 체계적으로 정리하여 일관성과 정확성을 유지하는 방법이라고 할 수 있어요.
✅ 데이터 정규화의 장점
- 데이터 중복 감소: 스토리지 낭비 방지
- 데이터 무결성 유지: 일관된 데이터 보장
- 데이터 삽입, 수정, 삭제 이상(Anomaly) 방지
- 데이터 구조 단순화: 유지보수 용이
📌 정규화 과정 (정규형)
정규화는 1NF(제1정규형) → 2NF(제2정규형) → 3NF(제3정규형) → BCNF(보이스-코드 정규형) → 4NF(제4정규형) → 5NF(제5정규형) 순으로 진행돼요.
각 단계를 쉽게 설명해볼게요!
1️⃣ 제1정규형(1NF) - 원자성(Atomicity) 확보
“각 컬럼의 값이 더 이상 쪼갤 수 없는 단일 값이어야 한다!”
🚫 잘못된 예시
학생ID | 학생이름 | 수강과목 |
---|---|---|
101 | 김철수 | 데이터베이스, 운영체제 |
102 | 이영희 | 자료구조 |
👉 문제점: 한 개의 셀에 여러 개의 값이 들어가 있어요!
✅ 제1정규형 적용 (원자성 유지)
학생ID | 학생이름 | 수강과목 |
---|---|---|
101 | 김철수 | 데이터베이스 |
101 | 김철수 | 운영체제 |
102 | 이영희 | 자료구조 |
📌 SQL 예제와 함께 살펴볼게요!
✅ 1NF (제1정규형) - 원자성(Atomicity) 유지
“한 칸에는 하나의 값만!”
❌ 잘못된 테이블 구조 (여러 값이 한 칸에 들어감)
CREATE TABLE Students (
student_id INT PRIMARY KEY,
student_name VARCHAR(50),
subjects VARCHAR(100) -- 여러 개의 과목이 하나의 컬럼에 저장됨 (문제 발생)
);
✅ 제1정규형 적용 (각 값을 개별 행으로 저장)
CREATE TABLE Students (
student_id INT,
student_name VARCHAR(50),
subject VARCHAR(50),
PRIMARY KEY (student_id, subject) -- 복합키 사용
);
📌 수강 과목이 여러 개일 경우, 별도의 행으로 분리하여 저장!
2️⃣ 제2정규형(2NF) - 부분적 함수 종속 제거
“기본키의 일부분만으로 결정되는 컬럼 제거!”
🚫 잘못된 예시
학생ID | 과목ID | 학생이름 | 과목명 |
---|---|---|---|
101 | DB101 | 김철수 | 데이터베이스 |
102 | OS201 | 이영희 | 운영체제 |
👉 문제점:
- 학생이름은 학생ID에만 의존
- 과목명은 과목ID에만 의존
- 부분적 함수 종속 발생 (학생ID가 바뀌면 학생이름이 영향을 받음)
✅ 제2정규형 적용 (분리)
학생 테이블
학생ID | 학생이름 |
---|---|
101 | 김철수 |
102 | 이영희 |
과목 테이블
과목ID | 과목명 |
---|---|
DB101 | 데이터베이스 |
OS201 | 운영체제 |
수강 테이블 (관계 테이블 추가)
학생ID | 과목ID |
---|---|
101 | DB101 |
102 | OS201 |
📌 SQL 예제와 함께 살펴볼게요!
✅ 2NF (제2정규형) - 부분적 함수 종속 제거
“기본키의 일부만으로 결정되는 컬럼 제거!”
❌ 잘못된 테이블 구조 (학생 이름이 학생 ID에만 종속됨)
CREATE TABLE StudentCourses (
student_id INT,
student_name VARCHAR(50),
course_id INT,
course_name VARCHAR(50),
PRIMARY KEY (student_id, course_id) -- 복합키 사용
);
👉 문제점:
- student_name은 student_id만 있으면 결정됨 (부분적 함수 종속 발생)
✅ 제2정규형 적용 (학생과 과목을 별도 테이블로 분리)
CREATE TABLE Students (
student_id INT PRIMARY KEY,
student_name VARCHAR(50)
);
CREATE TABLE Courses (
course_id INT PRIMARY KEY,
course_name VARCHAR(50)
);
CREATE TABLE Student_Course (
student_id INT,
course_id INT,
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES Students(student_id),
FOREIGN KEY (course_id) REFERENCES Courses(course_id)
);
📌 이제 학생 정보와 과목 정보를 각각 관리할 수 있어요!
3️⃣ 제3정규형(3NF) - 이행적 함수 종속 제거
“기본키 이외의 컬럼이 또 다른 컬럼을 결정하면 안 된다!”
🚫 잘못된 예시
학생ID | 학생이름 | 학과ID | 학과명 |
---|---|---|---|
101 | 김철수 | CS | 컴퓨터공학 |
102 | 이영희 | EE | 전자공학 |
👉 문제점:
- 학과명은 학과ID로 결정되는데, 학생ID를 통해 학과ID를 결정할 수 있음!
✅ 제3정규형 적용
학생 테이블
학생ID | 학생이름 | 학과ID |
---|---|---|
101 | 김철수 | CS |
102 | 이영희 | EE |
학과 테이블
학과ID | 학과명 |
---|---|
CS | 컴퓨터공학 |
EE | 전자공학 |
✅ 3NF (제3정규형) - 이행적 함수 종속 제거
“기본키 외의 컬럼이 다른 컬럼을 결정하면 안 됨!”
❌ 잘못된 테이블 구조
CREATE TABLE Students (
student_id INT PRIMARY KEY,
student_name VARCHAR(50),
department_id INT,
department_name VARCHAR(50)
);
👉 문제점:
- department_name은 department_id에 의해 결정됨 (이행적 종속 발생)
✅ 제3정규형 적용 (학과 테이블 분리)
CREATE TABLE Departments (
department_id INT PRIMARY KEY,
department_name VARCHAR(50)
);
CREATE TABLE Students (
student_id INT PRIMARY KEY,
student_name VARCHAR(50),
department_id INT,
FOREIGN KEY (department_id) REFERENCES Departments(department_id)
);
📌 이제 학과 정보가 변경되더라도 한 곳에서만 수정하면 돼요!
📌 데이터 역정규화란?
🚀 역정규화(Denormalization)는 정규화를 거친 데이터를 의도적으로 일부 중복시켜 성능을 최적화하는 과정이에요.
✅ 역정규화의 필요성
- JOIN 연산이 많아 속도가 느려질 때
- 읽기(Read) 성능을 높여야 할 때
- 데이터 일관성보다 성능이 더 중요할 때
✅ 역정규화 방법
- 컬럼 중복 저장 (예: 학과 테이블 없이 학생 테이블에 학과명 포함)
- 중간 테이블 제거 (예: 수강 테이블 없이 학생 테이블에 직접 과목 정보 포함)
- 데이터 병합 (예: 여러 테이블의 정보를 하나의 테이블로 합치기)
✅ 역정규화 예제 - 데이터 중복 허용하여 조회 성능 향상
기존 정규화된 구조 (JOIN이 많음)
SELECT s.student_name, c.course_name
FROM Students s
JOIN Student_Course sc ON s.student_id = sc.student_id
JOIN Courses c ON sc.course_id = c.course_id
WHERE s.student_id = 101;
👉 조인이 많아 성능 저하 가능성 있음!
✅ 역정규화 적용 (과목명을 직접 저장, JOIN 줄이기)
CREATE TABLE StudentsWithCourses (
student_id INT PRIMARY KEY,
student_name VARCHAR(50),
enrolled_courses TEXT -- 여러 과목을 쉼표로 구분하여 저장
);
INSERT INTO StudentsWithCourses VALUES (101, '김철수', '데이터베이스, 운영체제');
👉 이제 하나의 테이블에서 바로 조회 가능!
SELECT student_name, enrolled_courses FROM StudentsWithCourses WHERE student_id = 101;
📌 데이터 중복이 발생할 수 있지만, 조회 성능이 확실히 향상됨!
📌 제4정규형(4NF) - 다치 종속(Multi-Valued Dependency) 제거
✅ 문제: 한 테이블에 여러 개의 독립적인 다대다 관계가 존재하면 비효율 발생
- 한 개의 기본키가 여러 개의 값을 가질 수 있을 때 발생하는 문제
- 예를 들어, 한 학생이 여러 개의 전화번호와 여러 개의 수강 과목을 가질 수 있는 경우
❌ 잘못된 테이블 구조 (3NF까지만 적용한 상태)
CREATE TABLE StudentInfo (
student_id INT PRIMARY KEY,
student_name VARCHAR(50),
phone_number VARCHAR(20), -- 학생당 여러 개의 전화번호 가능
course_name VARCHAR(50) -- 학생당 여러 개의 과목 가능
);
✅ 제4정규형 적용 (다치 종속을 분리)
CREATE TABLE StudentPhones (
student_id INT,
phone_number VARCHAR(20),
PRIMARY KEY (student_id, phone_number),
FOREIGN KEY (student_id) REFERENCES Students(student_id)
);
CREATE TABLE StudentCourses (
student_id INT,
course_name VARCHAR(50),
PRIMARY KEY (student_id, course_name),
FOREIGN KEY (student_id) REFERENCES Students(student_id)
);
📌 이제 학생의 전화번호와 수강 과목을 각각 독립적인 테이블로 관리할 수 있어요! 동일한 학생 ID가 중복되어 저장되지 않아 관리하기 쉬움
📌 제5정규형(5NF) - 조인 종속(Join Dependency) 제거
✅ 문제: 테이블을 나눴을 때 다시 조인할 경우 데이터가 손실될 수 있음
조인 종속(Join Dependency)이란?
- 데이터를 여러 개의 테이블로 나누었을 때, 나중에 다시 조인하면 원래 데이터를 완벽하게 재구성할 수 없는 경우에 발생하는 문제예요.
- 3개 이상의 테이블이 관계를 맺고 있을 때 발생할 가능성이 높아요.
- 일반적인 정규화 과정에서는 잘 드러나지 않지만, 복잡한 관계형 데이터 모델에서 중요할 수 있음!
❌ 잘못된 테이블 구조 (4NF까지만 적용한 상태)
CREATE TABLE EmployeeProjects (
employee_id INT,
project_id INT,
role_id INT,
PRIMARY KEY (employee_id, project_id, role_id)
);
👉 문제점:
- 직원(Employee)은 여러 개의 프로젝트(Project)에 참여할 수 있음
- 프로젝트는 여러 개의 역할(Role)이 필요함
- 직원-프로젝트-역할 간의 3자 관계가 복잡해지면서 데이터 중복 가능성 발생!
✅ 제5정규형 적용 (각 관계를 별도 테이블로 분리)
CREATE TABLE Employees (
employee_id INT PRIMARY KEY,
employee_name VARCHAR(50)
);
CREATE TABLE Projects (
project_id INT PRIMARY KEY,
project_name VARCHAR(50)
);
CREATE TABLE Roles (
role_id INT PRIMARY KEY,
role_name VARCHAR(50)
);
CREATE TABLE Employee_Project (
employee_id INT,
project_id INT,
PRIMARY KEY (employee_id, project_id),
FOREIGN KEY (employee_id) REFERENCES Employees(employee_id),
FOREIGN KEY (project_id) REFERENCES Projects(project_id)
);
CREATE TABLE Project_Role (
project_id INT,
role_id INT,
PRIMARY KEY (project_id, role_id),
FOREIGN KEY (project_id) REFERENCES Projects(project_id),
FOREIGN KEY (role_id) REFERENCES Roles(role_id)
);
📌 이제 직원, 프로젝트, 역할 간의 복잡한 관계를 최적화할 수 있어요!
👉 데이터 중복 없이, 관계를 보다 깔끔하게 유지 가능!
📌 정규화 단계 요약표 (1NF~5NF)
정규형 | 핵심 개념 | 해결하는 문제 | SQL 예제 |
---|---|---|---|
1NF | 원자성 유지 | 한 컬럼에 여러 개의 값이 있는 경우 (ex. 과목1, 과목2 ) |
개별 행으로 분리 |
2NF | 부분적 함수 종속 제거 | 기본키 일부에만 종속된 컬럼 (ex. 학생이름 이 학생ID 에만 종속) |
테이블 분리 후 관계 정의 |
3NF | 이행적 종속 제거 | 기본키가 아닌 컬럼이 또 다른 컬럼을 결정하는 경우 | 독립적인 테이블로 분리 |
BCNF | 엄격한 3NF | 복합키를 사용할 때 종속성 문제 | 슈퍼키를 활용한 테이블 분리 |
4NF | 다치 종속 제거 | 하나의 테이블에 여러 개의 독립적인 다대다 관계가 존재할 때 | 다치 종속을 별도 테이블로 분리 |
5NF | 조인 종속 제거 | 여러 테이블을 조인할 때 원본 데이터를 완벽히 복원할 수 없는 경우 | 관계를 더 세분화하여 테이블 구성 |
📌 정규화 vs 역정규화 - 언제 사용할까?
기준 | 정규화 | 역정규화 |
---|---|---|
목적 | 데이터 무결성 유지, 중복 제거 | 성능 최적화, 빠른 조회 |
장점 | 데이터 일관성 유지, 저장공간 절약 | 읽기 속도 향상, JOIN 감소 |
단점 | 복잡한 JOIN, 성능 저하 가능 | 데이터 중복 증가, 수정 어려움 |
사용 예시 | 트랜잭션이 많은 시스템 (은행, ERP 등) | 검색·조회가 많은 시스템 (SNS, 데이터 분석 등) |
💡 즉, 무조건 정규화 vs 역정규화가 정답이 아니에요!
👉 데이터 특성에 따라 적절한 설계를 하는 것이 가장 중요합니다.
📌 결론 및 정리
- ✅ 정규화(Normalization): 데이터 중복을 줄이고 무결성을 유지하는 필수 과정!
- ✅ 역정규화(Denormalization): 조회 속도를 높이기 위해 데이터 중복을 일부 허용하는 전략!
- ✅ 제4정규형, 제5정규형까지 존재하지만, 대부분의 실무에서는 3NF 또는 BCNF까지만 적용하는 경우가 많음!
- ✅ SQL 예제와 함께 본 것처럼, 각각의 특성을 이해하고 실무에서 적절히 적용하는 것이 중요합니다.
💡 여러분은 어떤 방식으로 DB 설계를 하고 계신가요?
💬 의견을 댓글로 남겨주세요! 😊
오늘도 끝까지 읽어주셔서 감사합니다! 🚀
'컴퓨터과학' 카테고리의 다른 글
강화 학습, 이렇게 활용된다! | 구글, 테슬라, 아마존, JP모건 등 글로벌 기업들의 AI 적용 사례 및 강화학습 요약정리 (1) | 2025.02.10 |
---|---|
OLAP vs OLTP: 차이점과 활용 사례 완벽 정리! (1) | 2025.02.09 |
데이터 정규화와 역정규화! 데이터베이스 최적화의 핵심 개념 완벽 정리 (1) | 2025.02.03 |
CAP 이론과 BASE 원칙: 분산 시스템의 핵심 개념 완벽 정리 (0) | 2025.02.03 |
데이터베이스 복제(Master-Slave, Multi-Master) – 성능과 가용성을 높이는 필수 기술 (1) | 2025.02.01 |