컴퓨터 구조 운영체제 네트워크 자료구조 알고리즘 데이터베이스 내 학습
db · 06

정규화

DB정규화설계

정규화는 반복되는 데이터(중복)를 없애서, 값 하나를 고칠 때 한 곳만 고치면 되도록 테이블을 나누는 과정입니다. 1NF → 2NF → 3NF로 갈수록 규칙이 하나씩 더 엄격해지고, 2NF·3NF 위반은 테이블을 나누어 해결합니다. 대신 조회할 때 JOIN이 늘어나는 트레이드오프가 있습니다.

핵심 개념

제1정규형 (1NF)

모든 컬럼이 원자값(하나의 값)만 가져야 합니다. 한 칸에 "노트북, 마우스"처럼 여러 값이 들어있으면 위반입니다.

핵심 개념

부분 함수 종속

기본키가 복합키(A, B)일 때, 키에 속하지 않는 컬럼이 A나 B 중 하나에만 의존하면 부분 함수 종속입니다.

핵심 개념

제2정규형 (2NF)

1NF를 만족하면서, 부분 함수 종속이 없어야 합니다(후보키가 하나인 경우를 기준으로 한 설명입니다). 부분 종속되는 컬럼을 별도 테이블로 분리해서 해결합니다.

핵심 개념

이행 종속

키 A가 B를 결정하고, 키가 아닌 B가 다시 키가 아닌 C를 결정할 때(B는 A를 결정하지 못함) C는 A에 이행 종속입니다.

핵심 개념

제3정규형 (3NF)

2NF를 만족하면서, 이행 종속이 없어야 합니다. 키에 속하지 않는 모든 컬럼은 키에 "직접" 의존해야 합니다(후보키가 하나인 경우를 기준으로 한 설명입니다).

핵심 개념

정규화의 대가

테이블을 나눌수록 중복은 줄지만, 데이터를 한 번에 보려면 JOIN이 필요해집니다. 그래서 조회 성능을 위해 일부러 중복을 허용하는 반정규화를 쓰기도 합니다.

이 시각화는 하나의 예시를 계속 이어갑니다 — "주문 + 상품 + 사용자 등급"이 뒤섞인 비정규형 테이블 하나를 1NF, 2NF, 3NF 탭에서 순서대로 정규화합니다. 각 탭의 시작 테이블은 이전 탭의 결과입니다.
"왜 문제인지"를 먼저 보세요 — 각 탭은 위반 컬럼을 주황색으로 강조한 뒤에야 테이블을 분리합니다. 무엇을 고치는지보다 왜 고쳐야 하는지가 더 중요합니다.
USERS 테이블이 낯설지 않다면 — db-01(관계형 DB 기초), db-02(SQL JOIN)에서 다룬 USERS/ORDERS와 같은 맥락입니다. 정규화의 결과가 왜 그 구조였는지 거꾸로 이해하는 데 도움이 됩니다.
이 시각화에서 확인할 것 — 1NF 탭에서 콤마로 묶인 값이 여러 행으로 펼쳐지는 과정, 2NF·3NF 탭에서 표 하나가 두 개로 갈라지는 과정과 그 이유(부분 종속 vs 이행 종속)의 차이를 비교하세요.

정규화가 해결하려는 문제: 이상 현상

정규형은 에드거 코드(E. F. Codd)가 1970년 관계형 모델 논문에서 제1정규형 개념을 제시한 뒤 이어서 확장된 개념입니다. 중복이 있는 테이블에서는 이상 현상(anomaly)이 생깁니다. 예를 들어 주문 테이블 한 행마다 상품명이 함께 저장되어 있다고 해 봅시다.

갱신 이상 — 상품명을 바꾸려면 그 상품이 들어 있는 모든 주문 행을 고쳐야 합니다. 하나라도 빠뜨리면 같은 상품이 서로 다른 이름을 갖습니다.
삽입 이상 — 주문 번호가 기본키에 포함된 이 테이블에는, 아직 주문되지 않은 새 상품을 주문 번호 없이 넣을 수 없습니다.
삭제 이상 — 어떤 상품의 마지막 주문을 지우면 그 상품 정보까지 함께 사라집니다.

함수 종속과 BCNF

정규형은 함수 종속(functional dependency)으로 정의됩니다. A 값을 알면 B 값이 하나로 정해질 때 A → B라고 씁니다. 3NF 다음으로 자주 거론되는 보이스-코드 정규형(BCNF)은 자명하지 않은 모든 함수 종속의 결정자(→ 왼쪽)가 슈퍼키(후보키를 포함하는 컬럼 집합)여야 한다는 조건입니다. 후보키가 여러 개이고 서로 겹칠 때처럼, 3NF를 만족해도 슈퍼키가 아닌 결정자가 남는 경우가 있고, BCNF는 그것까지 없앱니다.

자주 하는 오해

정규화는 무조건 많이 할수록 좋다 — 정규화는 쓰기 일관성을 지키는 대신 읽을 때 JOIN을 늘립니다. 설계에서는 보통 3NF나 BCNF를 목표로 삼고, 읽기 중심의 분석용 구조는 일부러 중복을 허용하기도 합니다.
정규화는 테이블을 최대한 잘게 쪼개는 일이다 — 목적은 중복으로 인한 이상 현상을 없애는 것이지 테이블 수를 늘리는 것이 아닙니다.
기본키가 컬럼 하나이면 2NF를 항상 만족한다 — 후보키가 모두 컬럼 하나일 때만 그렇습니다. 기본키가 컬럼 하나여도 다른 후보키가 복합키이면, 그 일부에만 의존하는 컬럼이 있을 때 2NF를 위반합니다.
정규화하면 항상 느려진다 — JOIN이 늘어 읽기가 느려질 수 있지만, 성능은 워크로드에 따라 다르므로 항상 느려진다고 단정할 수 없습니다. 정규화의 주목적은 이상 현상을 막는 것입니다.

면접에서 이렇게 물어요

면접 질문

제2정규형과 제3정규형은 무엇이 다른가요?

후보키가 하나인 경우를 기준으로 설명합니다. 2NF는 키의 일부에만 의존하는 컬럼(부분 함수 종속)을 없앱니다. 3NF는 키가 아닌 컬럼을 거쳐 다른 키가 아닌 컬럼이 결정되는 관계(이행 종속)를 없앱니다. 둘 다 키에 속하지 않는 컬럼이 키 전체에 직접 의존하게 만드는 과정입니다.

면접 질문

정규화를 하는 이유와 하지 않는 경우를 말해 보세요.

이상 현상(갱신·삽입·삭제)을 막고 중복을 줄이기 위해서입니다. 읽기가 대부분이고 JOIN 비용이 성능 문제일 때, 또는 분석용 데이터처럼 변경이 거의 없을 때 반정규화를 쓰기도 합니다.

면접 질문

BCNF는 3NF와 무엇이 다른가요?

3NF는 후보키에 속한 컬럼(주 속성)이 결정되는 경우를 허용하지만, BCNF는 자명하지 않은 모든 종속의 결정자가 슈퍼키일 것을 요구합니다. 그래서 BCNF가 3NF보다 엄격합니다.

면접 질문

반정규화는 어떻게 안전하게 하나요?

정규화된 구조를 기준으로 두고 필요한 부분에만 중복 컬럼이나 요약 테이블을 추가합니다. 중복한 값이 서로 어긋나지 않게 하는 방법이 함께 필요합니다.

실제 시스템에서는

이 사이트의 태그 — 토픽의 태그를 topic 테이블 한 칸에 쉼표로 이어 붙이지 않고 topic_tag 테이블로 분리해 두었습니다. 한 칸에 여러 값을 두지 않는 제1정규형 원칙을 따른 설계입니다.
외래 키 제약 — 정규화로 나눈 테이블 사이의 연결은 외래 키로 선언해 DB가 정합성을 지키게 합니다. 참조되는 행을 지우면 거부하거나 함께 지우는 동작을 지정할 수 있습니다.

함께 보면 좋은 토픽

RDBMS 기초 — 테이블, 기본키, 외래키 같은 정규화의 바탕이 되는 개념을 봅니다.
SQL JOIN — 정규화로 나눈 테이블을 다시 합치는 방법을 봅니다.
인덱스 — JOIN이 늘어난 조회를 빠르게 만드는 방법을 봅니다.
트랜잭션 — 여러 테이블에 걸친 변경을 하나로 묶는 방법을 봅니다.

참고 자료

관계형 모델 원 논문 — Codd, "A Relational Model of Data for Large Shared Data Banks", Communications of the ACM, 1970 (제1정규형 개념을 제시한 논문)
교재 — Silberschatz, Korth, Sudarshan, "Database System Concepts" 7판, "Relational Database Design" 장 (3NF·BCNF 정의의 근거)
2NF·3NF 정의 — Codd, "Further Normalization of the Data Base Relational Model", 1971

SQL — 정규화 전(비정규형) vs 정규화 후(3NF)

-- 정규화 전: 한 테이블에 모든 게 뒤섞여 있고, products는 콤마로 여러 값
CREATE TABLE orders_raw (
    order_id    BIGINT PRIMARY KEY,
    user_name   VARCHAR(50),
    user_grade  VARCHAR(10),
    products    VARCHAR(200)   -- '노트북, 마우스' — 1NF 위반
);

-- 정규화 후(3NF): 세 테이블로 분리, 각 non-key 컬럼은 자기 테이블의 PK에만 의존
CREATE TABLE users (
    user_name   VARCHAR(50) PRIMARY KEY,
    user_grade  VARCHAR(10)  NOT NULL
);

CREATE TABLE orders (
    order_id    BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_name   VARCHAR(50) NOT NULL,
    FOREIGN KEY (user_name) REFERENCES users(user_name)
);

CREATE TABLE order_items (
    order_id    BIGINT      NOT NULL,
    product     VARCHAR(50) NOT NULL,
    PRIMARY KEY (order_id, product),
    FOREIGN KEY (order_id) REFERENCES orders(order_id)
);

이 시각화의 3NF 탭 결과와 정확히 같은 구조입니다 — users.user_grade는 더 이상 주문 정보에 끌려다니지 않고, 사용자 한 명당 딱 한 번만 저장됩니다.
김민준의 등급이 바뀌면 users 테이블 딱 한 행만 UPDATE하면 됩니다 — 정규화 전이었다면 김민준의 주문이 100건이면 100행을 다 고쳐야 했을 것입니다.

Java — 정규화된 구조를 그대로 반영한 JPA 엔티티

@Entity
public class User {
    @Id
    private String userName;
    private String userGrade;
}

@Entity
public class Order {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_name")
    private User user;              // user_grade는 여기 없다 — User를 통해서만 접근

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> items = new ArrayList<>();
}

@Entity
public class OrderItem {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Order order;
    private String product;
}

Order에는 userGrade 필드가 없습니다 — 등급이 필요하면 반드시 order.getUser().getUserGrade()처럼 User를 거쳐야 합니다.
이렇게 필드를 직접 두지 않는 것 자체가 "이행 종속을 만들지 않는다"는 3NF 규칙을 코드로 강제하는 방법입니다.

Java — (참고) 조회 성능을 위한 반정규화

@Entity
public class Order {
    // ...

    // 원칙적으로는 3NF 위반(이행 종속)이지만,
    // "주문 목록에 등급 뱃지 표시"가 매번 JOIN을 유발해 느리다면
    // 의도적으로 복제해 두는 경우가 있다 (반정규화)
    private String userGradeSnapshot;
}

이 시각화의 "정규화의 대가" 카드에서 언급한 트레이드오프가 바로 이것입니다 — userGradeSnapshot은 등급이 바뀌면 여러 곳을 고쳐야 하는 대신, 조회할 때 User와의 JOIN을 피할 수 있습니다.
이런 선택은 "정규화 규칙을 모르고 어긴 것"과 "알고서 성능을 위해 의도적으로 어긴 것"의 차이이므로, 반정규화는 항상 팀 안에서 합의하고 문서로 남겨야 합니다.

Q1

정규화(Normalization)의 목적은?

같은 정보가 여러 곳에 중복 저장되면 갱신할 때 문제가 생깁니다.
정규화는 테이블을 분해해 데이터 중복을 줄이고, 데이터를 추가/수정/삭제할 때 발생할 수 있는 이상 현상을 방지하는 설계 과정입니다.

Q2

제1정규형(1NF)을 만족하기 위한 조건은?

한 칸에 여러 값을 콤마로 나열해 넣으면 안 됩니다.
1NF는 각 컬럼이 하나의 원자적인 값만 가져야 한다는 규칙으로, 한 셀에 여러 값을 리스트 형태로 저장하는 것을 금지합니다.

Q3

제2정규형(2NF)에서 다루는 문제는?

복합키의 '일부'에만 의존하는 컬럼이 문제입니다.
2NF는 1NF를 만족하면서, 복합키를 사용하는 경우 기본키 전체가 아니라 일부에만 종속되는(부분 함수 종속) 컬럼을 별도 테이블로 분리해 제거합니다.