PostgreSQL 스키마: 정규화와 비정규화 비교
PostgreSQL 스키마 설계에서 정규화와 비정규화의 핵심 차이, 성능·운영 관점 고려사항, 실제 SQL 예제를 통한 적용 사례 정리된 설계
목차
들어가기
데이터 모델링에서 정규화와 비정규화는 빈번히 맞붙는 주제다. 목적은 명확하다. 데이터 무결성 유지와 쿼리 성능 개선 사이에서 균형을 찾는 일이다. 이 글은 postgres 스키마 설계 가이드 관점에서 두 접근의 차이와 실제 적용 사례를 쉽게 설명한다.
정규화 개요
정규화는 중복을 줄이고 일관성을 유지하는 방법이다. 관계형 원칙에 따라 데이터를 작은 단위로 나눈다. 업데이트, 삭제, 삽입 시 이상현상(anomaly)을 줄인다.
장점
- 데이터 무결성과 일관성 유지.
- 저장공간 절약 가능.
- 트랜잭션 관리와 백업이 명확함.
단점
- 복잡한 JOIN이 늘어나 쿼리 성능 저하 우려.
- 읽기 중심 워크로드에서 지연 발생 가능.
비정규화 개요
비정규화는 읽기 성능을 위해 데이터를 중복 저장하는 선택이다. 캐시와 유사한 역할을 한다. 읽기 응답 시간을 줄이는 대신 쓰기와 일관성 관리가 부담으로 남는다.
장점
- 쿼리 단순화로 읽기 성능 향상.
- 복잡한 JOIN을 피해 응답시간 감소.
단점
- 중복 데이터로 인한 저장공간 증가.
- 동기화 로직이 필요해 쓰기 비용 증가.
언제 어느 쪽을 선택할까
결정은 워크로드 특성에 따라 달라진다. OLTP처럼 트랜잭션 중심이면 정규화가 적합하다. 반면 보고서성 읽기 빈도가 높거나 지연시간이 핵심이라면 비정규화가 유리하다. 또한 인프라와 운영팀의 역량도 고려 대상이다.
판단 기준
- 읽기 대 쓰기 비율
- 데이터 변경 빈도
- 응답 시간 요구사항
- 백업과 복구 정책
실무 사례: 사용자와 주문 데이터
간단한 예제로 차이를 보여준다. 먼저 정규화된 스키마다. 사용자 정보와 주문 정보를 분리한다.
-- 정규화된 테이블
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
total NUMERIC,
created_at TIMESTAMP
);
이 구조는 사용자 정보 변경 시 모든 주문 레코드를 갱신할 필요가 없다. 무결성이 강점이다. 하지만 사용자 이름을 함께 보여주는 주문 목록을 자주 조회하면 JOIN 비용이 발생한다.
비정규화 예시는 주문에 사용자 이름을 복제하는 방식이다. 읽기 쿼리를 단순화한다.
-- 비정규화된 테이블
CREATE TABLE orders_denorm (
id SERIAL PRIMARY KEY,
user_id INT,
user_name TEXT,
total NUMERIC,
created_at TIMESTAMP
);
사용자 이름 변경 시 트리거나 애플리케이션 레벨 동기화가 필요하다. 그 대가로 주문 목록을 조회할 때 JOIN 없이 즉시 결과가 나온다.
성능 고려사항
PostgreSQL에서 인덱스와 파티셔닝은 정규화와 비정규화 성능에 큰 영향을 준다. 정규화 모델은 적절한 인덱스 설계로 JOIN 비용을 낮출 수 있다. 반대로 비정규화 모델은 중복 열에 인덱스를 추가하면 읽기 성능이 더 개선된다. 그러나 인덱스와 중복 모두 쓰기 부담을 증가시킨다.
운영 관점
백업, 마이그레이션, 스키마 변경의 난이도도 설계 선택에 영향을 준다. 정규화는 변경 범위가 좁아 안전하다. 비정규화는 동기화 로직이 복잡해 실패 지점이 늘어난다. 운영 자동화와 모니터링이 성패를 좌우한다.
요약
정규화는 무결성 중심이다. 비정규화는 성능 중심이다. postgres 스키마 설계 가이드 관점에서는 두 방법을 혼합한 혼합형 설계가 현실적이다. 핵심은 워크로드 분석과 테스트다. 정규화 postgres 장단점과 비정규화 설계 postgres의 trade-off를 이해하면 설계 결정을 체계적으로 내릴 수 있다.