콘텐츠로 이동

정규화 (Normalization)

정규화(Normalization): 데이터 중복을 줄이고 이상(Anomaly)을 방지하기 위해 테이블을 구조적으로 분리하는 설계 과정. 각 단계를 정규형(Normal Form, NF)이라 한다.

왜 쓰는가?

정규화 없이 설계하면 데이터를 삽입·수정·삭제할 때 이상(Anomaly)이 발생한다.

이상 종류 설명 예시
삽입 이상 일부 데이터가 없으면 행 자체를 삽입 불가 수강 신청 없으면 학생 정보를 저장할 수 없음
갱신 이상 중복 데이터 중 일부만 수정되어 불일치 교수 이름이 여러 행에 중복 저장 → 한 곳만 수정되면 불일치
삭제 이상 데이터 삭제 시 의도치 않은 다른 정보도 사라짐 수강 취소 시 교수 정보까지 삭제됨

함수 종속성 (Functional Dependency)

정규화의 핵심 개념. 컬럼 A의 값이 결정되면 B의 값도 결정될 때 "B는 A에 함수 종속"이라 한다.

A → B  (A가 B를 결정한다)
종류 설명
완전 함수 종속 기본키 전체에 종속 (복합키일 때 일부가 아닌 전체에 종속)
부분 함수 종속 복합키의 일부에만 종속 → 2NF 위반
이행 함수 종속 A → B → C (A가 C를 간접 결정) → 3NF 위반

정규형 단계별 정리

1NF — 원자값

조건: 모든 컬럼 값이 원자값(Atomic Value)이어야 한다. 즉, 한 셀에 여러 값이 들어가면 안 된다.

Bad — 1NF 위반

학번 이름 수강과목
1001 김철수 DB, Java, Spring

수강과목 열에 여러 값이 존재

Good — 1NF 만족

학번 이름 수강과목
1001 김철수 DB
1001 김철수 Java
1001 김철수 Spring

→ 각 행이 원자값


2NF — 부분 함수 종속 제거

조건: 1NF를 만족하면서, 기본키가 복합키일 때 비키 컬럼이 기본키 전체에 완전 함수 종속이어야 한다.

Bad — 2NF 위반

학번 과목코드 이름 과목명
1001 CS101 김철수 데이터베이스

기본키: (학번, 과목코드) - 이름 → 학번에만 종속 (부분 종속) ❌ - 과목명 → 과목코드에만 종속 (부분 종속) ❌

Good — 2NF 만족 (테이블 분리)

학생 테이블

학번 이름
1001 김철수

과목 테이블

과목코드 과목명
CS101 데이터베이스

수강 테이블

학번 과목코드
1001 CS101

3NF — 이행 함수 종속 제거

조건: 2NF를 만족하면서, 이행 함수 종속(A → B → C)이 없어야 한다. 비키 컬럼끼리 종속 관계가 없어야 한다.

Bad — 3NF 위반

사원번호 사원명 부서코드 부서명
E001 김철수 D10 개발팀

사원번호 → 부서코드 → 부서명 (이행 종속) ❌

Good — 3NF 만족 (테이블 분리)

사원 테이블

사원번호 사원명 부서코드
E001 김철수 D10

부서 테이블

부서코드 부서명
D10 개발팀

BCNF — 결정자가 후보키

조건: 3NF를 만족하면서, 함수 종속 관계에서 모든 결정자가 후보키여야 한다. 3NF보다 엄격한 형태.

Bad — BCNF 위반

학번 과목 교수
1001 DB 박교수
1002 DB 박교수
  • (학번, 과목) → 교수 ✓ (후보키)
  • 교수 → 과목 ❌ (교수는 후보키가 아닌데 결정자)

Good — BCNF 만족

수강 테이블

학번 교수
1001 박교수
1002 박교수

교수 테이블

교수 과목
박교수 DB

정규형 요약 비교

정규형 조건 제거 대상
1NF 원자값 반복 그룹, 다중 값
2NF 1NF + 완전 함수 종속 부분 함수 종속
3NF 2NF + 이행 종속 제거 이행 함수 종속
BCNF 3NF + 결정자 = 후보키 비후보키 결정자

실무에서는 보통 3NF 또는 BCNF까지 정규화하는 것이 일반적이다.


역정규화 (Denormalization)

역정규화: 정규화된 테이블을 의도적으로 합치거나 중복을 허용해 읽기 성능을 높이는 기법.

정규화를 하면 JOIN이 많아지고 대규모 조회 쿼리의 성능이 저하될 수 있다. 이때 역정규화로 트레이드오프를 선택한다.

-- 정규화: 주문 + 회원 JOIN 필요
SELECT o.order_id, m.name, m.email
FROM orders o
JOIN member m ON o.member_id = m.id;

-- 역정규화: orders 테이블에 member_name, member_email 컬럼 포함
-- → JOIN 없이 단일 테이블 조회 (읽기 성능 향상, 중복 데이터 증가)
SELECT order_id, member_name, member_email FROM orders;
항목 정규화 역정규화
데이터 중복 없음 있음
이상(Anomaly) 없음 발생 가능
쓰기 성능 좋음 갱신 이상 위험
읽기 성능 JOIN 비용 발생 빠름
적합 상황 정합성 중요 (금융, 결제) 조회 빈번 (통계, 캐시 테이블)

장점

  • 데이터 중복 최소화 → 저장 공간 효율
  • 갱신 이상 방지 → 데이터 정합성 유지
  • 구조가 명확해 유지보수 용이

단점

  • 테이블 수 증가 → JOIN이 많아져 복잡한 쿼리 필요
  • 과도한 정규화는 읽기 성능 저하
  • 설계 난이도 상승

주의할 점

정규화와 역정규화는 트레이드오프다. 무조건 높은 정규형이 좋은 것이 아니다. 서비스 특성(쓰기 빈도 vs 읽기 빈도, 정합성 요구 수준)에 맞게 선택해야 한다.

역정규화 후 갱신 이상: 중복 컬럼을 여러 테이블에 분산하면 한 곳을 수정할 때 다른 곳도 반드시 함께 수정해야 한다. 애플리케이션 레벨에서 동기화 로직을 관리해야 한다.

: 역정규화는 성능 문제가 실제로 측정된 뒤에 적용한다. 섣불리 역정규화하면 유지보수 비용이 급증한다.