본문 바로가기
컴퓨터과학

데이터 정규화 vs 역정규화 완벽 정리 | DB 성능 최적화 핵심 가이드!

by 코드그래피 2025. 2. 8.
반응형

안녕하세요, 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_namestudent_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_namedepartment_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 설계를 하고 계신가요?

💬 의견을 댓글로 남겨주세요! 😊

오늘도 끝까지 읽어주셔서 감사합니다! 🚀

반응형