kintex/docs/deliverables/02_설계서.md
2026-07-13 04:23:07 +09:00

421 lines
39 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# KINTEX AI 전시·행사시스템 — 설계서
> - 문서 종류: 설계서(System Design Document)
> - 프로젝트: **KINTEX AI 전시·행사시스템** (KINTEX AI Exhibition & Event System)
> - 작성일: 2026-07-12 · 버전: v1.0
> - 근거 문서: `docs/PLANNING.md`(v3.4) · `docs/design.md`(v2.4) · `docs/architecture/{app,system,tech,data,network,sso-hr-integration}.md` · `docs/COMMON_CODES.md` · **실제 구현 스키마(Flyway `V1~V49`)** · 실제 컨트롤러(REST 계약)
> - 보안: 본 문서에는 자격증명·비밀번호·SSH·내부 IP·시크릿을 기재하지 않는다.
> - 비고: 본 설계서는 구현된 스키마·API 계약을 정본으로 삼고, 계획(설계 완료·미구현) 항목은 그 취지를 명시한다.
---
## 목차
1. 시스템 아키텍처
2. 애플리케이션 아키텍처(레이어·모듈·의존성)
3. 데이터 설계
4. API 설계
5. 화면 설계
6. AI 설계(§8A)
7. 인증·인가 설계
8. 네트워크·보안영역 설계
9. 비기능 요구사항(NFR)
---
## 1. 시스템 아키텍처
### 1-1. 전체 구성
**6개 역할별 분리 프론트(React·Vite·TS) + 단일 공유 Spring Boot 백엔드 + 나노바나나 Python 워커 사이드카**를 SSO·역할 RBAC 위에 얹고, PostGIS·Redis 큐·오브젝트 스토리지를 공유한다.
```
[엣지/DMZ] CDN · WAF/nginx(TLS 종단)
[역할별 프론트] organizer · exhibitor · contractor · ops · admin(내부) · public/visitor
│ (SSO · 역할 RBAC 게이트)
[공유 Spring Boot 백엔드] REST + WebSocket(STOMP)
│ 룰 엔진(요율/규정) · 배치·배선 엔진(PostGIS) · 옥션 엔진(M15) · BI 집계(M16) · CMS(M17) · 공개 API
[비동기] Redis 큐 → 나노바나나 워커(google-genai) · 서류/PDF/EDM 워커 → 오브젝트 스토리지
│ (완료 시 WebSocket 푸시)
[데이터] PostgreSQL + PostGIS (프라이머리 + 읽기 복제) · 오브젝트 스토리지
```
### 1-2. 대원칙(5)
1. **단일 공간 데이터 모델** — 부스 폴리곤·트렌치 포인트·배선 경로를 PostGIS로 일원화(설계·시각화·검증·정산·wayfinding·BI가 동일 원천 재사용).
2. **이미지 생성 전면 비동기** — Spring이 RenderJob을 Redis 큐에 발행 → Python 워커가 Gemini로 생성 → 오브젝트 스토리지 적재 → WebSocket 완료 푸시. `GEMINI_API_KEY`는 워커 전유(백엔드 미취급).
3. **역할별 프론트 분리** — 최소권한·공격면 축소, 공유 디자인 시스템·컴포넌트·API 계약 상속.
4. **룰셋 = 버전 관리 데이터** — 규정(높이·방염·하중)·요율을 코드가 아닌 `rulesets/*.json` + master_data로 관리(연 단위 개정 무중단 반영).
5. **공개 vs 내부 백오피스 보안영역 분리**(§8).
### 1-3. 배포 토폴로지(존)
- **공개 존(DMZ)**: Reverse Proxy/WAF·nginx·TLS, CDN, 공개 프론트 SSR/SSG, 공개 API GW(화이트리스트 엔드포인트만), PG 콜백.
- **애플리케이션 존(내부망)**: 인증 프론트 정적 서빙(organizer·exhibitor·contractor·ops), 공유 백엔드 jar × N(systemd, 무상태 수평 확장), 워커 데몬 × M.
- **관리 존(내부 전용)**: admin 백오피스(웹 전용, VPN/허용 IP + 2FA 강제).
- **데이터 존(최내곽)**: PostgreSQL+PostGIS(프라이머리+스탠바이)·읽기 복제·Redis HA·오브젝트 스토리지. **아웃바운드 없음(유출 경로 제거)**.
> 물리 서버·도메인·포트는 G2 게이트 확정 후 Phase E에서 실체화(개발 도메인은 `kintex.zioinfo.co.kr`, 포트 8021). 근거: architecture/system.md·network.md.
---
## 2. 애플리케이션 아키텍처(레이어·모듈·의존성)
### 2-1. 패키지 구조
루트 `com.zioinfo.kintex`. 횡단 계층 + 도메인 모듈:
- `common`(응답봉투·페이징·에러·감사 AOP·공통코드 캐시 — **무의존**), `config`(Security·WebSocket·Redis·MyBatis), `auth`(JWT·RBAC·2FA/OTP·가드), `rules`(규정·요율 룰셋 로딩·평가), `system`(시스템관리), `work`(공통 업무), `module`(도메인 m2~m5 등).
- 리소스: `application.yml`(env 플레이스홀더), `mybatis/mapper/**/*.xml`(공간 SQL `ST_*`), `rulesets/`(compliance·rates JSON = 데이터).
### 2-2. 레이어링 표준
| 계층 | 책임 | 금지 |
|---|---|---|
| Controller | HTTP 바인딩·`@Valid`·RBAC 가드 호출·서비스 위임·`ApiResponse` 래핑 | 비즈니스 로직·SQL·트랜잭션·매퍼 직접 호출 |
| Service(interface+Impl) | 비즈니스 규칙·`@Transactional` 경계·룰 엔진 호출·매퍼 오케스트레이션·DTO 조립 | HTTP 타입 참조 |
| Mapper(MyBatis) | DB 접근·공간 SQL(ST_*) 바인딩 | 비즈니스 분기·DTO 조립 |
| DTO(record) / domain | 불변 전송 객체 / 순수 값객체(프레임워크 무의존) | — |
- 공간 연산은 매퍼 XML의 PostGIS SQL로 수행(서비스는 스칼라/GeoJSON 결과만 사용). 조회 `readOnly=true`, 버전 리소스는 낙관적 잠금(불일치=409).
### 2-3. 의존성 규칙(단방향·순환 금지)
- `module.mN``rules·auth·common`(횡단 허용). `common`**무의존**.
- **모듈 간 명시 방향만**: M3→M2, M4→M2, M5→M2·M3·M4, M15→M2·M3·M4·M5·M7, M9→M1·M4·M15, M13→M2, M14→M4·M10, M16→전 모듈(읽기), M12→M10·M17.
- 역방향 필요 시 직접 참조 금지 → **도메인 이벤트·공유 식별자로 디커플**(예: M15 낙찰→M9는 발주 링크). 모듈 간 결합은 서비스 인터페이스로만. 검증 = ArchUnit 빌드타임 아키텍처 테스트(역참조·순환 CI 차단).
- 백엔드는 **단일 공유 모듈러 모놀리스**(역할별로 쪼개지 않음) — 모듈 경계 + 의존 규칙으로 코드 레벨 강제, 노출 표면은 경로 접두 + RBAC로 분리.
> 근거: architecture/app.md.
---
## 3. 데이터 설계
### 3-1. 데이터 표준(명명·타입)
- 테이블/컬럼: PostgreSQL 무인용 소문자 `snake_case`, PK `<엔티티>_id`(도메인은 UUID), WISE 이식 테이블은 정본 PK 유지.
- 지오메트리 `geom`/`<용도>_geom`, 암호화 PII `<필드>_enc`, 코드 컬럼 `status`/`<의미>_code`, 시각 `*_at`(timestamptz, UTC 저장/Asia/Seoul 표시), 금액 `numeric`(KRW).
- **DTO(camelCase) ↔ 컬럼(snake_case)**: MyBatis `map-underscore-to-camel-case=true`.
- **응답 제외(불변)**: `*_enc`·`password_hash`·`otp_secret`·내부 IP/SSH·내부 식별자는 API 응답 완전 제외.
### 3-2. FK 최소화 원칙 (소유자 지시 2026-07-12)
- **원칙: "FK는 최소화, 공통코드로 관리."** 신규 도메인/테넌트 테이블은 물리 `FOREIGN KEY`를 두지 않고, 참조무결성은 **애플리케이션 레이어 검증 + `*_id` 소프트 참조 명명**으로 보장.
- **근거**: ① 멀티테넌트 복합 PK(`(tenant_id, id)`) 전환 시 단일 id FK가 `UNIQUE(id)` 보조제약을 강제하는 마찰 ② MyBatis가 조인·삭제 순서 제어 ③ 멱등 `ON CONFLICT` 시드 순서 자유 ④ 부스 재배치(replaceBooths) 시 자식 재지정 빈번(V3 `utility_order`·`render_job`이 이미 소프트 참조 = 정본 사례).
- **물리 FK 예외 화이트리스트(그 외 신규 FK 신설 금지)**: (W1) `common_code.grp_code`, (W2) `sys_menu.parent_id`(self), (W3) `sys_role_permission`, (W4) `sys_role_menu` — 전역 시스템/RBAC 무결성 필수분만.
- **소프트 참조 보완**: 임계 트랜잭션(낙찰·정산) 명시적 부모 존재 검증, 삭제는 서비스가 자식 선처리(soft-delete `use_yn='N'` 우선), 논리참조 인덱스 `(tenant_id, <ref>_id)`, 야간 고아 스캔(DQ 게이트).
### 3-3. 공통코드 표준
- 범주형 컬럼(상태·유형·심각도 등 열거 가능 소수값)은 자유문자열/DB enum이 아닌 `common_code_group`/`common_code`로 관리. 컬럼엔 **코드값(영문 상수)만 저장**, 표시명(한글)은 조인/캐시. **DB `ENUM` 물리타입 지양**.
- **코드 vs 마스터 경계**: 열거 가능 소수값=공통코드(BOOTH_TYPE·RENDER_STATUS·SHOT_PRESET 등), 다건·CRUD·버전 대상=마스터/룰셋(홀·요율·규정 룰셋·등록업체). 정본 = `docs/COMMON_CODES.md`.
- 확정 코드그룹 예: `EVENT_ROLE`(ORGANIZER·EXHIBITOR·CONTRACTOR·HALL_MANAGER), `PORTAL_ROLE`, `BOOTH_TYPE`(independent·assembled), `COMPLIANCE_SEVERITY`(block·warn·pass), `RENDER_STATUS`(QUEUED·RUNNING·DONE·FAILED), `SHOT_PRESET`(S1~S7), `VERIFY_METHOD`(EMAIL·OTP).
### 3-4. 공간 데이터 표준
- **SRID `0`**(홀 로컬 평면 데카르트 좌표, 단위 미터). 지리좌표(4326) 아님, `geometry`(geography 아님).
- 부스 = `Polygon`, 트렌치 = `Point`(+ 배선 run은 LineString), 배선 = `LineString`/`MultiLineString`. 전 `geom` **GiST 인덱스 필수**.
- 저장 전 `ST_IsValid`·닫힌 링·홀 내포 검증(실패 시 400). 미실측 트렌치는 `is_assumed=true` 플래그(PLANNING R4).
- 파생 연산: 최단 배선(라우팅), 통로 폭 검증(버퍼), 면적 정산(ST_Area)이 모두 SQL 수준에서 수행.
### 3-5. 핵심 엔티티(구현 스키마 기준)
**신원·마스터(V2)**
- `app_user`(id·email·display_name·`password_hash`(BCrypt, 응답 제외)·hall_manager·`otp_secret`(응답 제외)·failed_login_count·locked_until·status) — V7에서 otp_enabled·verify_method·role_code·dept_id·last_login_at 순증, V26에서 tenant_id 순증.
- `company`(id·registration_no(사업자번호, 초대 검증 키)·name·category(14분류)·region·**registered**(미등록 응찰 차단)).
- `event`·`hall`(exhibition_center·label·width/depth/height_m·area_m2·floor_load_t_per_m2·floor_type·booth_capacity·has_gas·is_assumed_trench)·`hall_assignment`.
- `event_member`(event_id·user_id·**role_code**(ORGANIZER·EXHIBITOR·CONTRACTOR·HALL_MANAGER)·booth_id·company_id) — 행사 단위 RBAC 멤버십.
- `booth_standard`(assembled·premium spec jsonb)·`master_data`(RATE·UTILITY_FEE·COMPLIANCE + ruleset_version).
**공간 코어(V3, P0)**
- `trench`(hall_id·geom(Point)·supply_power/water/air/network/gas·is_assumed).
- `hall_exit`(geom(Point)·clearance_m — 비상구 이격 버퍼).
- `layout`(event_id·hall_id·version·status, UNIQUE(event_id,hall_id,version)).
- `booth`(layout_id·booth_no·booth_type·geom(Polygon)·size_w/d_m·height_m·floor_load_t_per_m2·premium).
- `design_plan`(booth_id·version·status·spec jsonb, UNIQUE(booth_id,version)).
- `utility_order`(event_id·booth_id(소프트 참조)·quote jsonb·wiring(MultiLineString)·location_diagram_url).
- `render_job`(event_id·booth_id(소프트)·shot_preset(S1~S7)·status·image_url·schema_hash·model_version·error_message(요약만)).
**옥션 M15(V16)**
- `auction`(event_id·category·auction_type(reverse/rfq)·award_criteria(lowest/comprehensive)·weight_price/reputation/delivery·round·deadline·material_package_id·materials).
- `auction_invite`(등록업체만, UNIQUE(auction_id,company_id) — 미등록 원천 차단).
- `bid`(=Quotation: subtotal·vat·total·lead_days·valid_until·terms·`lines` jsonb·version·status, UNIQUE(auction_id,company_id) — 재응찰 시 버전 증가).
- `award`(옥션당 1건, bid_id·reason·awarded_by — 감사 추적)·`company_reputation`(rating·jobs_done·claim_rate).
- 봉인 입찰: 마감 전 경쟁 견적 비공개(서비스 레이어 강제 마스킹), 마감 후 발주자 전체 공개.
**관람·발주·정산·콘텐츠(V14~V24 등)**
- M6: `event_milestone`·`required_document`·`document_review_issue`(V14). M8: `dock`·`dock_reservation`(V15), `logistics_*`(V33).
- M10: `visitor_registration`·`lead`(V17). M12: `edm_campaign`·`sponsorship_package`·`sponsorship_sponsor`(V18). 공개: `exhibit_inquiry`(V20)·`visitor_guide`·`transport_info`(V43).
- M17 CMS: `cms_content`·`cms_translation`·`microsite`(V19), `cms_content_version`·`cms_media`(V21).
- M9: `invoice`·`invoice_payment`(V24), `invoice_refund`·`tax_invoice`(V34). M1 판매: `booth_sale`(V27). 결재: `approval`·`approval_line`·`approval_history`(V29).
**공통·시스템관리(V7·V8·V37)**
- `common_code_group`/`common_code`·`sys_menu`·`sys_role`/`sys_permission`/`sys_role_permission`·`sys_setting`·`audit_log`(actor·action·target·summary·ruleset_version·result·ip_hint)(V7).
- 공통 업무(V8): `worklog`·`schedule`·`message`/`message_recipient`·`notice`·`opinion`/`opinion_comment`·`meeting`/`meeting_action`·`report`·`notification`.
- 시스템 기초(V37): `dept`·`sys_program`·`sys_role_menu`·`sys_auth_policy`·`login_history`·`error_log`. 기타: `login_slide`(V11)·`holiday`(V41)·`sys_message`(V48)·`mail_log`(V36)·`webhook_*`(V35)·`ai_config`(V30).
**멀티테넌시(V26·V31)**
- `tenant`(id(slug: kintex·coex)·name·domain·status). KINTEX = 테넌트 #1 시드.
- 핵심 테이블 `tenant_id` 순증(`NOT NULL DEFAULT 'kintex'` 백필, 회귀 0) + `(tenant_id, …)` 선두 복합 인덱스. V31에서 테넌트 루트(event·hall·app_user)를 `PRIMARY KEY (tenant_id, id)` 복합 PK로 전환.
### 3-6. 마이그레이션 요약(Flyway V1~V49)
| 버전 | 주제 | 주요 산출 |
|---|---|---|
| V1 | 확장 | PostGIS·pgcrypto 등 extension |
| V2 | 신원·마스터 | app_user·company·event·hall·hall_assignment·event_member·booth_standard·master_data |
| V3 | 공간 코어(P0) | trench·hall_exit·layout·booth·design_plan·utility_order·render_job(+GiST) |
| V4·V5·V6 | 마스터 시드 | 홀 마스터·트렌치 그리드·마스터/데모 시드 |
| V7 | 시스템관리·보안 | common_code(_group)·sys_menu·sys_role/permission·sys_setting·audit_log |
| V8 | 공통 업무 | worklog·schedule·message·notice·opinion·meeting·report·notification |
| V9 | 공개 인증 | password_reset |
| V10 | 시드 | 10년치 행사 시드 |
| V11·V12·V13 | 부가 | login_slide·TOTP 2FA·카탈로그/대시보드 인덱스 |
| V14 | M6 | event_milestone·required_document·document_review_issue |
| V15 | M8 | dock·dock_reservation |
| V16 | M15 옥션 | auction·auction_invite·bid·award·company_reputation(+시드) |
| V17 | M10 | visitor_registration·lead |
| V18 | M12 | edm_campaign·sponsorship_package·sponsorship_sponsor |
| V19 | M17 CMS | cms_content·cms_translation·microsite |
| V20 | 공개 사이트 | exhibit_inquiry |
| V21 | 확장 | cms_content_version·cms_media(체크인·리드·CMS 확장) |
| V23 | 일정 보강 | schedule 확장 |
| V24 | M9 정산 | invoice·invoice_payment |
| V25 | M1 | 홀 배정 확장 |
| V26 | 멀티테넌시 1단계 | tenant + 핵심 테이블 tenant_id 순증·복합 인덱스 |
| V27 | 부스 판매 | booth_sale |
| V28 | 옥션 부가 | BOQ·옥션 수수료 |
| V29 | 결재 | approval·approval_line·approval_history |
| V30 | AI | ai_config |
| V31 | 테넌트 표준 | 테넌트 루트 복합 PK((tenant_id, id)) 전환 |
| V32 | 시드 | 2026 하반기 킨텍스 행사 |
| V33 | M8 확장 | logistics_equipment/request·rental_item/order·inbound |
| V34 | M9 확장 | invoice_refund·tax_invoice |
| V35 | 자동화 | webhook_subscription·delivery·inbound·edm_followup_log |
| V36 | 메일 | mail_log |
| V37 | 시스템 기초 | dept·sys_program·sys_role_menu·sys_auth_policy·login_history·error_log |
| V38·V39 | 데모 | full·realism 데모 시드 |
| V41·V42 | 마스터/데모 | holiday(2050)·대량 데모 시드 |
| V43 | 관람 가이드 | visitor_guide·transport_info + `v_event_calendar`·`v_event_monthly_summary`(뷰) |
| V44 | 성능 | 성능 인덱스 |
| V45·V46·V47 | 부가/데모 | 프로필 사진·포스터 로컬 에셋·라이브 행사 폐루프 시드 |
| V48 | 메시지 | sys_message(템플릿) |
| V49 | 데모 | 빈 테이블 데모 시드 |
> 멱등(`IF NOT EXISTS`·`ON CONFLICT`) + 비파괴 순증 원칙. V22·V40은 결번.
### 3-7. VIEW/MVIEW/배치 전략
- **`v_*` VIEW**: 실시간 파생·경량 조인·상태 산출(예 `v_event_calendar` — 날짜→UPCOMING/ONGOING/ENDED 산출로 별도 상태 컬럼 불필요). tenant_id 필터·민감 컬럼 미노출.
- **`mv_*` MATERIALIZED VIEW**: 무겁고 실시간성 낮은 집계(BI KPI·가동률·리드/ROI). 새로고침 전략 명시(야간 배치·`REFRESH ... CONCURRENTLY`·mview 인덱스).
- **BI 데이터마트(M16)**: 스타 스키마 `FACT_BOOKING/SETTLEMENT/UTILITY/AUCTION/VISITOR` + `DIM_DATE/HALL/EVENT/EXHIBITOR` + `KPI_SNAPSHOT`(built_at 각인). 별도 `mart` 스키마, 야간 ETL 또는 읽기 복제. **운영 DB 직조회 금지**.
- **배치 카탈로그(J1~J9)**: mview 새로고침·KPI 스냅샷·마감 알림/할일·EDM/옥션 통지·세션 만료 정리·고아 검출(DQ)·PII 보존/파기(행사종료+1년)·행사 crawl·정산 롤업. 공통 요건: 멱등·분산락·실패 격리·감사·테넌트 스코프.
> 근거: architecture/data.md.
---
## 4. API 설계
### 4-1. 표준
- 베이스 `/api`. 공개=`/api/public/**`, 워커=`/api/internal/**`, 행사 스코프=`/api/events/{eventId}/…`, 플랫폼 관리=`/api/admin/**`(hasRole ADMIN), 인증=`/api/auth/**`. 비-CRUD 액션은 하위 동사 세그먼트(POST).
- 응답 봉투 `ApiResponse<T>`{success·data·error{code,message}}, 목록 `PageResponse<T>`{items·page·size·total}.
- **ErrorCode→HTTP**: VALIDATION 400 · UNAUTHORIZED 401 · FORBIDDEN 403 · NOT_FOUND 404 · CONFLICT 409 · **COMPLIANCE_BLOCKED 422** · **RENDER_QUOTA_EXCEEDED 429** · **NOT_REGISTERED_COMPANY 403** · NOT_IMPLEMENTED 501 · INTERNAL 500.
### 4-2. 도메인별 주요 엔드포인트(실제 컨트롤러 기준)
| 도메인 | 베이스 경로 | 컨트롤러 |
|---|---|---|
| 인증(로그인·2FA·비번찾기·프로필사진·앱무결성) | `/api/auth`, `/api/auth/app-integrity` | AuthController·PublicAuthController·TwoFactorController·ProfilePhotoController·AppIntegrityController |
| M2 플로어플랜 | `/api/events/{eventId}/halls/{hallId}/layout` | FloorplanController |
| M3 부스 설계 | `/api/events/{eventId}/booths/{boothId}/design` | DesignController |
| M4 유틸리티 | `/api/events/{eventId}/booths/{boothId}/utility` | UtilityController |
| M5 렌더잡 | `/api/events/{eventId}`(render), `/api/internal/render`(워커 콜백) | RenderJobController·RenderWorkerCallbackController |
| M15 옥션 | `/api/auctions`, `/api/contractor` | AuctionController·ContractorController |
| M1 홀 배정·부스 판매 | `/api/halls`, `/api/booth-sales`, `/api/events`(catalog) | HallAssignController·BoothSalesController·EventCatalogController |
| M6 서류·마일스톤 | `/api/events/{eventId}`(document) | DocumentController |
| M8 물류 | `/api/events/{eventId}`, `/api/events/{eventId}/logistics` | LogisticsController·LogisticsExtController |
| M9 정산 | `/api/settlement` | SettlementController |
| M10 관람객·리드 | `/api/visitors` 등(VisitorController) | VisitorController |
| M12 마케팅 | `/api/events/{eventId}`(marketing) | MarketingController |
| M16 BI·분석 | `/api/analytics`, `/api/events/{eventId}/analytics`, `/api/events/{eventId}/dashboard`, `/api/admin/dashboard` | AnalyticsController·AnalyticsOverviewController·DashboardController·AdminDashboardController |
| M17 CMS·마이크로사이트 | `/api/cms/contents`, `/api/cms/media`, `/api/exhibitors/{exhibitorId}/microsite` | CmsContentController·CmsMediaController·MicrositeController |
| 공개 사이트·가이드·AI 도우미 | `/api/public`, `/api/public/cms`, `/api/public/microsites`, `/api/public/ai` | PublicSiteController·PublicGuideController·PublicCmsController·PublicMicrositeController·VisitorAssistantController |
| 현장 운영 | `/api/events/{eventId}/ops` | OpsController |
| 공통 업무(§5B) | `/api/work/{worklogs,schedules,messages,notices,opinions,search,meetings,reports,stats,approvals,notifications}` | Worklog·Schedule·Message·Notice·Opinion·Search·Meeting·Report·Stats·Approval·Notification Controller |
| 시스템관리(M18) | `/api/admin/{users,roles,roles/{role}/menus,programs,depts,companies,settings,audit,login-history,error-log,mail-config,notify-config,system-health,rulesets,auth-policy}` | SysUser·Role·RoleMenu·Program·Dept·Company·Setting·AuditLog·LoginHistory·ErrorLog·MailConfig·NotifyConfig·SystemHealth·AdminRuleset·AuthPolicy Controller |
| 테넌트 | `/api/admin/tenants` | TenantAdminController |
| AI 설정·NL 질의 | `/api/admin/ai`, `/api/ai` | AiConfigController·NlQueryController |
| 웹훅 | `/api/webhooks/in`, `/api/admin/webhooks` | WebhookInbound·WebhookAdmin Controller |
| 헬스 | `/health`, `/api/home` | HealthController·HomeController |
### 4-3. WebSocket(STOMP)·큐 계약
- `GET /ws`(SockJS), prefix `/topic`(서버→클라)·`/app`(클라→서버). 토픽 `/topic/render/{jobId}`·`/topic/auction/{auctionId}`·`/topic/events/{eventId}/notifications`·checkin. 페이로드는 REST DTO 재사용.
- Redis 큐: RenderJob 큐 `kintex:renderjob:queue`, 상태 `...:job:{jobId}`, 쿼터 `...:quota:{eventId}`. **성공 시에만 쿼터 차감**. G1 미승인 시에도 큐잉/상태는 동작(목/degraded).
> 근거: architecture/app.md, 실제 controller 매핑.
---
## 5. 화면 설계
### 5-1. 화면 총괄
design.md v2.4 집계 **총 89화면** — 웹 코어·도메인 52(SCR-01~51, SCR-HOME) + 관리자 10(SCR-A1~A10) + 공개사이트 8(SCR-P1~P8) + 3-트랙 관문 4(SCR-T0~T3) + 모바일 15(SCR-M1~M15). 역할 약칭: 주=주최자·참=참가업체·장=장치/공사업체·홀=킨텍스 직원·관=관리자·대=일반 대중/관람객.
### 5-2. 웹 코어·도메인(대표)
| SCR | 화면 | 모듈 | 역할 |
|---|---|---|---|
| SCR-01 | 로그인 & 행사 워크스페이스 선택 | 공통 | 전 |
| SCR-HOME | 로그인 후 메인 홈(랜딩 대시보드) | 공통·§5B·M1·M12 | 전 |
| SCR-02 | 주최자 대시보드(D-데이 마일스톤 타임라인) | M1·M6 | 주 |
| SCR-03 | 부스 배치 에디터(DnD+AI 자동배치+규정 오버레이) | M2 | 주 |
| SCR-04 | 배치안 비교(S7 홀 전경 조감) | M2·M5 | 주 |
| SCR-05 | 참가업체 부스 홈 | M3·M6 | 참 |
| SCR-06 | 부스 설계 스튜디오(스펙→나노바나나 4샷+before/after) | M3·M5 | 참·장 |
| SCR-07 | 유틸리티 배선 뷰(트렌치 오버레이+자동 견적) | M4 | 참·장 |
| SCR-08 | 유틸리티 신청 요약·위치표시도 | M4b | 참 |
| SCR-09 | 장치업체 규정 검증 리포트 | M3 | 장 |
| SCR-10/11 | 홀매니저 승인 큐 / 검수 상세 | M2·M3·M6 | 홀 |
| SCR-12 | 시각화 갤러리(S1~S7) | M5 | 전 |
| SCR-13 | 경영분석 대시보드 | M16 | 주·홀·관 |
| SCR-18~29 | 홀배정·부스판매·정산·서류·매칭·물류·옥션(개설·응찰·견적서·낙찰) | M1·M6·M7·M8·M9·M15 | 역할별 |
| SCR-30~37 | 관람객 등록·체크인·리드·EDM·스폰서십·CMS·마이크로사이트·다국어 | M10·M12·M17 | 역할별 |
| SCR-38 | 업체 수주 부스 대시보드 | 업체포털 | 장 |
| SCR-39~48 | §5B 공통 업무(업무일지·일정·쪽지·공지·의견·검색·회의록·업무보고·알림·마이페이지) | §5B | 전 |
| SCR-49~51 | 회원가입·비밀번호 재설정·2차 인증(OTP) 설정 | §5B-3 | 전 |
### 5-3. 관리자 백오피스(SCR-A*, M18·§5B-1, MDI 적용)
SCR-A1 사용자 · A2 역할·권한(RBAC) · A3 공통코드 · A4 메뉴 · A5 감사로그 · A6 시스템설정 · A7 마스터데이터(홀·요율·요금·부스표준·등록업체) · A8 규정 룰셋 버전 · A9 테넌트 온보딩(플랫폼 슈퍼관리자) · A10 AI 플랫폼 설정(AiConfig).
### 5-4. 공개 홍보 사이트(SCR-P*, MDI 비적용·SEO/다국어)
SCR-P1 홈 · P2 행사 상세 · P3 공개 인터랙티브 플로어플랜 · P4 관람객 사전등록 · P5 마이크로사이트 공개 뷰 · P6 참가/부스 문의 · P7 입장권 예매(티켓팅) · P8 예매 확인·취소.
### 5-5. 3-트랙 IA(§3C, SCR-T*)
코엑스 3-사이트 IA를 킨텍스 단일 도메인 위에 **visitor / business / agency** 3-트랙 얕은 진입 레이어로 정형화. 신규 라우트 4개(`/`=SCR-T0 관문, `/visitor`=T1, `/business`=T2, `/agency`=T3)만 추가하고 기존 딥라우트·MDI·티켓 공개 라우트는 불변(회귀 0).
- **SCR-T0 관문**(`/`, 미인증): 제품 히어로 + 3-트랙 대형 분기 카드(관람 1순위) + 행사 하이라이트 레일.
- **SCR-T1 visitor**: 세련된 마케팅 홈페이지로 격상, 중심 = 대화형 AI 관람 도우미(히어로 정중앙 자연어 입력창 + 예시 질문칩). 라이트/다크 토글(공개 예외).
- **SCR-T2 business**: B2B 실무 톤, 주최자(홀 임대)/참가업체(부스 신청) 2-분기 가치 제안 + 임대 절차 스텝 → 로그인 CTA.
- **SCR-T3 agency**: 옥션/역경매 차별점 강조, 등록업체 안내(739개사·14분류·미등록 시공 엄금) + 진행 중 공사 옥션 공고 리스트 → 등록업체 로그인 CTA.
- **공개 셸**: PublicShell + 상단 3-트랙 전환 탭(TrackSwitcher), 좌측 "KINTEX AI 전시·행사시스템" 워드마크, 우측 언어(한/영/중/일)·로그인. 서브도메인 테넌트 컨텍스트 하 해당 테넌트 콘텐츠만 노출.
### 5-6. 역할 → 랜딩·메뉴 결정 규칙(웹·모바일 공통)
- 미인증 기본 진입 = visitor 공개 관람 랜딩(SCR-T0). 인증 후 역할 매핑 트랙의 전용 메인으로 랜딩:
- primaryTrack 우선순위: 내부-admin > 내부-ops > business > agency > visitor.
- 랜딩: ADMIN→관리 메인(SCR-16) / MANAGER·HALL_MANAGER→운영 메인 / ORGANIZER·EXHIBITOR→비즈니스 메인(SCR-T2) / CONTRACTOR→에이전시 메인(SCR-T3) / VISITOR→관람객 메인(SCR-T1).
- 좌측 메뉴 그룹(GROUPS: ops·design·visitor·finance·work·system)은 트랙 필터로 노출/숨김. `system` 그룹은 ADMIN만.
- 모바일 하단 탭바는 역할별 4~5탭 구성.
### 5-7. 디자인 시스템(요약)
- **브랜드**: 킨텍스 CI 블루 계열 B2B 실무 톤. AI 생성물은 항상 "AI 생성/초안" 라벨.
- **컬러 토큰(kx)**: `primary-600 #0066B3`(주 브랜드), `primary-700 #004C86`, `ai-accent #6D4AFF`(AI 전용), success `#0E8A5F`·warning `#B45309`·error `#D92D20`, `canvas-bg #1C2536`(에디터 다크 서피스), 배선 전기 `#EF4444`/네트워크 `#3B82F6`/급배수 `#22C55E`. WCAG AA 이상.
- **타이포**: Pretendard(숫자·좌표 tabular), Display 28 / H1 24 / Body 14, 테이블 행 44px(모바일 48).
- **컴포넌트**: StatusBadge(작성중→제출→AI검토→승인→반려→시공→검수), DdayChip(D-3 warning), ViolationFlag(차단 빨강/경고 주황 + 도면 위 번호 핀 1:1), AI 라벨(보라 외곽선), 생성 이미지 상시 고지문(제거 불가). 라운드 버튼 4px/카드 8px(상한)/칩 pill. 12컬럼·컨테이너 max 1440px.
- **MDI 셸**: 좌측 사이드바(240px)+상단 문서 탭바+DocumentHost, ShellFooter 36px. 메뉴 클릭=탭 열기(라우트 이동 아님), 비활성 탭 상태 보존, 최대 12탭, 세션 `localStorage`(`kintex.mdi.{role}.{eventId}`). MDI 적용=관리자·주최자·참가·장치·홀매니저 / 비적용=공개(P·T)·모바일.
- **캘린더**: WISE(UIWS `CalendarView`) 패턴 문자 그대로 — 월 6주×7열, 기간 이벤트=가로 spanning bar, "+N" 팝업, 전시장 세그먼트 필터는 홀 마스터 센터 distinct 동적 옵션(하드코딩 금지).
- **반응형·다크모드**: 웹 1440px(설계·에디터·대시보드)/모바일 390px(조회·승인·현장). 다크모드 Phase 1 미지원(에디터 캔버스만 다크), 공개 마케팅 T1은 예외적 토글. 전 화면 풀블리드·공백 없는 반응형(전역 NFR).
### 5-8. 모바일(SCR-M*, Expo/RN 390px)
조회·승인·현장 전용(캔버스 편집 미제공, 뷰어+승인 액션만). 하단 탭 4개. SCR-M1 시공업체 현장 체크리스트 · M2 홀매니저 현장 검수 · M3 역할별 홈 · M4 리드캡처(배지 스캔) · M5 관람객 홈·배지/QR · M6 wayfinding · M7 플로어플랜·부스 검색 · M8 비즈매칭 · M9 세션·아젠다 · M10 반입 통행증·안전 · M11 알림센터 · M12 서류·승인 조회 · M13 옥션 순위 · M14 티켓 예매 · M15 내 티켓 지갑.
### 5-9. Stitch 연동
전 화면 Stitch 경유(프로젝트 `9385904003821333054`, 디자인 시스템 "Precision Enterprise AI"). 생성 화면 = `stitch_kintex_ai_system_architect/`(각 디렉터리 `code.html`·`screen.png`). design.md가 토큰·컴포넌트 권위(상충 시 design.md 우선). 신규 정의 SCR-HOME·SCR-T0~T3는 미생성(designer Stitch 의뢰 대상).
> 근거: design.md v2.4 §1~§4, PLANNING §2-3.
---
## 6. AI 설계(§8A — 사용성 & 토큰 최소화)
### 6-1. 사용성 표준(U1~U7)
전 화면 공통 인라인 AI 진입점(`AiAssistant`) + 예시 질문 칩 + 원탭 액션 + 다음 명령 제시(규칙 기반 우선) + **구조화 카드 + 근거 인용**(출처 레코드 링크, 근거 없으면 "모름" 폴백) + 대화 히스토리(요약 압축) + 접근성/다국어/모바일/음성. 관람객·업무 사용자가 단일 `AiAssistant` + 단일 `/ai/ask` 계약 공유, 노출 위치·칩·허용 액션만 역할/트랙으로 스코프.
### 6-2. 토큰 최소화 6원칙(P1~P6)
- **P1 결정론 우선 라우팅**: 사실 조회형(일정·교통·주차·마감일·요금·부스 위치·통계)은 DB/뷰가 직접 응답(LLM 미호출). LLM은 요약·추천·자연어 종합에만.
- **P2 소형모델 우선 티어링**: 온프레미스 소형(Ollama qwen3:1.7b/llama3.2:1b) → 난도·실패 시 Claude 승급(AiTextRouter).
- **P3 RAG 발췌**: top_k 제한·컬럼 프로젝션으로 짧은 컨텍스트만(전체 문서/테이블 주입 금지).
- **P4 캐싱**: 응답 캐시(의도+파라미터+tenant+locale, TTL)·프롬프트 프리픽스·기간 요약 재사용.
- **P5 출력 상한·구조화**: max_tokens 상한 + JSON/카드 구조화.
- **P6 집계는 SQL로**: 통계·랭킹·추이는 데이터마트 SQL이 산출, LLM은 설명·해석만.
### 6-3. 계약·측정
- **단일 계약** `POST /ai/ask`: 입력 `{question, contextRef, locale, history}`, 출력 `{answerCard, citations[], followups[], route(direct|small|escalated), usage(tokens·cached)}`.
- IntentRouter는 규칙 테이블(공통코드)로 관리, 사실조회 의도는 DB 리졸버 매핑, 미매핑만 LLM 경로. 캐시 키 `hash(intent+params+tenant_id+locale)`.
- AI 프로바이더: Claude 기본(`api.anthropic.com`, 키 env only) + AiTextRouter(실패 시 Ollama 폴백) + AiConfig 설정 화면(하드코딩 금지). 나노바나나(Gemini)는 별도 이미지 파이프라인(본 절 토큰 원칙 대상 아님, G1 게이트).
- **측정지표**(`ai_usage_log` 적재, tenant/모듈 비용 귀속): LLM 우회율 ≥40%·소형모델 처리율 ≥70%·캐시 적중률 ≥30%·평균 입력 ≤1500/출력 ≤400·근거 인용률 ≥95%(초기 가설).
> 근거: PLANNING §8A. 현행 visitor-assistant DB 근거 응답·AiTextRouter 폴백과 정합(표준화).
---
## 7. 인증·인가 설계
### 7-1. 현행(구현) — JWT + RBAC + 2FA
- **JWT(HS256)**: 클레임 sub·name·roles(eventId→역할)·hm(홀매니저)·plat(플랫폼 역할)·otp. STATELESS·CSRF disable. 공개(permitAll): `GET /health`·`POST /api/auth/login`·`/ws/**`·`/api/internal/render/callback`·(Phase D)`/api/public/**`.
- **이중 RBAC**: 플랫폼 역할(JWT `plat`+`hasRole`) × 행사 역할(JWT `roles`/`hm`+`EventAccessGuard.requireRole`). 열람=행사 멤버 or 홀매니저, 편집·액션=역할별. **등록업체 게이트(불변)**: CONTRACTOR 응찰은 등록업체 검증 필수(미등록=NOT_REGISTERED_COMPANY 403).
- **2차 인증(TOTP RFC6238)**: SHA1·30s·6자리·±1 윈도우. 최초 QR 등록, 마이페이지 재설정/해제, 관리자 OTP 초기화. 대상 = 업무 사용자 필수, 일반 관람객 미강제(승격 시 필수 전환).
- **로그인 실패 잠금**(failed_login_count·locked_until) + 관리자 해제. **admin 비밀번호** = env `ADMIN_PASSWORD_ENC`(AES-256-GCM) + 별도 키파일 주입, 기동 시 BCrypt 재시드(`admin123` 하드코딩 금지).
- 민감 컬럼(`password_hash`·`otp_secret`)은 API 응답에서 완전 제외.
### 7-2. 계정 체계(단일 통합 + 가입 트랙 분리)
- 계정/RBAC는 단일 통합, 가입 트랙만 분리: 업무 트랙(B2B, 승인/초대+2FA 필수) vs 관람객 트랙(B2C, 간편가입/게스트 예매·2FA 미강제). 관람객→바이어/참가 승격은 단일 계정 등급 상향(리드·비즈매칭·재방문 이력 연속성 유지).
### 7-3. Open SSO / HR 연동 (계획 — 설계 완료·미구현)
- **프로토콜**: OIDC(Authorization Code + PKCE) 우선 + SAML 2.0 옵션. **IdP = Keycloak 자체 호스팅(온프레미스, 내부망 IdP — 외부 API 금지 비해당)**.
- **기존 JWT+2FA와 공존(교체 아님)**: SSO는 로그인 게이트웨이만 교체, 성공 후 백엔드가 기존 `JwtService.issue()`로 동일 형태 앱 JWT를 브로커 발급 → 전 화면·RBAC·행사 스코프 불변. `/api/auth/oidc/callback`만 신설, 로컬 로그인 폴백 유지(IdP 장애 대비).
- **2FA**: IdP realm이 OTP 제공 시 위임(이중 2FA 방지), 미구성·로컬 폴백은 기존 TotpService 유지.
- **HR 연동**: 어댑터 `HrDirectoryClient`(+Mock)로 외부 HR API를 정본 조달 + 로컬 읽기 캐시 스냅샷(미가용 시 degraded). PII 최소(주민번호·연락처·급여 미수집), 삭제는 소프트. 전역 역할·hall_manager는 IdP 그룹/클레임 매핑, **행사 역할(event_member)은 로컬 권위 유지**.
- **3자 매핑**: SSO subject(`sub`) ↔ HR 사번(`empNo`) ↔ 로컬 `app_user.id`(연결 테이블 `user_identity`). 이메일 단독 매칭 금지, 충돌 시 자동 병합 금지·관리자 수동 링크. JIT 프로비저닝(첫 SSO 로그인 시 password_hash 없이 생성).
- **이행**: 피처플래그(`sso.enabled`) 4단계(준비→coexist→pilot→cutover→정착), 각 단계 롤백 게이트. 신설 모델(`user_identity`·`hr_dept_snapshot`·`hr_emp_snapshot`·`hr_sync_log`·`dept.source`)은 DA/backend 인계 백로그(미구현).
> 근거: architecture/sso-hr-integration.md, PLANNING §5B-3, 실제 스키마.
---
## 8. 네트워크·보안영역 설계
### 8-1. 존 모델(4계층 + 관리 존)
| 존 | 구성 | 인바운드 | 아웃바운드 |
|---|---|---|---|
| 엣지(무신뢰) | CDN·WAF/DDoS | 인터넷 80/443 | DMZ LB |
| DMZ(공개) | 공개 LB(TLS 종단)·public SSR·visitor 게이트웨이·공개 API GW·PG 콜백 | 엣지에서만 | 내부망 API GW(제한), **데이터 존 직결 금지** |
| 내부망(인증·운영) | 내부 LB·SSO 게이트·내부 API GW·공유 백엔드 | DMZ(허용 API)·관리 존(IP/VPN) | 데이터 존·AI 워커·승인 아웃바운드 |
| AI 워커 존 | Redis 큐·나노바나나 워커·Ollama | 내부망(큐 소비만) | 데이터 존(OBJ), Gemini egress 단일 경로만 |
| 데이터 존(최내곽) | PostgreSQL+PostGIS·오브젝트 스토리지·BI 읽기 복제 | 내부망·AI 워커(서비스 계정만) | **없음(전면 차단)** |
| 관리 존 | 배스천·관측성·백업 | 운영자 VPN/허용 IP | 대상 존 SSH·수집 |
- **핵심 규칙**: DMZ → 데이터 존 직접 접근 절대 금지(내부망 백엔드 API 경유). 데이터 존 아웃바운드 없음.
### 8-2. 공개 vs 내부 백오피스 분리
- DMZ: 공개 홍보 사이트(`www.`/`expo.`, SSR·CDN·SEO·다국어·비인증·쓰기 없음), 관람객 앱(`visitor.`·쓰기 제한).
- 내부망(인증): `organizer.`·`exhibitor.`·`contractor.`(등록업체 검증). 내부망(운영): `ops.`(IP/VPN 제한). 관리: `admin.`(M18) = VPN/허용 IP allowlist + 2FA 강제 + 감사로그 전량 + 웹 전용.
- 공개(DMZ) 3원칙: 쓰기 없음 · 데이터 존 직결 불가 · 캐시/CDN 적극.
### 8-3. 방화벽·LB·TLS·아웃바운드
- 기본 정책 = DROP(default-deny). DB 포트(5432)·Redis·OBJ 인터넷 미노출. DMZ↔내부망은 HTTPS/API만.
- WAF: OWASP Top10, 업로드 확장자·MIME·크기 이중 검증, PG 콜백 IP allowlist+서명 검증, admin·옥션 레이트리밋 강화.
- LB: 공개 LB(DMZ) / 내부 LB(백엔드, 무상태 JWT 라운드로빈+헬스체크) / WebSocket 스티키. TLS는 LB 종단(HSTS·TLS1.2+), 존 간 mTLS 옵션.
- **아웃바운드 게이트(승인 4목적지)**: `api.anthropic.com`(승인, 실패 시 Ollama 폴백)·`generativelanguage.googleapis.com`(G1 미승인, 워커에서만)·PG 결제·SMTP. 그 외 전량 차단. 키 격리=네트워크 격리(Gemini 키=워커 존, Claude/PG 키=백엔드, 데이터 존 무아웃바운드).
### 8-4. 보안 불변(위반 = QA 반려)
① 스택트레이스 미노출(요약만) ② 민감정보(자격증명·PII) 응답 완전 제외 ③ `GEMINI_API_KEY` 백엔드 미취급(M5는 큐 발행까지만) ④ AI 이미지 항상 워터마크·고지 강제 ⑤ admin 비번 env 주입 ⑥ 크로스-테넌트 접근은 플랫폼 슈퍼관리자 전용 API(감사).
> 근거: architecture/network.md·app.md, CLAUDE.md 보안 제약.
---
## 9. 비기능 요구사항(NFR)
### 9-1. 성능·용량
- 플로어플랜 3안 생성 수 분 내, 배치·배선 상호작용 P95 < 2s, 부스 목록 P95 < 500ms, 이미지 단건 평균 ~40s(비동기·SLA 대상 아님), 공개 캐시 히트 P95 < 200ms, 옥션 순위 갱신 < 1s.
- 대형 행사 3,000~5,000부스(홀당 200~600). **Hikari max = `${DB_POOL_MAX:3}` + PgBouncer 권고**(공유 PG 포화 방지).
### 9-2. 가용성
- 코어 인증·설계·조회 경로 HA SLO 99.5%(성수기 99.9% 지향). 백엔드 무상태 수평 확장(세션·순위·타이머·쿼터·캐시 Redis 외부화), Redis HA(Sentinel/Cluster+AOF), PG 프라이머리+읽기 복제(BI·공개조회 오프로드).
### 9-3. 기술 표준(핀 버전)
- 백엔드: Java 17 · Spring Boot 3.2.5 · Gradle · **MyBatis 3.0.3**(JPA 금지, `@MapperScan(annotationClass=Mapper.class)`) · jjwt 0.12.5 · UTF-8 강제.
- 프론트: React 18.3.1 · Vite 5.4.8 · TypeScript 5.6.2(strict) · react-router-dom 6 · @tanstack/react-query 5 · zustand 4(Redux 금지). 빌드 `tsc -b && vite build`(타입 에러=빌드 실패).
- 워커: Python 3.11+ · google-genai · `gemini-3.1-flash-image-preview`. 호출은 `NANOBANANA_LIVE=1`+`GEMINI_API_KEY` 동시 충족 시만(기본 /degraded).
- 데이터: PostgreSQL+PostGIS(`kintex_db`)·Redis·오브젝트 스토리지·Flyway.
- 관측성: Actuator+Micrometer 권고(`/actuator/health` 배포 게이트·`/metrics`). 로그 보안 불변(자격증명·IP·PII·스택트레이스 금지, `include-stacktrace:never`).
### 9-4. 접근성·국제화
- WCAG AA 이상, 차트 패턴/라벨 병기, KPI· aria-label, 키보드 포커스 `primary-600` 2px, 아이콘 (stroke) SVG(이모지 금지), i18n 분리(/// 로케일 숫자/통화/날짜 포맷), `prefers-reduced-motion` 준수.
> 근거: architecture/system.md·tech.md, design.md 전역 NFR.
---
> **후속 산출 명시**: 본 설계서와 개발계획서(`01_개발계획서.md`)는 개발 착수 시점 산출물이다. **사용자지침서·운영자지침서는 UI 정렬 안정화 이후 별도 산출**한다(deliverables 갱신 정책상 완성+QA 통과 후 최신 메뉴를 반영해야 하므로 이번 범위에서 제외).