# 킨텍스 자동전시시스템 — 데이터 아키텍처 (A-4)

> 작성: kintex-data-architect(DA) · 작성일: 2026-07-11 · 버전: **v1.0**
> 근거: [`docs/PLANNING.md`](../PLANNING.md) v2.0(§5A M15/M16·§5B 공통코드·§7 ERD·§8 아키텍처) · [`_workspace/01_backend_contracts.md`](../../_workspace/01_backend_contracts.md)(P0 API 계약·§8 매퍼 인수) · [`docs/COMMON_CODES.md`](../COMMON_CODES.md)(공통코드) · [`docs/assets/floorplans/README.md`](../assets/floorplans/README.md)(홀 실측·트렌치 CAD) · 룰셋 `rulesets/compliance-v1.json`·`rates-v1.json`
> **문서 소유권(DA 트랙)**: 본 문서는 **데이터 모델·표준·거버넌스의 단일 출처**다. 물리 스키마(DDL·PostGIS·MyBatis 매퍼 XML) **구현은 kintex-db-engineer**가 담당하며, 본 문서는 그 구현 대상(target model)·표준·검수 기준을 정의한다. DA는 설계·표준·검수만 하고 `src/backend/**/db`·매퍼는 수정하지 않는다.
> 정합 대상: A-1 app.md(AA)·A-2 system.md(SA)·A-3 tech.md(TA)·A-5 network.md(NA) — 상충 발견 시 A-6(reviewer) 티켓화.

---

## 0. 범위·계층·원칙

### 0-1. 데이터 아키텍처 스코프

| 계층 | 대상 | 저장소 |
|---|---|---|
| 공통·시스템관리 (WISE/UIWS 이식) | 사용자·역할·공통코드·메뉴·감사·업무모듈 | PostgreSQL `TB_*` |
| 도메인 (킨텍스 코어 P0) | 행사·홀·부스·설계·유틸리티·렌더잡 | PostgreSQL + **PostGIS** |
| 도메인 (v2.0 확장) | 옥션·관람객/리드·CMS·마스터데이터 | PostgreSQL |
| 마스터·룰셋 (버전 관리 데이터) | 홀·요율·유틸요금·규정 룰셋·등록업체 | 파일(룰셋 JSON) + `TB_*` 마스터 |
| BI 데이터마트 (M16) | Fact/Dim 스타 스키마 + KpiSnapshot | PostgreSQL(별도 스키마 `mart`) / 읽기 전용 복제 |
| 대용량 바이너리 | 도면·생성 이미지·서식·견적 PDF | 오브젝트 스토리지(경로만 DB) |

### 0-2. 설계 원칙 (불변)

1. **단일 공간 원천**: 부스 폴리곤·트렌치 포인트·배선 LineString은 **PostGIS 단일 지오메트리 원천**. M2 검증·M4 라우팅·M5 시각화·M13 wayfinding·M14 부하집계·M16 ㎡당 수익이 **같은 지오메트리를 재사용**(PLANNING §7-3 불변, §4 설계원칙 (1)).
2. **룰셋은 데이터**: 요율·규정은 코드가 아닌 **버전 관리 파일**(`compliance-v*.json`·`rates-v*.json`). 모든 산출물에 `rulesetVersion`·`disclaimer` 각인(감사·면책). 마스터데이터 개정은 M18 백오피스에서 무중단 반영.
3. **PII 최소수집·분리·암호화**: 관람객/리드 개인정보는 §5 분류·보존·동의·암호화 정책을 강제. 민감 컬럼은 API 응답에서 완전 제외(계약 §0-3).
4. **운영/분석 분리**: 경영 지표는 운영 DB 직조회 금지 — **BI 데이터마트(스타 스키마)** 배치 적재 또는 읽기 전용 복제로 운영 부하 회피(PLANNING §8-1).
5. **이식 우선(재설계 금지)**: 공통·시스템·인증 스키마는 `workspace/uiws` `TB_*`를 이식(멱등 DDL). 킨텍스 고유는 도메인 테이블에만.

---

## 1. 전사 데이터 모델 — 개념(Conceptual)

### 1-1. 개념 ERD (도메인 영역)

```mermaid
erDiagram
    EVENT ||--o{ HALL_ASSIGNMENT : "배정"
    HALL ||--o{ HALL_ASSIGNMENT : "가용"
    HALL_ASSIGNMENT ||--o{ LAYOUT : "배치안(버전)"
    LAYOUT ||--o{ BOOTH : "부스(폴리곤)"
    HALL ||--o{ TRENCH : "트렌치 그리드"
    BOOTH ||--o{ DESIGN_PLAN : "설계안(버전)"
    BOOTH ||--o{ UTILITY_ORDER : "유틸리티 신청"
    UTILITY_ORDER ||--o{ WIRING_PATH : "배선(LineString)"
    BOOTH ||--o{ RENDER_JOB : "시각화 샷"
    EVENT ||--o{ EVENT_MEMBER : "참여자(RBAC)"
    USER ||--o{ EVENT_MEMBER : "소속"
    COMPANY ||--o{ EVENT_MEMBER : "업체계정"

    EVENT ||--o{ AUCTION : "옥션(M15)"
    AUCTION ||--o{ QUOTATION : "견적서=응찰"
    AUCTION ||--o| AWARD : "낙찰"
    COMPANY ||--o{ QUOTATION : "응찰업체(등록검증)"
    BOOTH ||--o{ AUCTION : "자료첨부"

    EVENT ||--o{ REGISTRATION : "관람객 등록(M10)"
    VISITOR ||--o{ REGISTRATION : "관람객"
    REGISTRATION ||--o{ BADGE : "배지/QR"
    BADGE ||--o{ CHECK_IN : "체크인"
    BOOTH ||--o{ LEAD : "리드캡처"
    VISITOR ||--o{ LEAD : "스캔대상"

    EVENT ||--o{ SETTLEMENT : "정산(M9)"
    EVENT ||--o{ DOCUMENT : "서류/마일스톤(M6)"
    EVENT ||--o{ CONTENT : "CMS(M17)"

    EVENT ||--o{ FACT_MART : "BI 집계(M16)"
```

### 1-2. 주제영역(Subject Area) 맵

| 주제영역 | 핵심 엔티티 | 소유 모듈 | 특성 |
|---|---|---|---|
| **행사·조직·권한** | Event, User, Company, EventMember, Role | §5B·M18 | 마스터·RBAC 기준축 |
| **공간·시설** | Hall, HallFeature, Trench | M2·마스터 | PostGIS 지오메트리 |
| **설계·시공(P0)** | Layout, Booth, DesignPlan, UtilityOrder, WiringPath, RenderJob | M2~M5 | 버전·공간·비동기 |
| **발주·정산** | Auction, Quotation, Award, Settlement, PaymentSchedule, Document | M6·M9·M15 | 금액·계약·감사 |
| **관람·참가** | Visitor, Registration, Badge, CheckIn, Lead, Meeting | M10·M11 | **PII 집중 영역** |
| **콘텐츠·마스터** | Content, Microsite, MasterData, Ruleset | M17·M18 | 다국어·버전 |
| **분석(BI)** | FactBooking/Settlement/Utility/Auction/Visitor, Dim*, KpiSnapshot | M16 | 스타 스키마·집계 |
| **공통·감사** | AuditLog, CodeGroup, Code, Menu, Notification | §5B | 이식·전 모듈 공유 |

---

## 2. 전사 데이터 모델 — 논리·물리(Logical/Physical)

> 물리 테이블은 kintex-db-engineer가 구현. 아래는 **표준 대상 모델**(테이블·컬럼·타입·제약). 명명 규칙은 §4. `geom` 컬럼 상세는 §3.

### 2-1. 코어 P0 물리 ERD

```mermaid
erDiagram
    TB_EVENT {
        uuid    event_id PK
        varchar event_name
        date    start_date
        date    end_date
        varchar status
        timestamptz created_at
    }
    TB_HALL {
        varchar hall_id PK "H1..H10, 반홀 H1A"
        varchar hall_name
        numeric area_m2
        numeric floor_load_t_per_m2
        numeric width_m
        numeric depth_m
        numeric ceiling_m
        varchar floor_finish "concrete_polished|carpet"
        int     booth_capacity
        geometry footprint "Polygon,0"
    }
    TB_HALL_ASSIGNMENT {
        uuid    assignment_id PK
        uuid    event_id FK
        varchar hall_id FK
        date    occupy_from
        date    occupy_to
    }
    TB_BOOTH {
        uuid    booth_id PK
        uuid    layout_id FK
        varchar booth_no "A-102"
        varchar booth_type "independent|assembled"
        numeric width_m
        numeric depth_m
        numeric height_m
        numeric floor_load_t_per_m2
        boolean premium
        uuid    assigned_company_id FK "nullable"
        geometry geom "Polygon,0 · 홀로컬"
    }
    TB_LAYOUT {
        uuid    layout_id PK
        uuid    assignment_id FK
        int     version
        varchar name
        varchar status "draft|submitted|approved|rejected"
        int     source_option "선택/병합 출처 안번호"
        jsonb   merge_provenance "병합 출처 레이어"
        timestamptz updated_at
    }
    TB_TRENCH {
        uuid    trench_id PK
        varchar hall_id FK
        varchar supply_matrix "power,water,air,gas,network 비트/배열"
        boolean assumed "가정 그리드 여부(R4)"
        geometry geom "Point,0 · 탭포인트"
        geometry run_geom "LineString,0 · nullable"
    }
    TB_DESIGN_PLAN {
        uuid    design_id PK
        uuid    booth_id FK
        int     version
        varchar status
        jsonb   spec "DesignSpec"
        timestamptz updated_at
    }
    TB_UTILITY_ORDER {
        uuid    order_id PK
        uuid    booth_id FK
        varchar status "draft|submitted|relayed"
        jsonb   quote "UtilityQuote 스냅샷"
        varchar rateset_version
        varchar location_diagram_url
        timestamptz created_at
    }
    TB_WIRING_PATH {
        uuid    wiring_id PK
        uuid    order_id FK
        varchar kind "power|network|plumbing|air"
        numeric kw "nullable"
        numeric length_m
        geometry geom "LineString,0"
    }
    TB_RENDER_JOB {
        uuid    job_id PK
        uuid    booth_id FK
        uuid    event_id FK
        varchar shot_preset "S1..S7"
        varchar status "QUEUED|RUNNING|DONE|FAILED"
        varchar image_url
        varchar schema_hash "캐시키"
        varchar model_version
        varchar error_message "요약만"
        timestamptz created_at
    }
    TB_COMPANY {
        uuid    company_id PK
        varchar company_name
        varchar registration_no "사업자번호(등록검증)"
        varchar category_code "14분류 CONTRACTOR_CATEGORY"
        varchar region
        boolean kintex_registered "미등록 응찰 차단 게이트"
    }
    TB_EVENT_MEMBER {
        uuid    member_id PK
        uuid    event_id FK
        uuid    user_id FK
        uuid    company_id FK "nullable"
        uuid    booth_id FK "nullable · 참가업체 부스 스코프"
        varchar event_role "ORGANIZER|EXHIBITOR|CONTRACTOR|HALL_MANAGER"
    }

    TB_EVENT ||--o{ TB_HALL_ASSIGNMENT : ""
    TB_HALL ||--o{ TB_HALL_ASSIGNMENT : ""
    TB_HALL ||--o{ TB_TRENCH : ""
    TB_HALL_ASSIGNMENT ||--o{ TB_LAYOUT : ""
    TB_LAYOUT ||--o{ TB_BOOTH : ""
    TB_BOOTH ||--o{ TB_DESIGN_PLAN : ""
    TB_BOOTH ||--o{ TB_UTILITY_ORDER : ""
    TB_UTILITY_ORDER ||--o{ TB_WIRING_PATH : ""
    TB_BOOTH ||--o{ TB_RENDER_JOB : ""
    TB_COMPANY ||--o{ TB_BOOTH : "배정"
    TB_EVENT ||--o{ TB_EVENT_MEMBER : ""
```

**계약 정합 근거**(01_backend_contracts §8 매퍼 인수 목록):
- `BoothMapper` → `TB_BOOTH.geom`(ST_MakePolygon/ST_AsGeoJSON), 판매면적 `ST_Area(geom)`, 통로폭 `ST_Distance/ST_Buffer`, 비상구 `ST_Intersects(TB_HALL_FEATURE)`.
- `DesignMapper` → `TB_DESIGN_PLAN.spec`(jsonb), `findEventIdByBooth`(RBAC 역참조 = TB_BOOTH→TB_LAYOUT→TB_HALL_ASSIGNMENT→event_id).
- `WiringMapper` → `TB_TRENCH` KNN(`geom <-> :point`), `TB_WIRING_PATH.geom` 최단(ST_Length), `TB_TRENCH.assumed` 플래그.
- `RenderJobMapper` → `TB_RENDER_JOB` 내구 이력·쿼터 정본(`countSucceededByEvent`, Redis는 큐/실시간).
- `UserMapper` → `TB_USER`(§2-3) 인증행(해시 응답 제외)·`TB_EVENT_MEMBER` 역할.

### 2-2. v2.0 확장 물리 ERD (옥션·관람·CMS)

```mermaid
erDiagram
    TB_AUCTION {
        uuid    auction_id PK
        uuid    event_id FK
        varchar auction_type "REVERSE|RFQ|FIXED"
        varchar category_code "공종 14분류"
        varchar status "OPEN|BIDDING|AWARDED|CLOSED"
        int     round_no
        timestamptz deadline_at
        varchar award_criteria "LOWEST|COMPOSITE"
        jsonb   weight "가격/평판/납기 가중치"
        jsonb   attached_refs "Booth/Design/Utility/Render 참조 자료"
    }
    TB_QUOTATION {
        uuid    quotation_id PK
        uuid    auction_id FK
        uuid    company_id FK "등록업체 검증"
        int     version
        varchar status "SUBMITTED|REVISED|AWARDED|REJECTED"
        jsonb   line_items "공종·자재·수량·단가·금액"
        numeric subtotal
        numeric vat
        numeric total
        date    valid_until
        varchar lead_time
        varchar pdf_url
        timestamptz submitted_at
    }
    TB_AWARD {
        uuid    award_id PK
        uuid    auction_id FK
        uuid    quotation_id FK "선정 견적서"
        numeric composite_score
        varchar reason
        varchar contract_doc_url "M6/M9 연동"
        timestamptz awarded_at
    }
    TB_VISITOR {
        uuid    visitor_id PK
        varchar name_enc "PII·AES-GCM"
        varchar email_enc "PII·AES-GCM"
        varchar phone_enc "PII·AES-GCM"
        varchar org_name "준식별"
        varchar job_title
        jsonb   interests "관심 업종"
        varchar visitor_type "VISITOR|BUYER"
        timestamptz created_at
    }
    TB_REGISTRATION {
        uuid    registration_id PK
        uuid    event_id FK
        uuid    visitor_id FK
        varchar reg_type
        boolean consent_privacy "동의(필수)"
        boolean consent_marketing "동의(선택·정보통신망법)"
        timestamptz consent_at
        timestamptz registered_at
    }
    TB_BADGE {
        uuid    badge_id PK
        uuid    registration_id FK
        varchar qr_token "회전 토큰·비추측"
        varchar badge_template_id "M17"
    }
    TB_CHECK_IN {
        uuid    checkin_id PK
        uuid    badge_id FK
        timestamptz checked_at
        varchar gate
    }
    TB_LEAD {
        uuid    lead_id PK
        uuid    event_id FK
        uuid    booth_id FK "참가업체 스코프"
        uuid    visitor_id FK
        int     interest_score
        varchar memo_enc "PII·AES-GCM"
        boolean consent_share "리드 공유 동의"
        timestamptz captured_at
    }
    TB_CONTENT {
        uuid    content_id PK
        uuid    event_id FK "nullable"
        varchar content_type
        varchar locale "ko|en|zh|ja"
        int     version
        varchar status "draft|review|published"
        jsonb   body
    }

    TB_AUCTION ||--o{ TB_QUOTATION : ""
    TB_AUCTION ||--o| TB_AWARD : ""
    TB_QUOTATION ||--o| TB_AWARD : "선정"
    TB_VISITOR ||--o{ TB_REGISTRATION : ""
    TB_REGISTRATION ||--o{ TB_BADGE : ""
    TB_BADGE ||--o{ TB_CHECK_IN : ""
    TB_VISITOR ||--o{ TB_LEAD : ""
```

**보조 테이블**(도메인 완결): `TB_SETTLEMENT`(정산·M9)·`TB_PAYMENT_SCHEDULE`(납부 스케줄 20/30/20/30 + 예치금)·`TB_DOCUMENT`(서류·마일스톤 D-150/30/25/7·M6)·`TB_MEETING`(비즈매칭·M11)·`TB_MICROSITE`(참가업체·M17)·`TB_MASTER_DATA`(마스터 버전·M18). 상세 컬럼은 해당 도메인 에이전트 확정 시 본 문서 갱신.

### 2-3. 공통·시스템관리 물리 모델 (WISE/UIWS 이식 — 정본 참조)

> **재설계 금지**: 아래는 `workspace/uiws` `TB_*` 정본을 **그대로 이식**(멱등 DDL·`sql.init mode=always`+continue-on-error). 킨텍스는 표준 준수만 하고 컬럼을 임의 변경하지 않는다. 상세 컬럼 정의는 UIWS 레퍼런스가 정본.

| 테이블 | 역할 | 킨텍스 접합 |
|---|---|---|
| `TB_USER` | 사용자·인증(BCrypt 해시·`otp_secret` AES) | 6역할 + 등록업체 계정 + 관람객 셀프서비스 |
| `TB_CODE_GRP` / `TB_CODE` | 공통코드 그룹/값 | §4-3 도메인 코드 적재 |
| `TB_MENU` | 메뉴 트리·권한 매핑 | 역할별 포털 IA(§2-1) |
| `TB_AUDIT_LOG` | 감사 로그 | 승인·**낙찰**·설계변경·룰셋개정·**리드 접근(PII)** 전수 |
| `TB_NOTIFICATION` | 통합 알림 | D-데이 리마인더·낙찰·결제 |
| worklog/schedule/message/notice/meeting/report 등 | 공통 업무 | §5B-2 접합점만 이식 |

- **인증 표준(§5B-3)**: JWT + TOTP(RFC6238, SHA1·30s·6자리·±1) 2차 인증 + 로그인 실패 잠금. `admin` 비번은 env `ADMIN_PASSWORD_ENC`(AES-256-GCM) + 별도 키파일 복호 → 기동 시 BCrypt 재시드. **하드코딩 시드 금지**.
- **행사 RBAC 이중 평가**: 전역 `USER_ROLE`(WISE) + 행사 스코프 `EVENT_ROLE`(TB_EVENT_MEMBER) 병행. 킨텍스 1차 권한 = `EVENT_ROLE`(COMMON_CODES §3).

---

## 3. 공간 데이터 모델 표준 (PostGIS)

> M2~M5·M13·M14·M16이 공유하는 **단일 공간 원천**. 물리 구현(ST_* 매퍼 XML)은 db-engineer, 좌표계·타입·인덱스·검증 계약은 본 절이 표준.

### 3-1. 좌표계 표준 — 홀 로컬 데카르트

| 항목 | 표준 | 근거 |
|---|---|---|
| **SRID** | **`0`(로컬 데카르트, 미터)** — 지리좌표(4326) 아님 | 부스/트렌치/배선은 홀 로컬 미터 좌표(계약 `polygon`=홀 로컬 미터, `[[0,0],[6,0]...]`) |
| 타입 | **`geometry`**(geography 아님) | 평면 미터 연산: `ST_Area`=㎡ 직접, `ST_Distance`=m 직접, `ST_Length`=m 직접 |
| 원점 | 홀별 원점(도면 좌하단) 기준, hall_id로 좌표계 분리 | 홀마다 독립 로컬 원점 |
| 단위 | 미터(m). 각도는 도(°) | 계약 `sizeM`·`heightM`·`lengthM` |
| 정밀도 | 좌표 소수 3자리(mm), 면적/길이 소수 2자리 | 시공 실무 정밀도 |

> **주의(교차 좌표계 금지)**: 홀 로컬 좌표는 홀 간 직접 공간연산 불가(각 홀 원점 상이). 홀 전경(S7)·부지 컨텍스트가 필요하면 별도 `venue` 좌표계 변환 테이블로 배치(Phase 2). Phase 1은 단일 홀 기준(홀7 권장, PLANNING §9).

### 3-2. 지오메트리 컬럼 표준

| 엔티티 | 컬럼 | PostGIS 타입 | 규칙 |
|---|---|---|---|
| **부스** `TB_BOOTH` | `geom` | `geometry(Polygon, 0)` | 닫힌 링(첫=끝 좌표), 단순(ST_IsSimple)·유효(ST_IsValid), CCW 권장 |
| **홀 외곽** `TB_HALL` | `footprint` | `geometry(Polygon, 0)` | 홀 경계 |
| **홀 시설** `TB_HALL_FEATURE` | `geom` | `geometry(Geometry, 0)` | 기둥(Point Ø2.5m 버퍼)·비상구(Point/LineString)·셔터·화장실 — `feature_type` 구분 |
| **트렌치** `TB_TRENCH` | `geom` | `geometry(Point, 0)` | 탭/액세스 포인트(KNN 대상). `run_geom geometry(LineString,0)` 옵션(트렌치 런) |
| **배선** `TB_WIRING_PATH` | `geom` | `geometry(LineString, 0)` | 트렌치→단말 경로. `kind`별 1행 |

### 3-3. 공간 연산 계약 (매퍼 XML 대상 — db-engineer 인수)

| 용도 | 연산 | 규정/계약 매핑 |
|---|---|---|
| 판매면적 | `ST_Area(geom)` (m²) | LayoutSummary `salesAreaM2`, BI ㎡당 수익 |
| 통로 폭 최소 | `ST_Distance` + `ST_Buffer`(부스 간극) | 규정 `AISLE_WIDTH_MIN`(≥3m·block) |
| 비상구 차단 | `ST_Intersects(booth, exit_access_zone)` count | 규정 `EXIT_ACCESS`(=0·block) |
| 최근접 트렌치 | KNN `geom <-> :point ORDER BY … LIMIT k` | `WiringMapper.findNearestTrenches` |
| 최단 배선 | 경로 LineString `ST_Length` (통로 횡단 최소 휴리스틱) | `WiringMapper.shortestPath`, `WiringResult.lengthM` |
| 부스 겹침 | `ST_Overlaps` / `ST_Intersects` 자기조인 | 배치 무결성(솔버 후검증) |
| 홀 이탈 | `ST_Contains(hall.footprint, booth.geom)` | 부스가 홀 경계 내 |

- **가정 트렌치(R4)**: 실측 미확보 홀은 공개 규격 기반 가정 그리드 → `TB_TRENCH.assumed=true`. `WiringResult.assumedTrench=true` → 프론트 "가정 트렌치 좌표(실측 대기)" 배지(계약 §5). CAD 트렌치 실측(`평면,트렌치.dwg`, floorplans README) 확보 시 홀 단위 교체·`assumed=false`.
- **좌표 검증 게이트**: 저장 전 `ST_IsValid`·닫힌 링·홀 내포 검증 실패 시 `VALIDATION`(400). 무효 지오메트리 저장 금지.

### 3-4. 공간 인덱스·성능 표준

- 전 `geom` 컬럼 **GiST 인덱스**(`USING gist(geom)`) 필수. 트렌치 KNN·통로 버퍼·비상구 교차의 실시간 응답 근거.
- 부스 수 홀당 200~600(PLANNING). 배치 검증은 홀 단위 배치(bounding box 선필터 후 정밀 연산).
- 대량 좌표는 서버 산출값 권위 — 프론트 좌표는 참고, 규정 판정은 PostGIS 산출값과 대조(compliance-v1 `AISLE_WIDTH_MIN.note`).

---

## 4. 데이터 표준 (명명·코드·마스터)

### 4-1. 명명 규칙 (Naming Convention)

| 대상 | 규칙 | 예 |
|---|---|---|
| 테이블 | `TB_` + `UPPER_SNAKE`(단수) — WISE 표준 계승 | `TB_BOOTH`, `TB_AUCTION` |
| 컬럼(물리) | `snake_case`, PostgreSQL 무인용 소문자(대소문자 혼용·인용식별자 금지) | `booth_id`, `floor_load_t_per_m2` |
| PK | `<엔티티>_id`, **UUID**(도메인) / WISE 이식 테이블은 정본 PK 유지 | `event_id`, `job_id` |
| FK | 참조 PK명 동일 | `TB_BOOTH.layout_id` |
| 지오메트리 | `geom`(주 지오메트리) / `<용도>_geom` | `geom`, `footprint`, `run_geom` |
| 암호화 PII | `<필드>_enc` 접미 | `email_enc`, `otp_secret`(WISE) |
| 코드 컬럼 | `<의미>_code` 또는 상태 `status` | `category_code`, `status` |
| 불리언 | `is_`/동사 또는 `<x>_yn`(WISE 공통은 `USE_YN`) | `premium`, `assumed`, `use_yn` |
| 시각 | `*_at`(`timestamptz`), 날짜 `*_date`(`date`) | `created_at`, `deadline_at`, `start_date` |
| 금액 | `numeric`, 원(KRW) 정수 스케일, 통화 `currency` 명시 | `total`, `vat` |
| BI 마트 | 팩트 `FACT_*`, 차원 `DIM_*`, 스냅샷 `KPI_SNAPSHOT` | `FACT_BOOKING`, `DIM_HALL` |

- **DTO(camelCase) ↔ 컬럼(snake_case) 매핑**: MyBatis `mapUnderscoreToCamelCase=true` 또는 명시 `resultMap`. 계약 DTO(`boothNo`↔`booth_no`, `floorLoadTPerM2`↔`floor_load_t_per_m2`)는 01_backend_contracts를 정본으로 매핑.
- **응답 제외 컬럼(불변, 계약 §0-3)**: `*_enc`·비번 해시·`otp_secret`·내부 IP/SSH·내부 식별자는 API 응답 완전 제외. 표시는 이름·역할·번호 등 비민감 필드만.

### 4-2. 데이터 타입 표준

| 논리형 | 물리형(PostgreSQL) | 비고 |
|---|---|---|
| 식별자 | `uuid`(도메인) | `gen_random_uuid()` |
| 반정형 스펙 | `jsonb` | DesignSpec·quote·line_items·merge_provenance — GIN 인덱스 선택 |
| 공간 | `geometry(<type>, 0)` | §3 |
| 상태·코드 | `varchar` + 공통코드 검증(앱 레벨) | ENUM 물리타입 지양(룰셋/코드 유연성) |
| 금액 | `numeric(15,2)` | |
| 시각 | `timestamptz`(UTC 저장, Asia/Seoul 표시) | |

### 4-3. 공통코드 체계 (WISE 정합)

> 정본 = [`docs/COMMON_CODES.md`](../COMMON_CODES.md)(단일 출처). 적재 `TB_CODE_GRP`/`TB_CODE`, 코드값=영문 상수·코드명=한글. **DA 검수 관점**: 코드 vs 마스터 경계 준수, "확인 필요" 코드값 임의 확정 금지.

- **코드 vs 마스터 경계(핵심 표준)**: 열거 가능 소수값=**공통코드**(BOOTH_TYPE·RENDER_STATUS·SHOT_PRESET 등), 다건·CRUD·버전 대상=**마스터/룰셋**(홀·요율·규정·등록업체) — 공통코드에 넣지 않는다(COMMON_CODES §1·§4 말미).
- **확정 코드**(계약/PLANNING 근거): `EVENT_ROLE`·`PORTAL_ROLE`·`BOOTH_TYPE`·`COMPLIANCE_SEVERITY`·`RENDER_STATUS`·`SHOT_PRESET`·`USE_YN`.
- **DA 확정 대기(확인 필요)**: `AUCTION_STATUS`·`QUOTATION_STATUS`(M15)·`ZONE_TYPE` 확장·상태 전이(`LAYOUT_STATUS`/`DESIGN_STATUS`/`UTILITY_ORDER_STATUS`의 submitted 이후)·`COMPLIANCE_GROUP` 전체 — 도메인 에이전트 확정 시 **COMMON_CODES + 본 ERD 컬럼 주석 동시 갱신**(DA 검수 항목).
- **신규 도메인 코드 제안**(DA): `CONTRACTOR_CATEGORY`(등록업체 14분류: 전시디자인설치·리깅·전기시설·카펫/파이텍스·급배수/Air·가스설비·철거·운수통관·가구비품·경비용역·광고싸인물·지게차·방염·구조해석), `UTILITY_KIND`(power·network·plumbing·air·gas), `AUCTION_TYPE`(REVERSE·RFQ·FIXED), `CONTENT_LOCALE`(ko·en·zh·ja) — 확정 시 COMMON_CODES §2에 승격.

### 4-4. 마스터데이터 관리 정책 (M18 백오피스)

| 마스터 | 저장 형태 | 버전 관리 | 권한 | 개정 절차 |
|---|---|---|---|---|
| **홀 마스터** | `TB_HALL`(+`TB_HALL_FEATURE`·`footprint`) | 스키마 컬럼 `revision`·이력 테이블 | ADMIN | CAD 실측 확보 시 교체(floorplans README 대조), 제3전시장(2028) H11~H18 확장 구조 |
| **요율 룰셋** | `rulesets/rates-v*.json`(파일) | 파일 버전(`rates-v1.0`) + `TB_MASTER_DATA` 메타 | ADMIN | 연 단위 개정 → 새 파일 교체, 산출물에 `rulesetVersion` 각인, 기존 견적 스냅샷 불변 |
| **유틸리티 요금** | rates-v*.json `utility` 절 | 상동 | ADMIN | 인터넷 150,000 vs KT 80,000 정합 확인 후 확정(R8) |
| **규정 룰셋** | `rulesets/compliance-v*.json` | 파일 버전(`compliance-v1.0`) | ADMIN | 규정 개정 시 교체, 리포트에 `rulesetVersion`+`disclaimer` 각인(면책·감사) |
| **등록업체 DB** | `TB_COMPANY`(739개·14분류) | 주기 수집 + `kintex_registered` 검증 플래그 | ADMIN | 웹 공개 데이터 수집→자체 DB화, 추후 공식 피드. **미등록=옥션 응찰/초대 차단 게이트**(불변) |

- **룰셋 스냅샷 원칙**: 견적(`TB_UTILITY_ORDER.quote`·`rateset_version`)·규정 리포트는 산출 시점 룰셋 버전을 **스냅샷 각인**. 이후 룰셋 개정이 과거 산출물을 변경하지 않는다(감사·재현성).
- **버전 관리 = 룰 엔진 결합**: 룰셋은 코드가 아닌 데이터 → M18 개정이 무중단 반영(PLANNING §8-1). 개정은 `TB_AUDIT_LOG` 전수 기록.

---

## 5. 데이터 품질·거버넌스

### 5-1. 개인정보(PII) 분류 체계

> 집중 영역 = 관람·참가(M10/M11). **주민등록번호 등 고유식별정보는 수집하지 않는다**(수집 최소화).

| 등급 | 분류 | 대상 컬럼(예) | 처리 |
|---|---|---|---|
| **P1 식별정보** | 직접 식별 | `TB_VISITOR.name_enc/email_enc/phone_enc`, `TB_LEAD.memo_enc` | **컬럼 AES-256-GCM 암호화** 저장, 응답 제외/마스킹, 접근 감사 |
| **P2 준식별** | 결합 식별 | `org_name`·`job_title`·`interests`·`visitor_type` | 접근 통제, BI는 집계/익명화만 반입 |
| **P3 인증비밀** | 크리덴셜 | `TB_USER` 비번 해시(BCrypt)·`otp_secret`(AES) | 절대 응답 금지, 로그 금지 |
| **P4 공개/비식별** | 비민감 | 부스명·업체명(표시용)·행사·집계 | 일반 처리 |

- **BoothDto 준거**: `assignedCompanyName`는 "표시용, 내부 식별자·민감정보 미포함"(BoothDto Javadoc) — P4. 부스 설계(TB_DESIGN_PLAN.spec)는 영업비밀(R10) → 행사 격리·접근 제한.

### 5-2. 암호화 정책

| 데이터 | 방식 | 근거 |
|---|---|---|
| PII 컬럼(P1) | **AES-256-GCM** 컬럼 암호화, 키는 서버 env/별도 키파일(코드·DB·커밋·로그 금지) | GUARDiA 보안 불변 `os_pw_enc` 패턴 |
| 비밀번호 | BCrypt(단방향) | WISE 표준 |
| OTP 시크릿 | AES-256-GCM | §5B-3 TOTP |
| 전송 | TLS(포털·API·워커 콜백) | NA(network.md) 정합 |
| 오브젝트 스토리지 | 접근 제어 URL(서명·만료), 도면/설계 행사 격리 | R10 |

### 5-3. 동의(Consent)·수집 최소화

- **동의 분리**: `TB_REGISTRATION.consent_privacy`(개인정보 수집·이용, **필수**) / `consent_marketing`(EDM 발송, **선택**, 정보통신망법) / `TB_LEAD.consent_share`(참가업체 리드 공유). 각 `consent_at` 시각 기록.
- **목적 구속**: 마케팅 미동의자는 M12 EDM 세그먼트 제외(발송 파이프라인 게이트). 리드 공유 미동의는 참가업체 반출 차단.
- **최소 수집**: 관람객 폼은 목적 필요 최소 필드. 셀프서비스 계정은 행사 데이터 쓰기 권한 없음(PLANNING §2).

### 5-4. 보존·파기 정책

| 데이터 | 보존 | 파기 |
|---|---|---|
| 관람객 등록·배지·체크인(PII) | 행사 종료 후 정책 기간(기본 1년, 재참가 분석 목적 별도 동의 시 연장) | 기간 경과 자동 익명화/삭제 |
| 리드(참가업체 반출본) | 참가업체 자산 — 반출 시점 이후 참가업체 책임, 플랫폼 원본은 위 관람객 정책 준수 | 상동 |
| 도면·설계·생성 이미지 | 행사 종료 후 보존(참가업체 자산, PLANNING §8) — 별도 정의 | 참가업체 요청 시 삭제 |
| 견적서 PDF·낙찰(M15) | 계약·감사 목적 장기 보존 | 법정 보존기간 준수 |
| 감사로그 | 장기 보존(불변·append-only) | 미파기 |
| BI 마트·KpiSnapshot | 집계(비식별) — 장기 보존 | — |

### 5-5. 감사(Audit)·데이터 계보(Lineage)

- **감사 대상(전수, `TB_AUDIT_LOG`)**: 승인·**낙찰(M15 Award)**·설계 변경·**룰셋/마스터 개정**·**리드/PII 접근**·권한 변경·로그인/OTP. append-only, 행위자·시각·전후값·행사 스코프 기록(COMMON_CODES §2-1 audit 확장).
- **데이터 계보(원천→마트)**:

```
[운영 원천]                        [BI 데이터마트 M16]
TB_HALL_ASSIGNMENT / 행사일정  ──▶ FACT_BOOKING     (가동률·RevPAD·㎡당수익)
TB_SETTLEMENT (M9)             ──▶ FACT_SETTLEMENT  (매출구성·P&L)
TB_UTILITY_ORDER (M4)          ──▶ FACT_UTILITY     (유틸 매출)
TB_AUCTION/QUOTATION/AWARD     ──▶ FACT_AUCTION     (옥션 수수료)
TB_REGISTRATION/CHECK_IN (M10) ──▶ FACT_VISITOR     (관람·리텐션) ※PII 비반입, 집계만
TB_HALL/PostGIS ST_Area        ──▶ DIM_HALL         (면적 정규화)
                          ─(배치/야간 적재)─▶ KPI_SNAPSHOT (경영진 KPI)
```

- **PII 격리(계보 규칙)**: BI 마트는 P1/P2 원본을 반입하지 않는다 — 관람객은 **집계·코호트·익명 키**만 반입(FACT_VISITOR는 방문 카운트·세그먼트 차원, 개인 식별자 없음). M16 리텐션/LTV는 참가사(Company) 단위이며 개인 관람객이 아님.
- **데이터 품질 규칙(DQ)**: 참조무결성(FK), 지오메트리 유효성(§3-3 게이트), 룰셋 버전 각인 누락 0, 금액 통화 명시, 상태 코드 공통코드 준수. 마트 적재 시 원천-집계 정합 체크(§6-4).

---

## 6. BI 데이터마트 (M16) — 스타 스키마

> 대상: **kintex-bi-dev**. PLANNING §5A M16-1 운영사(킨텍스) 관점 7지표 정합. 관점 격리 = ① 참가업체 ROI(자기 부스) / ② 운영사 수익성(전 행사) 별도 대시보드·권한(PLANNING §440). 적재 = 배치/야간 `KPI_SNAPSHOT` 또는 읽기 전용 복제(운영 부하 회피, §8-1).

### 6-1. 스타 스키마 ERD

```mermaid
erDiagram
    DIM_DATE {
        int     date_key PK "YYYYMMDD"
        date    full_date
        int     year
        int     quarter
        int     month
        boolean is_peak "성수기 3-5·9-11"
        boolean is_offpeak "비수기 1·2·7·12"
    }
    DIM_HALL {
        varchar hall_key PK "H1..H10/반홀"
        varchar hall_name
        numeric area_m2 "㎡ 정규화 기준"
        varchar center "1전시장|2전시장"
        numeric floor_load
        varchar floor_finish
    }
    DIM_EVENT {
        uuid    event_key PK
        varchar event_name
        varchar event_type
        date    start_date
        date    end_date
        int     duration_days
    }
    DIM_EXHIBITOR {
        uuid    exhibitor_key PK "=company_id"
        varchar company_name
        varchar category
        varchar region
        int     first_participation_year "코호트"
    }
    FACT_BOOKING {
        uuid    booking_id PK
        int     date_key FK
        varchar hall_key FK
        uuid    event_key FK
        numeric occupied_area_m2
        numeric available_area_m2
        int     occupied_days
        int     available_days
        numeric rental_revenue
        numeric season_coeff "성수기/비수기/1전시장 계수"
    }
    FACT_SETTLEMENT {
        uuid    settlement_id PK
        int     date_key FK
        uuid    event_key FK
        varchar revenue_segment "rental|utility|auction_fee|lobby|outdoor|parking"
        numeric revenue
        numeric direct_cost "운영·에너지·인력"
        numeric contribution_margin
    }
    FACT_UTILITY {
        uuid    util_fact_id PK
        int     date_key FK
        uuid    event_key FK
        uuid    exhibitor_key FK
        varchar utility_kind "power|network|plumbing|air"
        numeric amount
    }
    FACT_AUCTION {
        uuid    auction_fact_id PK
        int     date_key FK
        uuid    event_key FK
        varchar category_code
        numeric awarded_amount
        numeric platform_fee
        int     bid_count
    }
    FACT_VISITOR {
        uuid    visitor_fact_id PK
        int     date_key FK
        uuid    event_key FK
        varchar visitor_segment "visitor|buyer (익명 세그먼트)"
        int     registered_count
        int     checkin_count
        int     lead_count "PII 없음·집계만"
    }
    KPI_SNAPSHOT {
        uuid    snapshot_id PK
        int     date_key FK
        varchar scope "venue|center|hall|event"
        varchar scope_key
        varchar kpi_code "OCC|REVPAD|MARGIN|RETENTION|LTV|YIELD"
        numeric kpi_value
        numeric target_value
        timestamptz built_at
    }

    DIM_DATE ||--o{ FACT_BOOKING : ""
    DIM_HALL ||--o{ FACT_BOOKING : ""
    DIM_EVENT ||--o{ FACT_BOOKING : ""
    DIM_DATE ||--o{ FACT_SETTLEMENT : ""
    DIM_EVENT ||--o{ FACT_SETTLEMENT : ""
    DIM_DATE ||--o{ FACT_UTILITY : ""
    DIM_EXHIBITOR ||--o{ FACT_UTILITY : ""
    DIM_DATE ||--o{ FACT_AUCTION : ""
    DIM_DATE ||--o{ FACT_VISITOR : ""
    DIM_EVENT ||--o{ FACT_VISITOR : ""
    DIM_DATE ||--o{ KPI_SNAPSHOT : ""
```

### 6-2. 팩트 그레인(Grain) 정의

| 팩트 | 그레인(1행 = ) | 가법성 |
|---|---|---|
| `FACT_BOOKING` | 행사×홀(반홀)×기간 배정 1건 | 면적·일수·매출 가법, 계수 비가법 |
| `FACT_SETTLEMENT` | 행사×매출세그먼트×정산일 | 매출·원가·공헌이익 가법 |
| `FACT_UTILITY` | 행사×참가사×유틸종류 신청 1건 | 금액 가법 |
| `FACT_AUCTION` | 옥션(낙찰) 1건 | 낙찰액·수수료 가법, bid_count 준가법 |
| `FACT_VISITOR` | 행사×관람일×세그먼트 집계 | 카운트 가법(**개인 식별자 없음**) |

### 6-3. M16-1 지표 → 마트 매핑

| # | 운영사 지표 | 산식 | 소스 팩트/차원 |
|---|---|---|---|
| ① | 홀·기간별 가동률 | Σ occupied_area×days / Σ available_area×days ×100 | FACT_BOOKING × DIM_HALL × DIM_DATE |
| ② | 매출 구성(mix) | revenue by segment | FACT_SETTLEMENT/FACT_UTILITY/FACT_AUCTION |
| ③ | 행사별 P&L·마진 | Σ revenue − Σ direct_cost = 공헌이익, 마진율 | FACT_SETTLEMENT × DIM_EVENT |
| ④ | 전시장별 ROI·RevPAD·㎡당 수익 | revenue / area_m2 (㎡ 정규화) | FACT_BOOKING × DIM_HALL(area_m2) |
| ⑤ | 참가사 리텐션·LTV | 코호트 재참가율, LTV=Σ(임대+유틸+옥션)/재참가주기 | DIM_EXHIBITOR(first_year) × FACT_* 다년 |
| ⑥ | 수요예측·수율/가격 | 성수기 계수·홀별 수요, 요율 시뮬레이션 | FACT_BOOKING 이력 × rates 룰셋 |
| ⑦ | 경영진 KPI 대시보드 | ①~⑥ 요약 + 목표 대비(점유 60~75%) | KPI_SNAPSHOT |

### 6-4. 적재·품질 표준

- **적재 방식**: 야간 배치 ETL(운영→`mart` 스키마) 또는 읽기 전용 복제(운영 부하 회피). `KPI_SNAPSHOT`은 스냅샷 시점(`built_at`) 각인 → 시계열 추이·재현성.
- **㎡ 정규화 권위**: DIM_HALL.area_m2는 **PostGIS `ST_Area` 산출값 또는 홀 마스터 확정값** 단일 출처(대형홀 vs 소형홀 생산성 비교 정합, ④ RevPAD 근거).
- **정합 체크(DQ)**: 마트 매출 합 = 운영 정산 합(허용오차 0), 가동률 분모(가용 홀·일수) = 행사일정×홀 마스터, 관점 격리(참가사 대시보드는 exhibitor_key 필터 강제).
- **PII 비반입(불변)**: FACT_VISITOR는 카운트/세그먼트만 — TB_VISITOR P1/P2 컬럼 마트 유입 금지(§5-5 계보 규칙).
- **한계 각인**: LTV·리텐션 다년 데이터 필요(초기 단년 근사), 수율 최적가=시뮬레이션 참고치(최종 요율은 킨텍스 경영 결정, R8) — 대시보드 고지.

---

## 7. DA 검수 체크리스트 (Phase A 게이트)

> 본 문서를 기준으로 db-engineer 물리 구현·타 트랙 산출물을 검수하는 항목(A-6 reviewer 정합 입력).

| # | 검수 항목 | 기준 |
|---|---|---|
| 1 | 명명 규칙 준수 | `TB_`·snake_case·`_enc`·`geom`·`FACT_/DIM_` (§4-1) |
| 2 | 공간 좌표계 | SRID 0·geometry·GiST 인덱스·`ST_IsValid` 게이트 (§3) |
| 3 | 공통코드 경계 | 코드 vs 마스터 분리, "확인 필요" 임의확정 금지 (§4-3) |
| 4 | 룰셋 스냅샷 | 견적·리포트에 `rulesetVersion` 각인, 과거본 불변 (§4-4) |
| 5 | PII 암호화·응답제외 | P1 `_enc` AES-GCM, 민감컬럼 API 완전 제외 (§5-1/5-2, 계약 §0-3) |
| 6 | 동의 게이트 | marketing 미동의 EDM 제외, share 미동의 반출 차단 (§5-3) |
| 7 | 감사 전수 | 낙찰·룰셋개정·리드접근·권한변경 기록 (§5-5) |
| 8 | BI PII 비반입 | 마트에 개인식별자 유입 0, 관점 격리 (§6-4) |
| 9 | 계약 정합 | §8 매퍼 인수 테이블/컬럼 = 본 ERD (§2-1) |

---

## 8. 미결·후속 (확정 대기)

| 항목 | 상태 | 담당 |
|---|---|---|
| M15 옥션 상태 코드(`AUCTION_STATUS`·`QUOTATION_STATUS`) 확정 | 확인 필요 | bidding-dev + DA |
| 상태 전이(layout/design/utility submitted 이후) | 확인 필요 | M6 승인 워크플로 + DA |
| `TB_SETTLEMENT`·`TB_MEETING`·`TB_MICROSITE` 상세 컬럼 | 골격만 | 도메인 에이전트 + DA |
| CAD 트렌치 실측 → `TB_TRENCH.assumed=false` 교체 | 미확보(R4) | 킨텍스 협의 |
| 홀 간 venue 좌표계 변환(S7·부지) | Phase 2 | DA + M2 |
| 제3전시장(2028) H11~H18 홀 마스터 확장 | 구조 대비 | DA |
| 인터넷 요금 정합(150,000 vs 80,000) | 확인 필요(R8) | 킨텍스 + M18 |

---

## 10. FK 최소화·공통코드 관리 표준 (소유자 지시 2026-07-12)

> 소유자 원칙: **"FK는 최소화, 공통코드로 관리"**. 본 절은 §4·§5-5(DQ) 위에 **참조무결성·범주값 관리 방식의 단일 정책**을 확정한다. 이미 적용된 마이그레이션(V1~V43)은 **불변** — 본 절은 표준·감사·백로그이며 파괴적 재작성을 지시하지 않는다. 구현(신규 멱등 마이그레이션)은 kintex-db-engineer.

### 10-1. 물리 FK 제약 최소화 정책

- **기본값 = 물리 FK 미설정**: 신규 테넌트/도메인 테이블은 DB `FOREIGN KEY`/`REFERENCES`를 **두지 않는다**. 참조무결성은 **애플리케이션 레이어(서비스·매퍼 검증) + 명명 규약(`*_id` 소프트 참조)**로 보장(§4-1 FK 명명 유지, 물리 제약만 생략).
- **근거(4)**: ① **멀티테넌트 복합키 마찰** — 테넌트 루트(`event`·`hall`·`app_user`)는 `PRIMARY KEY (tenant_id, id)`로 전환됨(V31). 단일 `id` 참조 FK는 `UNIQUE(id)` 보조제약을 강제하고, V31이 실제로 **전 자식 FK를 드롭→복합PK 전환→UNIQUE(id)로 재생성**하는 동적 스윕을 수행해야 했다(마찰 실증). ② **MyBatis** — 조인·삭제 순서를 앱이 제어. ③ **마이그레이션·시드 순서 자유** — 멱등 `ON CONFLICT` 시드가 부모 선삽입에 묶이지 않음. ④ **성능·재배치** — 부스 replaceBooths·부스 교체 시 자식 재지정이 잦음(V3 utility_order·render_job는 이미 소프트 참조 채택 — 정본 사례).
- **예외 화이트리스트(FK 유지 허용)**: 아래 **강한 무결성이 필수이고 테넌트 복합키가 아닌 전역 시스템/RBAC 구성**만 물리 FK를 허용한다. 그 외 신규 FK 신설 **금지**.

| # | 자식 → 부모 | 마이그레이션 | 유지 사유 |
|---|---|---|---|
| W1 | `common_code.grp_code` → `common_code_group` | V7 | 코드값 고아 방지(공통코드 정합의 근간)·전역·정적 |
| W2 | `sys_menu.parent_id` → `sys_menu`(self) | V7 | 메뉴 트리 순환/고아 방지·전역 |
| W3 | `sys_role_permission`(role_code→`sys_role`, perm_code→`sys_permission`) | V7 | RBAC 권한 매핑 무결성(보안 임계)·전역 |
| W4 | `sys_role_menu`(role_code→`sys_role`, menu_id→`sys_menu`) | V37 | RBAC 메뉴 매핑 무결성·전역 |

### 10-2. 공통코드로 범주값 관리 정책

- **범주형 컬럼 = 공통코드**: 상태·유형·카테고리·모드·구분·심각도 등 **열거 가능 소수값**은 자유문자열/DB enum/전용 참조테이블이 아닌 **공통코드(`common_code_group`/`common_code`)로 관리**. 컬럼엔 코드값(영문 상수)만 저장, 표시명은 조인/캐시(§4-3, COMMON_CODES 정본). DB `ENUM` 물리타입은 지양(§4-2 — 룰셋/코드 유연성).
- **정본 화면 = W12 공통코드 관리**(CommonCodeAdminPage): 그룹/상세 CRUD의 단일 관리 지점. 신규 범주 컬럼 도입 시 **먼저 공통코드 그룹을 정의**하고 컬럼 주석에 `-- 공통코드(GRP)` 표기(V37 `partner_type` 사례).
- **코드 vs 마스터 경계(불변, §4-3)**: 다건·CRUD·버전 대상(홀·요율·규정 룰셋·등록업체)은 공통코드가 아니라 마스터/룰셋. 범주 컬럼만 공통코드로.

### 10-3. 테넌트 표준 정합

- 공통코드도 **테넌트 스코프 원칙 준수**([[tenant-id-pk-standard]]). 현행 `common_code_group`/`common_code`는 전역(테넌트 미부여) 이식본 — 킨텍스 단일 테넌트 운영 중엔 전역 공유가 유효하나, 멀티테넌트 확장 시 **테넌트별 코드 오버라이드**가 필요하면 `(tenant_id, grp_code, code)` 확장을 백로그로 둔다(§10-7 B4, 지금은 순증 시드만).
- 소프트 참조 인덱스는 `(tenant_id, *_id)` 복합 선두로 생성(§10-4).

### 10-4. 소프트 참조 무결성 보완책 (명문화)

물리 FK를 생략하는 대신 아래를 강제한다:
1. **앱 레이어 검증**: 부모 존재 확인은 서비스/매퍼에서 수행(삽입 전 조회 또는 조인 검증). 낙찰·정산 등 임계 트랜잭션은 명시적 존재검증 필수.
2. **삭제 시 고아 방지 규약**: 부모 삭제는 서비스가 자식 선삭제/무효화(soft-delete `use_yn='N'` 우선). 물리 CASCADE에 의존하지 않는다.
3. **논리참조 인덱스**: 조회·조인·고아 스캔 가속을 위해 소프트 FK 컬럼에 `(tenant_id, <ref>_id)` 인덱스(§10-7 B2).
4. **고아 검증 쿼리(DQ)**: 야간/배포 후 `LEFT JOIN ... WHERE parent.id IS NULL` 고아 스캔을 운영 점검 쿼리로 상비(마트 적재 전 DQ 게이트, §6-4). §5-5 DQ의 "참조무결성(FK)"은 **소프트 참조 무결성(앱+검증쿼리)**으로 해석 갱신.

---

## 11. 뷰·구체화뷰·함수 사용 지침 (소유자 지시 2026-07-12)

> 파생·집계·재사용 계산은 **결정론적 SQL 객체**로 캡슐화해 상태 백필·중복 로직·토큰 낭비를 줄인다. **실사용 근거 없는 선제 생성 금지**(남용 방지).

### 11-1. VIEW (`v_*`) — 실시간 파생·경량 조인·상태 산출

- 용도: 저장 없이 **결정론적 파생**(생명주기 상태·경량 조인·표시용 코드명 조인). 상태/구분 컬럼 백필 대신 **뷰 산출 권장**.
- 정본 사례(V43): `v_event_calendar`(날짜로 `lifecycle` UPCOMING/ONGOING/ENDED 산출 — 별도 상태 컬럼 불필요), `v_event_monthly_summary`(월별 건수 집계). 이 패턴을 신규 파생에 재사용.
- 규약: `CREATE OR REPLACE VIEW`, `tenant_id` 컬럼 노출·필터 유지, 하부 테이블 인덱스에 의존(뷰 자체 인덱스 불가), 민감 컬럼(§5-1 P1/P3) 미노출·마스킹만.

### 11-2. MATERIALIZED VIEW (`mv_*`) — 무겁고 자주 조회·실시간성 낮은 집계

- 용도: **BI 대시보드 KPI·홀 가동률·리드/ROI·월/연 통계** 등 비용 큰 집계로 실시간성이 덜 중요한 것(§6 마트 KPI_SNAPSHOT과 정합 — mview는 경량 대체/보조).
- **새로고침 전략 명시 필수**: 주기(야간 배치)·`REFRESH MATERIALIZED VIEW CONCURRENTLY`(무중단·유니크 인덱스 전제)·mview 자체 인덱스(`tenant_id` + 조회 키) 생성.
- 남용 판단 기준:

| 상황 | 선택 |
|---|---|
| 실시간성 필수·경량 | VIEW |
| 실시간성 낮음·집계 무거움·반복 조회 | MATERIALIZED VIEW |
| 경영 KPI 시계열·스냅샷 재현성 | KPI_SNAPSHOT 테이블(§6) |
| 1회성·희소 조회 | 뷰/mview 생성 안 함(온디맨드 쿼리) |

### 11-3. FUNCTION (`fn_*`) — 재사용 계산 로직 캡슐화

- 용도: 여러 쿼리·화면 공유 계산(기간→분기 산출, 요율 계산 보조, 거리/트래블타임 보조). **부수효과 없는 순수/`IMMUTABLE`·`STABLE` 우선**, PL/pgSQL은 꼭 필요할 때만(SQL 함수 우선).
- 규약: `CREATE OR REPLACE FUNCTION fn_*`, `tenant_id` 파라미터화, 룰셋 의존 계산(요율)은 스냅샷 버전 인자(§4-4)로 재현성 보장.

### 11-4. STORED PROCEDURE (`sp_*`) — 집합연산·다단계 트랜잭션

- 용도: **대량 집합 처리·다단계 트랜잭션·정기 롤업**은 앱 루프(행 단위 왕복) 대신 **DB 프로시저(PL/pgSQL) 세트기반 처리**. 예: 월마감 집계, `REFRESH MATERIALIZED VIEW`, 대량 상태전이, 정산 롤업.
- **경계(남용 금지)**: **비즈니스 로직 대부분은 앱(Service) 유지**. DB 프로시저는 **성능이 결정적일 때만**(대량·세트기반이 앱 루프 대비 확연히 유리). 검증·권한·감사·룰셋 판정 등 도메인 규칙을 프로시저로 이관하지 않는다.
- 순수/부수효과 구분: 반환값만 있는 재사용 계산 = `fn_*`(`IMMUTABLE`/`STABLE`), **부수효과(쓰기·다단계 커밋) 있는 것만 `PROCEDURE sp_*`**(`CALL`). 명명 `fn_*`/`sp_*`, 멱등 `CREATE OR REPLACE`, `tenant_id` 파라미터·스코프 준수.

### 11-5. 공통 규약

- 명명 `v_*`·`mv_*`·`fn_*`·`sp_*`. 마이그레이션 멱등(`CREATE OR REPLACE` / mview는 `IF NOT EXISTS` 가드). 테넌트 스코프 유지. 실사용 근거(화면/API/AI 답변) 있는 것만 생성.

---

## 13. 자동화 배치 카탈로그 (대상·주기·멱등 — 데이터 표준 관점)

> **무엇을 배치로 돌릴지**(대상·주기·멱등·잠금)만 데이터 표준에서 정의한다. **실행 프레임워크**(Spring `@Scheduled`/Quartz/cron·분산락)는 아키텍처(A-1 app.md/A-3 tech.md, kintex-sa/ta) 표준 영역 — 본 카탈로그를 그쪽으로 링크. 구현 인계: **스케줄러=backend-dev, 프로시저/mview=db-engineer**.

| # | 배치 작업 | 데이터 대상 | 주기(권고) | 멱등성 | 재시도·잠금 요건 |
|---|---|---|---|---|---|
| J1 | **mview 새로고침** | `mv_hall_occupancy`·`mv_lead_roi_summary`·KPI 집계(§6) | 야간 1회(+수요 트리거) | `REFRESH ... CONCURRENTLY` 자연 멱등 | 단일 실행락(중복 REFRESH 방지)·실패 시 다음 주기 |
| J2 | **KPI 스냅샷 적재** | `KPI_SNAPSHOT`(§6-4, `built_at` 각인) | 야간 배치 | 스냅샷 키(date_key,scope) UPSERT | 원천-집계 정합 DQ 통과 후 커밋 |
| J3 | **마감임박 알림·해야할일 자동생성** | D-데이(문서/마일스톤 D-150/30/25/7)·옥션 deadline·납부 스케줄 | 일 1회(+정시) | 발송/생성 대상 dedup 키(대상+일자) | 이미 발송분 스킵(중복 통지 금지)·실패 재큐 |
| J4 | **EDM/옥션 통지 발송** | 캠페인·옥션 통지(SMTP, [[smtp-email-approval]]) | 예약시각·이벤트 트리거 | mail_log 상태(sent/skipped)로 재발송 차단 | 마케팅 미동의 세그먼트 제외 게이트(§5-3)·발송락 |
| J5 | **세션/토큰·만료 데이터 정리** | 만료 비번재설정 토큰·잠금 해제·로그인이력 보존기간 | 시간별/일별 | 조건부 삭제(자연 멱등) | — |
| J6 | **소프트 참조 고아 검출(DQ)** | §10-4 논리참조 무결성(event/hall/company 참조 고아) | 야간(마트 적재 전) | 읽기 전용 스캔 | 고아 발견 시 리포트·차단(마트 적재 게이트) |
| J7 | **PII 보존·파기** | 관람객 등록·배지·체크인(행사종료+1년, §5-4) | 일 1회 | 기간 경과분 익명화/삭제(재실행 안전) | 동의 연장분 제외·감사로그 기록 |
| J8 | **행사 crawl 갱신** | 외부 행사정보(kintex-crawler 트랙) | 정기(일/주) | 소스키 UPSERT | 소스 실패 격리·부분성공 허용 |
| J9 | **정산 롤업** | `sp_settlement_rollup`(정산→FACT_SETTLEMENT) | 마감 주기 | 재실행 시 기간 재계산(UPSERT) | 정산 확정 상태만 대상·실행락 |

- **공통 요건**: 모든 배치는 ① **멱등**(재실행 안전 — UPSERT/조건부/dedup), ② **중복실행 방지 잠금**(분산락 — 실행수단은 아키텍처), ③ **실패 격리·재시도**(부분성공 허용·다음 주기 복구), ④ **감사**(J4·J7 등 통지·파기는 `audit_log` 기록), ⑤ **테넌트 스코프**(배치도 tenant 루프/필터).
- 세트기반 롤업(J2·J9)·대량 상태전이는 §11-4 `sp_*` 프로시저 후보. 알림/발송(J3·J4)의 **판정·세그먼트 규칙은 앱(Service)** — 프로시저는 대량 적재만.

---

## 12. 감사표 + kintex-db-engineer 인계 백로그

> 대조 대상: `src/backend/src/main/resources/db/migration/` V1~V43 전수. **파괴적 재작성 금지** — FK 드롭은 권고(별도 승인 후), 즉시 실행 백로그 = 공통코드 시드·논리참조 인덱스·뷰(순증 멱등). 신규 마이그레이션 번호는 **V43 이후 여유 번호(V44~) 권고**(진행 중 V43·신규분과 충돌 회피 — 실제 번호는 db-engineer가 병합 시점 최댓값+1로 확정).

### 12-1. 감사표 A — 현행 물리 FK 목록 (유지/제거 권고)

현행 물리 FK 약 40건. **유지=화이트리스트 4그룹(§10-1)**, 그 외는 소프트 참조 **제거 권고**(우선순위 P1: 테넌트 루트 참조 / P3: 아그리게잇 내부 — 무해·비복제).

| 분류 | 자식 → 부모(FK) | 마이그레이션 | 판정 |
|---|---|---|---|
| **유지(W1~W4)** | common_code.grp_code→common_code_group; sys_menu.parent_id(self); sys_role_permission(role/perm); sys_role_menu(role/menu) | V7·V37 | **유지** |
| **P1 제거권고** | hall_assignment.event_id→event, hall_id→hall; event_member.event_id→event, user_id→app_user, company_id→company | V2 | 소프트 참조(테넌트 루트·V31 마찰) |
| **P1 제거권고** | trench.hall_id→hall; hall_exit.hall_id→hall; layout.event_id→event, hall_id→hall; utility_order.event_id→event | V3 | 소프트 참조(hall/event 루트) |
| **P1 제거권고** | doc/milestone 3× event_id→event | V14 | 소프트 참조 |
| **P1 제거권고** | dock_reservation.event_id→event, hall_id→hall, dock_id→dock; booth_sale.event_id→event, hall_id→hall | V15·V27 | 소프트 참조 |
| **P1 제거권고** | auction.event_id→event; auction_invite.company_id→company; bid.company_id→company; company_reputation.company_id→company | V16 | 소프트 참조(company/event 루트) |
| **P1 제거권고** | visitor_registration.event_id→event; lead.event_id→event; campaign 3× event_id→event; sponsorship.package_id; settlement.event_id→event; logistics 3× event_id→event | V17·V18·V24·V33 | 소프트 참조 |
| **P3 잔류허용(무해·비복제)** | booth.layout_id→layout; design_plan.booth_id→booth; auction_invite/bid/award.auction_id→auction, award.bid_id→bid; content_version.content_id→cms_content; payment_schedule/refund/tax.invoice_id→invoice; approval_line/history.approval_id→approval; message_recipient/opinion_reply/meeting_attendee | V3·V8·V16·V19·V24·V29·V34 | 아그리게잇 내부 CASCADE — 유지해도 무해, **신규 테이블엔 미복제**(소프트 우선) |

> **주의**: P1 제거는 **자동 실행 대상 아님** — 기존 FK 드롭은 테넌트 복합키 정합·데이터 검증 후 **소유자 승인 별도 DROP 마이그레이션**으로만. 표준의 실질 효력은 **신규 테이블에 FK 미신설 + P1 패턴 미복제**에 있다.

### 12-2. 감사표 B — 범주형 컬럼 → 공통코드 그룹 매핑 (전환 후보)

자유문자열/주석 열거 범주 컬럼 ≈ **34개**. 기존 시드 그룹(V7·V37)과 정합, 신규 그룹(★)은 V44 순증 시드 대상. 코드값=현행 저장값 유지(스키마 불변).

| 그룹코드 | 컬럼(테이블.컬럼) | 마이그 | 코드값(현행) | 상태 |
|---|---|---|---|---|
| `USER_STATUS`★ | app_user.status | V2 | ACTIVE·(LOCKED·INACTIVE) | 신규 |
| `CONTRACTOR_CATEGORY`★ | company.category, auction.category | V2·V16 | 14분류(전시디자인설치·리깅·전기시설·카펫/파이텍스·급배수Air·가스·철거·운수통관·가구비품·경비·광고싸인·지게차·방염·구조해석) | 신규(COMMON_CODES §2 승격) |
| `HALL_FLOOR_TYPE`★ | hall.floor_type | V2 | concrete_polished·carpet·outdoor | 신규 |
| `MASTER_DATA_CATEGORY`★ | master_data.category | V2 | RATE·UTILITY_FEE·COMPLIANCE | 신규 |
| `EVENT_STATUS`★ | event.status | V2 | active·cancelled·(closed) | 신규 |
| `EVENT_CATEGORY`★ | event.category | V23 | (행사 분류 — 값 확인 필요) | 신규 |
| `LAYOUT_STATUS` | layout.status | V3 | draft·submitted·approved·rejected | COMMON_CODES 등재(시드화 필요) |
| `BOOTH_TYPE` | booth.booth_type | V3 | assembled·independent·corner·island | 등재(corner·island 추가) |
| `DESIGN_STATUS` | design_plan.status | V3 | draft·submitted·approved·rejected | 등재(시드화) |
| `UTILITY_ORDER_STATUS` | utility_order.status | V3 | draft·submitted·relayed | 등재(시드화) |
| `RENDER_STATUS` | render_job.status | V3 | QUEUED·RUNNING·DONE·FAILED | 등재(시드화) |
| `SHOT_PRESET` | render_job.shot_preset | V3 | S1~S7 | 등재(시드화) |
| `MILESTONE_TYPE`★ | milestone.milestone_type | V14 | assignment·pre_review·utility·documents·opening | 신규 |
| `DOC_TYPE`★ | document.doc_type | V14 | operation_plan·booth_layout·disaster_plan… | 신규 |
| `APPROVAL_STATUS`★ | document.status, approval.status | V14·V29 | pending·draft·submitted·approved·rejected / DRAFT… | 신규 |
| `DOCK_RESV_STATUS`★ | dock_reservation.status | V15 | (예약 상태) | 신규 |
| `AUCTION_TYPE` | auction.auction_type | V16 | reverse·rfq | 등재(COMMON_CODES §2, 소문자 정합) |
| `AWARD_CRITERIA`★ | auction.award_criteria | V16 | lowest·comprehensive | 신규 |
| `QUOTATION_STATUS` | bid.status | V16 | submitted·revised·awarded·rejected | 등재(확인 필요 → 확정) |
| `VISITOR_TYPE` | visitor_registration.visitor_type | V17 | visitor·buyer·vip | 등재(vip 추가) |
| `CHECKIN_STATE`★ | visitor_registration.checkin_state | V17 | done·waiting·cancelled | 신규 |
| `CAMPAIGN_STATUS`★ | campaign.status | V18 | draft·scheduled·sending·done | 신규 |
| `SPONSOR_CONTRACT_STATUS`★ | sponsorship.contract_status | V18 | signed·pending | 신규 |
| `CONTENT_TYPE`★ | cms_content.content_type | V19 | PAGE·POST·NOTICE·BLOCK | 신규 |
| `CONTENT_STATUS`★ | cms_content.status | V19 | draft·review·approved·published | 신규 |
| `TRANS_STATUS`★ | content_i18n.trans_status | V19 | none·ai·reviewed | 신규 |
| `INQUIRY_TYPE`★ | public inquiry.inquiry_type | V20 | shell·raw·premium·general | 신규 |
| `INQUIRY_STATUS`★ | public inquiry.status | V20 | received·in_review·closed | 신규 |
| `HALL_ASSIGN_STATUS`★ | hall_assignment.status | V25 | assigned·… | 신규 |
| `TENANT_STATUS`★ | tenant.status | V26 | active·onboarding·suspended | 신규 |
| `BOOTH_SALE_STATUS`★ | booth_sale.status | V27 | available·held·sold·blocked | 신규 |
| `INVOICE_CATEGORY`★ | invoice.category | V28 | rental·utility·auction_fee… | 신규 |
| `SETTLEMENT_STATUS`★ | settlement.status | V24 | pending·invoiced·paid·overdue | 신규 |
| `PAYMENT_METHOD`★ | payment_schedule.method | V24 | manual·transfer·card | 신규 |
| `APPROVAL_LINE_STATUS`★ | approval_line.line_status | V29 | WAIT·approved·rejected | 신규 |
| `EQUIP_CATEGORY`★·`RENTAL_STATUS`★·`RENTAL_ITEM_CATEGORY`★·`ORDER_STATUS`★·`FREIGHT_STATUS`★ | V33 logistics 5종 | V33 | forklift…/requested…/furniture…/ordered…/registered… | 신규 |
| `REFUND_STATUS`★·`TAX_INVOICE_STATUS`★ | refund.status·tax_invoice.status | V34 | recorded·settled / issued·void | 신규 |
| `WEBHOOK_EVENT_TYPE`★·`WEBHOOK_STATUS`★ | webhook 등 | V35 | lead.hot…/disabled·queued·sent·failed | 신규 |
| `MAIL_CATEGORY`★·`MAIL_STATUS`★ | mail_log.category·status | V36 | AUCTION_OPEN·EDM·PASSWORD_RESET / sent·failed·skipped | 신규 |
| `VISITOR_GUIDE_CAT`★ | visitor_guide.category | V43 | TRANSPORT·PARKING·ADMISSION·FACILITY·ACCESS·OVERVIEW | 신규 |
| `TRANSPORT_MODE`★ | (관람객 교통 안내 세부) | V43 | (지하철·버스·자가용·KTX 등 — 확인 필요) | 신규 |

> 이미 시드된 그룹(V7·V37): USE_YN·USER_ROLE·VERIFY_METHOD·PRG_TYPE·MSG_RCV_TYPE·WORK_*·SCHE_GUBUN·IMPORTANCE·NOTICE_TYPE·OPINION_STATUS·NOTI_TYPE·REPORT_TYPE·PARTNER_TYPE — 재시드 불필요. **"확인 필요" 코드값(EVENT_CATEGORY·QUOTATION_STATUS·TRANSPORT_MODE 등)은 도메인 에이전트 확정 후 값 고정**(COMMON_CODES §2 규칙).

### 12-3. db-engineer 인계 백로그 (신규 멱등 마이그레이션 — 번호 V44~ 권고)

| ID | 백로그 | 형태 | 우선순위 |
|---|---|---|---|
| **B1** | 감사표 B ★ 신규 그룹 + 상세 공통코드 **순증 시드**(`common_code_group`/`common_code`, `ON CONFLICT DO UPDATE`) + 기존 등재 그룹(LAYOUT_STATUS·DESIGN_STATUS·RENDER_STATUS·SHOT_PRESET 등) 시드화. 컬럼 스키마 불변(코드값=현행 저장값). "확인 필요" 값은 확정분만. | V44(멱등 시드) | **P1** |
| **B2** | 소프트 참조 **논리참조 인덱스**: P1 소프트 참조 컬럼에 `(tenant_id, <ref>_id)` 인덱스(`CREATE INDEX IF NOT EXISTS`). 예: event 참조 자식들의 `(tenant_id, event_id)`, hall 참조의 `(tenant_id, hall_id)`. 이미 있는 idx(idx_lead_event 등)는 스킵. | V45(멱등 인덱스) | **P2** |
| **B3** | 뷰/mview/함수 **순증 후보**(§11): ⓐ `v_booth_display`(부스+코드명 조인·표시용), ⓑ `mv_hall_occupancy`(홀·기간 가동률 — REFRESH CONCURRENTLY·`(tenant_id,hall_key)` 유니크 인덱스), ⓒ `mv_lead_roi_summary`(리드/ROI 월별), ⓓ `fn_period_to_quarter(date)`·`fn_utility_fee(...,ruleset_version)`(재사용 계산). **실사용 화면/AI 근거 확인 후** 생성. | V46(뷰/함수, CREATE OR REPLACE) | P3 |
| **B4** | (멀티테넌트 확장 시) 공통코드 테넌트 오버라이드 `(tenant_id, grp_code, code)` 확장 — 현재 단일 테넌트라 **보류**(구조 대비만). | 후속 | P4 |
| **B5** | (소유자 승인 후) P1 FK **DROP 마이그레이션** — 테넌트 복합키 정합·고아 검증 통과 후에만. 자동 실행 금지. | 승인 대기 | P4 |
| **B6** | **프로시저 후보**(§11-4·§13): `sp_settlement_rollup`(J9)·`sp_kpi_snapshot_build`(J2)·`sp_refresh_marts`(J1 mview 일괄 REFRESH). 성능 결정적일 때만·CREATE OR REPLACE. | V46(뷰와 동반) | P3 |
| **B7** | **자동화 배치**(§13 J1~J9) — 대상·주기·멱등은 본 표 확정. 실행 프레임워크(스케줄러·분산락)는 **아키텍처(kintex-sa/ta) 인계**, 스케줄러 배선 backend-dev. | 인계(아키텍처+backend) | P2 |

---

## 9. 변경 이력

| 버전 | 일자 | 작성자 | 내용 |
|---|---|---|---|
| v1.1 | 2026-07-12 | kintex-data-architect(DA) | **§10 FK 최소화·공통코드 관리 표준**(소유자 지시) — 물리 FK 기본 미설정+화이트리스트 4그룹(W1~W4 RBAC/공통코드 구성), 범주값 공통코드화 정책, 테넌트 스코프 정합, 소프트 참조 무결성 보완책 / **§11 뷰·구체화뷰·함수·프로시저 지침**(`v_*` 실시간 파생·`mv_*` 무거운 집계+REFRESH 전략·`fn_*` 재사용 계산·`sp_*` 집합연산/다단계 트랜잭션·남용 판단 기준) / **§13 자동화 배치 카탈로그**(J1~J9 대상·주기·멱등·잠금 — 실행 프레임워크는 아키텍처 인계) / **§12 감사표 A(현행 FK 약 40건 유지4/제거권고 P1·잔류P3)+B(범주 컬럼 34개→공통코드 그룹 매핑)+db-engineer 백로그 B1~B7(V44 시드·V45 인덱스·V46 뷰·프로시저·배치 인계, 번호 V44~ 권고)**. 기존 마이그레이션 불변·비파괴. |
| v1.0 | 2026-07-11 | kintex-data-architect(DA) | 최초 — 전사 데이터 모델(개념→논리→물리 ERD, PLANNING §7 확장)·공간 데이터 표준(PostGIS SRID0·부스 POLYGON·트렌치 POINT·배선 LineString)·데이터 표준(명명·타입·공통코드 WISE 정합·마스터/룰셋 관리)·품질·거버넌스(PII 분류·암호화·동의·보존·감사·계보)·**BI 데이터마트 M16 스타 스키마(FACT_BOOKING/SETTLEMENT/UTILITY/AUCTION/VISITOR + DIM_DATE/HALL/EVENT/EXHIBITOR + KPI_SNAPSHOT, M16-1 7지표 정합)** 정의. 물리 구현은 db-engineer 인수, 코드값 확인필요 항목은 §8 후속. |
