DB 정규화 vs 비정규화

CH.dev·2025년 7월 31일
post-thumbnail

📄 요약

데이터베이스 설계의 핵심은 정규화(Normalization)와 비정규화(Denormalization) 사이의 줄다리기에 있음.
정규화는 데이터의 중복을 제거하여 데이터 무결성유연성을 확보하는 과정임. 반면 비정규화는 읽기 성능을 극대화하기 위해 의도적으로 중복을 허용하는 성능 최적화 전략임.

정규화는 데이터 변경(CUD) 비용을 줄이고 일관성을 지키는 데 유리하지만, 복잡한 조회(Read) 시 JOIN으로 인한 성능 저하를 감수해야 함. 비정규화는 JOIN을 최소화해 조회 성능을 끌어올리지만, 데이터 중복으로 인한 수정 비용 증가와 잠재적 불일치라는 대가를 치름. 따라서 성공적인 데이터 모델링은 시스템의 읽기/쓰기 패턴과 데이터 정합성 요구 수준을 정확히 분석하여 두 전략의 균형점을 찾는 것.

💡 주요 개념

데이터 정규화 (Normalization)

데이터의 중복을 제거하며 테이블을 논리적으로 분해하는 과정. 데이터 구조를 명확히 하고, 데이터 삽입/수정/삭제 시 발생할 수 있는 이상 현상(Anomaly)을 방지하는 것이 주 목적임.

  • 주요 정규형

    • 제1정규형(1NF): 테이블의 모든 컬럼 값이 원자 값(Atomic Value)을 갖도록 함. (하나의 필드에 여러 값이 들어가지 않도록 분리)
    • 제2정규형(2NF): 부분 함수 종속을 제거함. 기본 키의 일부에만 종속된 컬럼을 별도 테이블로 분리. (복합 키를 사용하는 테이블에서 주로 발생)
    • 제3정규형(3NF): 이행 함수 종속을 제거함. 기본 키가 아닌 다른 일반 컬럼에 종속된 컬럼을 분리. (A→B, B→C 일 때 A→C 관계를 분리)
  • 장점

    • 데이터 무결성 극대화: 데이터 중복이 없어 불일치 가능성이 원천적으로 차단됨.
    • 유지보수 용이성: 데이터 구조 변경 시 파급 효과가 적어 모델 확장이 유연함.
    • 저장 공간 효율화: 중복 데이터가 없어 저장 공간 낭비가 없음.
  • 단점

    • 조회 성능 저하: 원하는 데이터를 얻기 위해 여러 테이블을 JOIN해야 하므로, 쿼리가 복잡해지고 응답 속도가 느려질 수 있음.

데이터 비정규화 (Denormalization)

조회 성능이 매우 중요한 경우, 정규화된 모델에 의도적으로 중복을 허용하거나 테이블을 병합하여 JOIN의 비용을 줄이는 과정임. 정규화의 원칙을 위배하여 성능을 얻는 트레이드오프 전략임.

  • 주요 기법

    • 컬럼 중복: JOIN이 빈번한 컬럼을 조회 테이블에 미리 추가함. (예: posts 테이블에 author_username 추가)
    • 테이블 병합/분할: 1:1 관계의 테이블을 합치거나, 자주 함께 조회되는 부분을 묶어 파생 테이블을 생성함.
    • 계산된 값 추가: 통계나 합계처럼 계산이 복잡한 값을 미리 계산하여 컬럼으로 저장함. (예: 게시물 테이블에 comment_count 컬럼 추가)
  • 장점

    • 읽기 성능 극대화: JOIN 연산을 획기적으로 줄여 조회 쿼리의 성능을 크게 향상시킴.
    • 쿼리 단순화: 애플리케이션 개발 시 필요한 쿼리가 단순해져 개발 생산성이 높아짐.
  • 단점

    • 쓰기/수정 비용 증가: 데이터 변경 시 관련된 모든 중복 데이터를 함께 수정해야 하므로 INSERT, UPDATE 비용이 커짐.
    • 데이터 불일치 위험: 데이터 수정 시 일부 중복 데이터가 누락되면, 데이터 간의 정합성이 깨질 수 있음 (Update Anomaly).

실무적 트레이드오프

일단 정규화하고, 성능 병목 지점에서 비정규화 검토

  • 정규화를 우선하는 경우 (Write-heavy, OLTP 시스템)

    • 데이터의 생성/수정/삭제가 빈번한 시스템. (ex: 은행 거래 시스템, 주문/결제 시스템, 실시간 재고 관리)
    • 데이터의 정합성이 시스템의 신뢰도와 직결되는 경우.
    • 아직 시스템의 주요 쿼리 패턴이 명확하지 않은 개발 초기 단계.
  • 비정규화를 고려하는 경우 (Read-heavy, OLAP 시스템)

    • 읽기(조회)가 쓰기보다 압도적으로 많고, 응답 속도가 비즈니스 요구사항의 핵심일 때. (ex: 대규모 데이터 분석/통계 대시보드, BI 리포트, SNS 피드)
    • JOIN의 비용이 데이터 수정 비용을 압도할 만큼 비효율적인 것이 명확히 입증되었을 때.
    • 데이터가 거의 변하지 않는 정적인 데이터를 다룰 때. (ex: 과거 기록, 로그성 데이터)

🧠 코드 예시

사용자(users)의 등급(level) 정보가 있고, 사용자가 작성한 게시물(posts)을 조회하는 시나리오.

-- 1. 정규화된 테이블 설계 (Normalized)
-- 사용자 정보와 게시물 정보를 완벽히 분리. 사용자 등급 변경 시 users 테이블만 수정하면 됨.

CREATE TABLE users (
    user_id INT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    user_level VARCHAR(20) DEFAULT 'BRONZE'
);

CREATE TABLE posts (
    post_id INT PRIMARY KEY,
    author_id INT, -- Foreign Key
    title VARCHAR(255) NOT NULL,
    FOREIGN KEY (author_id) REFERENCES users(user_id)
);

-- 게시물 조회 시, 작성자 이름과 등급을 알려면 반드시 JOIN이 필요.
SELECT p.title, u.username, u.user_level
FROM posts p
JOIN users u ON p.author_id = u.user_id
WHERE p.post_id = 101;


-- 2. 비정규화된 테이블 설계 (Denormalized)
-- 게시물 테이블에 작성자 이름과 등급을 중복 저장하여 JOIN을 제거.

CREATE TABLE posts_denormalized (
    post_id INT PRIMARY KEY,
    author_id INT,
    author_username VARCHAR(50), -- 사용자 이름 중복 저장
    author_level VARCHAR(20),   -- 사용자 등급 중복 저장
    title VARCHAR(255) NOT NULL
);

-- JOIN 없이 단일 테이블 조회만으로 모든 정보를 가져와 성능상 유리.
SELECT title, author_username, author_level
FROM posts_denormalized
WHERE post_id = 101;


-- 3. 치명적인 트레이드오프: 데이터 수정 시 비용 비교
-- 사용자 ID 1번 유저의 등급이 'BRONZE'에서 'GOLD'로 변경되는 상황

-- [정규화 모델] 단 1개의 레코드만 수정하면 끝. 데이터 정합성 완벽 보장.
UPDATE users SET user_level = 'GOLD' WHERE user_id = 1;

-- [비정규화 모델] 해당 유저가 작성한 모든 게시물의 author_level을 일일이 찾아 수정해야 함.
-- 만약 이 과정에서 일부 레코드가 누락되면 데이터 불일치(Inconsistency) 발생.
UPDATE posts_denormalized SET author_level = 'GOLD' WHERE author_id = 1;

🔍 더 깊이 찾아보기

  1. 데이터베이스 인덱싱 (Indexing): 비정규화 이전에 시도해야 할 가장 기본적인 성능 튜닝 기법. JOIN이나 WHERE 절에 사용되는 컬럼에 인덱스를 생성하면 정규화된 상태에서도 드라마틱한 성능 향상을 기대할 수 있음.
  2. CAP 이론 (CAP Theorem): 분산 시스템은 일관성(C), 가용성(A), 분할 용인(P) 세 가지를 동시에 만족시킬 수 없다는 이론. RDBMS는 보통 일관성(C)을 우선하여 정규화 모델에 적합하고, 많은 NoSQL은 가용성(A)을 우선하기 위해 비정규화된 데이터 모델을 채택하는 경향이 있음. 이 이론은 아키텍처 선택의 근본적인 배경을 이해하게 해줌.
  3. 구체화된 뷰 (Materialized View): 데이터베이스가 관리하는 자동화된 비정규화. 복잡한 JOIN이나 집계 쿼리의 결과를 물리적인 테이블로 미리 저장해두고, 원본 데이터가 변경될 때마다 DBMS가 주기적으로 결과를 갱신해줌. 성능과 정합성 사이의 균형을 맞추는 강력한 도구.
  4. CQRS (Command Query Responsibility Segregation): 명령(쓰기) 모델과 조회(읽기) 모델을 아키텍처 수준에서 분리하는 패턴. 쓰기 모델은 정규화를 통해 데이터 무결성을 보장하고, 조회 모델은 비정규화를 통해 성능을 최적화하는 고급 전략. MSA(마이크로서비스 아키텍처) 환경에서 자주 고려됨.
profile
더 이상 미룰 수 없다 나의 공부 나의 성장

0개의 댓글