정규화 (함수 종속, 이상 현상, BCNF)
학생 성적 테이블 하나에 학생 이름·학과·학과사무실·과목·점수를 전부 욱여넣으면 처음엔 편하다. 그런데 한 학생이 다섯 과목을 들으면 그 학생의 학과사무실 정보가 다섯 번 중복 저장되고, 학과사무실이 이전하면 그 다섯 줄을 모두 고쳐야 하며, 한 줄이라도 빠뜨리면 데이터가 서로 모순된다. 이렇게 잘못 설계된 테이블이 일으키는 문제를 이상 현상(anomaly)이라 하고, 이를 함수 종속을 기준으로 테이블을 쪼개 제거하는 체계적 절차가 정규화(Normalization)다. 본 글은 이상 현상의 세 종류, 함수 종속이라는 핵심 개념, 그리고 제1정규형부터 BCNF까지의 단계를 세심하게 정리한다(출처: 위키백과 — Database normalization). 제가 데이터베이스 수업 프로젝트에서 처음엔 모든 걸 한 테이블에 몰아넣었다가 데이터 수정 한 번에 여러 행을 동기화하느라 버그를 양산한 경험이 있는데, 그날 "정규화는 시험용 이론이 아니라 나를 버그에서 구해 주는 설계 규율"임을 받아들였다.

이상 현상 — 정규화가 필요한 이유
정규화의 목적을 이해하려면 먼저 정규화하지 않은 테이블이 일으키는 세 가지 이상 현상을 알아야 한다. 모두 데이터가 부적절하게 한 테이블에 뭉쳐 있을 때 발생한다.
첫째, 삽입 이상(insertion anomaly)이다. 원하지 않는 데이터를 함께 넣어야만 삽입이 가능한 문제다. 예컨대 학생-과목 테이블에서 아직 수강 신청을 안 한 신입생을 등록하려면, 과목 정보가 없어 NULL을 억지로 채워야 한다. 둘째, 삭제 이상(deletion anomaly)이다. 한 데이터를 지우면 보존해야 할 다른 데이터까지 함께 사라지는 문제다. 어떤 과목을 수강한 마지막 학생의 수강 기록을 지웠더니 그 과목 자체의 정보가 통째로 없어지는 식이다. 셋째, 갱신 이상(update anomaly)이다. 중복 저장된 데이터 중 일부만 고쳐 데이터가 모순되는 문제로, 앞서 말한 학과사무실 이전 사례가 정확히 이것이다.
이 세 이상 현상의 근본 원인은 하나의 테이블에 서로 다른 주제의 데이터가 섞여 있고, 그로 인해 중복이 발생한다는 데 있다. 정규화는 "한 테이블은 한 가지 주제만 다룬다"는 원칙으로 테이블을 분해해 이 중복과 이상 현상을 제거한다.
함수 종속 — 정규화의 판단 기준
정규화를 기계적으로 외우면 금방 잊지만, 그 밑바탕인 함수 종속(Functional Dependency) 하나만 잡으면 모든 정규형이 한 줄기로 꿰어진다. 함수 종속이란 "어떤 속성 X의 값이 정해지면 다른 속성 Y의 값이 유일하게 결정된다"는 관계이며, X → Y로 표기한다. 여기서 X를 결정자(determinant)라 부른다.
예컨대 학번이 정해지면 학생 이름이 유일하게 정해지므로 학번 → 이름이 성립한다. 학번이 정해지면 학과가, 학과가 정해지면 학과사무실이 정해진다면 학번 → 학과, 학과 → 학과사무실이 성립한다. 이때 학번을 통해 간접적으로 학과사무실이 정해지는 학번 → 학과사무실 관계를 이행적 함수 종속(transitive dependency)이라 한다. 키가 아닌 속성을 거쳐 도는 이 종속이 갱신 이상의 주범이다.
| 정규형 | 핵심 조건 | 제거하는 종속 |
|---|---|---|
| 1NF | 모든 속성이 원자값(분해 불가) | 다중값·반복 그룹 |
| 2NF | 1NF + 부분 함수 종속 제거 | 부분 종속 |
| 3NF | 2NF + 이행적 함수 종속 제거 | 이행 종속 |
| BCNF | 3NF + 모든 결정자가 후보키 | 결정자가 키 아닌 종속 |
여기서 후보키(candidate key)란 테이블의 각 행을 유일하게 식별할 수 있는 최소한의 속성 집합을 가리킨다. 부분 함수 종속(partial dependency)은 후보키의 일부만으로 다른 속성이 결정되는 종속으로, 키가 여러 속성으로 이루어진 복합키일 때 문제가 된다.
1NF부터 BCNF까지 — 단계별 분해
정규화는 낮은 단계부터 차례로 진행된다. 제1정규형(1NF)은 모든 속성이 더 이상 쪼갤 수 없는 원자값을 갖도록 하는 것이다. 한 칸에 "수학, 영어, 과학"처럼 여러 값이 들어 있으면 1NF 위반이며, 각 값을 별도 행으로 분리해야 한다.
제2정규형(2NF)은 1NF를 만족하면서 부분 함수 종속을 제거한 상태다. (학번, 과목)이 복합키인 테이블에서 학생 이름이 학번에만 의존한다면, 이름은 키의 일부(학번)에만 종속되는 부분 종속이다. 이를 학생 테이블로 분리한다.
제3정규형(3NF)은 2NF를 만족하면서 이행적 함수 종속을 제거한 상태다. 학번 → 학과 → 학과사무실에서 학과사무실은 학번에 이행적으로 종속되므로, 학과 테이블을 따로 만들어 학과사무실을 옮긴다. 실무에서 대부분의 데이터베이스는 이 3NF까지를 표준 목표로 삼는다.
BCNF(보이스-코드 정규형)는 3NF보다 한 단계 엄격한 정규형으로, 모든 결정자가 후보키여야 한다는 조건을 요구한다(출처: 위키백과 — Boyce–Codd normal form). BCNF는 1974년 레이먼드 보이스와 에드거 코드가 3NF가 잡지 못하는 특수한 이상 현상을 다루기 위해 제안했다. 3NF를 만족해도 후보키가 여러 개이고 그들이 겹칠 때 BCNF를 위반할 수 있다.
-- 정규화 전: 갱신 이상 발생 (학과사무실 중복 저장)
CREATE TABLE 수강 (
학번 INT,
과목 VARCHAR(50),
점수 INT,
학과 VARCHAR(50),
학과사무실 VARCHAR(50), -- 학과에 종속 → 이행 종속
PRIMARY KEY (학번, 과목)
);
-- 3NF 분해 후: 주제별 테이블 분리
CREATE TABLE 학생 (
학번 INT PRIMARY KEY,
학과 VARCHAR(50) REFERENCES 학과(학과명)
);
CREATE TABLE 학과 (
학과명 VARCHAR(50) PRIMARY KEY,
학과사무실 VARCHAR(50) -- 한 곳에만 저장 → 갱신 이상 소멸
);
CREATE TABLE 수강 (
학번 INT REFERENCES 학생(학번),
과목 VARCHAR(50),
점수 INT,
PRIMARY KEY (학번, 과목)
);
다만 정규화가 항상 정답은 아니라는 점이 중요하다. 정규화를 강하게 적용할수록 테이블이 잘게 쪼개져 조회 시 조인(join)이 많아지고, 이는 읽기 성능을 떨어뜨린다. 그래서 읽기가 압도적으로 많은 분석용·통계용 데이터베이스에서는 일부러 중복을 허용해 테이블을 합치는 반정규화(denormalization)를 적용하기도 한다. 즉 정규화는 "데이터 무결성"을, 반정규화는 "조회 성능"을 우선하는 트레이드오프이며, 어느 쪽이 옳은지는 워크로드가 결정한다.
흔한 오해 하나를 짚으면 "정규화는 무조건 높은 단계까지 갈수록 좋다"는 통념인데, 실무 표준은 3NF이며 BCNF 이상의 고차 정규형(4NF, 5NF)은 특수한 경우에만 의미가 있다. 또한 데이터 웨어하우스는 의도적으로 3NF를 깨고 스타 스키마로 반정규화한다. 시험에서는 각 정규형의 정의와 어떤 종속을 제거하는지가 단골 출제되고, 실무에서는 "정규화로 무결성을 지키되, 성능 병목이 생기면 측정 근거를 가지고 반정규화한다"는 균형 감각이 핵심이다. 솔직히 정규화는 처음엔 추상적이지만, 함수 종속 화살표를 손으로 그려 보는 습관을 들이면 어느 순간 테이블 설계가 손에 잡힌다.
메타 디스크립션: 잘못된 테이블 설계가 일으키는 삽입·삭제·갱신 이상 현상부터, 정규화의 판단 기준인 함수 종속, 제1정규형부터 BCNF까지의 단계별 분해, 그리고 성능을 위한 반정규화 트레이드오프까지 SQL 예시와 함께 데이터베이스 입문자 관점에서 세심하게 정리합니다.