데이터베이스 정규화/비정규화 패턴 라이브러리. 1NF~BCNF 판별 기준, 함수 종속성 분석, 정규화 단계별 변환 절차, 전략적 비정규화 패턴, 공통 도메인 ERD 템플릿을 제공하는 data-modeler 확장 스킬. '정규화', '비정규화', 'ERD 패턴', '함수 종속성', '테이블 분리', '관계 설계' 등 데이터 모델링 시 사용한다. 단, DDL 생성이나 쿼리 최적화는 이 스킬의 범위가 아니다.
data-modeler 에이전트가 데이터 모델링 시 활용하는 정규화 규칙, 비정규화 전략, 도메인별 ERD 패턴.
data-modeler — 이 스킬의 정규화 규칙과 ERD 패턴을 데이터 모델 설계에 직접 적용한다.
규칙: 모든 컬럼은 원자값(atomic value)이어야 한다.
| 위반 패턴 | 문제 | 해결 |
|---|---|---|
| 다중값 컬럼 | tags = "java,python,go" | 별도 테이블 분리 (M:N) |
| 반복 그룹 | phone1, phone2, phone3 | 별도 테이블 분리 (1:N) |
| 복합값 | address = "서울시 강남구 역삼동" | 시/구/동 컬럼 분리 |
전제: 1NF 만족 : 부분 함수 종속성 제거 — 복합 기본키의 일부에만 종속되는 컬럼 분리.
| 위반 예 | 종속 관계 | 해결 |
|---|---|---|
주문상세(주문ID, 상품ID, 상품명, 수량) | 상품명 → 상품ID에만 종속 | 상품 테이블 분리 |
전제: 2NF 만족 규칙: 이행적 함수 종속성 제거 — 기본키가 아닌 컬럼이 다른 비키 컬럼을 결정하면 안 됨.
| 위반 예 | 종속 관계 | 해결 |
|---|---|---|
직원(ID, 부서ID, 부서명, 부서장) | 부서명, 부서장 → 부서ID 종속 (이행적) | 부서 테이블 분리 |
규칙: 모든 결정자(determinant)가 후보키여야 한다.
| 위반 예 | 문제 | 해결 |
|---|---|---|
수강(학생, 과목, 교수) 여기서 교수→과목 | 교수가 결정자이지만 후보키 아님 | 교수-과목 테이블 분리 |
데이터 분석
├─ 원자값 위반? → 1NF 변환
├─ 복합키 & 부분 종속? → 2NF 변환
├─ 이행적 종속? → 3NF 변환
├─ 비후보키 결정자? → BCNF 변환
└─ 성능 요구사항 → 전략적 비정규화 검토
| 패턴 | 설명 | 적합 상황 | 트레이드오프 |
|---|---|---|---|
| 파생 컬럼 | 계산값 저장 (total_price) | 빈번한 합계 조회 | 갱신 시 동기화 필요 |
| 중복 컬럼 | FK 대상의 자주 쓰는 컬럼 복사 | JOIN 회피 | 데이터 불일치 위험 |
| 사전 조인 테이블 | 조인 결과를 물리 테이블로 | 리포트/대시보드 | 저장 공간, 갱신 복잡 |
| 히스토리 스냅샷 | 시점 데이터 보존 (order_address) | 주문 시점 주소 보관 | 저장 공간 |
| 카운터 컬럼 | likes_count, comments_count | 실시간 카운트 표시 | 동시성 처리 |
| JSON/JSONB | 구조화되지 않은 확장 데이터 | 설정, 메타데이터 | 인덱싱 제한 |
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)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)
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 등)
user + user_profile)parent_id)created_at, role, quantity){table1}_{table2} 또는 의미 있는 이름 (enrollments)모든 테이블에 포함:
id — UUID 또는 BIGINT AUTO_INCREMENTcreated_at — TIMESTAMPTZ DEFAULT NOW()updated_at — TIMESTAMPTZ, 트리거로 자동 갱신deleted_at — TIMESTAMPTZ NULL (NULL이면 미삭제)WHERE deleted_at IS NULL 조건status — ENUM 또는 VARCHARstatus_history 테이블name_ko, name_en, name_ja)product_translations: product_id, locale, name, description)