name: normalization-patterns description: "데이터베이스 정규화/비정규화 패턴 라이브러리. 1NF~BCNF 판별 기준, 함수 종속성 분석, 정규화 단계별 변환 절차, 전략적 비정규화 패턴, 공통 도메인 ERD 템플릿을 제공하는 data-modeler 확장 스킬. '정규화', '비정규화', 'ERD 패턴', '함수 종속성', '테이블 분리', '관계 설계' 등 데이터 모델링 시 사용한다. 단, DDL 생성이나 쿼리 최적화는 이 스킬의 범위가 아니다."
Normalization Patterns — 정규화/비정규화 패턴 라이브러리
data-modeler 에이전트가 데이터 모델링 시 활용하는 정규화 규칙, 비정규화 전략, 도메인별 ERD 패턴.
대상 에이전트
data-modeler — 이 스킬의 정규화 규칙과 ERD 패턴을 데이터 모델 설계에 직접 적용한다.
정규화 단계 판별 & 변환
1NF (제1정규형)
규칙: 모든 컬럼은 원자값(atomic value)이어야 한다.
| 위반 패턴 | 문제 | 해결 |
|---|---|---|
| 다중값 컬럼 | tags = "java,python,go" |
별도 테이블 분리 (M:N) |
| 반복 그룹 | phone1, phone2, phone3 |
별도 테이블 분리 (1:N) |
| 복합값 | address = "서울시 강남구 역삼동" |
시/구/동 컬럼 분리 |
2NF (제2정규형)
전제: 1NF 만족 규칙: 부분 함수 종속성 제거 — 복합 기본키의 일부에만 종속되는 컬럼 분리.
| 위반 예 | 종속 관계 | 해결 |
|---|---|---|
주문상세(주문ID, 상품ID, 상품명, 수량) |
상품명 → 상품ID에만 종속 | 상품 테이블 분리 |
3NF (제3정규형)
전제: 2NF 만족 규칙: 이행적 함수 종속성 제거 — 기본키가 아닌 컬럼이 다른 비키 컬럼을 결정하면 안 됨.
| 위반 예 | 종속 관계 | 해결 |
|---|---|---|
직원(ID, 부서ID, 부서명, 부서장) |
부서명, 부서장 → 부서ID 종속 (이행적) | 부서 테이블 분리 |
BCNF (보이스-코드 정규형)
규칙: 모든 결정자(determinant)가 후보키여야 한다.
| 위반 예 | 문제 | 해결 |
|---|---|---|
수강(학생, 과목, 교수) 여기서 교수→과목 |
교수가 결정자이지만 후보키 아님 | 교수-과목 테이블 분리 |
정규화 결정 흐름도
데이터 분석
├─ 원자값 위반? → 1NF 변환
├─ 복합키 & 부분 종속? → 2NF 변환
├─ 이행적 종속? → 3NF 변환
├─ 비후보키 결정자? → BCNF 변환
└─ 성능 요구사항 → 전략적 비정규화 검토
전략적 비정규화 패턴
언제 비정규화하는가?
- 읽기 >> 쓰기 비율이 높을 때
- JOIN이 5개 이상 필요한 빈번한 쿼리
- 실시간 집계/통계가 필요할 때
- 대시보드/리포트 전용 데이터
비정규화 패턴 카탈로그
| 패턴 | 설명 | 적합 상황 | 트레이드오프 |
|---|---|---|---|
| 파생 컬럼 | 계산값 저장 (total_price) |
빈번한 합계 조회 | 갱신 시 동기화 필요 |
| 중복 컬럼 | FK 대상의 자주 쓰는 컬럼 복사 | JOIN 회피 | 데이터 불일치 위험 |
| 사전 조인 테이블 | 조인 결과를 물리 테이블로 | 리포트/대시보드 | 저장 공간, 갱신 복잡 |
| 히스토리 스냅샷 | 시점 데이터 보존 (order_address) |
주문 시점 주소 보관 | 저장 공간 |
| 카운터 컬럼 | likes_count, comments_count |
실시간 카운트 표시 | 동시성 처리 |
| JSON/JSONB | 구조화되지 않은 확장 데이터 | 설정, 메타데이터 | 인덱싱 제한 |
공통 도메인 ERD 패턴
이커머스
users ──1:N──> orders ──1:N──> order_items
│ │
└──1:N──> addresses products
└──1:N──> reviews ──N:1──────┘
│
products ──N:M──> categories (via product_categories)
products ──1:N──> product_images
products ──1:N──> product_variants
핵심 테이블:
users(id, email, name, password_hash, created_at)products(id, name, description, base_price, status)orders(id, user_id, status, total, shipping_address_snapshot)order_items(id, order_id, product_id, variant_id, quantity, unit_price)
SaaS 멀티테넌트
tenants ──1:N──> users ──N:M──> roles (via user_roles)
│ │
└──1:N──> subscriptions permissions ──N:M──> roles
└──1:N──> [도메인 테이블] (tenant_id FK)
핵심: 모든 비즈니스 테이블에 tenant_id 포함, RLS(Row Level Security) 적용
소셜 네트워크
users ──N:M──> users (via follows: follower_id, following_id)
│
└──1:N──> posts ──1:N──> comments
│ └──N:M──> tags (via post_tags)
│ └──1:N──> likes (user_id + post_id UNIQUE)
└──1:N──> messages (sender_id, receiver_id)
CMS/블로그
users ──1:N──> posts ──N:M──> tags (via post_tags)
│
└──1:N──> comments (self-referencing: parent_id)
└──1:N──> media
└──1:1──> post_meta (SEO, OG 등)
관계 패턴
1:1 관계
- 큰 테이블 분할 (자주 쓰는 컬럼 vs 가끔 쓰는 컬럼)
- 선택적 확장 (
user+user_profile) - 구현: FK + UNIQUE 제약
1:N 관계
- 가장 흔한 관계
- 자기참조: 카테고리 트리, 댓글 스레드 (
parent_id) - 구현: 자식 테이블에 FK
M:N 관계
- 중간 테이블(junction table) 필수
- 중간 테이블에 추가 속성 가능 (
created_at,role,quantity) - 네이밍:
{table1}_{table2}또는 의미 있는 이름 (enrollments)
공통 컬럼 패턴
기본 타임스탬프
모든 테이블에 포함:
id— UUID 또는 BIGINT AUTO_INCREMENTcreated_at— TIMESTAMPTZ DEFAULT NOW()updated_at— TIMESTAMPTZ, 트리거로 자동 갱신
Soft Delete
deleted_at— TIMESTAMPTZ NULL (NULL이면 미삭제)- 모든 쿼리에
WHERE deleted_at IS NULL조건 - 복원 가능, 감사 로그 역할
상태 관리
status— ENUM 또는 VARCHAR- 상태 전이 규칙 문서화 필수 (어떤 상태에서 어떤 상태로?)
- 이력이 필요하면 별도
status_history테이블
다국어
- 전략 1: 컬럼 확장 (
name_ko,name_en,name_ja) - 전략 2: 번역 테이블 (
product_translations: product_id, locale, name, description) - 전략 2 권장 (언어 추가 시 스키마 변경 불필요)