데이터베이스 정규화 완전 정복 — 1NF부터 BCNF까지

스터디·9분 읽기

정규화, 왜 해야 하나요?

테이블 설계를 대충 하면 어떤 일이 벌어질까요?

  • 삽입 이상 — 강의를 아직 수강하지 않은 학생 정보를 넣을 수 없습니다
  • 갱신 이상 — 교수 이름이 바뀌면 수백 행을 전부 수정해야 합니다
  • 삭제 이상 — 수강 기록을 지웠더니 학생 정보까지 사라집니다

이 세 가지를 이상 현상(Anomaly) 이라 부르며, 정규화는 이 이상 현상을 단계적으로 제거하는 과정입니다. 각 단계(Normal Form)가 어떤 문제를 해결하는지, 비정규 테이블 하나를 끝까지 분해하면서 살펴보겠습니다.

먼저 알아야 할 용어들

정규화를 이해하려면 몇 가지 용어를 먼저 짚고 넘어가야 합니다.

함수적 종속 (Functional Dependency)

X의 값이 정해지면 Y의 값이 하나로 결정되는 관계를 X → Y라고 쓰고, "Y는 X에 함수적으로 종속된다"라고 합니다.

학번 → 이름

학번S001이면 이름은 항상 김철수입니다. 학번을 알면 이름이 하나로 결정되므로 이름학번에 함수적으로 종속됩니다. 여기서 학번처럼 값을 결정하는 쪽을 결정자(Determinant), 이름처럼 결정되는 쪽을 종속자(Dependent) 라고 합니다.

완전 함수 종속 (Full Functional Dependency)

기본키의 모든 속성을 사용해야만 종속자가 결정되는 관계입니다.

(학번, 과목코드) → 성적

성적학번만으로도, 과목코드만으로도 알 수 없습니다. 두 속성이 모두 있어야 결정되므로 완전 함수 종속입니다.

부분 함수 종속 (Partial Functional Dependency)

복합 기본키의 일부 속성만으로 종속자가 결정되는 관계입니다.

(학번, 과목코드) → 이름    -- 실제로는 학번만으로 이름이 결정됨

기본키가 (학번, 과목코드)인데, 이름학번만 알면 결정됩니다. 기본키의 일부에만 종속되어 있으므로 부분 함수 종속이고, 이것이 2NF에서 제거해야 할 대상입니다.

이행적 함수 종속 (Transitive Functional Dependency)

X → Y → Z처럼, 기본키가 아닌 속성을 거쳐서 다른 속성이 결정되는 관계입니다.

학번 → 지도교수 → 연구실

연구실학번에 직접 종속된 것이 아니라 지도교수를 경유해서 결정됩니다. 이것이 이행적 함수 종속이고, 3NF에서 제거해야 할 대상입니다.

후보키와 결정자

  • 후보키(Candidate Key) — 테이블에서 각 행을 유일하게 식별할 수 있는 최소한의 속성 집합입니다. 후보키가 여러 개일 수 있고, 그중 하나를 기본키로 선택합니다
  • 결정자(Determinant) — 함수 종속에서 다른 속성의 값을 결정하는 속성입니다. X → Y에서 X가 결정자입니다

BCNF에서는 모든 결정자가 후보키여야 합니다. 결정자인데 후보키가 아닌 속성이 있으면 BCNF를 위반합니다.

참고: 이 용어들은 아래 각 정규형 설명에서 반복적으로 등장합니다. 읽다가 헷갈리면 여기로 돌아와 확인하세요.

출발점: 비정규 테이블

예제로 사용할 수강 관리 테이블입니다.

학번 이름 학과 과목코드 과목명 교수 성적
S001 김철수 컴퓨터공학 CS101 자료구조 박교수 A
S001 김철수 컴퓨터공학 CS102 운영체제 이교수 B+
S002 이영희 전자공학 CS101 자료구조 박교수 A+
S002 이영희 전자공학 EE201 회로이론 최교수 B

한눈에 봐도 이름, 학과, 과목명, 교수가 중복되고 있습니다. 이 테이블을 단계별로 정리해 보겠습니다.

Phase 1. 제1정규형 (1NF)

규칙: 모든 컬럼 값은 원자값(Atomic Value)이어야 한다

1NF는 가장 기본적인 규칙입니다. 하나의 셀에 여러 값이 들어가면 안 됩니다.

학번 이름 과목코드
S001 김철수 CS101, CS102

1NF 위반 예시:

학번 이름 과목코드
S001 김철수 CS101, CS102

위처럼 하나의 셀에 여러 과목코드가 들어가면 WHERE 과목코드 = 'CS101' 같은 쿼리가 정상 동작하지 않습니다. 행을 분리해서 각 셀이 하나의 값만 갖도록 만들면 1NF를 충족합니다.

우리 예제 테이블은 이미 행이 분리되어 있으므로 1NF를 만족합니다.

참고: 1NF의 핵심은 "반복 그룹 제거"입니다. 배열이나 콤마 구분 문자열이 컬럼에 들어가 있다면 1NF 위반을 의심하세요.

Phase 2. 제2정규형 (2NF)

규칙: 부분 함수 종속을 제거한다

2NF는 복합 기본키를 사용하는 테이블에서 의미가 있습니다. 기본키가 (학번, 과목코드)라고 했을 때, 기본키의 일부분에만 종속되는 컬럼이 있으면 안 됩니다.

문제: 부분 함수 종속이 뭔가요?

현재 테이블의 함수 종속(FD)을 나열하면 이렇습니다.

(학번, 과목코드) → 성적          -- 기본키 전체에 종속 ✅
학번 → 이름, 학과                -- 기본키의 일부(학번)에만 종속 ❌
과목코드 → 과목명, 교수           -- 기본키의 일부(과목코드)에만 종속 ❌

이름학과과목코드와 관계없이 학번만으로 결정됩니다. 이것이 부분 함수 종속이고, 갱신 이상의 원인입니다.

해결: 테이블 분리

부분 종속되는 컬럼들을 별도 테이블로 분리합니다.

학생 테이블

학번 이름 학과
S001 김철수 컴퓨터공학
S002 이영희 전자공학

과목 테이블

과목코드 과목명 교수
CS101 자료구조 박교수
CS102 운영체제 이교수
EE201 회로이론 최교수

수강 테이블

학번 과목코드 성적
S001 CS101 A
S001 CS102 B+
S002 CS101 A+
S002 EE201 B

이제 김철수의 학과가 바뀌어도 학생 테이블 한 행만 수정하면 됩니다. 2NF 달성입니다.

참고: 기본키가 단일 컬럼이면 부분 종속 자체가 발생하지 않으므로 자동으로 2NF를 만족합니다.

Phase 3. 제3정규형 (3NF)

규칙: 이행적 함수 종속을 제거한다

2NF까지 분리한 과목 테이블을 다시 보겠습니다.

과목코드 → 과목명
과목코드 → 교수

여기까지는 문제가 없어 보이지만, 만약 "하나의 과목은 반드시 한 명의 교수가 담당한다"는 규칙 대신 "한 교수는 한 과목만 담당한다"는 규칙이 있다면 어떨까요?

과목코드 → 교수
교수 → 과목코드

이 경우는 괜찮습니다. 하지만 다음과 같은 경우를 생각해 보겠습니다.

문제: A → B → C 이행 종속

학생 테이블에 지도교수지도교수_연구실이 추가된다고 가정합니다.

학번 이름 학과 지도교수 지도교수_연구실
S001 김철수 컴퓨터공학 박교수 공학관 301
S002 이영희 전자공학 최교수 공학관 502
S003 박민수 컴퓨터공학 박교수 공학관 301

함수 종속을 보면:

학번 → 지도교수 → 지도교수_연구실

지도교수_연구실은 기본키(학번)에 직접 종속된 것이 아니라 지도교수를 거쳐 이행적으로 종속됩니다. 박교수의 연구실이 이전하면 S001, S003 두 행 모두 수정해야 합니다.

해결: 이행 종속 분리

| 학번 | 이름 | 학과 | 지도교수 |

학생 테이블:

| 학번 | 이름 | 학과 | 지도교수 |

교수 테이블:

교수 연구실
박교수 공학관 301
최교수 공학관 502

이제 연구실 정보는 교수 테이블 한 곳에서만 관리합니다. 3NF 달성입니다.

참고: 3NF의 공식 정의는 "비주요 속성(non-prime attribute)이 어떤 후보키에도 비이행적으로 종속"입니다. 쉽게 말해, 후보키가 아닌 컬럼이 다른 비키 컬럼을 결정하면 안 된다는 뜻입니다.

Phase 4. 보이스-코드 정규형 (BCNF)

규칙: 모든 결정자가 후보키여야 한다

3NF는 "기본키가 아닌 속성" 사이의 종속만 제거합니다. BCNF는 한 단계 더 나아가 모든 함수 종속의 결정자가 후보키여야 한다고 요구합니다.

문제: 후보키가 아닌 결정자

수강 지도 테이블을 예로 들겠습니다. "한 학생은 과목당 한 명의 지도교수에게 배우고, 각 교수는 하나의 과목만 지도한다"는 규칙이 있다고 가정합니다.

학번 과목명 교수
S001 자료구조 박교수
S001 운영체제 이교수
S002 자료구조 박교수

후보키: (학번, 과목명) 또는 (학번, 교수)

함수 종속:

(학번, 과목명) → 교수    ✅ 결정자가 후보키
교수 → 과목명            ❌ 결정자(교수)가 후보키가 아님!

교수과목명을 결정하는데, 교수는 후보키가 아닙니다. 이 상태에서 "정교수가 자료구조를 담당한다"는 사실을 저장하려면 학생이 수강 신청할 때까지 기다려야 합니다 — 삽입 이상입니다.

해결: 결정자를 기본키로 분리

교수-과목 테이블:

교수 과목명
박교수 자료구조
이교수 운영체제

수강지도 테이블:

학번 교수
S001 박교수
S001 이교수
S002 박교수

모든 결정자가 후보키가 되었습니다. BCNF 달성입니다.

한눈에 보는 정규형 비교

정규형 핵심 규칙 제거 대상
1NF 모든 값이 원자값 반복 그룹
2NF 부분 함수 종속 제거 복합키 일부에 종속
3NF 이행적 함수 종속 제거 A → B → C 종속
BCNF 모든 결정자가 후보키 비후보키 결정자

실무에서는 어디까지?

많은 서비스에서는 3NF까지 적용해도 충분한 경우가 많습니다. BCNF까지 분해하면 테이블 수가 많아지고 JOIN이 늘어나면서 조회 쿼리가 복잡해질 수 있습니다.

오히려 실무에서는 의도적으로 정규화를 풀어 반정규화(Denormalization) 하는 경우도 많습니다.

  • 읽기 성능이 중요한 조회 테이블JOIN을 줄이기 위해 중복 허용
  • 집계/리포팅 테이블 — 매번 GROUP BY하는 대신 미리 합산값을 저장
  • 캐시 성격의 컬럼 — 댓글 수를 게시글 테이블에 함께 저장

핵심은 정규화 원칙을 이해한 상태에서 트레이드오프를 판단하는 것입니다.

정규화의 장점과 단점

장점

  • 데이터 무결성 보장 — 중복이 사라지므로 갱신 시 불일치가 발생하지 않습니다
  • 저장 공간 절약 — 같은 데이터를 여러 행에 반복 저장하지 않아 디스크를 효율적으로 사용합니다
  • 유지보수 용이 — 데이터 변경 지점이 한 곳이므로 수정 범위가 명확합니다
  • 확장성 — 새로운 요구사항이 생겨도 기존 테이블 구조를 크게 바꾸지 않고 테이블을 추가할 수 있습니다

단점

  • 조회 복잡도 증가 — 테이블이 분리될수록 JOIN이 늘어나 읽기 쿼리가 복잡해지거나 느려질 수 있습니다
  • 설계 복잡도 증가 — 테이블 수가 많아지면 ERD가 복잡해지고 쿼리 작성이 어려워집니다
  • 쓰기 성능 오버헤드 — 하나의 트랜잭션에서 여러 테이블을 동시에 갱신해야 하는 경우가 생깁니다
  • 과도한 정규화의 함정 — 읽기가 압도적으로 많은 서비스에서 BCNF까지 고집하면 오히려 전체 성능이 떨어집니다

참고: 정규화와 반정규화는 양자택일이 아닙니다. 쓰기 정합성이 중요한 테이블은 정규화, 읽기 속도가 중요한 테이블은 반정규화로 목적에 맞게 섞어 쓰는 것이 실무의 일반적인 접근입니다.

정리

  1. 1NF — 하나의 셀에 하나의 값만 저장합니다
  2. 2NF — 복합키의 일부에만 종속되는 컬럼을 분리합니다
  3. 3NF — 비키 컬럼이 다른 비키 컬럼을 결정하지 않도록 분리합니다
  4. BCNF — 모든 결정자가 후보키인지 확인합니다
  5. 실무 판단 — 정규화를 알아야 반정규화도 올바르게 할 수 있습니다