> [!abstract] Introduction
> 데이터베이스를 설계할 때 가장 흔한 실수는 하나의 테이블에 너무 많은 정보를 욱여넣는 것입니다. 정규화*Normalization*는 이런 "나쁜 형태"의 릴레이션을 체계적으로 분해하여 데이터 중복과 이상 현상*anomaly*을 제거하는 과정입니다. 이 글에서는 하나의 비정규 테이블을 1NF, 2NF, 3NF까지 단계적으로 분해하면서 각 정규형이 해결하는 문제를 살펴봅니다. BCNF 이상의 고급 정규형은 [[Boyce-Codd Normal Form]]과 [[Multivalued Dependency]]에서 다룹니다.
## 왜 정규화가 필요한가 — 이상 현상
정규화를 이해하려면 먼저 "나쁜 설계"가 어떤 문제를 일으키는지 알아야 한다. 다음은 학생의 수강 정보를 하나의 테이블에 담은 예시다.
| 학번 | 이름 | 학과 | 학과장 | 과목코드 | 과목명 | 성적 |
|------|------|------|--------|----------|--------|------|
| 101 | 김철수 | 컴퓨터공학 | 박교수 | CS101 | 자료구조 | A |
| 101 | 김철수 | 컴퓨터공학 | 박교수 | CS102 | 알고리즘 | B+ |
| 102 | 이영희 | 수학 | 최교수 | MA201 | 선형대수 | A+ |
| 103 | 박민수 | 컴퓨터공학 | 박교수 | CS101 | 자료구조 | B |
이 테이블은 직관적이지만, 세 가지 이상 현상을 안고 있다.
### 삽입 이상
신설 학과 "통계학"의 학과장 정보를 저장하려면 어떻게 해야 할까? 아직 수강 학생이 없으니 학번, 과목코드 등 필수 컬럼에 넣을 값이 없다. `NULL`을 채우거나 더미 데이터를 만들어야 하는데, 두 방법 모두 데이터 무결성을 해친다. 이처럼 원하는 정보를 삽입할 수 없거나 불필요한 데이터를 함께 삽입해야 하는 문제를 삽입 이상*Insertion Anomaly*이라 한다.
### 갱신 이상
"컴퓨터공학"의 학과장이 "박교수"에서 "정교수"로 바뀌었다고 하자. 위 테이블에서 학과장 정보는 학번 101의 두 행과 학번 103의 한 행, 총 세 곳에 중복되어 있다. 한 행만 갱신하고 나머지를 놓치면 같은 학과인데 학과장이 다른 모순 상태가 발생한다. 중복된 데이터의 일부만 변경되어 일관성이 깨지는 문제를 갱신 이상*Update Anomaly*이라 한다.
### 삭제 이상
학번 102 이영희가 "선형대수" 수강을 취소하면, 이 학생의 유일한 행이 삭제된다. 문제는 이 행이 "수학과 학과장은 최교수"라는 정보까지 담고 있다는 점이다. 수강 취소라는 의도와 무관하게 학과 정보가 함께 소실된다. 특정 데이터를 삭제할 때 의도하지 않은 다른 정보까지 사라지는 문제를 삭제 이상*Deletion Anomaly*이라 한다.
세 가지 이상 현상은 모두 동일한 근본 원인을 공유한다 — **서로 독립적인 사실(학생 정보, 학과 정보, 수강 정보)이 하나의 릴레이션에 뒤섞여 있기 때문이다.** 정규화는 이 뒤섞인 사실들을 체계적으로 분리하는 도구다.
## 함수 종속
정규화의 기준이 되는 핵심 개념은 함수 종속*Functional Dependency*, FD이다. 릴레이션 $R$에서 속성 집합 $X$의 값이 속성 집합 $Y$의 값을 유일하게 결정할 때, $Y$는 $X$에 함수적으로 종속된다고 하며 $X \to Y$로 표기한다.
위 테이블에서 발견되는 함수 종속은 다음과 같다.
- $\text{학번} \to \text{이름}, \text{학과}$ — 학번을 알면 이름과 소속 학과가 결정된다.
- $\text{학과} \to \text{학과장}$ — 학과를 알면 학과장이 결정된다.
- $\{\text{학번}, \text{과목코드}\} \to \text{성적}$ — 학번과 과목코드를 함께 알아야 성적이 결정된다.
- $\text{과목코드} \to \text{과목명}$ — 과목코드를 알면 과목명이 결정된다.
### 완전 함수 종속과 부분 함수 종속
함수 종속 $X \to Y$에서 $X$의 어떤 진부분집합도 $Y$를 결정할 수 없을 때, 이를 완전 함수 종속*Full Functional Dependency*이라 한다. 반대로 $X$의 진부분집합만으로도 $Y$를 결정할 수 있으면 부분 함수 종속*Partial Functional Dependency*이다.
예를 들어 $\{\text{학번}, \text{과목코드}\} \to \text{성적}$은 완전 함수 종속이다. 학번만으로는 성적을 알 수 없고, 과목코드만으로도 성적을 알 수 없기 때문이다. 반면 $\{\text{학번}, \text{과목코드}\} \to \text{이름}$은 부분 함수 종속이다. 과목코드 없이 학번만으로도 이름이 결정되기 때문이다.
### 이행 함수 종속
$X \to Y$이고 $Y \to Z$이면 $X \to Z$가 성립한다. 이때 $Z$가 $X$에 직접 종속되는 것이 아니라 $Y$를 거쳐 간접적으로 종속되는 관계를 이행 함수 종속*Transitive Functional Dependency*이라 한다.
위 테이블에서 $\text{학번} \to \text{학과}$이고 $\text{학과} \to \text{학과장}$이므로, $\text{학번} \to \text{학과장}$은 이행 함수 종속이다. 학과장은 학번에 직접 종속되는 것이 아니라 학과를 경유해 종속된다.
## 정규형
정규형은 1NF부터 5NF까지 계층적으로 정의되며, 높은 정규형은 낮은 정규형의 조건을 모두 포함한다. 실무에서는 대부분 3NF 또는 BCNF까지 적용하면 충분하다[^practical-normalization].
![[normalizationSteps.svg]]
### 제1정규형 (1NF)
> **1NF 조건**: 릴레이션의 모든 속성 값이 원자적*atomic*이어야 한다.
원자적이라 함은 각 셀에 단일 값만 존재하고, 반복 그룹*repeating group*이 없어야 한다는 뜻이다 (Codd, 1970). 다음 테이블은 1NF를 위반한다.
| 학번 | 이름 | 수강과목 |
|------|------|----------|
| 101 | 김철수 | CS101, CS102 |
| 102 | 이영희 | MA201 |
"수강과목" 열에 쉼표로 구분된 여러 값이 들어 있어 원자성을 위반한다. 이를 1NF로 변환하려면 반복 값을 별도의 행으로 분리한다.
| 학번 | 이름 | 수강과목 |
|------|------|----------|
| 101 | 김철수 | CS101 |
| 101 | 김철수 | CS102 |
| 102 | 이영희 | MA201 |
앞서 제시한 수강 정보 테이블은 이미 모든 속성이 원자적이므로 1NF를 만족한다. 이제 이 1NF 테이블을 출발점으로 2NF, 3NF를 적용한다.
### 제2정규형 (2NF)
> **2NF 조건**: 1NF를 만족하고, 모든 비주요 속성*non-prime attribute*[^non-prime]이 기본 키에 완전 함수 종속되어야 한다.
2NF가 금지하는 것은 부분 함수 종속이다. 수강 정보 테이블의 기본 키는 $\{\text{학번}, \text{과목코드}\}$인데, 이름, 학과, 학과장은 학번만으로 결정되고, 과목명은 과목코드만으로 결정된다. 모두 기본 키의 일부에만 종속되는 부분 함수 종속이다.
부분 함수 종속을 제거하려면, 기본 키의 부분집합이 결정하는 속성들을 별도의 릴레이션으로 분리한다.
**학생** 릴레이션
| <u>학번</u> | 이름 | 학과 | 학과장 |
|------|------|------|--------|
| 101 | 김철수 | 컴퓨터공학 | 박교수 |
| 102 | 이영희 | 수학 | 최교수 |
| 103 | 박민수 | 컴퓨터공학 | 박교수 |
**과목** 릴레이션
| <u>과목코드</u> | 과목명 |
|----------|--------|
| CS101 | 자료구조 |
| CS102 | 알고리즘 |
| MA201 | 선형대수 |
**수강** 릴레이션
| <u>학번</u> | <u>과목코드</u> | 성적 |
|------|----------|------|
| 101 | CS101 | A |
| 101 | CS102 | B+ |
| 102 | MA201 | A+ |
| 103 | CS101 | B |
이제 수강 릴레이션의 비주요 속성은 성적 하나뿐이며, 성적은 $\{\text{학번}, \text{과목코드}\}$ 전체에 완전 함수 종속된다. 학생과 과목 릴레이션도 각각 단일 속성 기본 키를 가지므로 부분 함수 종속이 원리적으로 불가능하다. 세 릴레이션 모두 2NF를 만족한다.
삽입 이상의 일부가 해소되었다. 새 과목을 추가할 때 수강 학생 없이도 과목 릴레이션에 저장할 수 있다. 그러나 **학생 릴레이션의 학과장 정보**에는 아직 문제가 남아 있다.
### 제3정규형 (3NF)
> **3NF 조건**: 2NF를 만족하고, 모든 비주요 속성이 기본 키에 이행적으로 종속되지 않아야 한다.
학생 릴레이션을 보자. $\text{학번} \to \text{학과}$이고 $\text{학과} \to \text{학과장}$이므로, 학과장은 학번에 이행 함수 종속된다. 이 이행 종속이 남아 있는 한 갱신 이상과 삭제 이상이 발생한다 — 컴퓨터공학 학과장이 바뀌면 여전히 여러 행을 수정해야 하고, 수학과 학생이 전부 자퇴하면 학과장 정보가 소실된다.
이행 함수 종속을 제거하려면, 경유 속성($\text{학과}$)이 결정하는 속성($\text{학과장}$)을 별도의 릴레이션으로 분리한다.
**학생** 릴레이션 (3NF)
| <u>학번</u> | 이름 | 학과 |
|------|------|------|
| 101 | 김철수 | 컴퓨터공학 |
| 102 | 이영희 | 수학 |
| 103 | 박민수 | 컴퓨터공학 |
**학과** 릴레이션
| <u>학과</u> | 학과장 |
|------|--------|
| 컴퓨터공학 | 박교수 |
| 수학 | 최교수 |
**과목** 릴레이션
| <u>과목코드</u> | 과목명 |
|----------|--------|
| CS101 | 자료구조 |
| CS102 | 알고리즘 |
| MA201 | 선형대수 |
**수강** 릴레이션
| <u>학번</u> | <u>과목코드</u> | 성적 |
|------|----------|------|
| 101 | CS101 | A |
| 101 | CS102 | B+ |
| 102 | MA201 | A+ |
| 103 | CS101 | B |
![[normalizationDecomposition.svg]]
이제 세 가지 이상 현상이 모두 해소된다.
- **삽입 이상 해소**: 신설 학과의 학과장 정보를 학과 릴레이션에 독립적으로 삽입할 수 있다. 수강 학생이 없어도 문제없다.
- **갱신 이상 해소**: 학과장 정보는 학과 릴레이션에 단 한 번만 존재한다. 한 곳만 수정하면 된다.
- **삭제 이상 해소**: 학생이 수강을 취소하거나 자퇴해도 학과 정보는 별도의 릴레이션에 보존된다.
## 정규화의 트레이드오프
정규화가 만능은 아니다. 릴레이션을 분해할수록 조인*join* 연산이 늘어나고, 이는 곧 쿼리 성능 저하로 이어진다. 읽기 성능이 중요한 시스템에서는 의도적으로 정규형을 일부 완화하여 중복을 허용하는 역정규화*Denormalization*를 적용하기도 한다.
핵심은 정규화와 역정규화 모두 **근거 있는 판단**이어야 한다는 것이다. 정규화를 거치지 않은 설계는 "역정규화"가 아니라 그냥 "나쁜 설계"다. 정규화로 정보 중복이 없는 상태를 먼저 확보한 후, 측정된 성능 요구에 따라 선택적으로 역정규화하는 것이 올바른 순서다.
또한 3NF까지 적용해도 모든 이상 현상이 해결되는 것은 아니다. 기본 키가 아닌 후보 키*candidate key*가 결정자*determinant*인 경우의 이상 현상은 [[Boyce-Codd Normal Form]]에서 다루고, 다치 종속*multivalued dependency*과 조인 종속*join dependency*에서 발생하는 문제는 [[Multivalued Dependency]]에서 다룬다.
## 무손실 분해와 종속 보존
릴레이션을 분해할 때 두 가지 조건을 반드시 만족해야 한다.
### 무손실 조인 분해
분해한 릴레이션들을 자연 조인*natural join*으로 다시 합쳤을 때 원래의 릴레이션과 정확히 같아야 한다. 즉 분해 과정에서 정보가 손실되거나 가짜 튜플*spurious tuple*이 생성되면 안 된다. 이를 무손실 조인 분해*Lossless-Join Decomposition*라 한다.
릴레이션 $R$을 $R_1$과 $R_2$로 분해할 때, $R_1 \cap R_2 \to R_1$ 또는 $R_1 \cap R_2 \to R_2$가 성립하면 무손실 조인이 보장된다[^heath-theorem]. 쉽게 말해, 두 릴레이션의 공통 속성이 적어도 한쪽의 키(또는 슈퍼키)가 되어야 한다.
### 종속 보존
분해 전에 성립하던 모든 함수 종속이 분해 후의 릴레이션들에서도 검증 가능해야 한다. 이를 종속 보존 분해*Dependency-Preserving Decomposition*라 한다.
종속 보존이 실패하면 데이터 무결성을 검증하기 위해 여러 릴레이션을 조인해야 하므로 제약 조건 검사의 비용이 증가한다. 3NF까지는 무손실 조인과 종속 보존을 동시에 만족하는 분해가 항상 존재하지만, BCNF에서는 종속 보존이 보장되지 않을 수 있다 — 이 역시 [[Boyce-Codd Normal Form]]에서 상세히 다룬다.
---
## 출처
- Codd, E. (1970) 'A Relational Model of Data for Large Shared Data Banks', *Communications of the ACM*, 13(6), pp. 377–387.
- Elmasri, R. and Navathe, S. (2015) *Fundamentals of Database Systems*. 7th edn. Pearson.
- Silberschatz, A., Korth, H. and Sudarshan, S. (2019) *Database System Concepts*. 7th edn. McGraw-Hill.
[^practical-normalization]: 실무에서 3NF를 넘어 4NF, 5NF까지 적용하는 경우는 드물다. 대부분의 이상 현상은 3NF 또는 BCNF 수준에서 해결되며, 고차 정규형은 다치 종속이나 조인 종속 같은 특수한 상황에서만 필요하다.
[^non-prime]: 비주요 속성은 어떤 후보 키에도 포함되지 않는 속성이다. 후보 키의 일부인 속성을 주요 속성*prime attribute*이라 하며, 2NF와 3NF의 규칙은 비주요 속성에만 적용된다.
[^heath-theorem]: Heath의 정리(1971). 릴레이션 $R(A, B, C)$에서 $A \to B$가 성립하면, $R$을 $R_1(A, B)$와 $R_2(A, C)$로 분해하는 것은 무손실 조인이다.