정규화(Normalization)는 관계형 데이터베이스 설계에서 데이터 중복을 최소화하고 데이터 무결성을 보장하기 위한 과정이다.


이상 현상 (Anomaly)

정규화되지 않은 테이블에서 발생하는 데이터 불일치 문제

예시 테이블 - 수강 정보

학번 학생명 학과 과목코드 과목명 교수
1001 김철수 컴퓨터공학 CS101 자료구조 이교수
1001 김철수 컴퓨터공학 CS102 알고리즘 박교수
1002 이영희 전자공학 CS101 자료구조 이교수
1003 박민수 컴퓨터공학 CS103 데이터베이스 최교수

1. 삽입 이상 (Insertion Anomaly)

새로운 과목 “운영체제(CS104)”를 추가하고 싶지만, 아직 수강생이 없어서 학번, 학생명이 NULL이 되어야 함.

-- 학번이 PK의 일부라면 삽입 불가능!
INSERT INTO 수강정보 (학번, 학생명, 학과, 과목코드, 과목명, 교수)
VALUES (NULL, NULL, NULL, 'CS104', '운영체제', '정교수');

2. 삭제 이상 (Deletion Anomaly)

박민수(1003)가 수강 취소하면 “데이터베이스” 과목 정보도 함께 삭제됨.

-- 박민수 삭제 시 CS103 과목 정보도 사라짐!
DELETE FROM 수강정보 WHERE 학번 = 1003;

3. 갱신 이상 (Update Anomaly)

“자료구조” 담당 교수가 변경되면 여러 행을 수정해야 함. 일부만 수정하면 데이터 불일치 발생.

-- 1001번 행만 수정하면 1002번 행과 불일치!
UPDATE 수강정보 SET 교수 = '김교수'
WHERE 학번 = 1001 AND 과목코드 = 'CS101';

-- 결과: 같은 과목인데 교수가 다름 (데이터 모순)

함수적 종속 (Functional Dependency)

정규화를 이해하기 위한 핵심 개념

정의

X → Y : X 값이 Y 값을 유일하게 결정한다.

학번 → 학생명        (학번이 결정되면 학생명이 결정됨)
과목코드 → 과목명    (과목코드가 결정되면 과목명이 결정됨)
과목코드 → 교수      (과목코드가 결정되면 담당교수가 결정됨)

종류

종류 설명 예시
완전 함수 종속 기본키 전체에 종속 {학번, 과목코드} → 성적
부분 함수 종속 기본키 일부에 종속 {학번, 과목코드} → 학생명 (학번만으로 결정)
이행적 함수 종속 A→B, B→C이면 A→C 학번→학과, 학과→학과장 ⇒ 학번→학과장

정규화 단계

비정규형 → 1NF → 2NF → 3NF → BCNF → 4NF → 5NF
         원자값  부분종속  이행종속  결정자  다치종속  조인종속
                 제거     제거     검증    제거      제거

실무에서는 보통 3NF 또는 BCNF까지 적용한다.


제1정규형 (1NF)

모든 속성의 도메인이 원자값(Atomic Value)으로만 구성되어야 한다.

위반 사례

학번 학생명 수강과목
1001 김철수 자료구조, 알고리즘, DB
1002 이영희 자료구조, 네트워크

문제점: 수강과목이 다중 값(Multi-valued)

1NF 적용 후

학번 학생명 수강과목
1001 김철수 자료구조
1001 김철수 알고리즘
1001 김철수 DB
1002 이영희 자료구조
1002 이영희 네트워크
-- 1NF 위반 테이블 (안티패턴)
CREATE TABLE 수강_bad (
    학번 INT,
    학생명 VARCHAR(50),
    수강과목 VARCHAR(200)  -- 콤마로 구분된 값 저장
);

-- 1NF 준수 테이블
CREATE TABLE 수강 (
    학번 INT,
    학생명 VARCHAR(50),
    수강과목 VARCHAR(50),
    PRIMARY KEY (학번, 수강과목)
);

제2정규형 (2NF)

1NF를 만족하고, 부분 함수 종속을 제거해야 한다. 기본키가 복합키일 때, 기본키의 일부에만 종속되는 속성이 없어야 한다.

위반 사례

기본키: {학번, 과목코드}

학번 과목코드 학생명 학과 과목명 성적
1001 CS101 김철수 컴공 자료구조 A
1001 CS102 김철수 컴공 알고리즘 B
1002 CS101 이영희 전자 자료구조 A

함수 종속 분석:

{학번, 과목코드} → 성적        (완전 함수 종속 ✓)
{학번} → 학생명, 학과          (부분 함수 종속 ✗)
{과목코드} → 과목명            (부분 함수 종속 ✗)

2NF 적용 후

학생 테이블

학번 학생명 학과
1001 김철수 컴공
1002 이영희 전자

과목 테이블

과목코드 과목명
CS101 자료구조
CS102 알고리즘

수강 테이블

학번 과목코드 성적
1001 CS101 A
1001 CS102 B
1002 CS101 A
-- 2NF 적용
CREATE TABLE 학생 (
    학번 INT PRIMARY KEY,
    학생명 VARCHAR(50),
    학과 VARCHAR(50)
);

CREATE TABLE 과목 (
    과목코드 VARCHAR(10) PRIMARY KEY,
    과목명 VARCHAR(100)
);

CREATE TABLE 수강 (
    학번 INT,
    과목코드 VARCHAR(10),
    성적 CHAR(1),
    PRIMARY KEY (학번, 과목코드),
    FOREIGN KEY (학번) REFERENCES 학생(학번),
    FOREIGN KEY (과목코드) REFERENCES 과목(과목코드)
);

제3정규형 (3NF)

2NF를 만족하고, 이행적 함수 종속을 제거해야 한다. 기본키가 아닌 속성이 다른 비키 속성에 종속되면 안 된다.

위반 사례

학번 학생명 학과코드 학과명 학과장
1001 김철수 CS 컴퓨터공학 이교수
1002 이영희 EE 전자공학 박교수
1003 박민수 CS 컴퓨터공학 이교수

함수 종속 분석:

학번 → 학과코드
학과코드 → 학과명, 학과장
------------------------------
학번 → 학과명, 학과장  (이행적 종속!)

문제점: 학과장이 바뀌면 여러 행 수정 필요

3NF 적용 후

학생 테이블

학번 학생명 학과코드
1001 김철수 CS
1002 이영희 EE
1003 박민수 CS

학과 테이블

학과코드 학과명 학과장
CS 컴퓨터공학 이교수
EE 전자공학 박교수
-- 3NF 적용
CREATE TABLE 학과 (
    학과코드 VARCHAR(10) PRIMARY KEY,
    학과명 VARCHAR(50),
    학과장 VARCHAR(50)
);

CREATE TABLE 학생 (
    학번 INT PRIMARY KEY,
    학생명 VARCHAR(50),
    학과코드 VARCHAR(10),
    FOREIGN KEY (학과코드) REFERENCES 학과(학과코드)
);

BCNF (Boyce-Codd Normal Form)

3NF를 만족하고, 모든 결정자가 후보키여야 한다. 3NF보다 엄격한 조건

3NF는 만족하지만 BCNF 위반 사례

수강 테이블 (한 학생은 한 과목당 한 교수에게만 수강)

학번 과목 교수
1001 DB 김교수
1001 알고리즘 이교수
1002 DB 김교수
1003 DB 박교수

후보키: {학번, 과목} 또는 {학번, 교수}

함수 종속:

{학번, 과목} → 교수
{학번, 교수} → 과목
교수 → 과목              (결정자가 후보키가 아님! BCNF 위반)

문제점: 김교수가 담당 과목을 변경하면 여러 행 수정 필요

BCNF 적용 후

교수_과목 테이블

교수 과목
김교수 DB
이교수 알고리즘
박교수 DB

수강 테이블

학번 교수
1001 김교수
1001 이교수
1002 김교수
1003 박교수
-- BCNF 적용
CREATE TABLE 교수_과목 (
    교수 VARCHAR(50) PRIMARY KEY,
    과목 VARCHAR(50)
);

CREATE TABLE 수강 (
    학번 INT,
    교수 VARCHAR(50),
    PRIMARY KEY (학번, 교수),
    FOREIGN KEY (교수) REFERENCES 교수_과목(교수)
);

제4정규형 (4NF)

BCNF를 만족하고, 다치 종속(Multi-valued Dependency)을 제거해야 한다.

다치 종속이란?

A →→ B : A 값 하나에 B 값이 여러 개 대응되며, 다른 속성과 독립적

위반 사례

교수 과목 취미
김교수 DB 골프
김교수 DB 독서
김교수 알고리즘 골프
김교수 알고리즘 독서

다치 종속:

교수 →→ 과목    (김교수는 DB, 알고리즘 담당)
교수 →→ 취미    (김교수는 골프, 독서가 취미)

과목과 취미는 서로 독립적인데 모든 조합이 저장됨 (카티션 곱)

4NF 적용 후

교수_과목 테이블

교수 과목
김교수 DB
김교수 알고리즘

교수_취미 테이블

교수 취미
김교수 골프
김교수 독서

제5정규형 (5NF)

4NF를 만족하고, 조인 종속(Join Dependency)을 제거해야 한다. 더 이상 무손실 분해가 불가능한 상태

실무에서는 거의 사용하지 않음. 이론적 완전성을 위한 정규형.


정규화 정리

정규형 조건 제거 대상
1NF 원자값만 허용 반복 그룹, 다중 값
2NF 1NF + 완전 함수 종속 부분 함수 종속
3NF 2NF + 비이행적 종속 이행적 함수 종속
BCNF 모든 결정자가 후보키 후보키 아닌 결정자
4NF BCNF + 다치 종속 제거 다치 종속
5NF 4NF + 조인 종속 제거 조인 종속

역정규화 (Denormalization)

정규화의 반대 과정. 성능 향상을 위해 의도적으로 중복을 허용하는 것.

역정규화가 필요한 경우

  1. 조인이 너무 많은 경우
    -- 정규화된 테이블: 6개 테이블 조인
    SELECT o.order_id, c.name, p.product_name, ...
    FROM orders o
    JOIN customers c ON ...
    JOIN order_items oi ON ...
    JOIN products p ON ...
    JOIN categories cat ON ...
    JOIN shipping s ON ...
    
  2. 집계 연산이 빈번한 경우
    -- 매번 COUNT 계산
    SELECT user_id, COUNT(*) as post_count
    FROM posts
    GROUP BY user_id;
    
    -- 역정규화: users 테이블에 post_count 컬럼 추가
    
  3. 읽기 성능이 중요한 경우 (OLAP, 리포팅)

역정규화 기법

기법 설명 예시
컬럼 중복 자주 조회하는 컬럼 복사 주문 테이블에 고객명 추가
파생 컬럼 계산 결과 저장 주문 테이블에 총액 저장
테이블 병합 1:1 관계 테이블 합침 사용자+프로필 병합
요약 테이블 집계 데이터 별도 저장 일별 매출 요약 테이블

역정규화 시 주의사항

// 중복 데이터 동기화 필요
@Transactional
public void updateCustomerName(Long customerId, String newName) {
    // 1. 고객 테이블 업데이트
    customerRepository.updateName(customerId, newName);

    // 2. 주문 테이블의 중복 데이터도 업데이트 (동기화)
    orderRepository.updateCustomerName(customerId, newName);
}

실무 가이드

1. 보통 3NF 또는 BCNF까지 적용

OLTP 시스템 → 3NF/BCNF (데이터 무결성 중요)
OLAP 시스템 → 역정규화 적용 (조회 성능 중요)

2. 정규화 vs 역정규화 판단 기준

기준 정규화 역정규화
데이터 변경 빈도 높음 낮음
조회 성능 요구 보통 높음
데이터 무결성 중요 타협 가능
저장 공간 절약 여유

3. 단계적 접근

1단계: 완전히 정규화된 설계로 시작
2단계: 성능 테스트 수행
3단계: 병목 지점 발견 시 선택적 역정규화
4단계: 트리거/애플리케이션으로 데이터 동기화 구현

정규화는 데이터 무결성을 위한 것이고, 역정규화는 성능 최적화를 위한 것이다. 둘 사이의 균형을 찾는 것이 좋은 데이터베이스 설계다.