본문 바로가기
Database2025년 8월 5일5분 읽기

데이터베이스 정규화와 비정규화 — 실전 판단 기준

YS
김영삼
조회 403

정규화의 본질과 단계

정규화는 데이터 중복을 제거하고 이상(anomaly)을 방지하기 위한 데이터 모델링 기법입니다. 실무에서는 보통 3NF(제3정규형)까지 적용하며, 성능상 필요한 경우 의도적으로 비정규화합니다.

정규화 단계 요약

단계조건제거 대상예시
1NF원자값만 허용반복 그룹전화번호 복수값 분리
2NF완전 함수적 종속부분 종속복합키 일부에만 종속된 컬럼
3NF이행적 종속 제거간접 종속우편번호 → 도시 분리
BCNF결정자가 모두 후보키비후보키 결정자교수-과목 관계

정규화 적용 예시

-- 비정규 상태 (주문 테이블)
CREATE TABLE orders_denorm (
  order_id    INT PRIMARY KEY,
  customer_name VARCHAR(100),
  customer_email VARCHAR(100),  -- 고객 변경 시 모든 주문 수정 필요
  product_name  VARCHAR(100),
  product_price DECIMAL(10,2),  -- 가격 변경 시 불일치 발생
  quantity      INT,
  order_date    TIMESTAMP
);

-- 3NF 적용
CREATE TABLE customers (
  id    SERIAL PRIMARY KEY,
  name  VARCHAR(100) NOT NULL,
  email VARCHAR(100) UNIQUE NOT NULL
);

CREATE TABLE products (
  id    SERIAL PRIMARY KEY,
  name  VARCHAR(100) NOT NULL,
  price DECIMAL(10,2) NOT NULL
);

CREATE TABLE orders (
  id          SERIAL PRIMARY KEY,
  customer_id INT REFERENCES customers(id),
  order_date  TIMESTAMP DEFAULT NOW()
);

CREATE TABLE order_items (
  id         SERIAL PRIMARY KEY,
  order_id   INT REFERENCES orders(id),
  product_id INT REFERENCES products(id),
  quantity   INT NOT NULL,
  unit_price DECIMAL(10,2) NOT NULL  -- 주문 시점 가격 스냅샷
);

비정규화가 필요한 상황

-- 읽기 성능 최적화: 게시글 목록 조회
-- 정규화 상태 → JOIN 필요
SELECT p.*, u.name AS author_name, COUNT(c.id) AS comment_count
FROM posts p
JOIN users u ON p.author_id = u.id
LEFT JOIN comments c ON c.post_id = p.id
GROUP BY p.id, u.name;

-- 비정규화 → JOIN 없이 바로 조회
ALTER TABLE posts ADD COLUMN author_name VARCHAR(100);
ALTER TABLE posts ADD COLUMN comment_count INT DEFAULT 0;

SELECT id, title, author_name, comment_count
FROM posts
ORDER BY created_at DESC
LIMIT 20;

비정규화 판단 기준

  • 읽기 비율: 읽기가 쓰기보다 10배 이상 많으면 비정규화 고려
  • 조인 비용: 3개 이상의 테이블 조인이 자주 발생하는 쿼리
  • 데이터 변경 빈도: 원본 데이터가 거의 변하지 않는 경우
  • 정합성 허용 범위: 약간의 지연된 일관성을 허용할 수 있는 경우
  • 집계 쿼리: COUNT, SUM 등을 미리 계산하여 저장

비정규화 동기화 전략

-- 트리거를 이용한 자동 동기화
CREATE OR REPLACE FUNCTION update_comment_count()
RETURNS TRIGGER AS $$
BEGIN
  IF TG_OP = 'INSERT' THEN
    UPDATE posts SET comment_count = comment_count + 1
    WHERE id = NEW.post_id;
  ELSIF TG_OP = 'DELETE' THEN
    UPDATE posts SET comment_count = comment_count - 1
    WHERE id = OLD.post_id;
  END IF;
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_comment_count
AFTER INSERT OR DELETE ON comments
FOR EACH ROW EXECUTE FUNCTION update_comment_count();

정규화와 비정규화는 이분법이 아닌 스펙트럼입니다. 데이터 모델의 정규화로 시작하되, 성능 병목이 측정된 지점에서만 의도적으로 비정규화하는 것이 가장 실용적인 접근입니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록