diff --git a/.claude/agents/kintex-gap-auditor.md b/.claude/agents/kintex-gap-auditor.md new file mode 100644 index 0000000..846e2bc --- /dev/null +++ b/.claude/agents/kintex-gap-auditor.md @@ -0,0 +1,50 @@ +--- +name: kintex-gap-auditor +description: 킨텍스 앱의 미개발·미완성을 전수 감사하는 에이전트. 메뉴↔라우트↔컴포넌트↔API 4자 체인을 교차 대조해 빈 화면·스텁·"준비중"/TODO·홈/대시보드 폴백·데드링크·미구현 엔드포인트·CRUD 누락·미배선 버튼을 색출하고 우선순위 백로그를 산출한다. "미개발 확인", "완성도 감사", "빈 화면 찾기", "준비중 화면", "홈으로 가는 메뉴", "미구현", 다시 실행·업데이트·보완 포함. general-purpose 타입. +tools: Read, Write, Glob, Grep, Bash +model: opus +--- + +당신은 킨텍스 AI 전시·행사시스템의 **미개발 감사관**이다. general-purpose 타입 — grep·빌드·서버 조회를 실제 실행한다. 목적은 **고치는 것이 아니라 "무엇이 아직 안 되어 있는지"를 정확·완전하게 확인**하는 것이다(수정은 구현 하네스가 담당). + +## 핵심 역할 +프론트(`src/frontend`)·백엔드(`src/backend`)를 전수 스캔해 **미개발/미완성 지점**을 근거(파일:라인)와 함께 인벤토리화하고, 심각도·우선순위를 매긴 백로그(`docs/GAP_AUDIT.md`)를 산출한다. + +## 감사 방법 (4자 체인 교차 대조 — 핵심) +킨텍스 화면은 **메뉴(AppShell GROUPS) → 라우트(App.tsx) → 컴포넌트(screens/**) → API(backend controller)** 로 이어진다. 각 연결이 끊긴 곳이 미개발이다: +1. **메뉴 인벤토리**: `components/layout/AppShell.tsx`의 `GROUPS`/`NavItem`(`to`·`scoped`) + 공개 네비(PublicShell) 전수 추출. +2. **라우트 인벤토리**: `App.tsx`의 `` 전수 추출. +3. **화면 상태**: 각 라우트 컴포넌트가 (a)실제 구현 (b)스텁/빈 (c)"준비중"/TODO/placeholder (d)홈·대시보드로 폴백/리다이렉트 인지 판정. +4. **API 배선**: 화면이 호출하는 엔드포인트가 백엔드에 실제 구현됐는지(컨트롤러 존재·스텁 아님) 대조. 미구현/스텁/501/빈배열 하드코딩 색출. + +## 색출 대상 (gap taxonomy — 상세 references/gap-taxonomy.md) +- **빈 화면/스텁**: `return null`·빈 EmptyState 고정·`TODO`·`준비중`·`coming soon`·`구현 예정`·placeholder만 렌더. +- **홈/대시보드 폴백**: 메뉴 클릭이 전용 화면 없이 `/home`·대시보드로 감(참가업체 사례). `EVENT_SCOPED_PATH` 폴백·`Navigate` 리다이렉트. +- **데드링크**: `to`/`href`가 정의된 라우트에 없음, `#`·`javascript:void`·빈 onClick. +- **미배선 버튼/액션**: onClick 없음·`disabled` 고정·`alert('미구현')`·핸들러 빈 함수. +- **미구현 API**: 프론트가 부르는 경로가 백엔드 컨트롤러에 없음, 또는 컨트롤러가 스텁(하드코딩·빈 리스트·`NOT_IMPLEMENTED`). +- **CRUD 누락**: 목록만 있고 생성/수정/삭제 미구현, 상세 없음. +- **메뉴 없는 라우트 / 라우트 없는 메뉴**: 고아 화면·죽은 메뉴. +- **모바일(mobile/)**: 화면은 있으나 API 미배선·mock 고정(선택 스캔). + +## 작업 원칙 +- **근거 필수**: 모든 지적은 `파일:라인` + 무엇이 왜 미개발인지. 추측 금지 — 실제 코드 확인. +- **오탐 억제**: "구현됐으나 데이터가 비어 보이는 것"(행사스코프 시드 미정렬 [[event-scope-seed-alignment]])과 "코드가 미개발인 것"을 구분. 데이터 이슈는 별도 표기. +- **우선순위**: P0(핵심 업무 흐름 차단)·P1(주요 화면 빈/폴백)·P2(부가·엣지). 역할별 노출도 고려(관리자 전용 등). +- **정본 대조**: `docs/PLANNING.md`(M1~M18 범위)·`docs/design.md`(SCR 인벤토리)와 대조해 "기획엔 있는데 미구현" 을 우선. + +## 입력/출력 프로토콜 +- 입력: 스캔 범위(전체/특정 영역), 이전 `docs/GAP_AUDIT.md`(있으면 델타 갱신). +- 출력: `docs/GAP_AUDIT.md` — ①요약(총 N건·심각도 분포) ②4자체인 인벤토리(메뉴·라우트·화면상태·API) ③미개발 목록(파일:라인·유형·심각도·정본근거) ④우선순위 백로그(수정 담당 하네스 매핑: 화면=kintex-frontend-dev/구현 오케스트레이터, API=kintex-backend-dev, 데이터=kintex-testdata-dev). 중간 산출 `_workspace/`. + +## 에러 핸들링 +빌드/서버 조회 실패 시 1회 재시도, 재실패면 해당 항목 "확인 불가"로 표기하고 계속. 상충(코드상 구현인데 런타임 빈)은 삭제 말고 양쪽 근거 병기. + +## 협업 +- 발견은 `docs/GAP_AUDIT.md`로 공유. **kintex-qa**가 P0/P1 지적을 실동작(경계면·라우트 렌더)으로 재검증(오탐 제거). 수정은 구현 하네스(kintex-impl-orchestrator)로 인계 — 이 에이전트는 **감사·확인만**. + +## 재호출 지침 +이전 `docs/GAP_AUDIT.md`가 있으면 읽고, 그 사이 배포로 해소된 항목은 ✅ 표기·신규 미개발만 추가(델타 감사). 특정 영역만 요청 시 그 영역만. + +## 보안 불변 +자격증명·PII·내부IP·스택트레이스 문서 미기재. 서버 조회는 존재/상태만(값 미출력). diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..2d6389f --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,24 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "C:\\Users\\ython\\AppData\\Local\\Python\\pythoncore-3.14-64\\Scripts\\graphify.EXE hook-guard search" + } + ] + }, + { + "matcher": "Read|Glob", + "hooks": [ + { + "type": "command", + "command": "C:\\Users\\ython\\AppData\\Local\\Python\\pythoncore-3.14-64\\Scripts\\graphify.EXE hook-guard read" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/.claude/skills/kintex-completeness-orchestrator/SKILL.md b/.claude/skills/kintex-completeness-orchestrator/SKILL.md new file mode 100644 index 0000000..940020d --- /dev/null +++ b/.claude/skills/kintex-completeness-orchestrator/SKILL.md @@ -0,0 +1,44 @@ +--- +name: kintex-completeness-orchestrator +description: 킨텍스 앱의 미개발·미완성을 전수 확인하는 감사 하네스. 메뉴↔라우트↔컴포넌트↔API 4자 체인을 교차 대조해 빈 화면·스텁·"준비중"/TODO·홈/대시보드 폴백·데드링크·미배선 버튼·미구현 엔드포인트·CRUD 누락을 색출하고 우선순위 백로그(docs/GAP_AUDIT.md)를 산출한다. "미개발 확인", "완성도 감사", "빈 화면/준비중 화면 찾기", "홈으로 가는 메뉴", "미구현 점검", "개발 안 된 것", 다시 실행·특정 영역만·업데이트·보완 요청 시 이 스킬을 사용하라. (수정·구현은 kintex-impl-orchestrator, UI정렬은 kintex-wise-ui-orchestrator 담당 — 이 하네스는 확인/감사 트랙.) +--- + +# 킨텍스 미개발 확인 (완성도 감사) 오케스트레이터 + +킨텍스 앱에서 **아직 개발 안 된/미완성 지점**을 정확·완전하게 확인한다. **실행 모드: 하이브리드** — Phase1 감사(단일 에이전트) → Phase2 검증(단일 QA). 모든 Agent 호출은 `model: "opus"`. + +**경계(중복 회피):** 이 하네스는 **확인/감사만** 한다. 수정·구현은 `kintex-impl-orchestrator`, UI 정렬은 `kintex-wise-ui-orchestrator`, 데이터 빈 화면은 `kintex-renewal`/testdata. 감사 결과 백로그를 그 하네스들로 인계한다. + +## 에이전트 로스터 +| 에이전트 | 역할 | +|---|---| +| **kintex-gap-auditor**(신규) | 4자 체인 교차 대조·미개발 전수 인벤토리·`docs/GAP_AUDIT.md` 산출 | +| **kintex-qa**(재사용) | P0/P1 지적을 실동작(라우트 렌더·경계면·API 200)으로 재검증 — 오탐 제거 | +| **reviewer**(재사용, 선택) | 정본(PLANNING/design) 대조 정합 확인 | + +## Phase 0: 컨텍스트 +- `docs/GAP_AUDIT.md` 존재 여부로 초기/델타 판별. 특정 영역만 요청이면 범위 한정. +- 최근 배포로 해소된 항목은 델타에서 ✅ 처리. + +## Phase 1: 미개발 감사 (kintex-gap-auditor 단일) +메뉴(AppShell GROUPS·PublicShell)·라우트(App.tsx)·화면(screens/**)·API(backend controller)를 전수 스캔·교차 대조. gap taxonomy(references/gap-taxonomy.md)로 분류. **오탐 억제**: "코드 미개발" vs "데이터만 빈(행사스코프 시드 [[event-scope-seed-alignment]])" 구분. `docs/GAP_AUDIT.md`에 요약·인벤토리·미개발목록(파일:라인·유형·심각도)·우선순위 백로그(수정 담당 하네스 매핑) 산출. + +## Phase 2: 검증 (kintex-qa 단일) +P0/P1 지적을 **실동작으로 재검증** — 해당 라우트가 정말 빈/폴백인지, API가 정말 미구현(404/스텁)인지 헤드리스/curl/grep로 확인. 오탐은 제거·확정은 CONFIRMED 표기. 결과를 GAP_AUDIT.md에 반영. + +## Phase 3: 인계 (오케스트레이터) +확정 백로그를 화면=kintex-frontend-dev/impl, API=kintex-backend-dev, 데이터=kintex-testdata-dev로 매핑해 요약 보고. **수정은 이 하네스가 하지 않는다** — 소유자 승인 후 해당 하네스로 넘긴다. + +## 데이터 전달 / 에러 +파일 기반(`docs/GAP_AUDIT.md` 정본 + `_workspace/` 중간). 조회 실패는 1회 재시도 후 "확인 불가" 표기. 상충은 양쪽 근거 병기(삭제 금지). + +## 보안 불변 +자격증명·PII·내부IP·스택트레이스 문서 미기재. 서버 조회는 존재/상태만. + +## 테스트 시나리오 +- **정상**: "미개발 확인 하네스 실행" → Phase1 감사(GAP_AUDIT.md) → Phase2 QA 재검증(오탐 제거) → Phase3 인계 보고. +- **델타**: "다시 확인" → 기존 GAP_AUDIT.md 대비 해소분 ✅·신규만 추가. +- **부분**: "옥션 화면만 미개발 확인" → 범위 한정 감사. + +## 후속 작업 +description 후속 키워드("다시 실행"·"특정 영역만"·"업데이트")로 델타/부분 감사 트리거. Phase 0이 초기/델타/부분 판별. diff --git a/.claude/skills/kintex-completeness-orchestrator/references/gap-taxonomy.md b/.claude/skills/kintex-completeness-orchestrator/references/gap-taxonomy.md new file mode 100644 index 0000000..074e67c --- /dev/null +++ b/.claude/skills/kintex-completeness-orchestrator/references/gap-taxonomy.md @@ -0,0 +1,37 @@ +# 미개발 분류 (Gap Taxonomy) — 킨텍스 + +감사 시 각 지점을 아래 유형으로 분류하고 근거(파일:라인)와 심각도를 붙인다. + +## 유형별 탐지 신호 (grep 힌트) + +| 유형 | 코드 신호(grep) | 판정 | +|------|----------------|------| +| **빈 화면/스텁** | `return null`(라우트 컴포넌트 최상위)·`준비중`·`준비 중`·`coming soon`·`구현 예정`·`TODO`·`FIXME`·`placeholder`·`더미`·`mock`(고정) | 렌더 내용이 없거나 안내문뿐 | +| **홈/대시보드 폴백** | `EVENT_SCOPED_PATH`·`Navigate to="/home"`·`navigate('/home')`·`navigate('/dashboard')`·메뉴 `scoped` 인데 전용 라우트 없음 | 메뉴 클릭이 전용 화면 없이 홈/대시보드로 감 | +| **데드링크** | `to="#"`·`href="#"`·`href="javascript:`·`to=` 값이 App.tsx 라우트에 없음 | 이동 대상이 없음 | +| **미배선 버튼/액션** | `onClick={() => {}}`·`onClick` 부재(버튼인데)·`disabled` 하드코딩·`alert(`·`console.log('TODO` | 눌러도 동작 없음 | +| **미구현 API** | 프론트 `api.get/post('/api/...')` 경로가 backend `@*Mapping`에 없음·컨트롤러가 `NOT_IMPLEMENTED`·빈 리스트/하드코딩 반환·`return List.of()` 고정 | 서버가 없거나 스텁 | +| **CRUD 누락** | 목록 화면만·`POST/PUT/DELETE` 핸들러 부재·상세 라우트/화면 없음 | 조회만 가능 | +| **고아/죽은** | 라우트 있으나 메뉴/링크 진입 없음 / 메뉴 있으나 라우트 없음 | 접근 불가 또는 죽은 메뉴 | + +## 오탐(false positive) 주의 — 미개발 아님 +- **데이터만 빈**: 코드는 구현됐는데 행사스코프 시드가 데모 해소행사에 없어 빈 것 → [[event-scope-seed-alignment]]. **미개발 아님, 데이터 이슈로 별도 표기**(담당=testdata). +- **역할 게이트로 미노출**: 권한상 안 보이는 것(관리자 전용 등)은 미개발 아님. +- **degraded 폴백**: API 실패 시 의도적 폴백(그라디언트·안내) 있는 것은 정상 처리. +- **의도적 외부/준비중**: iOS TestFlight "준비중" 등 명시적 로드맵 항목은 "계획됨"으로 표기(미개발과 구분). + +## 심각도 +- **P0**: 핵심 업무 흐름(로그인 후 주요 도메인 진입·부스/옥션/정산/관람객 핵심 CRUD) 차단. +- **P1**: 주요 화면이 빈/폴백/데드링크. 메뉴에 있으나 동작 안 함. +- **P2**: 부가·엣지·관리자 세부·모바일 mock. + +## 정본 대조 +`docs/PLANNING.md`(M1~M18·§5B 공통·역할/트랙)·`docs/design.md`(SCR 인벤토리)에 **있는데 미구현**인 것을 최우선. 기획에 없는 것은 "범위 외"로 표기. + +## 4자 체인 스캔 순서 +1. `AppShell.tsx` GROUPS/NavItem → 메뉴·`to`·`scoped` 목록 +2. `App.tsx` `` → 라우트·컴포넌트 목록 +3. 각 컴포넌트 파일 → 렌더 내용 판정(위 신호) +4. 컴포넌트가 부르는 `*Api.ts`/`api.*` 경로 → backend `@*Mapping` 대조 +5. 공개 사이트: `PublicShell`·`screens/public/*` 동일 절차 +6. (선택) `mobile/app/*` → API 배선 여부 diff --git a/.gitignore b/.gitignore index 6541a43..96c1dba 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,7 @@ dist/ # 전시장 CAD 원본(대용량 DWG zip) — 로컬 보존, git 제외 docs/assets/floorplans/cad/ + +# graphify / screenshot generated outputs (local only) +graphify-out/ +screenshot/ diff --git a/CLAUDE.md b/CLAUDE.md index 02fca7b..d75e610 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -82,6 +82,8 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL | 2026-07-11 | 구현 하네스 초기 구성 — 전문 에이전트 6종 + kintex-impl-orchestrator + 구현 백로그 | 전체 | 범용 에이전트만 존재 → 스택 확정 후 실제 구현 조율 팀 구성 | | 2026-07-11 | **v2.0 재구성** — 자동전시시스템 확장(PLANNING v2.0). 신규 에이전트 11종(아키텍트 5·공통 1·도메인 5·AI 1) + 오케스트레이터·백로그 v2.0(Phase A 아키텍처→B 공통레이어→C 부스코어→D 도메인→E 배포) | 전체 | 스코프 확장(전시 전기능·경영분석·CMS·관리자·역할분리·공사 옥션·WISE 공통레이어·AI Claude 전환) | | 2026-07-11 | WISE 웹 기능 레퍼런스 강화 — kintex-frontend-dev에 uiws frontend(pages 공통 업무기능) 차용 원칙, 오케스트레이터에 웹 기능 화면 WISE 패턴·Stitch 디자인 경유·모바일 경계 명시 + 로스터에 kintex-mobile-dev(경계) 추가 | kintex-frontend-dev·kintex-impl-orchestrator | "웹도 wise 기능 참고" 요청 | +| 2026-07-12 | **세션 대량 구현·배포** — visitor 메인(AI 관람 도우미 홈·기능타일·연월 브리핑, 근거=크롤 적재 DB)·3-트랙 관문/서브홈·역할별 랜딩+메뉴 게이트(plan_role_routing)·공개 AI/캘린더 API(V43)·정산 데이터 정렬(V47)·포스터 이미지 빌드내장(V46)·성능 인덱스(V44)·아바타 API(V45). AI 토큰최소화 기획(PLANNING v3.4 §8A)·데이터표준(FK최소화·공통코드·view/mview·배치, data.md)·Open SSO+HR 연동 설계(sso-hr-integration.md). 배포 커밋 214b6df→3077cb0, health 게이트 통과 | 전체 | 소유자 라이브 피드백 연속 반영. **전 지시 로그: `docs/OWNER_FEEDBACK.md`(권위)** | +| 2026-07-12 | **선행 게이트 해소·검증 교훈** — G1 나노바나나 Gemini 라이브·G2 kintex.zioinfo.co.kr:8021 CI/CD 라이브. 배포 전 필수: **Flyway 마이그레이션 라이브 dry-run(BEGIN…ROLLBACK)**·시크릿 fail-fast 프로파일 확인·파일단위 커밋(공유 트리 교차 방지)·health 게이트. Stitch 불안정 시 design.md 스펙 직접 구현(문서화 폴백) | 전체·kintex-devops-dev | 다수 배포 롤백/캐시/교차커밋 사고 후 표준화 | --- @@ -95,6 +97,7 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL | 날짜 | 변경 내용 | 대상 | 사유 | |------|----------|------|------| | 2026-07-12 | 초기 구성(analyst·crawler 에이전트 + 오케스트레이터) | 전체 | "크롤링/벤치마킹 하네스 생성" 요청 — COEX 3-트랙 재구성·행사 크롤 정례화 | +| 2026-07-12 | **모바일 앱 벤치마킹**(전시·공연 앱 다운로드·평점 상위순) → `docs/analysis/mobile-benchmark-exhibition-performance.md` + 백로그 12항목. 핵심: 다운로드↔만족도 상반(티켓팅 빅3 저평점) → MMCA·Eventbrite·DICE·Fever 롤모델. **관람객 정보 크롤→DB 적재**(V43 visitor_guide·transport, 포스터 34장 V46) | kintex-benchmark-analyst·kintex-crawler-dev | "모바일은 전시·공연 앱 벤치마킹 다운로드·좋아요순" + "크롤링해서 db 저장" | --- @@ -108,6 +111,7 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL | 날짜 | 변경 내용 | 대상 | 사유 | |------|----------|------|------| | 2026-07-12 | 초기 구성(kintex-testdata-dev 신규 + wise-ui/menu-ia/qa 재사용 + 오케스트레이터) | 전체 | 소유자 지시 — 상세→메인 복귀 부재·전면 리뉴얼·테스트 데이터/공통코드/프로그램 채움 | +| 2026-07-12 | **코엑스풍 공개사이트 리뉴얼**(design.md v2.4 §3C-V 비주얼·모션 — 시네마틱 히어로·스크롤 리빌·카운트업·포스터 카드 리프트/줌·오토스크롤, prefers-reduced-motion·CLS 0) + **행사스코프 데이터 정렬**(V47 e-2026-live 폐루프·V49 잔여 보강). ★교훈: 화면이 비는 진짜 원인=시드가 데모 해소행사(workspaces[0]=start_date 최소)에 안 묶임 | kintex-frontend-dev·kintex-testdata-dev·designer | "코엑스처럼 지금 착수"·"정산 데이터 없음"·"모든 테이블 데이터" | --- @@ -122,6 +126,7 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL |------|----------|------|------| | 2026-07-12 | 초기 구성(에이전트 2 신규 + kintex-qa 재사용 + 오케스트레이터) | 전체 | 소유자 강한 피드백("전부 WISE대로 안 되어 있다" — 시스템관리 메뉴 실종·햄버거/아코디언 부재·캘린더·대시보드·로고) | | 2026-07-12 | 메뉴 IA 전담 추가 — kintex-menu-ia-dev + kintex-menu-recompose 스킬(표준 카테고리 v1·라우트↔메뉴 정합 절차), 오케스트레이터 Phase 2.5 편입 | agents·skills | "카테고리별 메뉴 재구성 하네스" 요청 — 별도 오케스트레이터 대신 중복 회피 확장 | +| 2026-07-12 | **Nifty 디자인 시스템 전면 정렬** — 카드 Nifty 타입(`.kx-card` subtle 그림자·hover·`--flush`/`__media`/`__header`/`__body`/`__footer` 구조)·타이포(`--fs-micro`/`--fs-nano`·`--fw-*` 토큰·공용 `.kx-page__title`/`.kx-section__title`·font-size 179+weight 415 토큰화)·그리드(`.kx-table` Nifty Advanced header로 6화면 수렴). **캘린더 Nifty**(옅은 격자·연노랑 today·소프트 이벤트칩)·**파란버튼 흰글자**(`--color-on-accent` 다크대응)·favicon 정정. 기준: `docs/DESIGN_SYSTEM_NIFTY.md`·`_workspace/audit_typography_grid.md` | kintex-frontend-dev·kintex-qa | 소유자 반복 강피드백("Nifty 카드/그리드/폰트 안 맞음"·"파란버튼 흰글자"·"달력 Nifty") | --- @@ -138,3 +143,31 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL |------|----------|------|------| | 2026-07-11 | 초기 구성 — kintex-mobile-dev 신규 + 오케스트레이터(재사용: designer·backend-dev·qa·devops-dev·reviewer). WISE 모바일 레퍼런스·Stitch 디자인 경유 반영 | 전체 | 모바일 앱 트랙 전담 하네스 부재("하네스 생성" + "wise 모바일 참고" + "디자인은 스티치" 요청) | | 2026-07-11 | **앱 타깃 2개 확정 반영** — 코드베이스 1(mobile/) + 배포 타깃 2(①운영 B2B: 2FA 필수·사내 QR ②관람객 B2C: 스토어 공개·간편가입·게스트). 계정은 단일 통합+가입 트랙 분리(PLANNING v3.1) | kintex-mobile-dev·오케스트레이터 | 소유자 확정 — 회원가입 관람객 포함 질의 → 하이브리드(계정 통합·앱 분리) 채택 | +| 2026-07-12 | **내정보(WISE)+생체인식(expo-local-authentication)+프로필 사진(expo-image-picker)** + **앱 위변조 방지·시큐어코딩 보안 체크리스트**(루트/탈옥·무결성·Hermes+R8·화면캡처·cleartext·권한최소, `docs/security/mobile-security-*.md`) + **다국어(react-i18next)·역할별 랜딩 패리티** 착수 | kintex-mobile-dev·kintex-qa | "모바일 내정보 WISE+생체+사진"·"앱 위변조방지·시큐어코딩·보안체크"·"모바일도 동일 로직"·"웹 4개국어인데 모바일은?" | + +## graphify + +This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships. + +Rules: +- For codebase questions, first run `graphify query ""` when graphify-out/graph.json exists. Use `graphify path "" ""` for relationships and `graphify explain ""` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output. +- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing. +- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context. +- After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost). + +--- + +## 하네스: 미개발 확인 (kintex-completeness-orchestrator) + +**목표:** 킨텍스 앱의 **미개발·미완성**을 전수 확인. 메뉴↔라우트↔컴포넌트↔API 4자 체인을 교차 대조해 빈 화면·스텁·"준비중"/TODO·홈/대시보드 폴백·데드링크·미배선 버튼·미구현 엔드포인트·CRUD 누락을 색출하고 우선순위 백로그(`docs/GAP_AUDIT.md`) 산출. **확인/감사만** — 수정은 구현 하네스 인계. + +**트리거:** 미개발 확인, 완성도 감사, 빈 화면/준비중 화면 찾기, 홈으로 가는 메뉴, 미구현 점검, 개발 안 된 것, 다시 실행·특정 영역만·업데이트 요청 시 `kintex-completeness-orchestrator` 스킬을 사용하라. + +**에이전트:** kintex-gap-auditor(신규, 4자체인 감사·인벤토리) + kintex-qa(재사용, P0/P1 실동작 재검증·오탐 제거) + reviewer(선택, 정본 대조). + +**경계(중복 회피):** 확인=이 하네스. 수정·구현=kintex-impl-orchestrator, UI정렬=kintex-wise-ui-orchestrator, 데이터 빈 화면=kintex-renewal/testdata. ★오탐 주의: "코드 미개발" vs "데이터만 빈(행사스코프 시드 미정렬)" 구분. + +**변경 이력:** +| 날짜 | 변경 내용 | 대상 | 사유 | +|------|----------|------|------| +| 2026-07-12 | 초기 구성(kintex-gap-auditor 신규 + kintex-qa 재사용 + 오케스트레이터 + gap-taxonomy 레퍼런스) | 전체 | "미개발 확인 하네스 생성" 요청 — 4자체인 교차대조 완성도 감사 트랙 부재 | diff --git a/docs/GAP_AUDIT.md b/docs/GAP_AUDIT.md new file mode 100644 index 0000000..b80fb26 --- /dev/null +++ b/docs/GAP_AUDIT.md @@ -0,0 +1,107 @@ +# KINTEX AI 전시·행사시스템 — 미개발 전수 감사 (GAP_AUDIT) + +- 감사일: 2026-07-12 · 감사자: `kintex-gap-auditor` +- 범위: `src/frontend`(85 라우트) + `src/backend`(74 컨트롤러) 4자 체인(메뉴↔라우트↔화면↔API) 교차 대조 +- 방법: gap-taxonomy grep 신호 + 실코드 확인. 수정 없음(감사·문서 전용). + +--- + +## ① 요약 + +| 구분 | 건수 | +|------|------| +| **미개발 총계(코드)** | **9건** | +| P0 (핵심 흐름 차단) | 0 | +| P1 (주요 화면 미배선/백엔드 전무) | 2 | +| P2 (부분·degraded·planned) | 7 | +| 데이터 이슈(별도, 코드 정상) | 행사스코프 시드 정렬 계열(전수 아님) | +| 문서 위생(별도, 미개발 아님) | 4 (stale "미구현" 주석) | + +**총평:** 앱 완성도는 매우 높다. 데드 메뉴 0, 빈 onClick/데드링크 0, 최상위 `return null` 라우트 0. 도메인 백엔드(auction·visitor·cms·marketing·logistics·settlement·hallassign·boothsales)는 전부 실구현. **가장 중요한 발견은 "미개발이 많다"가 아니라 "프론트 `sample` 헤더 주석이 Phase D 백엔드 배포 이후 갱신되지 않아 STALE 하다"는 점** — 실제 미개발은 아래 2건(P1)뿐이며 나머지는 graceful degraded/planned. + +### 최우선(P0/P1) 목록 +1. **[P1] TenantAdminPage** — 백엔드 `/api/admin/tenants`(목록+생성) 실존하나 프론트가 하드코딩 배열·`disabled` 버튼으로 미배선. +2. **[P1] PublicTicketPage(입장권 예매/조회)** — 티켓·재고·결제(PG) 백엔드가 아예 없음(고객 대면 공개 라우트). + +--- + +## ② 4자 체인 인벤토리 (요약) + +메뉴 `to` 타깃 ↔ App.tsx 라우트 ↔ 화면 컴포넌트 ↔ 백엔드 매핑. **전 메뉴 항목이 라우트에 정합(데드 메뉴 없음).** + +| 메뉴 그룹 | 대표 라우트 | 화면 상태 | 백엔드 컨트롤러 | 판정 | +|-----------|-------------|-----------|-----------------|------| +| 전시운영 | `/exhibitors`,`/schedule`,`/halls`,`/booth-sales`,`/ops/operations`,`:eventId/dashboard` | 실렌더 | Exhibitor·EventCatalog·HallAssign·BoothSales·Ops·Dashboard | ✅ | +| 설계·시공 | `:eventId/halls/:hallId/layout`,`/docs`,`/auctions`,`/contractor/dashboard`,`/logistics` | 실렌더 | Floorplan(m2)·Design(m3)·Utility(m4)·RenderJob(m5)·Document·Auction·Contractor·Logistics | ✅ | +| 관람객·마케팅 | `/visitors`,`/visitors/checkin`,`/leads`,`/campaigns`,`/sponsorship`,`/cms` | 실렌더 | Visitor·Marketing·CmsContent/Media/Microsite | ✅ | +| 정산·분석 | `/settlement`,`/analytics` | 실렌더 | Settlement·Analytics(+Overview) | ✅ | +| 업무공통 | `/work/*`(worklog·schedule·message·notice·meeting·report·approval·opinion·search),`/app-qr` | 실렌더 | work.* 11 컨트롤러 | ✅ | +| 시스템관리(admin) | `/admin`,`/admin/users·roles·depts·companies·menus·programs·role-menus·codes·auth-policy·login-history·audit·error-log·system-health·settings·mail-config·notify-config·**tenants**·masterdata/rulesets·holidays·login-slides` | 실렌더 | system.* + AdminDashboard·AuditLog·Ruleset·**TenantAdmin** | ⚠ tenants만 미배선 | +| 공개사이트 | `/public/*`,`/visitor·/business·/agency`,`/tickets/*` | 실렌더 | PublicSite·PublicGuide·PublicCms·PublicMicrosite·VisitorAssistant | ⚠ `/tickets/*`만 백엔드 전무 | +| 모바일 셀프 | `/m/booths/:id/checklist·/inspection` | 실렌더(웹뷰) | (모바일 앱 `mobile/` 별도 트랙) | ✅ | + +--- + +## ③ 미개발 목록 (근거·유형·심각도·정본) + +### P1 — 주요 미배선/백엔드 전무 + +**G-01 [P1] 테넌트 관리 화면 미배선 (frontend)** +- 파일: `src/frontend/src/screens/admin/TenantAdminPage.tsx:20`(하드코딩 `TENANTS`), `:53`(추가 버튼 `disabled title=notImpl`) +- 유형: 미배선(백엔드 존재, 프론트 sample 고정) — CRUD 누락(목록·생성 미연결) +- 근거: 백엔드 `TenantAdminController`(`/api/admin/tenants` GET list + POST create) **실존**. 화면 주석 "백엔드 미구현"은 **STALE**. +- 정본: PLANNING M18 §1A(테넌트 온보딩), design.md SCR-A9. 기획에 있음. + +**G-02 [P1] 입장권 예매/조회 백엔드 전무** +- 파일: `src/frontend/src/screens/public/PublicTicketPage.tsx:5`(주석 "티켓·재고·결제 API 부재 → 샘플"), `:36`(하드코딩 `TICKETS`) +- 라우트: `/tickets/:eventId/purchase`, `/tickets/lookup` (공개, 비로그인 고객 대면) +- 유형: 미구현 API(백엔드 컨트롤러 부재 — grep "ticket" in backend = 0) + PG 미연동 +- 정본: PLANNING M10(관람객)·M9(정산) design.md SCR-P7. 기획에 있음. + +### P2 — 부분 미개발(정상 degraded/planned) + +| ID | 항목 | 근거(파일:라인) | 유형 | 비고 | +|----|------|-----------------|------|------| +| G-03 | CMS AI 자동번역 | `cms/MultilingualCmsPage.tsx:127` · `cmsApi.ts:150` | 백엔드 501 → degraded "준비 중" | 초벌 번역 AI 미배선 | +| G-04 | 마케팅 캠페인 실발송 | `marketing/MarketingController.java:49` | send 엔드포인트 존재하나 실 이메일 발송 미구현(status 전이만) | SMTP 연동 후속 | +| G-05 | 서류 HWP/PDF 생성 | `docs/ReportAuthoringPage.tsx:476-479` · `docs/DocsMilestonePage.tsx:34` | 버튼 `disabled` "준비중" | 파일 큐 연동 후속 | +| G-06 | 회의 녹음/STT 전사 | `work/MeetingPage.tsx:161` | "준비 중" 표기 | 녹음·STT AI 미배선 | +| G-07 | 통합검색 자연어(AI) | `work/SearchPage.tsx:132,144` | "준비 중", 키워드 검색은 동작 | AI 검색 미배선 | +| G-08 | 마이페이지 프로필 수정·알림 서버동기화 | `work/MyPage.tsx:127,192` | "준비 중"(로컬 저장) | 프로필 PUT·알림설정 API 미배선 | +| G-09 | 부스설계 렌더 이력 저장 | `design/BoothDesignStudioPage.tsx:135` | 501 → "큐잉만 동작" | 렌더 이력 영속 후속 | + +### 계획됨(미개발 아님) +- AppQr **iOS** — `appqr/AppQrCodePage.tsx`: Android APK 배포, iOS는 EAS G3 게이트 명시적 로드맵. + +--- + +## ④ 별도 분류 (미개발 아님) + +### 데이터 이슈 (코드 정상 · 담당=kintex-testdata-dev) +- sample-import 12개 화면(analytics·dashboard·ops·floorplan·gallery·marketing×2·schedule·visitor×3)은 **조건부 degraded 폴백**(NETWORK/NOT_FOUND/NOT_IMPLEMENTED 시에만 sample). 실 API가 primary. 빈 화면으로 보이면 코드가 아니라 **행사스코프 시드 미정렬** → testdata 담당. + +### 문서 위생 — STALE 주석 (기능 영향 없음 · 담당=frontend-dev 청소) +- `screens/auction/sampleAuction.ts:3` "M15 역경매 엔진 미구현" → 실구현됨 +- `screens/visitor/sampleVisitor.ts:3` "M10/M11 백엔드 미구현" → 실구현됨 +- `screens/marketing/sampleMarketing.ts:3` "M12 백엔드 미구현" → 실구현됨 +- `screens/cms/cms.css:4` "M17 백엔드 미구현 → 전 화면 로컬 샘플" → cmsApi 실배선됨 +→ 주석만 갱신(오해 유발). 재감사 오탐 방지 위해 정리 권장. + +--- + +## ⑤ 우선순위 백로그 (수정 담당 매핑) + +| 순위 | ID | 작업 | 담당 | +|------|-----|------|------| +| 1 | G-01 | TenantAdminPage → `/api/admin/tenants` GET/POST 배선(하드코딩·disabled 제거) | **kintex-frontend-dev** | +| 2 | G-02 | 티켓 도메인 백엔드(티켓·재고·예매·조회) + PG 어댑터 + 프론트 배선 | **kintex-backend-dev**(+visitor-dev) → frontend-dev | +| 3 | G-04 | 캠페인 실발송(SMTP, 수신동의·상한) | kintex-backend-dev(marketing) | +| 4 | G-03 | CMS AI 자동번역 배선(AiTextRouter) | kintex-ai-dev | +| 5 | G-06/G-07 | 회의 STT·통합검색 자연어 AI | kintex-ai-dev | +| 6 | G-05/G-09 | 서류 HWP/PDF 생성·렌더 이력 영속(파일/렌더 큐) | kintex-backend-dev | +| 7 | G-08 | 마이페이지 프로필 PUT·알림설정 API | kintex-backend-dev + frontend-dev | +| 8 | (위생) | STALE "미구현" 주석 4건 정리 | kintex-frontend-dev | +| — | (데이터) | 행사스코프 시드 정렬(빈 화면 폴백 해소) | kintex-testdata-dev | + +--- +*P0/P1 지적은 kintex-qa가 실동작(라우트 렌더·경계면)으로 재검증 후 kintex-impl-orchestrator로 인계.* diff --git a/docs/OWNER_FEEDBACK.md b/docs/OWNER_FEEDBACK.md new file mode 100644 index 0000000..f68e6f5 --- /dev/null +++ b/docs/OWNER_FEEDBACK.md @@ -0,0 +1,184 @@ +# 소유자 피드백·지시 로그 (KINTEX AI 전시·행사시스템) + +> 이번 세션에서 소유자가 지시한 모든 항목을 전수 기록한다. 상태: ✅완료(배포) · 🚚배포중 · ⏳작업중 · 📋대기. +> 이 파일을 기준으로 누락 없이 처리한다. 새 지시는 여기에 추가한다. +> 최종 갱신: 2026-07-12 + +--- + +## 1. 디자인 시스템 (Nifty / WISE) — 최우선 반복 지시 + +| # | 지시 | 상태 | 비고 | +|---|------|------|------| +| 1.1 | **Nifty 카드 타입** 적용 (https://preview.themeon.net/nifty/ui-elements/cards/) — 라운딩·그림자·헤더/바디/푸터 구조·보더 톤 | 📋대기 | `.kxp-ecard`는 8px 라운딩 있으나 Nifty 카드 구조 전면 정렬 필요. 다음 패스 | +| 1.2 | **그리드는 Nifty Advanced table headers**로 통일 (tables/static-tables) | ⏳작업중 | 정본 `.kx-table` 존재, 미준수 6화면 수렴 (감사완료) | +| 1.3 | **Tabulator 기능** 추가 (tables/tabulator — 정렬·필터·페이지·CSV) | 📋대기 | task #32 | +| 1.4 | **상세페이지 = Nifty blog** 스타일 (blog-apps/blog) | 📋대기 | task #33 | +| 1.5 | **버튼 = Nifty buttons**, **Dropdowns**, **컴포넌트**, **List Group**, **Typography/Modals/Offcanvas** = ui-elements 전체 채택 | ⏳부분 | Offcanvas 완료, 나머지 정렬 진행 | +| 1.6 | **Nifty 스타일 학습 md 생성** | ✅완료 | `docs/DESIGN_SYSTEM_NIFTY.md`·`docs/analysis/nifty-design-refs.md` | +| 1.7 | **아이콘은 선(line/stroke) SVG**로 직접 (이모지 금지) | ✅원칙 | 전 신규화면 준수 | +| 1.8 | **UI는 WISE(UIWS) 문자 그대로** — 셸·메뉴·캘린더 자체 재해석 금지 | ✅원칙 | 메모리 [[ui-follow-wise-strictly]] | +| 1.9 | **폰트를 페이지마다 안 맞춤** — 전 화면 타이포 일관화 | ⏳작업중 | 감사완료(하드코딩 262건/36파일). `--fs-micro`·공용 `.kx-page__title`·weight 토큰 신설로 정렬. 다음 패스 | +| 1.10 | **모든 경계선 엷은 회색** | ⏳부분 | 구조 보더는 neutral-200(#e4e7ec) 사용 중. 의미색·hover는 유지, 지정화면 정렬 | +| 1.11 | **좌우 2단 폭 50:50**, 전 화면 **공백 없는 반응형**(풀블리드) | ✅원칙 | task #39 스윕 대기 | +| 1.12 | **top 설정 아이콘** 추가 + **설정 레이아웃 커스터마이저**(offcanvas 테마/레이아웃/색상) | ⏳부분 | 설정 아이콘 완료, 커스터마이저 task #43 | + +## 2. 캘린더 + +| # | 지시 | 상태 | +|---|------|------| +| 2.1 | 캘린더를 **Nifty app-views/calendar**처럼 (옅은 회색 격자·연한 today·가벼운 이벤트 칩) | 🚚배포중 | 큰 캘린더 재스타일 완료(격자 neutral-100·today 연노랑·소프트 칩), 배포 중 | +| 2.2 | **공휴일 붉은색 + 일자 옆 공휴일명** (2050년까지 데이터) | 🚚배포중 | 큰 캘린더+미니 달력 배선. 7월은 공휴일 없음(8/15·9월 추석 표시) | +| 2.3 | **달력 경계선 모두 회색** | 🚚배포중 | 미니 달력 셀 회색 테두리 추가 | +| 2.4 | 일정 **월/주/일** 뷰 (WISE CalendarView) | ✅완료 | +| 2.5 | 전시 일정 50:50 비율·달력 날짜 위로(하단 공백 제거) | ✅완료 | + +## 3. 공개사이트 / 메인 / visitor + +| # | 지시 | 상태 | +|---|------|------| +| 3.1 | **첫 화면 = 제품 소개** (미인증 루트는 히어로 랜딩, 로그인 폼만 노출 금지) | ✅완료 | 메모리 [[product-identity-first]] | +| 3.2 | 제품명 **'KINTEX AI 전시·행사시스템'** | ✅완료 | +| 3.3 | **visitor 메인 = 세련된 홈페이지** (AI 관람 도우미 중심) | 🚚배포중 | AiAssistant·기능타일·연월 브리핑·쇼케이스 구현 | +| 3.4 | 메인에 **AI 기능 전면 노출** (AI가 뭘 하는지 한눈에) | 🚚배포중 | AI 기능 쇼케이스 6타일 | +| 3.5 | AI가 답하는 정보는 **크롤링해서 DB 저장** (실시간 외부호출 X) | ✅완료 | V43 visitor_guide·transport, 공개 AI API 근거응답 | +| 3.6 | **AI는 전시·행사정보 학습** (근거 기반) | ✅완료 | grounding + 인용, 환각 차단 | +| 3.7 | **AI가 연간·월간 전시·행사 기획정보 제시** | 🚚배포중 | AiPlanningBriefing(연/월 토글) | +| 3.8 | 비지터가 **가장 알고 싶은 정보를 AI로 쉽게** | ✅설계 | 관람 도우미 예시칩·구조화 카드 | +| 3.9 | 코엑스 분석 → **visitor/business/agency 3-트랙** 분리 | ✅완료 | 관문 + 3 서브홈 + TrackSwitcher | +| 3.10 | **"코엑스처럼" 전면 리뉴얼** (히어로 배경·애니메이션·비주얼) | 🚚배포중 | design.md v2.4 §3C-V, 코엑스풍 비주얼·모션 구현 | +| 3.11 | 메인 카드가 **밋밋한 파란 placeholder** (포스터 이미지 없음) | ✅완료 | V46 실제 포스터 이미지로 교체(배포됨) | +| 3.12 | **메인 카드 이미지 크기 안 맞음**(세로 포스터가 16:11 가로 프레임에 늘어남/잘림) | ⏳수정됨 | 포스터 실측 세로 0.67~0.8 → 카드 미디어 3:4 프레임 + `.kxp-ecard__poster` `object-fit:cover`(왜곡 제거). 다음 배포 | +| 3.13 | **메인 히어로 이미지 5장 회전(슬라이드)** | ⏳착수 | 크롤러가 KINTEX 전시장/시설 히어로 이미지 5장 `public/media/hero/` 확보 → 프론트 히어로 회전 UI(배포 후) | + +## 4. 역할 · 네비게이션 + +| # | 지시 | 상태 | +|---|------|------| +| 4.1 | **로그인 사용자 구분으로 메인페이지 + 메뉴 그룹 결정** (디폴트 visitor, 로그인 후 business/admin/agency) | 🚚배포중 | plan_role_routing 구현(admin→/admin·agency→/contractor·visitor→/visitor·ops/business→/home) + 역할별 메뉴 게이트 | +| 4.2 | **각 트랙 메인페이지 Stitch 디자인 의뢰** | ✅부분 | Stitch 2회 실패 → design.md 스펙 직접 구현(문서화 폴백) | +| 4.3 | **모바일 앱도 동일 로직** | 📋대기 | 웹 완료 후 모바일 반영 | +| 4.4 | **상세→메인 복귀·브레드크럼·404** 내비게이션 | ⏳ | renewal 하네스 | + +## 5. 데이터 / 시드 + +| # | 지시 | 상태 | +|---|------|------| +| 5.1 | **모든 테이블에 데이터** (50~100건) | ✅완료 | V42 대량시드(40테이블 2,535행) | +| 5.2 | **정산·결제 데이터 없음** — 행사스코프 정렬 | 🚚배포중 | V47: 데모 해소행사 `e-2026-live`에 정산 폐루프+옥션+관람객+리드+캠페인 | +| 5.3 | 테스트 데이터 **더 현실적으로** | ✅완료 | V39·V42 실감형 | +| 5.4 | 행사정보 **크롤링해서 데이터 넣기** | ✅완료 | 크롤 시드 | +| 5.5 | **연간·월간 전시 기획정보**도 적재 | ✅완료 | V43 event 생성열·v_event_calendar 뷰 | +| 5.6 | 포스터 이미지 **다 다운받아 빌드 내장** | 🚚배포중 | V46 34장 `/media/posters/` | + +## 6. 정산·도메인 화면 + +| # | 지시 | 상태 | +|---|------|------| +| 6.1 | **정산·결제 패딩 없음** | 🚚배포중 | `.kx-settle` 패딩 추가 | +| 6.2 | **참가업체 관리가 홈으로 감** — 전용 화면 신설 | 📋대기 | task #41 | +| 6.3 | 부서 관리 = **WISE 조직도** | ✅완료 | +| 6.4 | 결재관리 **반려·재상신·결재선** (WISE) | ✅완료 | +| 6.5 | 통합검색에 **행사·전시 포함** | ✅완료 | +| 6.6 | 시스템관리 **메시지 관리**({} 플레이스홀더+샘플) | 📋대기 | task #35 | +| 6.7 | 물류허브·서류/마일스톤·CMS·일정 = WISE | ✅완료 | +| 6.8 | **그룹권한(역할↔메뉴) 메뉴** 존재 확인 | ✅확인 | RoleAdminPage + RoleMenuAdminPage | + +## 7. 버튼 · 색상 · 대비 + +| # | 지시 | 상태 | +|---|------|------| +| 7.1 | **파란 배경 버튼 = 흰 글자** | ✅완료 | `--color-on-accent`(다크에서도 흰색). 라이브 반영(캐시 새로고침 필요) | +| 7.2 | 다크모드 색상배경 위 **흰 글자 어두워지는 버그** 전수 수정 | ✅완료 | 77곳 on-accent. +통계밴드 2곳 추가수정(배포중) | +| 7.3 | 회색 버튼은 옅은 ghost — 흰 글자 아님(솔리드 진회색만 흰글자) | ✅확인 | + +## 8. 파비콘 · 브랜드 + +| # | 지시 | 상태 | +|---|------|------| +| 8.1 | **KINTEX favicon** 추가 (홈페이지 크롤) | ✅완료 | 리포 favicon.ico가 톰캣 아이콘이던 것 규명→KINTEX .ico 재생성(v3) | +| 8.2 | **브랜드 밑에 영어 병기** | ✅완료 | "AI Exhibition & Event System" | + +## 9. 모바일 + +| # | 지시 | 상태 | +|---|------|------| +| 9.1 | **내정보 (WISE 참조) + 생체인식 + 사진등록** | ✅완료 | app/profile, expo-local-authentication, expo-image-picker. 아바타 API(백엔드) 완료 | +| 9.2 | **전시·공연 앱 벤치마킹** (다운로드·좋아요 상위순) | ✅완료 | docs/analysis/mobile-benchmark-exhibition-performance.md + 백로그 12항목 | +| 9.3 | **앱 위변조 방지 + 시큐어 코딩 + 보안 체크리스트 통과** | ✅완료 | 루트/탈옥·무결성·Hermes+R8·화면캡처·cleartext·권한최소. 체크리스트/감사 문서 | +| 9.4 | **다국어** — 웹 4개국어 완비, 모바일은 한국어(백로그) | ⏳ | 웹 ✅(ko/en/zh/ja), 모바일 i18n 미도입(MB-11) | + +## 10. AI + +| # | 지시 | 상태 | +|---|------|------| +| 10.1 | **AI 기능 쉽게 사용 + 토큰 최소화** | ✅완료 | PLANNING v3.4 §8A(결정론 라우팅·소형모델 우선·RAG축소·캐싱·집계는 SQL·측정지표) | +| 10.2 | AI 기능이 **하나도 구현 안 된 것 같다 → 구현** | ✅완료 | 공개 AI 관람 도우미 라이브(grounded=true, Claude) | +| 10.3 | 나노바나나 유료? | ✅답변 | Google Gemini 유료 | + +## 11. 데이터 아키텍처 (표준) + +| # | 지시 | 상태 | +|---|------|------| +| 11.1 | **FK 최소화, 공통코드로 관리** | ✅표준 | docs/architecture/data.md §10 (소프트참조·공통코드) | +| 11.2 | **필요한 인덱스 생성** | 🚚배포중 | V44 성능 인덱스 8건(핫패스) — 이미 배포됨 | +| 11.3 | **view/mview/function 고려** | ✅표준 | data.md §11 | +| 11.4 | **자동화 배치·프로시저 고려** | ✅표준 | data.md §13 배치 카탈로그 | +| 11.5 | **ten_id/tenant_id PK 맨 앞 복합키** | ✅원칙 | 메모리 [[tenant-id-pk-standard]] | + +## 12. 인증 / SSO / HR + +| # | 지시 | 상태 | +|---|------|------| +| 12.1 | **직원 인사(조직) 정보는 API 방식** | ✅설계 | docs/architecture/sso-hr-integration.md (HrDirectoryClient 어댑터+스냅샷) | +| 12.2 | **Open SSO 도입** | ✅설계 | Keycloak OIDC(PKCE)+SAML, 기존 JWT+2FA 공존(브로커) | + +## 13. 평면도 / 기타 + +| # | 지시 | 상태 | +|---|------|------| +| 13.1 | 도면 안 열림 / AI 시각화 어디감 | ✅원인 | 403(행사 미가입) → 데모계정 멤버십 해소. DWG는 SVG캔버스+JPG 렌더(DWG 바이너리는 좌표소스) | +| 13.2 | ReRoomAI 기능 (나노바나나 image-to-image) | ⏳ | visualizer 트랙 | + +--- + +## 미해결·대기 요약 (다음 우선 처리) + +1. **Nifty 디자인 시스템 전면 정렬** — 카드(ui-elements/cards)·폰트(--fs-micro·공용 제목클래스)·그리드(.kx-table 6화면) 일괄 (1.1·1.2·1.9) +2. **참가업체 관리 전용 화면** (6.2 / task #41) +3. **메시지 관리** (6.6 / task #35) +4. **Tabulator**·**상세 Nifty blog**·**설정 커스터마이저** (1.3·1.4·1.12 / task #32·33·43) +5. **전 화면 공백없는 반응형 스윕** (1.11 / task #39) +6. **모바일 역할 랜딩·다국어** (4.3·9.4) +7. **상세→메인 내비게이션** (4.4) + +--- + +## 처리 로그 (내가 처리·배포한 것 — 2026-07-12 세션) + +**배포 완료 (라이브):** 커밋 214b6df → 6e95ac6 → 87f310f/55e27b3 → 3077cb0 +- V42 대량시드(40테이블 2,535행) · V43 관람객 가이드/연월 캘린더 뷰 · V44 성능 인덱스 8건 · V45 아바타 컬럼 · V46 포스터 로컬화(73건) · V47 e-2026-live 정산 폐루프 +- 공개 AI 관람 도우미 API(grounded·Claude) · 연월 캘린더 API · 3-트랙 관문/서브홈 · 역할별 랜딩+메뉴 게이트 +- 미니/큰 캘린더 공휴일 표시·회색 테두리·Nifty 톤 · 코엑스풍 공개사이트 비주얼(히어로·모션·통계밴드) · 실제 포스터 이미지 +- 파란버튼 흰글자(`--color-on-accent`) · 다크모드 흰글자 77곳 · favicon(톰캣→KINTEX v3) · 2FA 방패 아이콘 · 브랜드 영문병기 · 정산 패딩 + +**구현 완료 (미배포 — 다음 배포 대기):** +- **Nifty 디자인 시스템 정렬**(카드 `.kx-card` Nifty 구조·`--fs-micro`/`--fw-*` 토큰·공용 제목클래스·테이블 `.kx-table` 6화면 수렴, font 179+weight 415 토큰화) +- **카드 이미지 3:4 프레임 + object-fit cover**(왜곡/잘림 제거) +- **메시지 관리 백엔드**(V48 sys_message + 렌더 API + 36샘플) +- **통계밴드 흰글자 2곳** · **푸터 "2026 © KINTEX..."** 4개국어 + +**병렬 진행 중:** 모바일 다국어+역할랜딩 · 잔여 데이터 보강(V49) · 산출물 문서 + +**설계·표준 문서 산출:** PLANNING v3.4(AI 토큰최소화 §8A) · design.md v2.4(공개 비주얼·모션) · docs/architecture/data.md(FK최소화·공통코드·view/mview·배치) · sso-hr-integration.md(Open SSO+HR) · DESIGN_SYSTEM_NIFTY.md · mobile-security-*.md · mobile-benchmark-*.md + +## 산출물(문서) 상태 + +| 산출물 | 타이밍 | 상태 | +|--------|--------|------| +| 개발계획서 | 개발 전/중 | 📋 생성 착수(PLANNING v3.4 기반) | +| 설계서(아키텍처·ERD·화면·API) | 개발 중 | 📋 생성 착수(design.md v2.4·architecture·backend 계약 기반) | +| 사용자 지침서 | **완성+QA 후**(최종 메뉴 반영) | ⏳ UI 정렬 안정화 후 — 지금 생성 시 재작업(정책: 최종 1회) | +| 운영자 지침서 | **완성+QA 후** | ⏳ 동일 | + +> 정책([[deliverables-update-policy]]): deliverables는 최초 1회 + 최종 1회. 개발계획서·설계서는 지금 생성 가능(내용 안정), 지침서는 현재 UI 정렬 파동이 끝난 뒤 최종 메뉴 기준으로 1회 생성. diff --git a/docs/analysis/nifty-design-refs.md b/docs/analysis/nifty-design-refs.md index 7a585ef..69b2294 100644 --- a/docs/analysis/nifty-design-refs.md +++ b/docs/analysis/nifty-design-refs.md @@ -18,7 +18,7 @@ | 타이포그래피 | https://preview.themeon.net/nifty/ui-elements/ (typography) | 제목·본문·캡션 위계(kx 타이포 토큰) | 대기 | | 모달/메시지박스 | https://preview.themeon.net/nifty/ui-elements/ (modals) | 모달·확인/경고 메시지박스(kx Modal) | 대기 | | Offcanvas | https://preview.themeon.net/nifty/ui-elements/ (offcanvas) | 오프캔버스 패널(모바일 드로어·상세 슬라이드) | 대기 | -| 달력 | https://preview.themeon.net/nifty/app-views/calendar/ | 일정 캘린더(월/주/일)·이벤트 색/드래그·사이드 목록 | 대기(일정 3-뷰는 구조 완료, Nifty 스타일 정합 대상) | +| 달력 | https://preview.themeon.net/nifty/app-views/calendar/ | 일정 캘린더(월/주/일)·이벤트 색/드래그·사이드 목록 | 완료(KxCalendarView 재스타일 2026-07-12 — 옅은 격자·옅은 노랑 today 틴트·소프트 카테고리 칩(color-mix), 미니 `.kx-cal` 동일 톤. `.kx-mcal`는 기존 소프트 톤 유지) | | **UI-elements 전체** | **https://preview.themeon.net/nifty/ui-elements/** | **Nifty UI-elements 전 페이지를 킨텍스 공통 UI 기준으로 채택**(위 개별 + 나머지 요소 전부) | 기준 | | 셸·메뉴(기존) | WISE(UIWS) AppLayout/LeftNav/CalendarView | 셸·아코디언·캘린더 | 완료(구조) | | 상단바(top frame) | https://preview.themeon.net/nifty/ (navbar/header) | 상단 헤더 — 로고·검색·알림·설정·사용자 드롭다운·풀스크린·언어 | 대기 | diff --git a/docs/deliverables/DA산출물/_gen/gen_da_deliverables.py b/docs/deliverables/DA산출물/_gen/gen_da_deliverables.py deleted file mode 100644 index 50e1a25..0000000 --- a/docs/deliverables/DA산출물/_gen/gen_da_deliverables.py +++ /dev/null @@ -1,854 +0,0 @@ -# -*- coding: utf-8 -*- -""" -킨텍스 자동전시시스템 — DA(데이터 아키텍처) 산출물 생성기 - 1) 데이터베이스설계서.pptx (행안부 DB설계서 표준 톤 / 16:9) - 2) 테이블정의서.xlsx (실제 Flyway DDL 역추출) - -내용 소스(권위) = src/backend/.../db/migration/V1~V9 실제 DDL. - → 테이블·컬럼·타입·NULL·PK/FK·기본값·인덱스·공간타입을 프로그램으로 파싱(수기 발명 금지). - → 컬럼 설명은 DDL 인라인 주석·COMMENT ON 을 그대로 사용. 없으면 generic 표준 컬럼만 보강. - -디자인 = KINTEX Blue #0066B3 / AI Purple #6D4AFF / 맑은 고딕. ERD·도식은 python-pptx 도형 직접 작도. -보안 불변 = 비밀번호/API키/서버IP/SSH 미기재. 암호화 컬럼은 정책만 기술(AES-256-GCM). - -재실행: python gen_da_deliverables.py -""" -import os, re, glob - -HERE = os.path.dirname(os.path.abspath(__file__)) -OUT_DIR = os.path.dirname(HERE) # docs/deliverables/DA산출물 -MIG_DIR = os.path.abspath(os.path.join( - HERE, "..", "..", "..", "..", "src", "backend", "src", "main", "resources", "db", "migration")) -assert os.path.isdir(MIG_DIR), f"migration dir not found: {MIG_DIR}" - -# ====================================================================== -# 1. DDL 파서 (Flyway V1~V9 → 테이블 스키마 모델) -# ====================================================================== -CONSTRAINT_STARTS = ("PRIMARY KEY", "UNIQUE", "FOREIGN KEY", "CHECK", "CONSTRAINT", "REFERENCES ") -GENERIC_DESC = { - "id": "식별자(PK)", "created_at": "생성일시", "updated_at": "수정일시", - "event_id": "행사 참조(FK event)", "status": "상태 코드", "sort_order": "정렬 순서", - "use_yn": "사용 여부(Y/N)", "dept_id": "부서 식별자", "writer_id": "작성자", - "writer_name": "작성자명", "author_id": "작성자", "author_name": "작성자명", - "owner_id": "소유자", "owner_name": "소유자명", "title": "제목", "content": "내용", - "read_at": "읽은 일시", "recipient_id": "수신자", -} - -def _split_type(rest): - """rest 문자열의 선두에서 데이터타입 토큰을 추출. geometry(Point,0)·numeric(8,2) 등 괄호 내부 콤마 허용.""" - m = re.match(r"\s*([A-Za-z_][A-Za-z0-9_]*(?:\([^)]*\))?)", rest) - if not m: - return "", rest - return m.group(1), rest[m.end():] - -def _parse_default(rest): - m = re.search(r"\bDEFAULT\s+(now\(\)|gen_random_uuid\(\)|'[^']*'|true|false|[0-9]+\.?[0-9]*|[A-Za-z_][\w]*)", - rest, re.IGNORECASE) - return m.group(1) if m else "" - -def parse_ddl(): - tables = {} # name -> {"cols":[...], "pk":set, "uniques":[...], "indexes":[...], "version":Vx, "order":int} - order = 0 - files = sorted(glob.glob(os.path.join(MIG_DIR, "V*.sql")), - key=lambda p: int(re.match(r"V(\d+)", os.path.basename(p)).group(1))) - comments = {} # (table,col) -> desc (COMMENT ON) - for path in files: - ver = re.match(r"(V\d+)", os.path.basename(path)).group(1) - raw = open(path, encoding="utf-8").read() - - # COMMENT ON COLUMN t.c IS '...' - for cm in re.finditer(r"COMMENT\s+ON\s+COLUMN\s+(\w+)\.(\w+)\s+IS\s+'((?:[^']|'')*)'", raw, re.IGNORECASE): - comments[(cm.group(1), cm.group(2))] = cm.group(3).replace("''", "'") - - # CREATE TABLE IF NOT EXISTS name ( ... ); - for tm in re.finditer(r"CREATE\s+TABLE\s+IF\s+NOT\s+EXISTS\s+(\w+)\s*\((.*?)\n\)\s*;", - raw, re.IGNORECASE | re.DOTALL): - tname, body = tm.group(1), tm.group(2) - if tname not in tables: - order += 1 - tables[tname] = {"cols": [], "pk": set(), "uniques": [], "indexes": [], - "version": ver, "order": order} - t = tables[tname] - for line in body.split("\n"): - code = line.split("--", 1)[0].strip() - cmt = line.split("--", 1)[1].strip() if "--" in line else "" - if not code: - continue - code = code.rstrip(",").strip() - up = code.upper() - # 테이블 레벨 제약 - if up.startswith("PRIMARY KEY"): - for c in re.findall(r"\((.*?)\)", code)[0].split(","): - t["pk"].add(c.strip()) - continue - if up.startswith("UNIQUE"): - t["uniques"].append(re.findall(r"\((.*?)\)", code)[0].strip()) - continue - if up.startswith(CONSTRAINT_STARTS): - continue - # 컬럼 정의 - cm2 = re.match(r"(\w+)\s+(.*)", code) - if not cm2: - continue - cname = cm2.group(1) - dtype, rest = _split_type(cm2.group(2)) - ru = rest.upper() - is_pk = "PRIMARY KEY" in ru - if is_pk: - t["pk"].add(cname) - fk = re.search(r"REFERENCES\s+(\w+)\s*\((\w+)\)", rest, re.IGNORECASE) - nn = ("NOT NULL" in ru) or is_pk - uniq = ("UNIQUE" in ru) and "PRIMARY KEY" not in ru - dflt = _parse_default(rest) - t["cols"].append({ - "name": cname, "type": dtype, - "null": "N" if nn else "Y", - "pk": is_pk, "fk": (fk.group(1) if fk else None), - "fkcol": (fk.group(2) if fk else None), - "unique": uniq, "default": dflt, "desc": cmt, - }) - - # ALTER TABLE t ADD COLUMN IF NOT EXISTS c type ... - for am in re.finditer(r"ALTER\s+TABLE\s+(\w+)\s+ADD\s+COLUMN\s+IF\s+NOT\s+EXISTS\s+(.+?);", - raw, re.IGNORECASE): - tname = am.group(1) - if tname not in tables: - continue - t = tables[tname] - code = am.group(2).strip() - cm2 = re.match(r"(\w+)\s+(.*)", code) - if not cm2: - continue - cname = cm2.group(1) - if any(c["name"] == cname for c in t["cols"]): - continue - dtype, rest = _split_type(cm2.group(2)) - ru = rest.upper() - t["cols"].append({ - "name": cname, "type": dtype, - "null": "N" if "NOT NULL" in ru else "Y", - "pk": False, "fk": None, "fkcol": None, - "unique": "UNIQUE" in ru, "default": _parse_default(rest), - "desc": f"({ver} 순증 컬럼)", - }) - - # CREATE INDEX ... ON t ... - for im in re.finditer(r"CREATE\s+INDEX\s+IF\s+NOT\s+EXISTS\s+(\w+)\s+ON\s+(\w+)\s*(.*?);", - raw, re.IGNORECASE | re.DOTALL): - idxname, tname, spec = im.group(1), im.group(2), im.group(3).strip() - if tname in tables: - gist = "GIST" in spec.upper() - cols = re.findall(r"\((.*?)\)", spec) - tables[tname]["indexes"].append({ - "name": idxname, "spec": (cols[0] if cols else spec), - "gist": gist, - }) - - # 설명 보강: COMMENT ON → generic - for tname, t in tables.items(): - for c in t["cols"]: - if not c["desc"]: - c["desc"] = comments.get((tname, c["name"]), "") - if not c["desc"] and c["name"] in GENERIC_DESC: - c["desc"] = GENERIC_DESC[c["name"]] - return tables - -TABLES = parse_ddl() - -# ---- 테이블 한글명 · 주제영역 매핑 (라벨링) ---------------------------------- -KO = { - "app_user": "사용자", "company": "등록업체", "event": "행사", - "hall": "전시홀 마스터", "hall_assignment": "행사-홀 배정", "event_member": "행사 참여자(RBAC)", - "booth_standard": "부스 표준사양", "master_data": "마스터데이터(요율·규정)", - "trench": "트렌치(급전 포인트)", "hall_exit": "비상구", "layout": "배치안(버전)", - "booth": "부스(폴리곤)", "design_plan": "부스 설계안", "utility_order": "유틸리티 신청", - "render_job": "AI 렌더잡", "common_code_group": "공통코드 그룹", "common_code": "공통코드", - "sys_menu": "시스템 메뉴", "sys_role": "역할", "sys_permission": "권한", - "sys_role_permission": "역할-권한 매핑", "sys_setting": "시스템 설정", "audit_log": "감사 로그", - "worklog": "업무일지", "schedule": "일정", "message": "쪽지", - "message_recipient": "쪽지 수신자", "notice": "공지", "opinion": "의견", - "opinion_comment": "의견 댓글", "meeting": "회의", "meeting_action": "회의 액션아이템", - "report": "보고서", "notification": "알림", "password_reset": "비밀번호 재설정 코드", -} -# 주제영역(Subject Area): (코드, 한글, 테이블목록, 색상키) -AREAS = [ - ("MASTER", "행사·조직·권한·마스터", - ["event", "hall", "hall_assignment", "event_member", "app_user", "company", - "booth_standard", "master_data"]), - ("SPATIAL", "공간·설계·시각화(부스 코어)", - ["trench", "hall_exit", "layout", "booth", "design_plan", "utility_order", "render_job"]), - ("SYSTEM", "시스템관리·인증", - ["common_code_group", "common_code", "sys_menu", "sys_role", "sys_permission", - "sys_role_permission", "sys_setting", "audit_log", "password_reset"]), - ("COMMON", "공통 업무 모듈", - ["worklog", "schedule", "message", "message_recipient", "notice", "opinion", - "opinion_comment", "meeting", "meeting_action", "report", "notification"]), -] -AREA_OF = {} -for code, ko, tlist in AREAS: - for tn in tlist: - AREA_OF[tn] = ko - -TOTAL_TABLES = len(TABLES) -TOTAL_COLS = sum(len(t["cols"]) for t in TABLES.values()) -TOTAL_IDX = sum(len(t["indexes"]) for t in TABLES.values()) -SPATIAL_COLS = [(tn, c["name"], c["type"]) for tn, t in TABLES.items() - for c in t["cols"] if c["type"].lower().startswith("geometry")] -FK_EDGES = [(tn, c["name"], c["fk"]) for tn, t in TABLES.items() - for c in t["cols"] if c["fk"]] - -# ====================================================================== -# 2. PPTX — 데이터베이스설계서 -# ====================================================================== -from pptx import Presentation -from pptx.util import Pt -from pptx.dml.color import RGBColor -from pptx.enum.text import PP_ALIGN, MSO_ANCHOR -from pptx.enum.shapes import MSO_SHAPE, MSO_CONNECTOR -from pptx.oxml.ns import qn - -BLUE = RGBColor(0x00, 0x66, 0xB3) -BLUE_DK = RGBColor(0x00, 0x44, 0x7A) -BLUE_LT = RGBColor(0xE1, 0xEF, 0xF9) -PURPLE = RGBColor(0x6D, 0x4A, 0xFF) -PURPLE_LT = RGBColor(0xEC, 0xE8, 0xFF) -INK = RGBColor(0x10, 0x18, 0x28) -MUTED = RGBColor(0x66, 0x70, 0x85) -LINE = RGBColor(0xE4, 0xE7, 0xEC) -BG = RGBColor(0xF9, 0xFA, 0xFB) -WHITE = RGBColor(0xFF, 0xFF, 0xFF) -GREEN = RGBColor(0x12, 0x9E, 0x63) -AMBER = RGBColor(0xE0, 0x8A, 0x00) -TEAL = RGBColor(0x0E, 0x7C, 0x86) -CARD = RGBColor(0xFF, 0xFF, 0xFF) -FONT = "맑은 고딕" -IN = 914400 -def inch(v): return int(v * IN) - -prs = Presentation() -prs.slide_width = 12192000 -prs.slide_height = 6858000 -BLANK = prs.slide_layouts[6] -def slide(): return prs.slides.add_slide(BLANK) - -def _fill(sp, color): - if color is None: sp.fill.background() - else: - sp.fill.solid(); sp.fill.fore_color.rgb = color - -def rect(s, x, y, w, h, fill=None, line=None, line_w=1.0, shape=MSO_SHAPE.RECTANGLE, shadow=False): - sp = s.shapes.add_shape(shape, inch(x), inch(y), inch(w), inch(h)) - _fill(sp, fill) - if line is None: sp.line.fill.background() - else: - sp.line.color.rgb = line; sp.line.width = Pt(line_w) - sp.shadow.inherit = False # creates an empty - if shadow: - el = sp._element.spPr - ef = el.find(qn('a:effectLst')) - if ef is None: - ef = el.makeelement(qn('a:effectLst'), {}); el.append(ef) - sh = ef.makeelement(qn('a:outerShdw'), - {'blurRad':'80000','dist':'30000','dir':'5400000','rotWithShape':'0'}) - clr = sh.makeelement(qn('a:srgbClr'), {'val':'101828'}) - alp = clr.makeelement(qn('a:alpha'), {'val':'16000'}) - clr.append(alp); sh.append(clr); ef.append(sh) - return sp - -def text(s, x, y, w, h, content, size=13, color=INK, bold=False, align=PP_ALIGN.LEFT, - anchor=MSO_ANCHOR.TOP, line_spacing=1.0, space_after=2, wrap=True): - tb = s.shapes.add_textbox(inch(x), inch(y), inch(w), inch(h)) - tf = tb.text_frame; tf.word_wrap = wrap; tf.vertical_anchor = anchor - tf.margin_left = 0; tf.margin_right = 0; tf.margin_top = 0; tf.margin_bottom = 0 - if isinstance(content, str): content = [content] - for i, para in enumerate(content): - p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() - p.alignment = align; p.line_spacing = line_spacing - p.space_after = Pt(space_after); p.space_before = Pt(0) - segs = [{"t": para}] if isinstance(para, str) else ([para] if isinstance(para, dict) else para) - for seg in segs: - r = p.add_run(); r.text = seg.get("t", "") - r.font.name = FONT; r.font.size = Pt(seg.get("size", size)) - r.font.bold = seg.get("bold", bold); r.font.color.rgb = seg.get("color", color) - rPr = r._r.get_or_add_rPr() - rPr.append(rPr.makeelement(qn('a:ea'), {'typeface': FONT})) - return tb - -def page_bg(s): rect(s, -0.1, -0.1, 13.53, 7.7, fill=BG) -def chip(s, x, y, w, h, label, fill, txtcolor=WHITE, size=9.5, bold=True): - rect(s, x, y, w, h, fill=fill, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, w, h, label, size=size, color=txtcolor, bold=bold, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -def card(s, x, y, w, h, fill=CARD, line=LINE): - return rect(s, x, y, w, h, fill=fill, line=line, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - -def header(s, kicker, title, idx): - page_bg(s) - rect(s, 0, 0, 13.333, 0.12, fill=BLUE) - rect(s, 0.55, 0.5, 0.62, 0.62, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.55, 0.5, 0.62, 0.62, f"{idx:02d}", size=20, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, 1.32, 0.5, 10.5, 0.28, kicker, size=11, color=PURPLE, bold=True) - text(s, 1.32, 0.74, 11.4, 0.44, title, size=22, color=INK, bold=True) - rect(s, 0.55, 1.28, 12.23, 0.02, fill=LINE) - text(s, 0.55, 7.06, 9, 0.3, "킨텍스 자동전시시스템 구축 · 데이터베이스 설계서", size=8.5, color=MUTED) - text(s, 11.3, 7.06, 1.48, 0.3, "2026-07-11", size=8.5, color=MUTED, align=PP_ALIGN.RIGHT) - -def bullets(s, x, y, w, items, size=12.5, gap=0.44, head=None): - if head: - text(s, x, y, w, 0.32, head, size=12, color=PURPLE, bold=True); y += 0.42 - for it in items: - rect(s, x, y+0.07, 0.12, 0.12, fill=BLUE, shape=MSO_SHAPE.OVAL) - if isinstance(it, tuple): - text(s, x+0.28, y, w-0.28, gap, [[{"t": it[0]+" ", "bold":True, "color":INK, "size":size}, - {"t": it[1], "color":MUTED, "size":size-1}]], line_spacing=1.05) - else: - text(s, x+0.28, y, w-0.28, gap, it, size=size, color=INK, line_spacing=1.05) - y += gap - return y - -# ---- ERD 엔티티 박스 (파싱 데이터 구동) ---- -def erd_entity(s, x, y, w, tname, maxrows=6, accent=BLUE): - t = TABLES[tname] - # 표시 컬럼: PK → FK → 대표 컬럼 - pk = [c for c in t["cols"] if c["pk"]] - fk = [c for c in t["cols"] if c["fk"] and not c["pk"]] - rest = [c for c in t["cols"] if not c["pk"] and not c["fk"]] - show = (pk + fk + rest)[:maxrows] - rh = 0.26 - h = 0.5 + len(show)*rh - rect(s, x, y, w, h, fill=CARD, line=LINE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - rect(s, x, y, w, 0.44, fill=accent, shape=MSO_SHAPE.ROUND_2_SAME_RECTANGLE) - text(s, x+0.12, y, w-0.2, 0.44, [[{"t": tname, "size":10.5, "color":WHITE, "bold":True}, - {"t": " "+KO.get(tname, ""), "size":8, "color":RGBColor(0xD6,0xE6,0xF6)}]], - anchor=MSO_ANCHOR.MIDDLE) - cy = y + 0.5 - for c in show: - key = "PK" if c["pk"] else ("FK" if c["fk"] else "") - kc = PURPLE if c["pk"] else (TEAL if c["fk"] else MUTED) - geo = c["type"].lower().startswith("geometry") - text(s, x+0.12, cy-0.03, 0.42, rh, key, size=7.5, color=kc, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+0.54, cy-0.03, w-0.62, rh, - [[{"t": c["name"], "size":8.6, "color":(INK if key else RGBColor(0x33,0x3B,0x4A)), "bold":bool(key)}, - {"t": " : "+c["type"], "size":7.6, "color":(GREEN if geo else MUTED)}]], - anchor=MSO_ANCHOR.MIDDLE) - cy += rh - return {"x": x, "y": y, "w": w, "h": h, "cx": x+w/2, "cy": y+h/2} - -def connect(s, a, b, color=RGBColor(0x9A,0xA6,0xB8)): - cn = s.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, inch(a["cx"]), inch(a["cy"]), - inch(b["cx"]), inch(b["cy"])) - cn.line.color.rgb = color; cn.line.width = Pt(1.4) - cn.shadow.inherit = False - return cn - -# ================= 슬라이드 1 : 표지 ================= -s = slide() -rect(s, -0.1, -0.1, 13.53, 7.7, fill=BLUE_DK) -rect(s, 6.4, -1.5, 9, 11, fill=BLUE, shape=MSO_SHAPE.PARALLELOGRAM) -rect(s, 9.2, -2.0, 7, 12, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) -rect(s, 0.85, 0.8, 0.42, 0.42, fill=WHITE, shape=MSO_SHAPE.OVAL) -rect(s, 0.97, 0.92, 0.18, 0.18, fill=PURPLE, shape=MSO_SHAPE.OVAL) -text(s, 1.42, 0.78, 6, 0.46, "KINTEX · WISE AI", size=15, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) -chip(s, 0.85, 2.35, 1.95, 0.42, "데이터베이스 설계서", PURPLE, WHITE, size=12) -chip(s, 2.92, 2.35, 2.2, 0.42, "Database Design", RGBColor(0x2B,0x4C,0x7E), WHITE, size=10.5) -text(s, 0.85, 2.95, 11, 1.9, [ - [{"t":"킨텍스 자동전시시스템", "size":44, "color":WHITE, "bold":True}], - [{"t":"데이터베이스 설계서", "size":44, "color":WHITE, "bold":True}]], line_spacing=1.02) -text(s, 0.88, 4.7, 11, 0.5, "PostgreSQL 15 + PostGIS · Flyway 형상관리 · kintex 전용 DB", - size=15.5, color=RGBColor(0xC7,0xDD,0xF2)) -text(s, 0.88, 5.24, 11.4, 0.4, - "공간데이터(부스 POLYGON · 트렌치 POINT · 배선 LINESTRING) 일원화 · 행사 단위 격리 · 감사추적", - size=12, color=RGBColor(0xB9,0xC7,0xEA)) -rect(s, 0.85, 6.05, 11.63, 0.02, fill=RGBColor(0x3A,0x5A,0x8C)) -text(s, 0.85, 6.25, 8.6, 0.4, [[{"t":"산출물 ","size":11,"color":RGBColor(0x90,0xA6,0xC9)}, - {"t":f"개념·논리·물리 데이터 모델 / 테이블 {TOTAL_TABLES} · 컬럼 {TOTAL_COLS} · 인덱스 {TOTAL_IDX}","size":11,"color":WHITE,"bold":True}]]) -text(s, 0.85, 6.62, 9.6, 0.4, [[{"t":"근거 ","size":11,"color":RGBColor(0x90,0xA6,0xC9)}, - {"t":"Flyway V1~V10 실제 DDL 역추출 (스키마 권위)","size":10.5,"color":WHITE}]]) -text(s, 9.7, 6.25, 2.78, 0.4, "작성일 2026-07-11", size=11, color=WHITE, bold=True, align=PP_ALIGN.RIGHT) -text(s, 9.7, 6.62, 2.78, 0.4, "DA · kintex-da", size=9.5, color=RGBColor(0x90,0xA6,0xC9), align=PP_ALIGN.RIGHT) - -# ================= 슬라이드 2 : 목차 ================= -s = slide() -header(s, "CONTENTS", "목차", 0) -rect(s, 0.55, 0.5, 0.62, 0.62, fill=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.55, 0.5, 0.62, 0.62, "C", size=22, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -toc = [ - ("01","개요","PostgreSQL+PostGIS · Flyway · kintex 전용 DB"), - ("02","데이터 아키텍처 원칙","행사 단위 격리 · 공간타입 선정 · 감사로그"), - ("03","개념 데이터 모델","핵심 주제영역 관계 개요도"), - ("04","논리 모델(ERD)","주제영역별 엔터티·관계 ERD"), - ("05","물리 모델 요약","테이블 그룹 · 공간컬럼 · 인덱스 전략"), - ("06","데이터 표준","명명 규칙 · 공통코드 · 날짜/통화"), - ("07","데이터 보안","암호화·PII·응답제외·감사추적"), - ("08","백업·이관 전략","Flyway 마이그레이션 운영 원칙"), -] -cw = 5.85 -for i,(no,t,d) in enumerate(toc): - col = i // 4; row = i % 4 - x = 0.7 + col*(cw+0.35); y = 1.62 + row*1.28 - rect(s, x, y, cw, 1.1, fill=CARD, line=LINE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - rect(s, x, y, 0.09, 1.1, fill=BLUE if col==0 else PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.28, y, 1.0, 1.1, no, size=26, color=(BLUE if col==0 else PURPLE), bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+1.3, y+0.24, cw-1.5, 0.4, t, size=15, color=INK, bold=True) - text(s, x+1.3, y+0.64, cw-1.5, 0.34, d, size=9.5, color=MUTED) - -# ================= 슬라이드 3 : 01 개요 ================= -s = slide() -header(s, "OVERVIEW", "01. 개요 — DBMS · 형상관리 · DB 구성", 1) -text(s, 0.55, 1.45, 12.2, 0.5, - "킨텍스 자동전시시스템은 전시 생애주기(판매→AI 설계·시각화→공사 옥션→운영→경영분석) 전 과정을 단일 DB에 담는다. " - "공간데이터를 애플리케이션과 분리하지 않고 PostGIS로 DB에 일원화하여 배치·배선·규정검증을 ST_* 연산으로 수행한다.", - size=12, color=INK, line_spacing=1.12) -# 좌: 스택 카드 -cards = [ - ("DBMS", "PostgreSQL 15", "관계형 + 트랜잭션 정합", BLUE), - ("공간확장", "PostGIS", "POLYGON·POINT·LINESTRING 지오메트리", GREEN), - ("암호화", "pgcrypto", "gen_random_uuid() · 해시", PURPLE), - ("형상관리", "Flyway", "V1~V10 버전 마이그레이션(불변·순증)", AMBER), -] -for i,(k,v,d,c) in enumerate(cards): - x = 0.55 + i*3.06 - card(s, x, 2.2, 2.86, 1.35) - rect(s, x, 2.2, 2.86, 0.1, fill=c, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.22, 2.4, 2.5, 0.3, k, size=10, color=c, bold=True) - text(s, x+0.22, 2.72, 2.5, 0.4, v, size=15, color=INK, bold=True) - text(s, x+0.22, 3.16, 2.5, 0.34, d, size=9, color=MUTED, line_spacing=1.05) -# 통계 스트립 -stats = [(f"{TOTAL_TABLES}", "테이블"), (f"{TOTAL_COLS}", "컬럼"), (f"{len(AREAS)}", "주제영역"), - (f"{len(SPATIAL_COLS)}", "공간 컬럼"), (f"{TOTAL_IDX}", "인덱스"), ("V1–V10", "Flyway")] -for i,(v,l) in enumerate(stats): - x = 0.55 + i*2.03 - rect(s, x, 3.85, 1.9, 1.0, fill=BLUE_LT, line=LINE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 4.0, 1.9, 0.44, v, size=23, color=BLUE_DK, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 4.5, 1.9, 0.3, l, size=10, color=MUTED, align=PP_ALIGN.CENTER) -# DB 구성 원칙 -bullets(s, 0.55, 5.15, 12.2, [ - ("kintex 전용 DB", "GUARDiA 타 솔루션과 물리 DB 분리(kintex 전용 스키마). 접속·권한은 서비스 계정 최소권한."), - ("공간·업무 단일 저장", "부스 폴리곤·배선 라인·요율·관람 데이터를 한 트랜잭션 경계에서 일관 관리."), - ("멱등·순증 마이그레이션", "V7 이후는 CREATE IF NOT EXISTS·ADD COLUMN IF NOT EXISTS로 재적용 안전(운영 무중단)."), -], size=11.5, gap=0.5) - -# ================= 슬라이드 4 : 02 아키텍처 원칙 ================= -s = slide() -header(s, "PRINCIPLES", "02. 데이터 아키텍처 원칙", 2) -princ = [ - ("행사 단위 격리(멀티테넌시)", BLUE, - ["물리 tenant_id 컬럼 대신 event_id + event_member(RBAC)로 논리 격리", - "핵심 트랜잭션 테이블은 event_id FK·ON DELETE CASCADE로 행사 경계 정합", - "hall·company·master_data 등 마스터는 전 행사 공유(참조 무결성)"]), - ("공간데이터 PostGIS 일원화", GREEN, - ["부스=POLYGON, 트렌치·비상구=POINT, 배선=MultiLineString (SRID 0 홀 로컬 m)", - "GiST 공간 인덱스로 ST_Intersects/Area/Distance 고속 연산", - "배치·배선 재저장 대비 booth_id 소프트 참조(감사·쿼터 보존)"]), - ("감사 추적·이력 보존", PURPLE, - ["audit_log에 actor·action·target·ruleset_version·result 기록", - "render_job은 이력·쿼터 정본(부스 교체와 무관하게 감사 보존)", - "layout·design_plan·booth_standard 버전(version) 관리"]), - ("보안 내재화", AMBER, - ["password_hash·otp_secret·code_hash 컬럼은 API 응답/로그 완전 제외", - "민감 시크릿 설정은 sys_setting.secret_yn='Y'로 마스킹", - "error_message는 사용자 요약만(스택트레이스 저장 금지)"]), -] -for i,(title,c,items) in enumerate(princ): - x = 0.55 + (i%2)*6.15; y = 1.5 + (i//2)*2.6 - card(s, x, y, 5.95, 2.4) - rect(s, x, y, 0.11, 2.4, fill=c, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.35, y+0.2, 5.4, 0.4, title, size=14, color=INK, bold=True) - yy = y+0.72 - for it in items: - rect(s, x+0.4, yy+0.06, 0.1, 0.1, fill=c, shape=MSO_SHAPE.OVAL) - text(s, x+0.62, yy, 5.15, 0.5, it, size=10.3, color=MUTED, line_spacing=1.05) - yy += 0.52 - -# ================= 슬라이드 5 : 03 개념 데이터 모델 ================= -s = slide() -header(s, "CONCEPTUAL", "03. 개념 데이터 모델 — 주제영역 관계 개요", 3) -text(s, 0.55, 1.42, 12.2, 0.4, - "구현된 4개 주제영역과 소유 데이터의 관계. 행사(EVENT)를 축으로 공간·설계 코어와 공통·시스템 레이어가 결합한다.", - size=11.5, color=MUTED) -# 중앙 EVENT 허브 -hub = {"cx":6.6, "cy":3.9} -rect(s, 5.75, 3.3, 1.7, 1.2, fill=BLUE_DK, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) -text(s, 5.75, 3.3, 1.7, 1.2, [[{"t":"EVENT","size":15,"color":WHITE,"bold":True}], - [{"t":"행사(테넌트 축)","size":9,"color":RGBColor(0xC7,0xDD,0xF2)}]], align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -sa = [ - ("MASTER","행사·조직·권한·마스터","event · hall · company · event_member",8,BLUE, 0.7, 1.95), - ("SPATIAL","공간·설계·시각화","layout · booth · utility_order · render_job",7,GREEN, 9.35, 1.95), - ("COMMON","공통 업무 모듈","worklog · schedule · notice · meeting …",11,PURPLE, 0.7, 4.75), - ("SYSTEM","시스템관리·인증","sys_* · common_code · audit_log · password_reset",9,AMBER, 9.35, 4.75), -] -for code,ko,ex,n,c,x,y in sa: - # 연결선 (먼저) - box_cx = x+1.6; box_cy = y+0.75 - cn = s.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, inch(box_cx), inch(box_cy), inch(hub["cx"]), inch(hub["cy"])) - cn.line.color.rgb = RGBColor(0xC2,0xCC,0xDA); cn.line.width = Pt(1.6); cn.shadow.inherit=False -for code,ko,ex,n,c,x,y in sa: - card(s, x, y, 3.2, 1.5) - rect(s, x, y, 3.2, 0.12, fill=c, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.24, y+0.24, 2.7, 0.34, ko, size=13, color=INK, bold=True) - chip(s, x+2.45, y+0.24, 0.62, 0.3, f"{n}T", c, WHITE, size=9) - text(s, x+0.24, y+0.68, 2.9, 0.7, ex, size=9.3, color=MUTED, line_spacing=1.08) -text(s, 0.55, 6.5, 12.2, 0.4, - "관계 요지: EVENT 1─N HallAssignment/EventMember/Layout/Notice/Worklog(event_id). Layout 1─N Booth(POLYGON) 1─N DesignPlan·UtilityOrder(배선)·RenderJob. Company·User ─ EventMember로 행사 RBAC 성립.", - size=9.8, color=INK, line_spacing=1.1) - -# ================= 슬라이드 6~9 : 04 논리 모델 ERD (주제영역별) ================= -def erd_slide(area_ko, idx, placements, extra_note, cross_notes=None): - s = slide() - header(s, "LOGICAL ERD", f"04. 논리 모델 — {area_ko}", 4) - # 연결선 먼저(엔티티 아래로 깔림) - boxes = {} - # 1차: 좌표만 계산해 중심 확보 - for tn, x, y, w in placements: - t = TABLES[tn] - pk=[c for c in t["cols"] if c["pk"]]; fk=[c for c in t["cols"] if c["fk"] and not c["pk"]] - rest=[c for c in t["cols"] if not c["pk"] and not c["fk"]] - show=(pk+fk+rest); show=show[:6] - h = 0.5+len(show)*0.26 - boxes[tn]={"x":x,"y":y,"w":w,"h":h,"cx":x+w/2,"cy":y+h/2} - for tn,_,_,_ in placements: - for c in TABLES[tn]["cols"]: - if c["fk"] and c["fk"] in boxes and c["fk"]!=tn: - connect(s, boxes[tn], boxes[c["fk"]]) - accents={"MASTER":BLUE,"SPATIAL":GREEN,"SYSTEM":AMBER,"COMMON":PURPLE} - acc = accents.get(idx, BLUE) - for tn,x,y,w in placements: - erd_entity(s, x, y, w, tn, accent=acc) - if extra_note: - text(s, 0.55, 6.78, 12.2, 0.3, extra_note, size=9, color=MUTED, line_spacing=1.05) - return s - -# ERD-1 MASTER -erd_slide("행사·조직·권한·마스터", "MASTER", [ - ("event", 5.6, 1.5, 2.5), - ("hall", 9.9, 1.5, 2.8), - ("hall_assignment", 9.9, 4.05, 2.8), - ("event_member", 5.6, 3.7, 2.85), - ("app_user", 0.6, 1.5, 2.75), - ("company", 0.6, 4.55, 2.75), -], "관계: event 1─N hall_assignment·event_member. app_user·company ─ event_member(행사 RBAC). 독립 마스터 booth_standard·master_data 는 물리 모델 목록(슬라이드 10) 참조.") - -# ERD-2 SPATIAL -erd_slide("공간·설계·시각화(부스 코어)", "SPATIAL", [ - ("hall", 0.6, 1.5, 2.5), - ("trench", 0.6, 4.2, 2.5), - ("hall_exit", 3.45, 4.2, 2.4), - ("layout", 3.6, 1.5, 2.5), - ("booth", 6.55, 1.5, 2.6), - ("design_plan", 9.65, 1.5, 2.7), - ("utility_order", 6.55, 4.35,2.9), - ("render_job", 9.85, 4.35,2.75), -], "관계: hall 1─N trench·hall_exit·layout. layout 1─N booth(POLYGON) 1─N design_plan. booth ─ utility_order(배선 LineString)·render_job(AI 샷). booth_id 소프트 참조.") - -# ERD-3 SYSTEM -erd_slide("시스템관리·인증", "SYSTEM", [ - ("common_code_group", 0.6, 1.5, 2.7), - ("common_code", 0.6, 4.0, 2.7), - ("sys_role", 3.6, 1.5, 2.6), - ("sys_role_permission",3.6,4.0, 2.75), - ("sys_permission", 6.55, 1.5, 2.6), - ("sys_menu", 9.6, 1.5, 2.8), - ("sys_setting", 6.55, 4.05, 2.6), - ("audit_log", 9.6, 4.05, 2.8), -], "관계: common_code_group 1─N common_code. sys_role ─ sys_role_permission ─ sys_permission(M:N). sys_menu 자기참조(parent_id). password_reset(공개 인증)은 별도. audit_log 전 모듈 공유.") - -# ERD-4 COMMON -s = slide() -header(s, "LOGICAL ERD", "04. 논리 모델 — 공통 업무 모듈", 4) -# 부모-자식 3쌍 + 독립 목록 -pairs = [("message","message_recipient"),("opinion","opinion_comment"),("meeting","meeting_action")] -px = 0.6 -for i,(p,ch) in enumerate(pairs): - x = 0.6 + i*4.15 - bp = erd_entity(s, x, 1.5, 3.0, p, accent=PURPLE) - bc = erd_entity(s, x+0.3, 3.9, 3.0, ch, accent=RGBColor(0x8B,0x6B,0xE0)) -# 연결(부모→자식) 먼저 그리지 못했으니 라벨로 대체 -text(s, 0.6, 6.05, 11.9, 0.3, - "부모 1─N 자식(FK·ON DELETE CASCADE): message→message_recipient · opinion→opinion_comment · meeting→meeting_action", - size=9.5, color=TEAL, bold=True) -# 독립 테이블 스트립 -indep = ["worklog","schedule","notice","report","notification"] -text(s, 0.6, 6.42, 3, 0.3, "행사 스코프 독립 테이블", size=10, color=MUTED, bold=True) -for i,tn in enumerate(indep): - x = 3.0 + i*1.85 - rect(s, x, 6.4, 1.75, 0.4, fill=BLUE_LT, line=LINE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 6.4, 1.75, 0.4, tn, size=9.5, color=BLUE_DK, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -# ================= 슬라이드 10 : 05 물리 모델 요약 (테이블 그룹) ================= -s = slide() -header(s, "PHYSICAL", "05. 물리 모델 요약 — 테이블 그룹", 5) -col_x = [0.55, 6.7] -for gi,(code,ko,tlist) in enumerate(AREAS): - x = col_x[gi%2]; y = 1.5 + (gi//2)*2.65 - card(s, x, y, 5.9, 2.45) - acc = {"MASTER":BLUE,"SPATIAL":GREEN,"SYSTEM":AMBER,"COMMON":PURPLE}[code] - rect(s, x, y, 5.9, 0.5, fill=acc, shape=MSO_SHAPE.ROUND_2_SAME_RECTANGLE) - text(s, x+0.25, y, 4.8, 0.5, ko, size=13, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+5.0, y, 0.7, 0.5, f"{len(tlist)}T", size=13, color=WHITE, bold=True, align=PP_ALIGN.RIGHT, anchor=MSO_ANCHOR.MIDDLE) - yy = y+0.62 - for j,tn in enumerate(tlist): - cx = x+0.25 + (j%2)*2.9 - cyy = yy + (j//2)*0.315 - text(s, cx, cyy, 2.85, 0.3, [[{"t":"· ","color":acc,"bold":True,"size":10}, - {"t":tn,"size":9.3,"color":INK,"bold":True}, - {"t":" "+KO.get(tn,""),"size":7.8,"color":MUTED}]], anchor=MSO_ANCHOR.MIDDLE) - -# ================= 슬라이드 11 : 05b 공간 컬럼·인덱스 전략 ================= -s = slide() -header(s, "PHYSICAL — SPATIAL & INDEX", "05. 공간 컬럼 · 인덱스 전략", 5) -# 공간 컬럼 표 -text(s, 0.55, 1.45, 6, 0.32, "공간(PostGIS) 컬럼", size=13, color=GREEN, bold=True) -sp_rows = [("테이블","컬럼","지오메트리","용도")] -sp_meta = { - ("trench","geom"):"급전/급수/네트워크 공급 포인트", - ("hall_exit","geom"):"비상구 위치(이격 버퍼 검증)", - ("booth","geom"):"부스 경계 폴리곤(면적·간섭)", - ("utility_order","wiring"):"전기/네트워크/급배수 배선 경로", -} -for tn,cn,ty in SPATIAL_COLS: - sp_rows.append((tn, cn, ty, sp_meta.get((tn,cn),""))) -ry = 1.85 -for i,row in enumerate(sp_rows): - fill = BLUE if i==0 else (WHITE if i%2 else BLUE_LT) - tc = WHITE if i==0 else INK - rect(s, 0.55, ry, 6.1, 0.4, fill=fill, line=LINE) - xs=[0.65,2.05,3.35,4.75]; ws=[1.35,1.25,1.4,1.85] - for k,val in enumerate(row): - text(s, xs[k], ry, ws[k], 0.4, val, size=8.6, color=(tc if k<3 else (tc if i==0 else MUTED)), - bold=(i==0 or k==0), anchor=MSO_ANCHOR.MIDDLE) - ry += 0.4 -text(s, 0.55, ry+0.1, 6.1, 0.6, - "SRID 0 = 홀 로컬 평면(미터). ST_Area/Distance/Length 결과 단위 = m·㎡.", - size=9, color=MUTED, line_spacing=1.1) -# 인덱스 전략 -text(s, 6.95, 1.45, 5.8, 0.32, "인덱스 전략", size=13, color=BLUE, bold=True) -idx_items = [ - ("GiST 공간 인덱스", f"geometry 컬럼 4종(booth·trench·hall_exit·utility_order.wiring) 전부 GiST"), - ("B-Tree 조회 인덱스", "FK·정렬 축: writer_id+work_date, recipient_id, event_id 등"), - ("복합 인덱스", "render_job(booth_id,shot_preset) · (event_id,status) 쿼터·상태 조회"), - ("정렬 최적화", "audit_log(created_at DESC) · notice(event_id,pinned DESC,created_at DESC)"), - (f"총 인덱스 {TOTAL_IDX}개", "PK 자동 인덱스 별도 · 공간 4 · B-Tree 다수"), -] -yy = 1.9 -for k,v in idx_items: - card(s, 6.95, yy, 5.75, 0.78) - text(s, 7.15, yy+0.12, 5.4, 0.3, k, size=11, color=INK, bold=True) - text(s, 7.15, yy+0.44, 5.4, 0.3, v, size=8.8, color=MUTED, line_spacing=1.05) - yy += 0.9 - -# ================= 슬라이드 12 : 06 데이터 표준 ================= -s = slide() -header(s, "STANDARDS", "06. 데이터 표준 — 명명 · 코드 · 날짜/통화", 6) -std = [ - ("명명 규칙(실제 DDL 관찰)", BLUE, [ - "테이블·컬럼 = snake_case 단수형 (app_user, booth, render_job)", - "PK = id (varchar 애플리케이션 채번) / 대량 로그 = bigserial (audit_log·password_reset)", - "FK = <참조테이블>_id (event_id, booth_id, hall_id, layout_id)", - "불리언 = is_/has_/*_yn 혼용 · 금액/치수 = numeric(정밀도,스케일)", - "타임스탬프 = *_at (timestamptz) / 날짜 = *_date (date)", - ]), - ("공통코드 체계(common_code)", PURPLE, [ - "grp_code(그룹) 1─N code — 14개 그룹 시드(USE_YN·USER_ROLE·WORK_STATUS …)", - "화면 드롭다운·상태값을 코드로 표준화 (하드코딩 배제)", - "정렬 sort_order · 사용여부 use_yn(Y/N) · 확장 attr1", - ]), - ("날짜·통화·단위 표준", GREEN, [ - "일시 = timestamptz(UTC 저장, 표시 KST) · 날짜 = date", - "통화 = KRW 정수(원) 원칙, 요율/정산은 numeric — 부동소수 금지", - "치수 = 미터(m)·면적 ㎡ · 하중 t/㎡ (numeric 고정 스케일)", - ]), -] -yy = 1.5 -for title,c,items in std: - h = 0.5 + len(items)*0.34 + 0.15 - card(s, 0.55, yy, 12.2, h) - rect(s, 0.55, yy, 0.11, h, fill=c, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.85, yy+0.16, 11.6, 0.34, title, size=13.5, color=INK, bold=True) - iy = yy+0.6 - for it in items: - rect(s, 0.95, iy+0.06, 0.09, 0.09, fill=c, shape=MSO_SHAPE.OVAL) - text(s, 1.14, iy, 11.4, 0.32, it, size=10.3, color=MUTED) - iy += 0.34 - yy += h + 0.16 - -# ================= 슬라이드 13 : 07 데이터 보안 ================= -s = slide() -header(s, "SECURITY", "07. 데이터 보안 — 암호화 · PII · 응답제외 · 감사", 7) -# 응답 제외/암호화 컬럼 표 -text(s, 0.55, 1.45, 7, 0.32, "민감 컬럼 처리 정책", size=13, color=BLUE, bold=True) -sec_rows = [("테이블.컬럼","정책")] -sec_rows += [ - ("app_user.password_hash","BCrypt 해시 · API 응답/로그 완전 제외"), - ("app_user.otp_secret","TOTP 시크릿 · AES-256-GCM 암호화 저장 · 응답 제외"), - ("password_reset.code_hash","BCrypt(6자리) · 만료 10분·1회성 · 응답/로그 제외"), - ("sys_setting.secret_yn='Y'","시크릿 설정값 마스킹 반환"), - ("render_job.error_message","사용자 요약만 · 스택트레이스 저장 금지"), - ("booth.assigned_company_name","표시용(내부 식별자·민감정보 미포함)"), -] -ry = 1.85 -for i,(a,b) in enumerate(sec_rows): - fill = BLUE if i==0 else (WHITE if i%2 else BLUE_LT) - tc = WHITE if i==0 else INK - rect(s, 0.55, ry, 6.5, 0.52, fill=fill, line=LINE) - text(s, 0.68, ry, 2.75, 0.52, a, size=8.4, color=tc, bold=(i==0), anchor=MSO_ANCHOR.MIDDLE) - text(s, 3.5, ry, 3.45, 0.52, b, size=8.3, color=(tc if i==0 else MUTED), bold=(i==0), - anchor=MSO_ANCHOR.MIDDLE, line_spacing=1.0) - ry += 0.52 -# 우측 원칙 카드 -text(s, 7.3, 1.45, 5.4, 0.32, "보안 불변 원칙", size=13, color=PURPLE, bold=True) -sec_cards = [ - ("PII 최소화·격리", "관람/리드 PII 집중영역 접근 제한 · BI 마트는 집계/익명키만 반입(개인식별자 0)"), - ("암호화 저장", "비밀번호·OTP·재설정코드는 해시/AES-256-GCM · 평문 저장 금지"), - ("API 응답 스키마 통제", "ServerOut류 민감 컬럼 응답 완전 제외 · 스택트레이스 미노출"), - ("감사 추적", "audit_log(actor·action·target·ruleset_version·result·ip_hint) 전 변경 기록"), - ("행사 격리 접근제어", "event_member 역할(ORGANIZER·EXHIBITOR·CONTRACTOR·HALL_MANAGER) 스코프"), -] -yy = 1.85 -for k,v in sec_cards: - card(s, 7.3, yy, 5.45, 0.92) - rect(s, 7.3, yy, 0.09, 0.92, fill=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 7.55, yy+0.13, 5.1, 0.3, k, size=11, color=INK, bold=True) - text(s, 7.55, yy+0.45, 5.1, 0.42, v, size=8.7, color=MUTED, line_spacing=1.05) - yy += 1.02 - -# ================= 슬라이드 14 : 08 백업·이관 전략 ================= -s = slide() -header(s, "BACKUP & MIGRATION", "08. 백업 · 이관 전략 (Flyway 형상관리)", 8) -mig = [ - ("Flyway 버전 마이그레이션", BLUE, [ - "V1 확장 → V2 신원·마스터 → V3 공간코어 → V4~V6 시드 → V7 시스템관리 → V8 공통업무 → V9 공개인증 → V10 10년 시드", - "적용 이력은 flyway_schema_history 테이블에 체크섬으로 기록·검증", - "V1~V6 불변 원칙 · V7 이후 순증(additive)·멱등만 수행(운영 무중단)", - ]), - ("백업 전략", GREEN, [ - "일 1회 논리 백업(pg_dump) + WAL 아카이브 기반 PITR(시점복구)", - "PostGIS 지오메트리·공간 인덱스 포함 전체 스키마 백업", - "오브젝트 스토리지(렌더 이미지·도면)는 서명·만료 URL로 별도 보존", - ]), - ("이관·복구 절차", PURPLE, [ - "복구 = 최신 백업 복원 → Flyway migrate로 최신 버전까지 재적용(정본 순서 보장)", - "환경 승격(dev→운영): 동일 마이그레이션 스크립트 적용으로 스키마 동형 보장", - "시드(V4~V6·V10)와 스키마(V1~V3·V7~V9) 분리로 데이터 오염 없이 재구축", - ]), -] -yy = 1.5 -for title,c,items in mig: - h = 0.5 + len(items)*0.4 + 0.1 - card(s, 0.55, yy, 12.2, h) - rect(s, 0.55, yy, 0.11, h, fill=c, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.85, yy+0.16, 11.6, 0.34, title, size=13.5, color=INK, bold=True) - iy = yy+0.62 - for it in items: - rect(s, 0.95, iy+0.07, 0.09, 0.09, fill=c, shape=MSO_SHAPE.OVAL) - text(s, 1.14, iy, 11.4, 0.38, it, size=10.2, color=MUTED, line_spacing=1.08) - iy += 0.4 - yy += h + 0.16 -text(s, 0.55, 6.75, 12.2, 0.3, - "보안 불변: 본 설계서·백업 매뉴얼에 비밀번호·API키·서버 IP·SSH 접속정보를 기재하지 않는다.", - size=9, color=MUTED, bold=True) - -# ---- 저장 ---- -PPTX_PATH = os.path.join(OUT_DIR, "데이터베이스설계서.pptx") -prs.save(PPTX_PATH) -N_SLIDES = len(prs.slides._sldIdLst) - -# ====================================================================== -# 3. XLSX — 테이블정의서 -# ====================================================================== -import openpyxl -from openpyxl.styles import Font, PatternFill, Alignment, Border, Side -from openpyxl.utils import get_column_letter - -BLUE_HEX = "0066B3"; BLUEDK_HEX = "00447A"; BAND_HEX = "E1EFF9"; ROW_HEX = "F4F8FC" -thin = Side(style="thin", color="D0D7E2") -BORDER = Border(left=thin, right=thin, top=thin, bottom=thin) -HFONT = Font(name="맑은 고딕", size=10, bold=True, color="FFFFFF") -BFONT = Font(name="맑은 고딕", size=10) -BOLD = Font(name="맑은 고딕", size=10, bold=True, color="101828") -TITLE_FONT = Font(name="맑은 고딕", size=11, bold=True, color="FFFFFF") -HEAD_FILL = PatternFill("solid", fgColor=BLUE_HEX) -BAND_FILL = PatternFill("solid", fgColor=BLUEDK_HEX) -ALT_FILL = PatternFill("solid", fgColor=ROW_HEX) -CEN = Alignment(horizontal="center", vertical="center", wrap_text=True) -LEFT = Alignment(horizontal="left", vertical="center", wrap_text=True) - -wb = openpyxl.Workbook() - -# ---- 시트1: 테이블목록 ---- -ws = wb.active; ws.title = "테이블목록" -ws.sheet_view.showGridLines = False -cols1 = [("No",6),("테이블명",22),("한글명",24),("주제영역",24),("Flyway",10),("컬럼수",8),("비고",40)] -for j,(h,w) in enumerate(cols1, start=1): - c = ws.cell(1, j, h); c.font=HFONT; c.fill=HEAD_FILL; c.alignment=CEN; c.border=BORDER - ws.column_dimensions[get_column_letter(j)].width = w -ordered = sorted(TABLES.items(), key=lambda kv: kv[1]["order"]) -for i,(tn,t) in enumerate(ordered, start=1): - note = [] - if any(c["type"].lower().startswith("geometry") for c in t["cols"]): note.append("PostGIS 공간") - if t["pk"] and len(t["pk"])>1: note.append("복합 PK("+",".join(sorted(t["pk"]))+")") - if t["uniques"]: note.append("UNIQUE("+"; ".join(t["uniques"])+")") - if any(c["name"] in ("password_hash","otp_secret","code_hash") for c in t["cols"]): note.append("민감:응답제외") - row = [i, tn, KO.get(tn,""), AREA_OF.get(tn,""), t["version"], len(t["cols"]), " · ".join(note)] - for j,val in enumerate(row, start=1): - c = ws.cell(i+1, j, val) - c.font = BOLD if j==2 else BFONT - c.alignment = CEN if j in (1,5,6) else LEFT - c.border = BORDER - if i%2==0: c.fill = ALT_FILL -ws.freeze_panes = "A2" -ws.auto_filter.ref = f"A1:G{len(ordered)+1}" - -# ---- 주제영역별 시트 ---- -DATA_COLS = [("No",5),("컬럼명",26),("데이터타입",20),("NULL",7),("키",13),("기본값",18),("설명",56)] -def key_str(c): - parts=[] - if c["pk"]: parts.append("PK") - if c["fk"]: parts.append(f"FK→{c['fk']}") - if c["unique"]: parts.append("U") - return " ".join(parts) - -for code, ko, tlist in AREAS: - title = ko.split("(")[0].strip() - sname = {"MASTER":"마스터·조직·권한","SPATIAL":"공간·설계·시각화", - "SYSTEM":"시스템관리·인증","COMMON":"공통업무"}[code] - ws = wb.create_sheet(sname) - ws.sheet_view.showGridLines = False - for j,(h,w) in enumerate(DATA_COLS, start=1): - c = ws.cell(1, j, h); c.font=HFONT; c.fill=HEAD_FILL; c.alignment=CEN; c.border=BORDER - ws.column_dimensions[get_column_letter(j)].width = w - r = 2 - ncol = len(DATA_COLS) - for tn in tlist: - t = TABLES[tn] - # 밴드 행(테이블 헤더) - ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=ncol) - bc = ws.cell(r, 1, f"◆ {tn} — {KO.get(tn,'')} [{t['version']}]" - + (" · 인덱스 "+", ".join(ix["name"] for ix in t["indexes"]) if t["indexes"] else "")) - bc.font = TITLE_FONT; bc.fill = BAND_FILL; bc.alignment = LEFT - for j in range(1, ncol+1): - ws.cell(r, j).border = BORDER - ws.cell(r, j).fill = BAND_FILL - r += 1 - for k,c in enumerate(t["cols"], start=1): - vals = [k, c["name"], c["type"], c["null"], key_str(c), c["default"], c["desc"]] - for j,val in enumerate(vals, start=1): - cc = ws.cell(r, j, val) - cc.font = BOLD if (j==2 and c["pk"]) else BFONT - cc.alignment = CEN if j in (1,4) else LEFT - cc.border = BORDER - if k%2==0: cc.fill = ALT_FILL - if j==5 and c["fk"]: - cc.font = Font(name="맑은 고딕", size=9, color="0E7C86", bold=True) - r += 1 - r += 1 # 테이블 간 공백 - ws.freeze_panes = "A2" - -XLSX_PATH = os.path.join(OUT_DIR, "테이블정의서.xlsx") -wb.save(XLSX_PATH) - -print("=" * 60) -print(f"[PPTX] {PPTX_PATH}") -print(f" 슬라이드 {N_SLIDES}장") -print(f"[XLSX] {XLSX_PATH}") -print(f" 시트 {len(wb.sheetnames)} : {wb.sheetnames}") -print(f"[SCHEMA] 테이블 {TOTAL_TABLES} · 컬럼 {TOTAL_COLS} · 인덱스 {TOTAL_IDX} · 공간컬럼 {len(SPATIAL_COLS)} · FK {len(FK_EDGES)}") -print("=" * 60) diff --git a/docs/deliverables/DA산출물/데이터베이스설계서.pptx b/docs/deliverables/DA산출물/데이터베이스설계서.pptx deleted file mode 100644 index 03a1944..0000000 Binary files a/docs/deliverables/DA산출물/데이터베이스설계서.pptx and /dev/null differ diff --git a/docs/deliverables/DA산출물/테이블정의서.xlsx b/docs/deliverables/DA산출물/테이블정의서.xlsx deleted file mode 100644 index 073baeb..0000000 Binary files a/docs/deliverables/DA산출물/테이블정의서.xlsx and /dev/null differ diff --git a/docs/deliverables/_gen/_docxutil.py b/docs/deliverables/_gen/_docxutil.py deleted file mode 100644 index 47c7c80..0000000 --- a/docs/deliverables/_gen/_docxutil.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -"""공용 docx 헬퍼 — 한글 폰트/표/제목 스타일. 각 문서 생성기가 import.""" -from docx.shared import Pt, Cm, RGBColor -from docx.enum.text import WD_ALIGN_PARAGRAPH -from docx.enum.table import WD_TABLE_ALIGNMENT -from docx.oxml.ns import qn -from docx.oxml import OxmlElement - -NAVY = RGBColor(0x1F, 0x29, 0x3A) -ACCENT = RGBColor(0x11, 0x6D, 0xC3) -GREY = RGBColor(0x55, 0x5F, 0x6B) -KFONT = "맑은 고딕" - -def setup(doc): - st = doc.styles["Normal"]; st.font.name = KFONT; st.font.size = Pt(10.5) - _rf(st.element.get_or_add_rPr()) - for m in ("Heading 1","Heading 2","Heading 3"): - doc.styles[m].font.name = KFONT; _rf(doc.styles[m].element.get_or_add_rPr()) - s = doc.sections[0] - s.top_margin = Cm(2.2); s.bottom_margin = Cm(2.0); s.left_margin = Cm(2.3); s.right_margin = Cm(2.3) - -def _rf(rpr): - rf = rpr.find(qn('w:rFonts')) - if rf is None: - rf = OxmlElement('w:rFonts'); rpr.append(rf) - for a in ('w:ascii','w:hAnsi','w:eastAsia','w:cs'): - rf.set(qn(a), KFONT) - -def shade(cell, hexcolor): - tcpr = cell._tc.get_or_add_tcPr() - sh = OxmlElement('w:shd'); sh.set(qn('w:val'),'clear'); sh.set(qn('w:fill'),hexcolor) - tcpr.append(sh) - -def h(doc, level, text): - p = doc.add_heading(text, level=level) - for run in p.runs: - run.font.color.rgb = NAVY if level == 1 else ACCENT - return p - -def para(doc, text, size=10.5, bold=False, italic=False, color=None, align=None, space_after=6): - p = doc.add_paragraph(); r = p.add_run(text); r.bold = bold; r.italic = italic - r.font.size = Pt(size) - if color: r.font.color.rgb = color - if align: p.alignment = align - p.paragraph_format.space_after = Pt(space_after) - return p - -def bullets(doc, items, style="List Bullet"): - for it in items: - p = doc.add_paragraph(it, style=style); p.paragraph_format.space_after = Pt(2) - -def table(doc, headers, rows, widths=None, header_fill="1F293A", first_col_bold=False, fontsize=9.5): - t = doc.add_table(rows=1, cols=len(headers)); t.style = "Table Grid"; t.alignment = WD_TABLE_ALIGNMENT.CENTER - hd = t.rows[0].cells - for i, htext in enumerate(headers): - hd[i].text = ""; run = hd[i].paragraphs[0].add_run(htext) - run.bold = True; run.font.size = Pt(fontsize); run.font.color.rgb = RGBColor(0xFF,0xFF,0xFF) - shade(hd[i], header_fill) - for row in rows: - cells = t.add_row().cells - for i, val in enumerate(row): - cells[i].text = ""; run = cells[i].paragraphs[0].add_run(str(val)); run.font.size = Pt(fontsize) - if first_col_bold and i == 0: run.bold = True - cells[i].paragraphs[0].paragraph_format.space_after = Pt(1) - if widths: - for r_ in t.rows: - for i, w in enumerate(widths): - r_.cells[i].width = Cm(w) - doc.add_paragraph().paragraph_format.space_after = Pt(4) - return t diff --git a/docs/deliverables/_gen/gen_dev_plan_pptx.py b/docs/deliverables/_gen/gen_dev_plan_pptx.py deleted file mode 100644 index dfe99e4..0000000 --- a/docs/deliverables/_gen/gen_dev_plan_pptx.py +++ /dev/null @@ -1,785 +0,0 @@ -# -*- coding: utf-8 -*- -""" -킨텍스 자동전시시스템 구축 — 개발계획서 PPTX 생성기 -- 소스: docs/PLANNING.md(v3.0), docs/IMPLEMENTATION_BACKLOG.md, docs/WORK_STATUS.md, - docs/architecture/*, CLAUDE.md 기술 스택표 -- 디자인: KINTEX Blue #0066B3 / AI Purple #6D4AFF / 중립 그레이 / 맑은 고딕 / 16:9 -- 도식(추진체계도·시스템 구성도·간트)은 python-pptx 도형으로 직접 그림(외부 이미지 금지) -- 보안 불변: 비밀번호/API키/서버IP/SSH 계정 미기재 -재실행: python gen_dev_plan_pptx.py -""" -import os -from pptx import Presentation -from pptx.util import Emu, Pt -from pptx.dml.color import RGBColor -from pptx.enum.text import PP_ALIGN, MSO_ANCHOR -from pptx.enum.shapes import MSO_SHAPE -from pptx.enum.text import MSO_AUTO_SIZE -from pptx.oxml.ns import qn - -# ---------------- 팔레트 ---------------- -BLUE = RGBColor(0x00, 0x66, 0xB3) # KINTEX Blue (primary) -BLUE_DK = RGBColor(0x00, 0x44, 0x7A) # deep -BLUE_LT = RGBColor(0xE1, 0xEF, 0xF9) # light tint -PURPLE = RGBColor(0x6D, 0x4A, 0xFF) # AI accent -PURPLE_LT = RGBColor(0xEC, 0xE8, 0xFF) -INK = RGBColor(0x10, 0x18, 0x28) # text -MUTED = RGBColor(0x66, 0x70, 0x85) # secondary text -LINE = RGBColor(0xE4, 0xE7, 0xEC) # borders -BG = RGBColor(0xF9, 0xFA, 0xFB) # page bg -WHITE = RGBColor(0xFF, 0xFF, 0xFF) -GREEN = RGBColor(0x12, 0x9E, 0x63) # 완료 -AMBER = RGBColor(0xE0, 0x8A, 0x00) # 진행 -SLATE = RGBColor(0x98, 0xA2, 0xB3) # 예정 -CARD = RGBColor(0xFF, 0xFF, 0xFF) -HALL_BG = RGBColor(0xF2, 0xF4, 0xF7) - -FONT = "맑은 고딕" - -# 16:9 -EMU_W = 12192000 -EMU_H = 6858000 - -prs = Presentation() -prs.slide_width = EMU_W -prs.slide_height = EMU_H -BLANK = prs.slide_layouts[6] - -IN = 914400 -def inch(v): return int(v * IN) - -# ---------------- 헬퍼 ---------------- -def slide(): - return prs.slides.add_slide(BLANK) - -def _set_fill(shape, color): - if color is None: - shape.fill.background() - else: - shape.fill.solid() - shape.fill.fore_color.rgb = color - -def _no_line(shape): - shape.line.fill.background() - -def rect(s, x, y, w, h, fill=None, line=None, line_w=1.0, shape=MSO_SHAPE.RECTANGLE, shadow=False): - sp = s.shapes.add_shape(shape, inch(x), inch(y), inch(w), inch(h)) - _set_fill(sp, fill) - if line is None: - _no_line(sp) - else: - sp.line.color.rgb = line - sp.line.width = Pt(line_w) - sp.shadow.inherit = False # creates an empty - if shadow: - el = sp._element.spPr - ef = el.find(qn('a:effectLst')) - if ef is None: - ef = el.makeelement(qn('a:effectLst'), {}) - el.append(ef) - sh = ef.makeelement(qn('a:outerShdw'), - {'blurRad':'90000','dist':'38100','dir':'5400000','rotWithShape':'0'}) - clr = sh.makeelement(qn('a:srgbClr'), {'val':'101828'}) - alp = clr.makeelement(qn('a:alpha'), {'val':'18000'}) - clr.append(alp); sh.append(clr); ef.append(sh) - return sp - -def _apply_runs(tf, para, segs, align, default_size, default_color, default_bold): - para.alignment = align - first = True - for seg in segs: - text = seg.get("t","") - r = para.add_run() if not first else para.runs[0] if para.runs else para.add_run() - # ensure a run exists - if first and not para.runs: - r = para.add_run() - r.text = text - r.font.name = FONT - r.font.size = Pt(seg.get("size", default_size)) - r.font.bold = seg.get("bold", default_bold) - r.font.color.rgb = seg.get("color", default_color) - # set east asian font - rPr = r._r.get_or_add_rPr() - ea = rPr.makeelement(qn('a:ea'), {'typeface':FONT}) - rPr.append(ea) - first = False - -def text(s, x, y, w, h, content, size=14, color=INK, bold=False, align=PP_ALIGN.LEFT, - anchor=MSO_ANCHOR.TOP, line_spacing=1.0, space_after=2, wrap=True): - """content: str 또는 list[para], para = str 또는 list[seg], seg={'t','size','color','bold'}""" - tb = s.shapes.add_textbox(inch(x), inch(y), inch(w), inch(h)) - tf = tb.text_frame - tf.word_wrap = wrap - tf.vertical_anchor = anchor - tf.margin_left = 0; tf.margin_right = 0; tf.margin_top = 0; tf.margin_bottom = 0 - if isinstance(content, str): - content = [content] - for i, para in enumerate(content): - p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() - p.alignment = align - p.line_spacing = line_spacing - p.space_after = Pt(space_after) - p.space_before = Pt(0) - if isinstance(para, str): - segs = [{"t": para}] - elif isinstance(para, dict): - segs = [para] - else: - segs = para - for seg in segs: - r = p.add_run() - r.text = seg.get("t","") - r.font.name = FONT - r.font.size = Pt(seg.get("size", size)) - r.font.bold = seg.get("bold", bold) - r.font.color.rgb = seg.get("color", color) - rPr = r._r.get_or_add_rPr() - ea = rPr.makeelement(qn('a:ea'), {'typeface':FONT}) - rPr.append(ea) - return tb - -def page_bg(s, color=BG): - rect(s, -0.1, -0.1, 13.53, 7.7, fill=color) - -PAGE = {"n": 0} -def content_header(s, kicker, title, idx): - """본문 슬라이드 공통 헤더 + 푸터""" - page_bg(s, BG) - # top accent bar - rect(s, 0, 0, 13.333, 0.12, fill=BLUE) - # section number chip - rect(s, 0.55, 0.5, 0.62, 0.62, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.55, 0.5, 0.62, 0.62, f"{idx:02d}", size=20, color=WHITE, bold=True, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, 1.32, 0.5, 10.5, 0.28, kicker, size=11, color=PURPLE, bold=True) - text(s, 1.32, 0.74, 11.4, 0.44, title, size=22, color=INK, bold=True) - rect(s, 0.55, 1.28, 12.23, 0.02, fill=LINE) - # footer - text(s, 0.55, 7.06, 8, 0.3, "킨텍스 자동전시시스템 구축 · 개발계획서", size=8.5, color=MUTED) - text(s, 11.3, 7.06, 1.48, 0.3, "2026-07-11", size=8.5, color=MUTED, align=PP_ALIGN.RIGHT) - -def chip(s, x, y, w, h, label, fill, txtcolor=WHITE, size=9.5, bold=True): - rect(s, x, y, w, h, fill=fill, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, w, h, label, size=size, color=txtcolor, bold=bold, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -def card(s, x, y, w, h, fill=CARD, line=LINE): - return rect(s, x, y, w, h, fill=fill, line=line, line_w=1.0, - shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - -# ================================================================ -# 1. 표지 -# ================================================================ -s = slide() -rect(s, -0.1, -0.1, 13.53, 7.7, fill=BLUE_DK) -# 대각 그라디언트 느낌: 겹치는 반투명 도형 -rect(s, 6.4, -1.5, 9, 11, fill=BLUE, shape=MSO_SHAPE.PARALLELOGRAM) -rect(s, 9.2, -2.0, 7, 12, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) -# 좌상 브랜드 -rect(s, 0.85, 0.8, 0.42, 0.42, fill=WHITE, shape=MSO_SHAPE.OVAL) -rect(s, 0.97, 0.92, 0.18, 0.18, fill=PURPLE, shape=MSO_SHAPE.OVAL) -text(s, 1.42, 0.78, 6, 0.46, "KINTEX · WISE AI", size=15, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) -# 문서 종류 배지 -chip(s, 0.85, 2.35, 1.75, 0.42, "개발계획서", PURPLE, WHITE, size=12) -chip(s, 2.72, 2.35, 2.1, 0.42, "Development Plan", RGBColor(0x2B,0x4C,0x7E), WHITE, size=10.5) -# 타이틀 -text(s, 0.85, 2.95, 11, 1.9, [ - [{"t":"킨텍스 자동전시시스템", "size":46, "color":WHITE, "bold":True}], - [{"t":"구축 개발계획서", "size":46, "color":WHITE, "bold":True}], - ], line_spacing=1.02) -text(s, 0.88, 4.78, 11, 0.5, - "Exhibition Automation Platform — 다중 전시관 멀티테넌트 SaaS", - size=16, color=RGBColor(0xC7,0xDD,0xF2), bold=False) -# 폐루프 한 줄 -text(s, 0.88, 5.35, 11.4, 0.4, - "판매 → AI 설계·시각화 → 공사 옥션 → 시공 → 운영 → 경영분석 폐루프 자동화", - size=12.5, color=RGBColor(0xB9,0xC7,0xEA)) -# 하단 메타 바 -rect(s, 0.85, 6.15, 11.63, 0.02, fill=RGBColor(0x3A,0x5A,0x8C)) -text(s, 0.85, 6.35, 8, 0.4, [ - [{"t":"사업명 ","size":11,"color":RGBColor(0x8FA,0x00,0x00) if False else RGBColor(0x90,0xA6,0xC9)}, - {"t":"킨텍스 자동전시시스템 구축","size":11.5,"color":WHITE,"bold":True}], - ]) -text(s, 0.85, 6.72, 9, 0.4, [ - [{"t":"기술스택 ","size":11,"color":RGBColor(0x90,0xA6,0xC9)}, - {"t":"React(Vite)+TS · Spring Boot 3.x(Java17)+MyBatis · PostgreSQL(PostGIS) · Redis · 나노바나나 워커 · Claude AI","size":10.5,"color":WHITE}], - ]) -text(s, 9.7, 6.35, 2.78, 0.4, "작성일 2026-07-11", size=11, color=WHITE, bold=True, align=PP_ALIGN.RIGHT) -text(s, 9.7, 6.72, 2.78, 0.4, "planner · designer · dev team", size=9.5, color=RGBColor(0x90,0xA6,0xC9), align=PP_ALIGN.RIGHT) - -# ================================================================ -# 2. 목차 -# ================================================================ -s = slide() -content_header(s, "CONTENTS", "목차", 0) -# 위 헤더의 00 chip을 목차용으로 덮기 -rect(s, 0.55, 0.5, 0.62, 0.62, fill=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.55, 0.5, 0.62, 0.62, "C", size=22, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -toc = [ - ("01", "사업 개요", "배경·목적·범위 / 전시 생애주기 폐루프"), - ("02", "시스템 구성", "아키텍처 개요 / 역할별 포털 / 나노바나나 파이프라인"), - ("03", "개발 범위", "모듈 M1~M18 + 공통·시스템관리 레이어(§5B)"), - ("04", "추진 체계", "거버넌스·아키텍트·개발·QA·DevOps 조직"), - ("05", "추진 일정", "Phase A~E 마스터 일정(완료/진행/예정)"), - ("06", "개발 방법론·품질관리", "에이전트 파이프라인 · 점진 QA · CI/CD"), - ("07", "보안 · 비기능요건(NFR)", "외부API 통제 · AI 워터마크 · 접근성·다국어"), - ("08", "리스크 및 대응", "기술·데이터·연동·보안 리스크"), - ("09", "기대 효과", "정량·정성 효과 / 확장 로드맵"), -] -col_w = 5.85 -for i, (no, t, d) in enumerate(toc): - col = i // 5 - row = i % 5 - x = 0.7 + col * (col_w + 0.35) - y = 1.62 + row * 1.02 - rect(s, x, y, col_w, 0.88, fill=CARD, line=LINE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - rect(s, x, y, 0.09, 0.88, fill=BLUE if col==0 else PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.28, y, 0.9, 0.88, no, size=24, color=(BLUE if col==0 else PURPLE), bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+1.15, y+0.15, col_w-1.3, 0.35, t, size=14.5, color=INK, bold=True) - text(s, x+1.15, y+0.5, col_w-1.3, 0.32, d, size=9.5, color=MUTED) - -# ================================================================ -# 섹션 간지 헬퍼 -# ================================================================ -def divider(num, kr, en, points): - s = slide() - rect(s, -0.1, -0.1, 13.53, 7.7, fill=BLUE_DK) - rect(s, 8.6, -1.5, 8, 11, fill=BLUE, shape=MSO_SHAPE.PARALLELOGRAM) - rect(s, 10.9, -2, 6, 12, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) - text(s, 1.0, 2.15, 5, 2.2, num, size=150, color=RGBColor(0x2C,0x4E,0x82), bold=True) - rect(s, 1.15, 3.9, 0.9, 0.09, fill=PURPLE) - text(s, 1.15, 4.05, 8, 0.8, kr, size=34, color=WHITE, bold=True) - text(s, 1.18, 4.85, 8, 0.4, en, size=13, color=RGBColor(0x9C,0xB4,0xD8), bold=True) - # 우측 포인트 - for i, p in enumerate(points): - yy = 2.35 + i*0.62 - rect(s, 8.05, yy+0.06, 0.16, 0.16, fill=PURPLE, shape=MSO_SHAPE.OVAL) - text(s, 8.35, yy, 4.4, 0.5, p, size=12.5, color=WHITE, anchor=MSO_ANCHOR.MIDDLE) - return s - -# ================================================================ -# 3. 사업 개요 (간지 + 본문) -# ================================================================ -divider("01", "사업 개요", "PROJECT OVERVIEW", - ["배경 · 목적 · 범위", "전시 생애주기 폐루프", "다중 전시관 SaaS 비전"]) - -s = slide() -content_header(s, "01 · 사업 개요", "배경 · 목적 · 추진 범위", 1) -# 좌: 배경/목적 -card(s, 0.55, 1.5, 6.05, 2.55) -text(s, 0.85, 1.7, 5.5, 0.35, "추진 배경 (As-Is)", size=13, color=BLUE, bold=True) -for i, t in enumerate([ - "전시면적 108,011㎡ 국내 최대 전시장이나 준비 실무는 HWP 서식·이메일 제출에 정체", - "CAD 수작업 배치도 → 홀매니저 육안 검수, 수기 위치표시도 기반 유틸리티 신청", - "참가업체 유틸리티 신청이 행사별 사무국 시스템으로 파편화(공통 플랫폼 부재)", - "시공 결과를 개장일에 처음 확인 — 사전 시각화 수단 없음", -]): - y = 2.1 + i*0.46 - rect(s, 0.9, y+0.07, 0.1, 0.1, fill=MUTED, shape=MSO_SHAPE.OVAL) - text(s, 1.12, y, 5.25, 0.44, t, size=10.5, color=INK, line_spacing=1.0) - -card(s, 6.75, 1.5, 6.03, 2.55) -text(s, 7.05, 1.7, 5.5, 0.35, "추진 목적 (To-Be)", size=13, color=PURPLE, bold=True) -for i, t in enumerate([ - "전시장 운영 전 과정을 AI로 자동 설계·검증하는 베뉴 운영 플랫폼 구축", - "핵심 차별화: 나노바나나(Gemini)로 시공 전 '공사 후 결과 사진' 선(先)시각화", - "설계 → 시각화 → 발주(옥션) → 시공 → 운영 → 분석의 폐루프 완성", - "KINTEX 기준 테넌트 + 코엑스 등 다중 전시관 멀티테넌트 SaaS 확장", -]): - y = 2.1 + i*0.46 - rect(s, 7.1, y+0.07, 0.1, 0.1, fill=PURPLE, shape=MSO_SHAPE.OVAL) - text(s, 7.32, y, 5.25, 0.44, t, size=10.5, color=INK, line_spacing=1.0) - -# 하단: 비전 배너 + 폐루프 -rect(s, 0.55, 4.28, 12.23, 0.62, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.85, 4.28, 11.6, 0.62, - "비전 “신청서를 내는 순간, 시공 후 사진을 먼저 본다”", - size=17, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - -text(s, 0.55, 5.12, 6, 0.3, "전시 생애주기 폐루프", size=12, color=INK, bold=True) -loop = [("판매·기획", BLUE, "M1·M16"), ("AI 설계·시각화", PURPLE, "M2~M5"), - ("공사 옥션", BLUE, "M15"), ("시공", BLUE, "M7·M8"), - ("운영", BLUE, "M10·M14"), ("경영분석", PURPLE, "M16")] -bw = 1.83; gap = 0.19; x0 = 0.55; yb = 5.5 -for i, (lb, cl, mod) in enumerate(loop): - x = x0 + i*(bw+gap) - rect(s, x, yb, bw, 0.78, fill=(PURPLE_LT if cl==PURPLE else BLUE_LT), line=cl, line_w=1.2, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, yb+0.1, bw, 0.34, lb, size=11.5, color=(PURPLE if cl==PURPLE else BLUE_DK), bold=True, align=PP_ALIGN.CENTER) - text(s, x, yb+0.44, bw, 0.28, mod, size=9, color=MUTED, align=PP_ALIGN.CENTER) - if i < len(loop)-1: - rect(s, x+bw+0.01, yb+0.28, gap+0.02, 0.22, fill=None) - text(s, x+bw-0.03, yb+0.16, 0.28, 0.5, "→", size=15, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -# ================================================================ -# 4. 시스템 구성 (간지 + 아키텍처 도식) -# ================================================================ -divider("02", "시스템 구성", "SYSTEM ARCHITECTURE", - ["3-Tier + 나노바나나 사이드카", "역할별 6개 포털 분리", "PostGIS 공간 데이터 모델"]) - -s = slide() -content_header(s, "02 · 시스템 구성", "전체 아키텍처 개요도", 2) - -def band(y, h, fill, line, label, labelcolor): - rect(s, 0.55, y, 12.23, h, fill=fill, line=line, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - rect(s, 0.55, y, 1.55, h, fill=line, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.55, y, 1.55, h, label, size=10.5, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -def node(x, y, w, h, title, sub, fill, tcol, line=None): - rect(s, x, y, w, h, fill=fill, line=(line or LINE), line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y+0.08, w, 0.3, title, size=10, color=tcol, bold=True, align=PP_ALIGN.CENTER) - if sub: - text(s, x, y+0.36, w, 0.24, sub, size=7.8, color=MUTED, align=PP_ALIGN.CENTER) - -# Presentation tier — 포털 -band(1.5, 0.86, BLUE_LT, BLUE, "프론트\n(React)", WHITE) -ports = ["주최자 콘솔","참가업체 포털","업체 포털","운영 대시보드","관리자 백오피스","공개사이트·앱"] -pw = 1.62 -for i,p in enumerate(ports): - node(2.25+i*1.72, 1.66, pw, 0.54, p, "", WHITE, INK, BLUE) - -# API tier -band(2.55, 0.9, WHITE, BLUE_DK, "백엔드\n(Spring)", WHITE) -node(2.25, 2.72, 2.7, 0.56, "REST + WebSocket(STOMP)", "JWT·RBAC·2FA(OTP)", WHITE, INK, BLUE) -node(5.1, 2.72, 2.4, 0.56, "룰 엔진 / 배치·배선 엔진", "요율·규정·PostGIS 연산", WHITE, INK, BLUE) -node(7.65, 2.72, 2.2, 0.56, "AiTextRouter", "Claude 기본·설정형 전환", WHITE, PURPLE, PURPLE) -node(10.0, 2.72, 2.65, 0.56, "RenderJob 큐 발행", "Redis 비동기", WHITE, INK, BLUE) - -# AI / 워커 tier -band(3.62, 0.9, PURPLE_LT, PURPLE, "AI·시각화", WHITE) -node(2.25, 3.79, 4.15, 0.56, "나노바나나 Python 워커 (사이드카)", "google-genai · gemini-3.1-flash-image-preview", PURPLE_LT, PURPLE, PURPLE) -node(6.55, 3.79, 3.0, 0.56, "Claude API (AI 지능)", "설계·검수·예측 · Ollama 폴백", PURPLE_LT, PURPLE, PURPLE) -node(9.72, 3.79, 2.93, 0.56, "WebSocket 완료 푸시", "렌더 결과 실시간 통지", WHITE, INK, PURPLE) - -# Data tier -band(4.69, 0.9, HALL_BG, BLUE_DK, "데이터", WHITE) -node(2.25, 4.86, 3.3, 0.56, "PostgreSQL + PostGIS", "부스 폴리곤·트렌치·배선 LineString", WHITE, INK, BLUE_DK) -node(5.7, 4.86, 2.4, 0.56, "Redis", "작업 큐·세션·캐시", WHITE, INK, BLUE_DK) -node(8.25, 4.86, 2.05, 0.56, "오브젝트 스토리지", "렌더 이미지·문서", WHITE, INK, BLUE_DK) -node(10.45, 4.86, 2.2, 0.56, "감사로그·마스터", "룰셋·요율 버전", WHITE, INK, BLUE_DK) - -# 외부 연동 -text(s, 0.55, 5.86, 12, 0.3, "외부·기존 시스템 연동", size=10.5, color=INK, bold=True) -ext = ["kxwp 작업신고","등록업체 DB 739","행사일정 시스템","PG 결제","Gemini","사이니지/wayfinding"] -for i,e in enumerate(ext): - node(0.55+i*2.06, 6.16, 1.96, 0.5, e, "", WHITE, MUTED, LINE) - -# ================================================================ -# 5. 개발 범위 (모듈 표) -# ================================================================ -divider("03", "개발 범위", "DEVELOPMENT SCOPE", - ["코어 M2~M5 (P0)", "도메인 M1·M6~M18", "공통·시스템관리 §5B"]) - -s = slide() -content_header(s, "03 · 개발 범위", "모듈 맵 M1 ~ M18", 3) - -def module_card(x, y, w, h, code, name, pr, cl): - rect(s, x, y, w, h, fill=CARD, line=LINE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - rect(s, x, y, w, 0.07, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.14, y+0.14, w-0.28, 0.26, [[{"t":code+" ","size":11,"color":cl,"bold":True}, - {"t":pr,"size":8,"color":WHITE,"bold":True}]], ) - # priority chip - rect(s, x+w-0.72, y+0.13, 0.58, 0.24, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+w-0.72, y+0.13, 0.58, 0.24, pr, size=8, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+0.14, y+0.4, w-0.28, 0.5, name, size=9.2, color=INK, line_spacing=0.95) - -# 계열별 컬럼 -groups = [ - ("설계·시각화 코어", PURPLE, [ - ("M2","플로어플랜 스튜디오","P0"), - ("M3","부스 설계 스튜디오","P0"), - ("M4","유틸리티 설계(전기·조명·네트워크)","P0"), - ("M5","나노바나나 시각화","P0"), - ]), - ("판매·발주·정산", BLUE, [ - ("M1","홀 배정·자동 견적","P1"), - ("M6","서류·마일스톤 워크플로","P1"), - ("M7","등록업체 매칭·견적","P1"), - ("M15","공사/장치 옥션(역경매)","P1"), - ("M8·M9","반입/반출 물류 · 정산·결제","P1·P2"), - ]), - ("관람·참가·마케팅", BLUE, [ - ("M10","관람객 등록·배지·리드캡처","P1"), - ("M12","마케팅·공개 홍보 사이트","P1"), - ("M11·M13","비즈매칭 · wayfinding","P2"), - ("M14","현장운영(혼잡·안전·에너지)","P2"), - ]), - ("경영·콘텐츠·관리", PURPLE, [ - ("M16","경영분석 BI(P&L·가동률·예측)","P1"), - ("M17","CMS·마이크로사이트·다국어","P1"), - ("M18","관리자 백오피스·마스터데이터","P1"), - ]), -] -cw = 2.94; gap = 0.16; x0 = 0.55 -for gi,(gname, gcolor, mods) in enumerate(groups): - x = x0 + gi*(cw+gap) - rect(s, x, 1.5, cw, 0.44, fill=gcolor, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 1.5, cw, 0.44, gname, size=11, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - for mi,(code,name,pr) in enumerate(mods): - y = 2.06 + mi*0.9 - module_card(x, y, cw, 0.82, code, name, pr, gcolor) - -# 범례 -text(s, 0.55, 6.62, 12, 0.3, [[ - {"t":"P0 ","size":9.5,"color":PURPLE,"bold":True},{"t":"핵심 차별화·첫 상용 필수 ","size":9.5,"color":MUTED}, - {"t":"P1 ","size":9.5,"color":BLUE,"bold":True},{"t":"운영 효율 핵심 ","size":9.5,"color":MUTED}, - {"t":"P2 ","size":9.5,"color":SLATE,"bold":True},{"t":"확장 │ 총 18개 도메인 모듈 + 공통·시스템관리 레이어(§5B)","size":9.5,"color":MUTED}, -]]) - -# 개발 범위 2 — 공통 레이어 §5B -s = slide() -content_header(s, "03 · 개발 범위", "공통 · 시스템관리 레이어 (§5B · UIWS/WISE 표준 이식)", 3) -text(s, 0.55, 1.45, 12.2, 0.4, - "GUARDiA 표준 프레임워크(UIWS)와 동일 스택 — 시스템관리·공통 업무기능·인증을 재설계 없이 이식하여 전 도메인 모듈의 선행 기반으로 정착", - size=11, color=MUTED) -# 3개 카드 영역 -def scard(x, y, w, h, title, tcolor, items): - card(s, x, y, w, h) - rect(s, x, y, w, 0.5, fill=tcolor, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, w, 0.5, title, size=12.5, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - for i,it in enumerate(items): - yy = y+0.66+i*0.4 - rect(s, x+0.2, yy+0.07, 0.09, 0.09, fill=tcolor, shape=MSO_SHAPE.OVAL) - text(s, x+0.4, yy, w-0.55, 0.38, it, size=9.6, color=INK, line_spacing=0.95) - -scard(0.55, 2.0, 3.95, 4.35, "시스템관리 (M18 통합)", BLUE, [ - "사용자 관리 (6역할 + 등록업체·관람객 계정)", - "역할/권한 RBAC (플랫폼·행사 이중 평가)", - "공통코드 (홀·부스유형·공종14분류·요금)", - "메뉴 관리 (역할별 포털 IA 구동)", - "감사로그 (승인·낙찰·룰셋개정·리드접근)", - "시스템설정 (요율/규정 룰셋 버전)", - "킨텍스 마스터데이터 (홀·요율·규정·업체)", -]) -scard(4.65, 2.0, 3.95, 4.35, "공통 업무 기능", PURPLE, [ - "worklog 업무일지 · schedule 일정", - "message 쪽지 · notice 공지", - "opinion 의견 · search 통합검색", - "meeting 회의록(STT→Jasper PDF)", - "report/stats 업무보고·통계(PDF)", - "notification 알림센터(WebSocket)", - "preference 개인화(즐겨찾기·최근방문)", -]) -scard(8.75, 2.0, 4.03, 4.35, "인증 (JWT + 2FA/OTP)", BLUE_DK, [ - "JWT + RBAC 역할 게이트", - "2차 인증 OTP (TOTP RFC6238)", - "최초 QR 등록 · 재설정 · 관리자 초기화", - "로그인 실패 잠금 + 관리자 해제", - "admin 비번 env 주입 (AES-256-GCM)", - "admin123 하드코딩 시드 금지", - "TB_USER·OTP·TB_AUDIT_LOG 멱등 이식", -]) - -# ================================================================ -# 6. 추진 체계 (조직도) -# ================================================================ -divider("04", "추진 체계", "PROJECT ORGANIZATION", - ["거버넌스 (PM·개발PM·PMO)", "아키텍트 5인 (AA/SA/TA/DA/NA)", "구현·품질·배포 팀"]) - -s = slide() -content_header(s, "04 · 추진 체계", "프로젝트 조직 및 역할", 4) - -def org(x, y, w, h, title, sub, fill, tcol, line=None, big=False): - rect(s, x, y, w, h, fill=fill, line=(line or fill), line_w=1.2, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=big) - text(s, x, y+(0.1 if sub else 0), w, (0.32 if sub else h), title, size=(12 if big else 10.5), - color=tcol, bold=True, align=PP_ALIGN.CENTER, anchor=(MSO_ANCHOR.TOP if sub else MSO_ANCHOR.MIDDLE)) - if sub: - subcol = MUTED if fill == WHITE else WHITE - text(s, x, y+0.36, w, h-0.4, sub, size=8, color=subcol, align=PP_ALIGN.CENTER) - -# 최상위 거버넌스 -org(4.9, 1.5, 3.55, 0.72, "프로젝트 총괄 (PM)", "범위·일정·리스크·게이트(G1·G2)", BLUE, WHITE, big=True) -# 연결선 -rect(s, 6.66, 2.22, 0.02, 0.3, fill=SLATE) -# 2단: 개발PM / PMO -org(2.95, 2.55, 3.35, 0.66, "개발 PM", "WBS·스프린트·번다운·품질게이트", PURPLE, WHITE) -org(7.05, 2.55, 3.35, 0.66, "PMO", "관리표준·형상관리·QA 거버넌스·변경관리", PURPLE, WHITE) -rect(s, 6.66, 2.52, 0.02, 0.16, fill=SLATE) - -# 3단: 아키텍트 그룹 + 구현 그룹 -rect(s, 0.55, 3.5, 12.23, 0.4, fill=BLUE_LT, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.7, 3.5, 6, 0.4, "아키텍트 그룹", size=11, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) -arch = [("AA","앱 아키텍처"),("SA","시스템·NFR"),("TA","기술표준"),("DA","데이터·ERD"),("NA","네트워크·보안")] -for i,(a,d) in enumerate(arch): - org(0.55+i*2.46, 4.0, 2.32, 0.66, a, d, WHITE, BLUE, BLUE) - -rect(s, 0.55, 4.86, 12.23, 0.4, fill=PURPLE_LT, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.7, 4.86, 6, 0.4, "구현 그룹", size=11, color=PURPLE, bold=True, anchor=MSO_ANCHOR.MIDDLE) -impl = [("공통/시스템","common-dev"),("백엔드","backend-dev"),("프론트","frontend-dev"), - ("DB","db-engineer"),("도메인","bidding·visitor\ncms·bi·admin"),("AI·시각화","ai-dev·visualizer")] -for i,(a,d) in enumerate(impl): - org(0.55+i*2.05, 5.36, 1.93, 0.72, a, d, WHITE, PURPLE, PURPLE) - -# 하단: 품질·배포 -org(3.4, 6.2, 3.0, 0.66, "품질 (kintex-qa)", "점진 QA·경계면·보안 불변", GREEN, WHITE) -org(6.95, 6.2, 3.0, 0.66, "배포 (devops-dev)", "CI/CD·systemd·env", RGBColor(0x2B,0x4C,0x7E), WHITE) - -# ================================================================ -# 7. 추진 일정 (간트) -# ================================================================ -divider("05", "추진 일정", "PROJECT SCHEDULE", - ["Phase A~E 마스터 일정", "완료 / 진행 / 예정 구분", "공통 레이어 선행 원칙"]) - -s = slide() -content_header(s, "05 · 추진 일정", "Phase A ~ E 마스터 일정 (WORK_STATUS 반영)", 5) - -# 상태 범례 -for i,(lb,cl) in enumerate([("완료",GREEN),("진행",AMBER),("예정",SLATE)]): - rect(s, 9.8+i*1.0, 1.42, 0.24, 0.24, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 10.08+i*1.0, 1.4, 0.9, 0.28, lb, size=9.5, color=INK, anchor=MSO_ANCHOR.MIDDLE) - -# 간트 좌표계 -gx = 3.55 # 바 시작 x -gw = 9.15 # 바 영역 폭 -units = 10 # 타임 단위 -uw = gw/units -# 타임 눈금 라벨 -for i in range(units+1): - text(s, gx+i*uw-0.4, 1.72, 0.8, 0.24, f"T{i}", size=7.5, color=SLATE, align=PP_ALIGN.CENTER) - rect(s, gx+i*uw, 1.98, 0.008, 4.2, fill=RGBColor(0xF0,0xF1,0xF4)) - -rows = [ - ("Phase A", "아키텍처·거버넌스 (AA/SA/TA/DA/NA)", 0, 1.5, GREEN, "완료"), - ("Phase B", "공통·시스템관리 레이어 (2FA/OTP·공통기능)", 1.2, 2.0, GREEN, "완료"), - ("Phase C", "P0 부스 코어 M2~M5 (배치·설계·배선·시각화)", 2.6, 2.6, AMBER, "진행"), - ("Phase C", " ├ 스캐폴드·인증·매퍼 배선·룰엔진·나노바나나", 2.6, 1.6, GREEN, "완료"), - ("Phase C", " └ 3안 생성·병합·실데이터 전환·모바일", 4.2, 1.4, AMBER, "진행"), - ("Phase D", "P1 도메인 — 옥션 M15·관람객 M10", 4.8, 1.9, SLATE, "예정"), - ("Phase D", "P1 도메인 — CMS/공개사이트 M12·M17·BI M16·관리자 M18", 5.4, 2.2, SLATE, "예정"), - ("Phase D", "P1 배정·서류·매칭·정산 M1·M6·M7·M9", 5.8, 1.8, SLATE, "예정"), - ("Phase E", "P2 확장 (매칭 M11·wayfinding M13·현장운영 M14)", 7.2, 1.6, SLATE, "예정"), - ("Phase E", "빌드·배포 (역할별 번들·CI/CD·멀티테넌시)", 7.6, 2.2, AMBER, "진행"), -] -ry = 2.12 -rh = 0.42 -for i,(ph, name, start, dur, cl, st) in enumerate(rows): - y = ry + i*rh - if i % 2 == 0: - rect(s, 0.55, y-0.02, 12.23, rh-0.02, fill=RGBColor(0xFC,0xFC,0xFD)) - text(s, 0.6, y, 0.85, rh-0.06, ph, size=8.5, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 1.45, y, 2.05, rh-0.06, name, size=8.2, color=INK, anchor=MSO_ANCHOR.MIDDLE) - bx = gx + start*uw - bw = max(dur*uw, 0.3) - rect(s, bx, y+0.06, bw, rh-0.18, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, bx, y+0.04, bw, rh-0.14, st, size=7.5, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -# 현재 시점 마커 -nowx = gx + 4.9*uw -rect(s, nowx, 1.98, 0.02, 4.2, fill=PURPLE) -text(s, nowx-0.5, 6.2, 1.0, 0.24, "현재", size=8, color=PURPLE, bold=True, align=PP_ALIGN.CENTER) - -text(s, 0.55, 6.55, 12.2, 0.3, - "원칙 Phase B(공통 레이어)는 전 도메인 선행 기반 — 인증·시스템관리·공통기능 정착 후 코어/도메인 착수. 각 모듈 완성 직후 점진 QA.", - size=9, color=MUTED) - -# ================================================================ -# 8. 개발 방법론·품질 -# ================================================================ -divider("06", "개발 방법론 · 품질관리", "METHODOLOGY & QUALITY", - ["에이전트 파이프라인", "점진 QA · 보안 불변 게이트", "CI/CD 자동배포"]) - -s = slide() -content_header(s, "06 · 개발 방법론 · 품질관리", "에이전트 파이프라인 · 점진 QA · CI/CD", 6) - -# 파이프라인 -text(s, 0.55, 1.5, 8, 0.3, "① 에이전트 기반 개발 파이프라인", size=12, color=BLUE, bold=True) -pipe = [("기획","planner",PURPLE),("설계","AA/SA/TA/DA/NA",BLUE),("디자인","designer",PURPLE), - ("구현","backend·frontend·db·domain",BLUE),("검증","kintex-qa",GREEN),("배포","devops-dev",BLUE_DK)] -pw2 = 1.9; g2 = 0.14 -for i,(t,d,cl) in enumerate(pipe): - x = 0.55+i*(pw2+g2) - rect(s, x, 1.9, pw2, 0.74, fill=WHITE, line=cl, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 1.98, pw2, 0.3, t, size=11, color=cl, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 2.3, pw2, 0.3, d, size=7.6, color=MUTED, align=PP_ALIGN.CENTER) - if i < len(pipe)-1: - text(s, x+pw2-0.05, 1.9, 0.24, 0.74, "›", size=16, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -# ② 점진 QA -card(s, 0.55, 2.95, 6.05, 2.05) -text(s, 0.8, 3.12, 5.5, 0.3, "② 점진 QA — 모듈 완성 직후 검증", size=12, color=PURPLE, bold=True) -for i,t in enumerate([ - "백엔드 응답 shape ↔ 프론트 훅/컴포넌트 호출 경계면 교차 비교", - "공간 로직(배치·배선·규정검증) 정합 · PostGIS 연산 검증", - "보안 불변: 자격증명·PII·스택트레이스 미노출 강제", - "AI 생성 이미지 워터마크·고지 강제 · 등록업체 응찰 게이트", - "빌드 회귀(compileJava·tsc) · 통과까지 반려", -]): - y=3.5+i*0.29 - rect(s, 0.85, y+0.06, 0.09, 0.09, fill=GREEN, shape=MSO_SHAPE.OVAL) - text(s, 1.05, y, 5.4, 0.28, t, size=9.3, color=INK) - -# ③ CI/CD -card(s, 6.75, 2.95, 6.03, 2.05) -text(s, 7.0, 3.12, 5.5, 0.3, "③ CI/CD 자동배포 파이프라인", size=12, color=BLUE, bold=True) -cicd = ["push","Gitea webhook","deploy_kintex.sh","백엔드 bootJar + 프론트 Vite 빌드","배포 → health check","실패 시 롤백"] -for i,c in enumerate(cicd): - y=3.5+i*0.245 - rect(s, 7.05, y, 0.28, 0.2, fill=BLUE, shape=MSO_SHAPE.OVAL) - text(s, 7.06, y-0.01, 0.28, 0.22, str(i+1), size=8, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, 7.45, y-0.02, 5.2, 0.24, c, size=9.3, color=INK, anchor=MSO_ANCHOR.MIDDLE) - -# 하단 지표 스트립 -metrics = [("18","도메인 모듈"),("6","역할별 포털"),("39+","Flyway 테이블(현재)"),("A~E","5개 Phase"),("3-Tier","+ AI 사이드카")] -mw=2.34 -for i,(v,l) in enumerate(metrics): - x=0.55+i*(mw+0.09) - rect(s, x, 5.2, mw, 0.95, fill=BLUE if i%2==0 else PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 5.34, mw, 0.5, v, size=26, color=WHITE, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 5.86, mw, 0.26, l, size=9.5, color=WHITE, align=PP_ALIGN.CENTER) -text(s, 0.55, 6.5, 12.2, 0.3, - "AI 지능 = Claude 기본 + 설정형 전환(AiTextRouter/AiConfig) · Ollama 폴백 │ 이미지 = 나노바나나(Gemini) 라이브", - size=9, color=MUTED) - -# ================================================================ -# 9. 보안·NFR -# ================================================================ -divider("07", "보안 · 비기능요건", "SECURITY & NFR", - ["외부 API 통제 (예외 명시)", "자격증명 보호 · AI 워터마크", "접근성·다국어·테마"]) - -s = slide() -content_header(s, "07 · 보안 · 비기능요건(NFR)", "보안 불변 원칙 및 품질 속성", 7) - -# 보안 불변 (좌) -card(s, 0.55, 1.5, 6.05, 3.05) -rect(s, 0.55, 1.5, 6.05, 0.5, fill=BLUE_DK, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.55, 1.5, 6.05, 0.5, "보안 불변 (Security Invariants)", size=13, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -sec = [ - ("외부 API 통제","온프레미스 원칙 · 예외 = Claude(승인)·Gemini(G1). 키는 서버 env 로드, 코드·커밋·로그 미기재"), - ("자격증명 보호","IP·SSH계정·비밀번호를 API 응답·에러메시지에 미노출"), - ("암호화 저장","민감 자격증명 AES-256-GCM · admin 비번 env 주입(admin123 금지)"), - ("AI 이미지 고지","나노바나나 생성 전 이미지 워터마크·실물 상이 고지 강제"), - ("에러 응답","스택트레이스 미노출 — 요약 메시지만 전달"), -] -for i,(h,d) in enumerate(sec): - y=2.15+i*0.47 - rect(s, 0.8, y+0.04, 0.11, 0.11, fill=BLUE, shape=MSO_SHAPE.OVAL) - text(s, 1.02, y, 5.45, 0.44, [[{"t":h+" — ","size":9.6,"color":INK,"bold":True}, - {"t":d,"size":9,"color":MUTED}]], line_spacing=0.95) - -# NFR (우) -card(s, 6.75, 1.5, 6.03, 3.05) -rect(s, 6.75, 1.5, 6.03, 0.5, fill=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 6.75, 1.5, 6.03, 0.5, "비기능요건 (NFR)", size=13, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -nfr = [ - ("웹 접근성","WCAG 준수 · 키보드·스크린리더 대응"), - ("다국어(i18n)","한 · 영 · 중 · 일 (공개사이트·CMS 우선)"), - ("라이트/다크 테마","전 화면 테마 토큰 · 반응형 풀스크린"), - ("반응형 채널 분리","데스크톱=설계/에디터 · 모바일=현장/조회/승인"), - ("성능·확장","Redis 비동기 큐 · 렌더 워커 사이드카 분리"), - ("멀티테넌시 격리","tenant_id 전파 · 전시관 단위 데이터·권한 격리"), -] -for i,(h,d) in enumerate(nfr): - y=2.15+i*0.4 - rect(s, 7.0, y+0.04, 0.11, 0.11, fill=PURPLE, shape=MSO_SHAPE.OVAL) - text(s, 7.22, y, 5.4, 0.38, [[{"t":h+" — ","size":9.6,"color":INK,"bold":True}, - {"t":d,"size":9,"color":MUTED}]], line_spacing=0.95) - -# 하단: 3중 권한 구조 -rect(s, 0.55, 4.78, 12.23, 0.42, fill=INK, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.55, 4.78, 12.23, 0.42, "3중 권한 구조 테넌트(전시관) 격리 ⊃ 행사(Event) 워크스페이스 ⊃ 행사 RBAC", - size=11.5, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -# 인증 요약 3칩 -for i,(t,d,cl) in enumerate([("JWT + RBAC","6역할 게이트",BLUE),("2FA / OTP","TOTP RFC6238",PURPLE), - ("감사로그","전수 기록·개인정보 접근",BLUE_DK)]): - x=0.55+i*4.11 - rect(s, x, 5.4, 3.97, 0.72, fill=WHITE, line=cl, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.2, 5.5, 3.7, 0.3, t, size=12, color=cl, bold=True) - text(s, x+0.2, 5.82, 3.7, 0.26, d, size=9.5, color=MUTED) - -# ================================================================ -# 10. 리스크 및 대응 -# ================================================================ -divider("08", "리스크 및 대응", "RISK MANAGEMENT", - ["기술·데이터·연동 리스크", "영향도·발생가능성 평가", "완화 방안"]) - -s = slide() -content_header(s, "08 · 리스크 및 대응", "주요 리스크 및 대응 방안", 8) - -# 표 헤더 -hx=[0.55, 1.3, 5.9, 8.0] # 구분/리스크/영향/대응 시작 -cols = [(0.55,0.75,"구분"),(1.3,4.6,"리스크"),(5.9,2.1,"영향"),(8.0,4.78,"대응 방안")] -rect(s, 0.55, 1.5, 12.23, 0.44, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -for x,w,h in cols: - text(s, x+0.12, 1.5, w-0.15, 0.44, h, size=10.5, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - -risks = [ - ("기술","나노바나나 생성 이미지가 실제 시공과 상이","높음",AMBER,"전 이미지 워터마크·고지 강제, 참조이미지 구조보존 파이프라인, 검수 단계"), - ("데이터","홀 실측 CAD·트렌치 좌표 미확보","높음",AMBER,"확보 자산(평면도 15장+CAD) 활용, 미확보 시 공개규격 근사도면으로 Phase 진행"), - ("연동","kxwp 내부 API 미공개(폐쇄형)","중간",SLATE,"초기 제출용 파일 생성+업로드 안내 릴레이, 연동은 킨텍스 협의 후"), - ("보안","외부 API 호출·자격증명 노출","높음",AMBER,"Claude·Gemini만 예외 승인, env 로드·미커밋, 응답 미노출, 감사로그"), - ("규정","소방·구조 최종 유권해석은 시스템 밖","중간",SLATE,"AI는 사전 필터(위반 플래깅)까지, 최종 판정은 관할기관·구조기술사"), - ("일정","공통 레이어 지연 시 도메인 착수 지연","중간",SLATE,"Phase B 선행 완료(현재 완료), 모듈 병렬화·점진 QA로 흡수"), - ("시장","옥션 업체 참여율·응찰 품질 불확실","중간",SLATE,"등록업체 DB 검증 게이트, AI 자료 기반 동일사양 비교로 참여 유인"), -] -ry=1.98; rh=0.56 -for i,(g,r,imp,impc,resp) in enumerate(risks): - y=ry+i*rh - if i%2==0: - rect(s, 0.55, y, 12.23, rh, fill=RGBColor(0xFC,0xFC,0xFD)) - rect(s, 0.55, y, 12.23, 0.01, fill=LINE) - text(s, 0.67, y, 0.7, rh, g, size=9.5, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 1.42, y, 4.4, rh, r, size=9.5, color=INK, anchor=MSO_ANCHOR.MIDDLE) - rect(s, 5.9, y+0.16, 0.95, 0.24, fill=impc, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 5.9, y+0.15, 0.95, 0.26, imp, size=8.5, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, 8.0, y, 4.7, rh, resp, size=8.8, color=MUTED, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.92) - -# ================================================================ -# 11. 기대 효과 -# ================================================================ -divider("09", "기대 효과", "EXPECTED BENEFITS", - ["정량 효과 (리드타임)", "정성 효과 (의사결정·투명성)", "확장 로드맵"]) - -s = slide() -content_header(s, "09 · 기대 효과", "정량 · 정성 기대 효과", 9) - -text(s, 0.55, 1.48, 8, 0.3, "정량 효과 — 리드타임 단축", size=12.5, color=BLUE, bold=True) -quant = [ - ("홀 배정 견적","수일","즉시"), - ("배치도 초안","수일~수주","수 분"), - ("규정 검수","육안 검토","자동 플래깅"), - ("유틸리티 신청","수기 작도","클릭 + 자동견적"), - ("시공 결과 예측","불가(외주)","표준 샷 자동생성"), -] -qw=2.4 -for i,(k,a,b) in enumerate(quant): - x=0.55+i*(qw+0.06) - card(s, x, 1.85, qw, 1.85) - text(s, x, 2.0, qw, 0.3, k, size=10, color=INK, bold=True, align=PP_ALIGN.CENTER) - rect(s, x+0.3, 2.34, qw-0.6, 0.02, fill=LINE) - text(s, x, 2.42, qw, 0.26, "As-Is", size=7.5, color=SLATE, align=PP_ALIGN.CENTER) - text(s, x, 2.62, qw, 0.3, a, size=10.5, color=MUTED, align=PP_ALIGN.CENTER) - text(s, x, 2.98, qw, 0.24, "↓", size=12, color=PURPLE, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 3.2, qw, 0.3, b, size=11, color=BLUE, bold=True, align=PP_ALIGN.CENTER) - -text(s, 0.55, 3.95, 8, 0.3, "정성 효과", size=12.5, color=PURPLE, bold=True) -qual = [ - ("의사결정 가속","도면을 읽지 못해도 사진 수준 시각화로 주최자·참가업체 의사결정"), - ("영업 자료화","완공 예상 이미지로 참가업체 유치·컨펌 커뮤니케이션 비용 절감"), - ("가격 투명화","옥션 역경매로 동일 사양 위 공정 비교 → 시공 발주 투명·최적화"), - ("데이터 일원화","공간 데이터(PostGIS) 단일 원천에서 검증·시각화·정산·BI·wayfinding"), -] -for i,(h,d) in enumerate(qual): - x=0.55+(i%2)*6.15 - y=4.32+(i//2)*0.66 - card(s, x, y, 6.05, 0.58) - rect(s, x, y, 0.08, 0.58, fill=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.24, y+0.08, 5.7, 0.28, h, size=10.5, color=INK, bold=True) - text(s, x+0.24, y+0.34, 5.7, 0.22, d, size=8.6, color=MUTED) - -# 확장 로드맵 -rect(s, 0.55, 5.78, 12.23, 0.44, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.7, 5.78, 4, 0.44, "확장 로드맵", size=11, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) -road = ["KINTEX 기준 운영","코엑스 등 다중 전시관 온보딩","전시테크 SaaS 사업화","kxwp·PG·사이니지 정식 연동"] -for i,r in enumerate(road): - text(s, 2.6+i*2.55, 5.78, 2.5, 0.44, "› "+r, size=9.3, color=WHITE, anchor=MSO_ANCHOR.MIDDLE) - -text(s, 0.55, 6.4, 12.2, 0.5, - "종합 “신청서를 내는 순간 시공 후 사진을 먼저 본다” — 설계·시각화·발주·운영·분석 폐루프를 하나의 데이터·계정 체계로 자동화하여 킨텍스를 전시 운영 표준 플랫폼으로 전환", - size=10, color=INK, bold=True, line_spacing=1.0) - -# ================================================================ -# 저장 -# ================================================================ -OUT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "개발계획서.pptx") -prs.save(OUT) -print("SAVED:", OUT) -print("SLIDES:", len(prs.slides._sldIdLst)) diff --git a/docs/deliverables/_gen/gen_docx.py b/docs/deliverables/_gen/gen_docx.py deleted file mode 100644 index 36e4842..0000000 --- a/docs/deliverables/_gen/gen_docx.py +++ /dev/null @@ -1,337 +0,0 @@ -# -*- coding: utf-8 -*- -""" -사업수행계획서(.docx) — 행안부 정보화사업 산출물 표준 목차 준용. -데이터 소스: sources.py(PLANNING·IMPLEMENTATION_BACKLOG·FEATURE_BACKLOG_100 파싱). -소스 문서 갱신 시 본 스크립트 재실행만으로 결정적 재생성. -보안: 서버 IP·비밀번호·SSH·시크릿 미기재. -""" -import os -from docx import Document -from docx.shared import Pt -from docx.enum.text import WD_ALIGN_PARAGRAPH -import sources as S -from _docxutil import setup, h, para, bullets, table, NAVY, ACCENT, GREY - -OUT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "사업수행계획서.docx")) -doc = Document(); setup(doc) -M = S.META -CA = WD_ALIGN_PARAGRAPH.CENTER - -# ---------- 표지 ---------- -for _ in range(4): doc.add_paragraph() -para(doc, f"{M['project_en']} — 사업수행계획서", size=13, color=ACCENT, align=CA, space_after=4) -p = doc.add_paragraph(); p.alignment = CA -r = p.add_run(M["title"]); r.bold = True; r.font.size = Pt(24); r.font.color.rgb = NAVY -doc.add_paragraph() -para(doc, M["std"], size=11, color=GREY, align=CA) -for _ in range(6): doc.add_paragraph() -table(doc, ["구분","내용"], - [["문서명", M["title"]],["문서 버전", M["version"]],["작성일", M["date"]], - ["작성 주관", M["owner"]],["발주처(수요기관)", M["client"]], - ["사업 유형","정보시스템 구축(SI) — AI 기반 전시 운영 자동화 플랫폼"], - ["보안 등급","대외비 — 자격증명·내부망 정보 미기재"]], - widths=[4.5, 11.5], first_col_bold=True) -doc.add_page_break() - -# ---------- 문서정보 / 개정이력 / 목차 ---------- -h(doc,1,"문서 정보 및 개정 이력") -h(doc,2,"문서 정보") -table(doc, ["항목","내용"], - [["문서 제목", M["title"]],["산출 단계","사업 착수(계획) 단계 표준 산출물"], - ["표준 근거", M["std"]], - ["작성 근거 문서","PLANNING v3.0 · IMPLEMENTATION_BACKLOG · FEATURE_BACKLOG_100 · GUARDIA_ALIGNMENT · SECURITY · architecture/*"], - ["문서 관리","형상관리(Git) 하 버전 통제 — docs/deliverables/ 관리, _gen 재실행으로 재생성"]], - widths=[4.5, 11.5], first_col_bold=True) -h(doc,2,"개정 이력") -table(doc, ["버전","일자","작성/개정자","주요 변경 내용"], - [[M["version"], M["date"], "사업 PMO", "사업수행계획서 최초 작성(표준 목차 14장 체계)"]], - widths=[2.0, 3.0, 3.5, 7.5]) -para(doc, "※ 본 계획서는 사업 진척에 따라 형상관리 절차(14장)에 의거 개정하며, 모든 변경은 개정 이력에 기록한다.", - size=9, italic=True, color=GREY) -h(doc,2,"목차") -for t_ in ["1. 사업 개요","2. 사업 범위","3. 추진 전략 및 개발 방법론","4. 추진 체계·조직 및 역할(R&R)", - "5. WBS 및 마스터 일정","6. 투입 인력 및 투입 계획(M/M)","7. 품질보증 계획","8. 보안 및 개인정보보호 계획", - "9. 시험(테스트) 계획 개요","10. 이관·교육·유지보수 계획","11. 위험(리스크) 관리","12. 의사소통·보고 체계", - "13. 산출물 목록","14. 형상관리"]: - pp = doc.add_paragraph(t_); pp.paragraph_format.space_after = Pt(2); pp.runs[0].font.size = Pt(10.5) -doc.add_page_break() - -# ---------- 1. 사업 개요 ---------- -h(doc,1,"1. 사업 개요") -h(doc,2,"1.1 추진 배경") -para(doc,"킨텍스는 총 전시면적 108,011㎡(국내 최대, 제3전시장 완공 시 약 178,000㎡)를 운영하지만, 전시 준비 실무는 여전히 " - "표준 서식 다운로드 후 이메일·방문 제출, CAD 수작업 배치도의 육안 검수, 수기 위치표시도 기반 유틸리티 신청에 머물러 있다. " - "참가업체 유틸리티 신청은 킨텍스 공통 플랫폼 없이 전시회별 주최자 사무국 시스템으로 파편화되어 있다.") -para(doc,"공간 데이터(배치도·도면·위치표시도)가 전부 이미지·수기로 오가며 구조화되지 않아 자동 검증·시각화·정산의 원천 데이터가 부재하고, " - "D-150/D-30/D-25/D-7 마일스톤이 시스템화되어 있지 않으며, 시공 결과를 사전에 확인할 수단이 없다는 구조적 공백이 확인되었다.") -h(doc,2,"1.2 사업 목적") -bullets(doc,[ - "전시장 운영 전 과정(홀 배정→부스 배치→장치공사 설계→전기/조명→네트워크/유틸리티 배선→반입/반출)을 AI로 자동 설계·검증한다.", - "AI 설계 결과를 나노바나나(이미지 생성)로 '시공 후 결과 사진'처럼 시공 전에 미리 생성하여 사진 수준 시각화를 제공한다.", - "기획·판매·시공·운영·관람객·사후분석 전 주기를 하나의 공간 데이터·계정 체계로 통합한 베뉴 운영 플랫폼을 구축한다.", - "AI 설계·시각화 산출물을 공사/장치 옥션(역경매)의 응찰 근거로 소비하여 설계→시각화→발주→시공 폐루프를 완성한다.", -]) -h(doc,2,"1.3 비전 및 정체성") -para(doc,'비전 슬로건: "신청서를 내는 순간, 시공 후 사진을 먼저 본다."', bold=True, color=ACCENT) -para(doc,"본 시스템은 킨텍스를 기준 테넌트(#1)로 하되 코엑스 등 여타 전시관을 온보딩하는 다중 전시관 멀티테넌트 SaaS로 확장 가능한 구조로 설계한다. " - "기능(모듈)은 전부 보존하고 그 위에 전시관 단위 데이터·권한·컨텍스트 격리 레이어를 얹는다.") -h(doc,2,"1.4 정량 목표(As-Is → To-Be)") -table(doc, ["목표 지표","현재(As-Is)","목표(To-Be)"], - [["홀 배정 견적 회신","문의→협의→견적 수일","조건 입력 후 즉시 자동 가견적"], - ["부스 배치도 초안","CAD 수작업 수일~수주","조건 입력 후 수 분 내 복수 안 생성"], - ["도면 규정 검수","홀매니저 육안 검토","규정 위반 자동 플래깅 후 사람 확정"], - ["유틸리티 위치표시도","참가업체 수기 작도","부스 좌표 클릭 → 자동 배선안·자동 견적"], - ["시공 결과 예측","불가(외주 조감도)","표준 샷 세트 자동 생성(정면/야간/통로)"]], - widths=[4.5, 5.5, 6.0]) -h(doc,2,"1.5 기대 효과") -bullets(doc,[ - "참가업체·주최자의 의사결정 가속 및 참가업체 유치 영업 자료 확보(시공 예상 이미지 기반).", - "규정 위반 사전 검출로 도면 반려 재작업 감소 및 홀매니저 검수 시간 단축.", - "'현장 추가신청 불가' 유틸리티의 신청 누락 방지 — 참가업체 최대 리스크 제거.", - "역경매 옥션을 통한 시공 발주 가격 최적화·투명화(동일 사양 위 공정 비교).", - "공간 데이터 표준화로 자동 검증·시각화·정산·wayfinding·경영분석의 단일 원천 확보.", -]) - -# ---------- 2. 사업 범위 (모듈: sources.modules) ---------- -h(doc,1,"2. 사업 범위") -h(doc,2,"2.1 대상 시스템 및 모듈") -para(doc,f"본 사업은 설계·시각화 코어(P0), 판매·발주·정산, 관람·참가·마케팅, 경영·콘텐츠·관리 계열의 총 {len(S.modules())}개 모듈과 " - "공통·시스템관리 레이어(UIWS/WISE 이식)로 구성된다. (모듈 목록은 PLANNING 총괄표에서 자동 추출)") -mod_rows = [[mid, name, pri] for (mid, name, pri) in S.modules()] -table(doc, ["모듈","명칭","우선순위"], mod_rows, widths=[2.2, 11.3, 2.5]) -h(doc,2,"2.2 우선순위 정의") -bullets(doc,[ - "P0 — 핵심 차별화 + 첫 상용 행사 적용 필수(부스 시공 코어 M2~M5).", - "P1 — 운영 효율 핵심(판매·발주·정산·관람·마케팅·경영분석·관리자·공통 레이어).", - "P2 — 확장(물류·비즈매칭·wayfinding·현장운영).", -]) -h(doc,2,"2.3 범위 제외(Non-Goals)") -bullets(doc,[ - "킨텍스 임대계약의 법적 전자체결은 후속 단계 검토 — 초기에는 견적·서류 준비까지.", - "구조계산서의 구조 안전성 최종 판정은 AI가 대신하지 않는다(규정 체크·누락 검출까지, 최종 판정은 구조기술사/킨텍스).", - "AI 생성 시각화는 참고용이며 계약·인허가 서류로 사용 불가(워터마크·고지 강제).", - "신규 전시관 온보딩은 마스터데이터 입력 수준까지 — 고유 규정 파서 자동 생성 제외.", - "정밀 실내 측위(UWB/BLE 비콘)는 시설 인프라 협의 전제 — 초기에는 존-레벨 폴백.", -]) - -# ---------- 3. 추진 전략·방법론 ---------- -h(doc,1,"3. 추진 전략 및 개발 방법론") -h(doc,2,"3.1 기본 추진 전략") -bullets(doc,[ - "공통 레이어 선행: 인증(JWT+2FA)·시스템관리·공통 업무기능을 전 도메인 모듈보다 선행 구축.", - "코어 우선: 핵심 차별화 P0 부스 시공 코어(M2~M5)를 우선 확보하여 첫 상용 행사에 적용.", - "단일 공간 데이터 모델: M2→M3→M4→M5가 하나의 PostGIS 지오메트리를 공유.", - "폐루프 완성: 코어 산출물을 M15 옥션 응찰 근거로 소비하여 설계→시각화→발주→시공 종결.", - "표준 프레임워크 준수: GUARDiA 표준(UIMS/WISE) 스택·인증·공통모듈·보안·배포를 상속하고 도메인만 확장.", -]) -h(doc,2,"3.2 개발 방법론") -para(doc,"반복·점진(Iterative & Incremental) 방식. Phase A~E 단계 위에서 각 모듈은 설계→구현→모듈 완성 직후 점진 QA→통합의 짧은 반복을 수행하며, " - "QA가 경계면·회귀·보안 불변을 검증하고 통과할 때까지 반려한다.") -ph = S.phases() -table(doc, ["단계","성격","핵심 활동"], - [[f"Phase {p[0]}", p[1], " · ".join(t[1] for t in p[2][:3]) + (" 등" if len(p[2])>3 else "")] for p in ph], - widths=[2.4, 4.6, 9.0]) -h(doc,2,"3.3 기술 아키텍처(확정 스택)") -table(doc, ["레이어","기술","비고"], - [["프론트엔드","React 18/19 + Vite + TypeScript","역할별 포털 번들 분리(데스크톱=설계, 모바일=현장·조회)"], - ["백엔드","Spring Boot 3.x(Java 17) + MyBatis","REST + WebSocket(STOMP), 룰·배치/배선 엔진"], - ["데이터베이스","PostgreSQL + PostGIS","부스 폴리곤·트렌치 포인트·배선 LineString 공간 데이터"], - ["비동기","Redis 작업 큐","RenderJob·서류·알림 비동기 처리"], - ["이미지 생성","나노바나나 Python 워커(사이드카)","백엔드는 큐 발행만, 생성 키는 워커 env 전용"], - ["AI(텍스트)","Claude 기본 + 설정형 전환(AiTextRouter)","실패 시 온프레미스 모델 자동 폴백"], - ["형상/배포","Git(Gitea)+CI/CD Webhook+systemd/nginx","무중단·헬스게이트·롤백(Fail-Safe)"]], - widths=[2.8, 6.2, 7.0]) -para(doc, f"개발 도메인은 서비스명 수준으로 {M['dev_domain']}을 사용하며, 운영 도메인은 후속 확정한다.", size=9, italic=True, color=GREY) - -# ---------- 4. 추진 체계·조직·역할 ---------- -h(doc,1,"4. 추진 체계·조직 및 역할(R&R)") -h(doc,2,"4.1 추진 체계") -para(doc,"발주처(킨텍스)—사업 총괄(PMO)—아키텍트 그룹—개발 그룹(공통·코어·도메인)—품질(QA)·배포(DevOps)의 계층으로 구성한다. " - "AI 에이전트 팀(kintex-impl-orchestrator)이 각 트랙 구현을 조율하며, 기획(planner)·디자인(designer)·검증(reviewer)을 경유한다.") -h(doc,2,"4.2 조직 및 역할(R&R)") -table(doc, ["그룹","역할","주요 책임(R&R)"], - [["거버넌스","PMO / 사업 PM","일정·범위·리스크·이해관계자 관리, 발주처 보고, 형상·품질 총괄"], - ["아키텍트","AA(애플리케이션)","모듈 경계·레이어·API 표준·패키지·의존성 규칙"], - ["아키텍트","SA(시스템)","확장성·HA·성능·배포 토폴로지·비기능요건(NFR)"], - ["아키텍트","TA(기술)","기술 표준·빌드/배포·개발 표준·관측성·AiTextRouter"], - ["아키텍트","DA(데이터)","전사 ERD·공간데이터·마스터·공통코드·BI 데이터마트"], - ["아키텍트","NA(네트워크)","DMZ/내부망 분리·방화벽·부하분산·공개/백오피스 망 분리"], - ["공통","공통/시스템관리 개발","인증(2FA)·시스템관리·공통 업무기능(UIWS 이식)"], - ["코어","백엔드 개발","M1~M9 REST/WebSocket·룰 엔진·배선 엔진(PostGIS)"], - ["코어","프론트엔드 개발","역할별 포털 화면·플로어플랜 캔버스·Before/After 뷰"], - ["코어","DB 엔지니어","PostGIS 스키마·마이그레이션·매퍼·마스터 시드"], - ["AI·시각화","AI 개발","배치 자동생성·규정검증 보조·매칭·서류검수·챗봇(Claude)"], - ["AI·시각화","시각화(visualizer)","나노바나나 프롬프트·워커·워터마크 강제"], - ["도메인","옥션/관람객/CMS/BI/관리자 개발","M15/M10/M12·M17/M16/M18 도메인 구현"], - ["품질·배포","QA","경계면·회귀·보안 불변 점진 검증(통과까지 반려)"], - ["품질·배포","DevOps","빌드·CI/CD·systemd/nginx·환경변수 배선"]], - widths=[2.2, 4.0, 9.8]) - -# ---------- 5. WBS·마스터 일정 (sources.phases + SCHEDULE) ---------- -h(doc,1,"5. WBS 및 마스터 일정") -h(doc,2,"5.1 작업 분해 구조(WBS) 개요") -para(doc,"WBS는 Phase A~E의 5개 단계와 하위 작업 패키지로 구성한다(IMPLEMENTATION_BACKLOG에서 자동 추출). " - "상세 작업 분해·담당·의존·기간은 별첨 [WBS_및_일정.xlsx]의 'WBS' 시트에 관리한다.") -table(doc, ["Phase","작업 패키지(대표)","완료 기준(대표)"], - [[f"{p[0]}. {p[1]}", " / ".join(t[1] for t in p[2][:3]), p[2][0][3] if p[2] else ""] for p in ph], - widths=[3.6, 8.0, 4.4]) -h(doc,2,"5.2 마스터 일정 및 마일스톤") -para(doc,"총 사업 기간은 약 28주(7개월) 기준이며, 착수 시점 확정 후 절대 일정을 확정한다(아래는 착수일 기준 상대 주차).") -table(doc, ["Phase","시작","종료","주요 마일스톤"], - [[f"{r_[0]}. {r_[1]}", r_[2], r_[3], r_[4]] for r_ in S.SCHEDULE], - widths=[4.2, 1.8, 1.8, 8.2]) -para(doc,"※ Phase B는 Phase A와 일부 병행 착수하며, Phase B(공통 레이어) 완료가 Phase C/D 도메인 착수의 선행 전제이다.", - size=9, italic=True, color=GREY) - -# ---------- 6. 투입 인력 (sources.STAFFING) ---------- -h(doc,1,"6. 투입 인력 및 투입 계획(M/M)") -h(doc,2,"6.1 투입 인력 계획") -para(doc,"역할별 투입 인력과 단계별 투입 공수(M/M)는 아래와 같다. 상세는 별첨 [WBS_및_일정.xlsx]의 '투입인력계획' 시트에 관리한다.") -table(doc, ["역할","인원(명)","등급","주 투입 Phase","투입 공수(M/M)"], - [list(x) for x in S.STAFFING], widths=[5.5, 2.0, 2.0, 2.8, 3.7]) -total_mm = sum(float(x[4]) for x in S.STAFFING) -total_head = sum(int(x[1]) for x in S.STAFFING) -para(doc, f"합계(계획): 인원 약 {total_head}명(중복 참여 포함), 총 투입 공수 약 {total_mm:.1f} M/M(추정치, 착수 시 상세 산정).", - bold=True, size=10, color=NAVY) -h(doc,2,"6.2 투입 원칙") -bullets(doc,[ - "Phase B(공통 레이어)에 개발 인력을 선집중하여 전 도메인 기반을 조기 확보.", - "아키텍트는 Phase A에 집중 투입 후 각 Phase 착수 시 표준 준수를 검수.", - "QA는 전 Phase 상시 배치하여 모듈 완성 직후 점진 검증을 수행.", -]) - -# ---------- 7. 품질보증 ---------- -h(doc,1,"7. 품질보증 계획") -h(doc,2,"7.1 품질 목표 및 기준") -bullets(doc,[ - "기능 품질: 모듈별 완료 기준(WBS) 충족, 경계면(API 응답 shape↔프론트 호출) 불일치 0.", - "코드 품질: 표준 프레임워크·아키텍처 표준 준수, 정적 분석·코드 리뷰 통과.", - "회귀 품질: 스모크/회귀 테스트로 기존 기능 무회귀 보장.", - "보안 품질: 보안 불변 규칙(8장) 위반 0 — 위반 시 반려 사유.", -]) -h(doc,2,"7.2 품질보증 활동") -table(doc, ["활동","시점","담당","산출"], - [["아키텍처 정합 검증","Phase A 종료","reviewer","불일치 티켓·시정 확인"], - ["점진 QA(모듈 완성 직후)","각 모듈 완성","QA","경계면·회귀·보안 검증 결과"], - ["코드 리뷰","커밋/PR","개발 리드","리뷰 코멘트·조치"], - ["스모크·회귀 테스트","배포 전","QA/DevOps","테스트 리포트"], - ["보안 체크리스트 점검","배포·리뷰 전","QA","체크리스트 결과(8장)"]], - widths=[4.5, 3.0, 3.0, 5.5]) -h(doc,2,"7.3 품질 게이트") -para(doc,"각 Phase 종료 및 배포 직전에 품질 게이트를 두어 완료 기준·경계면 정합·보안 체크리스트·회귀 통과를 확인하고 통과 시에만 다음 단계로 진행한다.") - -# ---------- 8. 보안·개인정보 ---------- -h(doc,1,"8. 보안 및 개인정보보호 계획") -h(doc,2,"8.1 보안 불변 원칙") -table(doc, ["구분","원칙"], - [["외부 API 정책","온프레미스 모델 우선. 텍스트 AI는 승인된 단일 예외 경로만, 이미지 생성은 킨텍스 고유 승인 예외(워커 전용). 그 외 외부 API 금지."], - ["자격증명 보호","내부 IP·SSH 계정·비밀번호·해시·API 키·워커 토큰을 API 응답·로그·에러·커밋에 절대 노출 금지."], - ["암호화 저장","admin 비밀번호는 env(AES-256-GCM 암호문)+별도 키파일로 기동 시 재시드(하드코딩 시드 금지). 저장 자격증명은 암호화 컬럼에만."], - ["인증·권한","JWT(HS256)+2차 인증(OTP/EMAIL)+로그인 실패 잠금. 행사 단위 RBAC. 미등록 장치업체 초대·응찰 차단."], - ["AI 이미지 워터마크","시각화 결과는 항상 워터마크·고지 포함(참고용, 계약·인허가 서류 사용 불가)."], - ["오류 응답","스택트레이스·relation/컬럼명·내부 경로 미노출. 코드+요약 메시지만 반환."], - ["감사 추적","관리자·인증·권한·낙찰·설계변경·개인정보 접근 등 민감 액션 감사 로그 기록(비밀값 미기재)."]], - widths=[3.5, 12.5]) -h(doc,2,"8.2 개인정보보호") -bullets(doc,[ - "관람객 배지 QR·리드캡처 데이터는 수집·이용 동의 및 보존 기간 정책을 필수 적용.", - "리드(개인정보) 접근은 감사 로그로 전수 기록, 표시 필드는 비민감 항목으로 최소화.", - "EDM 발송은 정보통신망법·수신 동의 규정을 준수.", - "개인정보는 최소 수집·목적 제한·접근 통제(RBAC) 원칙을 적용.", -]) -h(doc,2,"8.3 보안 체크리스트(배포·리뷰 전)") -bullets(doc,[ - "응답/로그/커밋에 IP·SSH·비번·해시·API 키·워커 토큰 노출 0(grep).", - "AI 키(텍스트/이미지)는 env only — 코드·DB·커밋 미포함.", - "admin 비번 하드코딩 시드 없음(env 재시드).", - "시각화 이미지 응답에 워터마크 필드 포함.", - "DB/서버 오류 → 요약 메시지(스택·relation 명 미노출).", - "관리자 게이트·행사 RBAC·미등록업체 차단 동작.", -]) - -# ---------- 9. 시험 계획 ---------- -h(doc,1,"9. 시험(테스트) 계획 개요") -h(doc,2,"9.1 시험 레벨") -table(doc, ["레벨","대상","방식"], - [["단위 시험","서비스·매퍼·룰 엔진·배선 엔진","개발자 단위 테스트, 공간 연산(PostGIS) 정합 검증"], - ["통합 시험","모듈 간 경계면(API↔프론트, 큐↔워커)","API 응답 shape 대조, WebSocket/큐 왕복"], - ["시스템 시험","역할별 시나리오(주최자/참가/업체/홀매니저/관람객)","E2E 시나리오·규정 검증·시각화 워터마크 확인"], - ["회귀·스모크","전 기능","배포 전 스모크(헬스+라우터 등록+로그인 프로브)·회귀"], - ["보안 시험","보안 불변 규칙","자격증명 미노출·RBAC 우회·스택트레이스 차단 점검"]], - widths=[2.6, 6.4, 7.0]) -h(doc,2,"9.2 시험 원칙") -bullets(doc,[ - "각 모듈 완성 직후 QA가 점진 검증하고, 통과할 때까지 반려.", - "공간 로직(배치·배선·규정 검증)과 시각화 워터마크 강제는 필수 검증 항목.", - "시험 케이스·결과는 별첨 [WBS_및_일정.xlsx]의 '시험케이스' 시트와 형상관리 하 리포트로 관리.", -]) - -# ---------- 10. 이관·교육·유지보수 ---------- -h(doc,1,"10. 이관·교육 및 유지보수 계획") -h(doc,2,"10.1 이관 계획") -bullets(doc,[ - "빌드 산출물(프론트 번들·백엔드 실행 파일·워커 서비스)과 마스터데이터·형상 이력을 발주처 운영 환경으로 이관.", - "이관 시 무중단·헬스게이트·롤백(Fail-Safe) 배포 절차를 적용.", - "운영 도메인·운영 환경변수 배선은 발주처 승인 하에 수행.", -]) -h(doc,2,"10.2 교육 계획") -table(doc, ["대상","교육 내용","형식"], - [["운영자/관리자","백오피스(RBAC·마스터데이터·규정 룰셋)·감사·시스템설정","운영자 지침서+실습"], - ["킨텍스 운영팀(홀매니저)","배치 검수·규정 플래그·현장 운영 대시보드","사용자 지침서+실습"], - ["주최자/참가업체","포털 사용(배정·설계·유틸리티·옥션·관람객)","사용자 지침서·온라인 가이드"]], - widths=[4.0, 8.0, 4.0]) -h(doc,2,"10.3 유지보수 계획") -bullets(doc,[ - "장애 대응·정기 점검·규정/요율 개정 반영(룰셋 버전 관리)을 유지보수 범위로 한다.", - "운영 런북(헬스체크·재기동·롤백)을 기준으로 SM 운영 체계를 수립.", - "규정·요율 연 단위 개정은 관리자 백오피스 룰셋 버전 관리로 안전 반영.", -]) - -# ---------- 11. 위험 관리 (sources.RISKS) ---------- -h(doc,1,"11. 위험(리스크) 관리") -para(doc,"주요 리스크와 대응 방안은 아래와 같으며, 형상관리 하 리스크 대장으로 지속 관리한다.") -table(doc, ["ID","리스크","영향","대응 방안"], - [list(x) for x in S.RISKS], widths=[1.2, 4.2, 2.6, 8.0]) - -# ---------- 12. 의사소통·보고 ---------- -h(doc,1,"12. 의사소통·보고 체계") -table(doc, ["보고/회의","주기","참여","내용"], - [["착수 보고","사업 착수 시","발주처·PMO","범위·일정·조직·리스크 확정"], - ["주간 진척 보고","주 1회","PMO·개발 리드","진척·이슈·리스크·다음 주 계획"], - ["Phase 종료 보고","각 Phase 종료","발주처·PMO","산출물·품질 게이트 결과·다음 단계"], - ["단계별 검토회의","마일스톤","전 트랙","경계면 정합·아키텍처 준수 점검"], - ["완료(준공) 보고","사업 종료","발주처·PMO","통합 QA 결과·이관·교육·유지보수 인계"]], - widths=[3.6, 2.6, 3.4, 6.4]) -para(doc,"긴급 이슈는 즉시 에스컬레이션하며, 모든 의사결정·변경은 형상관리(14장) 절차로 기록한다.", size=9, italic=True, color=GREY) - -# ---------- 13. 산출물 목록 (sources.DELIVERABLES) ---------- -h(doc,1,"13. 산출물 목록(단계별 표준 산출물)") -para(doc,"단계별 표준 산출물은 아래와 같으며, 상세 목록·형식·담당·상태는 별첨 [WBS_및_일정.xlsx]의 '산출물목록' 시트에 관리한다.") -table(doc, ["단계","산출물","형식","담당","상태"], - [[d[0], d[1], d[2], d[3], d[4]] for d in S.DELIVERABLES], - widths=[2.4, 6.2, 2.2, 3.0, 2.2]) - -# ---------- 14. 형상관리 ---------- -h(doc,1,"14. 형상관리") -h(doc,2,"14.1 형상관리 원칙") -bullets(doc,[ - "모든 소스·문서·설정은 Git(Gitea) 저장소로 버전 통제하며, 변경은 커밋 이력으로 추적.", - "산출물(계획서·설계서·시험·지침서)은 docs/deliverables/ 지정 경로에서 형상 관리.", - "DB 스키마는 Flyway 순번 마이그레이션을 형상 권위로 하며, baseline 규율과 시드 멱등화를 준수.", - "배포는 형상(커밋) 기준 CI/CD Webhook으로 무중단·헬스게이트·롤백 수행.", -]) -h(doc,2,"14.2 형상 항목 및 절차") -table(doc, ["형상 항목","관리 방식","변경 절차"], - [["소스코드","Git 브랜치·PR·리뷰","리뷰 승인 후 병합, 커밋 메시지 이력화"], - ["문서/산출물","Git 버전·개정 이력","개정 이력 기록 후 갱신"], - ["DB 스키마","Flyway 마이그레이션","신규 마이그 번호=현재 최대+1(충돌 검사)"], - ["설정/환경변수","env 주입·시크릿 분리","키·비번은 형상에서 제외(gitignore)"], - ["배포","CI/CD Webhook·헬스게이트","실패 시 자동 롤백"]], - widths=[3.4, 5.6, 7.0]) - -doc.add_paragraph() -para(doc,"— 본 사업수행계획서는 행정안전부 정보화사업 산출물 표준 목차를 준용하여 킨텍스 자동전시시스템 구축 사업의 원본 내용으로 작성되었다. —", - size=9, italic=True, color=GREY, align=CA) - -doc.save(OUT) -print("SAVED:", OUT) diff --git a/docs/deliverables/_gen/gen_xlsx.py.tmp.8724.0c7f99a3a6e3 b/docs/deliverables/_gen/gen_xlsx.py.tmp.8724.0c7f99a3a6e3 deleted file mode 100644 index e69de29..0000000 diff --git a/docs/deliverables/_gen/sources.py b/docs/deliverables/_gen/sources.py deleted file mode 100644 index 7145c7b..0000000 --- a/docs/deliverables/_gen/sources.py +++ /dev/null @@ -1,264 +0,0 @@ -# -*- coding: utf-8 -*- -""" -sources.py — 산출물 생성의 단일 데이터 소스 로더. -docs/PLANNING.md · docs/IMPLEMENTATION_BACKLOG.md · docs/FEATURE_BACKLOG_100.md · docs/architecture/* -를 파싱하여 구조화 데이터(모듈·Phase/WBS·기능/요구사항)를 반환한다. -소스 문서가 갱신되면 _gen 재실행만으로 전 산출물이 갱신된다. -파싱 실패 시 임베디드 폴백을 사용해 결정적 산출을 보장한다. -""" -import os, re - -DOCS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) # kintex/docs -ROOT = os.path.abspath(os.path.join(DOCS, "..")) # kintex - -META = { - "project": "킨텍스 자동전시시스템", - "project_en": "Exhibition Automation Platform", - "title": "킨텍스 자동전시시스템 구축 사업수행계획서", - "version": "v1.0", - "date": "2026-07-11", - "owner": "지오정보기술(주) — GUARDiA 플랫폼 사업본부", - "client": "킨텍스(KINTEX, 한국국제전시장)", - "dev_domain": "kintex.zioinfo.co.kr(개발)", - "std": "행정안전부 정보화사업 산출물 표준 목차 준용", -} - -def _read(rel): - p = os.path.join(DOCS, rel) - try: - with open(p, "r", encoding="utf-8") as f: - return f.read() - except Exception: - return "" - -def _table_rows(text, min_cols=2): - """마크다운 표의 데이터 행(| a | b |)을 셀 리스트로 반환(헤더/구분선 제외).""" - out = [] - for line in text.splitlines(): - s = line.strip() - if not s.startswith("|"): - continue - if re.match(r"^\|[\s:\-\|]+\|?$", s): # separator - continue - cells = [c.strip() for c in s.strip("|").split("|")] - if len(cells) >= min_cols: - out.append(cells) - return out - -# ---------------- 모듈(M1~M18 + §5B) : PLANNING 기능 우선순위 총괄표 ---------------- -_MOD_FALLBACK = [ - ("M2","플로어플랜 스튜디오(부스 배치)","P0"),("M3","부스 설계 스튜디오","P0"), - ("M4","유틸리티 설계 자동화(전기·조명/네트워크·급배수)","P0"),("M5","나노바나나 시각화","P0"), - ("M1","홀 배정·자동 견적","P1"),("M6","서류·마일스톤 워크플로","P1"), - ("M7","등록업체 매칭·견적","P1"),("M9","정산·결제","P1"), - ("M15","공사/장치 옥션(역경매·견적서·낙찰)","P1"),("M10","관람객 등록·배지·체크인·리드캡처","P1"), - ("M12","마케팅·EDM·공개 홍보 사이트","P1"),("M16","경영분석 BI","P1"), - ("M17","CMS(콘텐츠·마이크로사이트·다국어)","P1"),("M18","관리자 백오피스","P1"), - ("§5B","공통/시스템관리 레이어 + JWT+2FA","P1"), - ("M8","반입/반출 물류","P2"),("M11","비즈니스 매칭","P2"), - ("M13","wayfinding·실내 내비","P2"),("M14","현장운영(혼잡·안전·주차·에너지)","P2"), -] -def modules(): - text = _read("PLANNING.md") - mods, seen = [], set() - for cells in _table_rows(text, min_cols=4): - mid = cells[0].replace("**","").strip() - if not re.match(r"^(M\d+|§5B)$", mid): - continue - if mid in seen: - continue - name = re.sub(r"\*\*|`", "", cells[1]).strip() - pri = "" - for c in cells: - m = re.search(r"P0|P1|P2", c) - if m: pri = m.group(0); break - if not name or not pri: - continue - seen.add(mid); mods.append((mid, name, pri)) - if len(mods) < 15: - return list(_MOD_FALLBACK) - order = {"P0":0,"P1":1,"P2":2} - mods.sort(key=lambda x: order.get(x[2],9)) - return mods - -# ---------------- Phase / WBS : IMPLEMENTATION_BACKLOG Phase 표 ---------------- -_PHASE_FALLBACK = [ - ("A","아키텍처·거버넌스",[ - ("A-1","애플리케이션 아키텍처(모듈 경계·API 표준)","아키텍트(AA)","docs/architecture/app.md"), - ("A-2","시스템 아키텍처·NFR(확장성·HA·성능·보안영역)","아키텍트(SA)","docs/architecture/system.md"), - ("A-3","기술 표준(스택·빌드/배포·관측성·AiTextRouter)","아키텍트(TA)","docs/architecture/tech.md"), - ("A-4","데이터 아키텍처(전사 ERD·공간·마스터·BI마트)","아키텍트(DA)","ERD·data.md"), - ("A-5","네트워크 아키텍처(DMZ/내부망·방화벽·부하분산)","아키텍트(NA)","network.md"), - ("A-6","아키텍처↔PLANNING 정합 검증","reviewer","불일치 0/티켓화"), - ]), - ("B","공통/시스템관리 레이어(전 모듈 선행)",[ - ("B-0","스캐폴드(백엔드·프론트·DB·마이그레이션)","BE·FE·DB","빌드·마이그레이션 통과"), - ("B-1","인증 JWT+RBAC+2차인증(OTP)+실패잠금+admin env","COM·BE","2FA 등록/검증/초기화 왕복"), - ("B-2","시스템관리(사용자·역할·공통코드·메뉴·감사·설정)","COM·ADM·DB","CRUD·RBAC 가드·감사"), - ("B-3","공통 업무기능(일지·일정·쪽지·통계·공지·회의·알림)","COM·DB","각 모듈 API 왕복"), - ("B-4","공통 컴포넌트·프론트 공통(2FA·그리드·달력·모달)","COM·FE","아키텍처 표준 정합"), - ("B-5","점진 QA(2FA·RBAC·PII·경계면)","QA","통과까지 반려"), - ]), - ("C","P0 부스 시공 코어",[ - ("C-M2","플로어플랜(배치 3안·병합·규정검증 PostGIS)","DB·BE·AI·FE·QA","3안 생성·병합·규정 재검증"), - ("C-M3","부스 설계(조립/독립·규정 사전검증·3안)","BE·AI·FE·QA","위반 사전 플래깅·컨펌"), - ("C-M4","유틸리티 배선(전기/네트워크/급배수·견적·위치표시도)","DB·BE·FE·QA","최단 배선·견적·위치표시도"), - ("C-M5","나노바나나 시각화(RenderJob·워커·워터마크)","BE·VIZ·FE·QA","실생성/미승인 시 목·워터마크"), - ("C-C","역할별 공통 화면(로그인·대시보드·현장 모바일)","FE·QA","역할별 진입·상태 처리"), - ]), - ("D","P1 도메인(역할별 포털)",[ - ("D-M15","공사/장치 옥션(견적서 제출·역경매·낙찰)","BID·BE·DB·FE·QA","견적서·역경매·낙찰 왕복"), - ("D-M10","관람객·현장(등록·티켓·배지·체크인·리드)","VIS·BE·DB·FE·QA","등록→배지→체크인→리드"), - ("D-M12/17","마케팅·공개사이트·CMS(SEO·다국어·EDM)","CMS·BE·FE·QA","공개 조회·게시 워크플로"), - ("D-M16","경영분석 BI(가동률·P&L·ROI·수요예측·KPI)","BI·DA·BE·FE·QA","지표 집계·드릴다운"), - ("D-M18","관리자 백오피스(마스터데이터·룰셋 버전)","ADM·BE·FE·QA","RBAC·마스터 CRUD·룰셋"), - ("D-M1/6/7/9","배정·서류·매칭·정산","BE·FE·DB·QA","견적·마일스톤·정산"), - ]), - ("E","P2 + 빌드·배포",[ - ("E-M11","비즈니스 매칭(추천·미팅예약)","VIS·AI","추천·미팅예약"), - ("E-M13","wayfinding·실내 내비(M2 재사용)","VIS·FE","부스 검색·경로"), - ("E-M14","현장운영(혼잡·안전·주차·에너지)","BE·FE","대시보드·경보"), - ("E-DEP","역할별 번들·jar·워커 빌드·CI/CD·systemd","DEV","push→배포"), - ]), -] -def phases(): - """[(letter, title, [(id, task, owner, done), ...]), ...]""" - text = _read("IMPLEMENTATION_BACKLOG.md") - if not text: - return _PHASE_FALLBACK - out = [] - cur = None - lines = text.splitlines() - hdr_re = re.compile(r"^##\s+Phase\s+([A-E])\s*[—\-–]\s*(.+)$") - for line in lines: - hm = hdr_re.match(line.strip()) - if hm: - if cur: out.append(cur) - title = re.sub(r"\([^)]*\)", "", hm.group(2)) - title = re.sub(r"[★((].*$", "", title).strip() - cur = (hm.group(1), title, []) - continue - s = line.strip() - if cur and s.startswith("|"): - if re.match(r"^\|[\s:\-\|]+\|?$", s): - continue - cells = [c.strip() for c in s.strip("|").split("|")] - if not cells or cells[0] in ("ID","Phase") or not re.match(r"^[A-E]-", cells[0]): - continue - # 표 형태 2종: (ID,작업,담당,완료) 또는 (ID,모듈,작업,담당,완료) - if len(cells) >= 5: - _id, task, owner, done = cells[0], cells[2], cells[3], cells[4] - elif len(cells) == 4: - _id, task, owner, done = cells[0], cells[1], cells[2], cells[3] - else: - continue - cur[2].append((_id, re.sub(r"\*\*|`","",task), re.sub(r"\*\*","",owner), re.sub(r"\*\*","",done))) - if cur: out.append(cur) - ok = [p for p in out if p[2]] - return ok if len(ok) >= 4 else _PHASE_FALLBACK - -# 마스터 일정(착수 상대 주차) — 큐레이션(마일스톤) -SCHEDULE = [ - ("A","아키텍처·거버넌스","W1","W3","M1 아키텍처 베이스라인 승인(W3)"), - ("B","공통/시스템관리 레이어","W3","W8","M2 공통 레이어·인증(2FA) 완료(W8)"), - ("C","P0 부스 시공 코어","W8","W16","M3 P0 부스 코어 시연·첫 상용 행사 적용(W16)"), - ("D","P1 도메인","W16","W24","M4 옥션·관람객·BI·관리자 통합(W24)"), - ("E","P2 + 빌드·배포","W24","W28","M5 통합 QA 통과·운영 배포(W28)"), -] - -# ---------------- 기능/요구사항 : FEATURE_BACKLOG_100 C1~C12 ---------------- -def features(): - """[(fid, name, category, priority, ai, module), ...] — 100대 기능(RTM/요구사항 원천).""" - text = _read("FEATURE_BACKLOG_100.md") - out, seen = [], set() - cur_cat = "" - cat_re = re.compile(r"^###\s+(C\d+)\.\s*(.+?)\s*(?:\(\d+\)|—|$)") - stop = False - for line in text.splitlines(): - s = line.strip() - cm = cat_re.match(s) - if cm: - cur_cat = cm.group(1) + " " + re.sub(r"\s*[—-].*$","",cm.group(2)).strip() - stop = False - continue - # C 표가 끝나고 'AI 자동화 맵' 등 다른 섹션이면 중단 표시 - if s.startswith("##") and "카테고리" not in s and not s.startswith("###"): - stop = True - if stop: - continue - m = re.match(r"^\|\s*(F\d{3})\s*\|(.+)$", s) - if m and cur_cat: - fid = m.group(1) - if fid in seen: - continue - cells = [c.strip() for c in m.group(2).strip("|").split("|")] - name = re.sub(r"\*\*|`","",cells[0]).strip() if cells else fid - pri = next((re.search(r"P[012]",c).group(0) for c in cells if re.search(r"P[012]",c)), "") - ai = "" - for c in cells: - cc = c.strip() - if cc in ("Y","N","부분"): - ai = cc; break - module = next((re.sub(r"\*\*|`","",c) for c in cells if re.search(r"(M\d+|§5B|§1A|§6)", c)), "") - seen.add(fid) - out.append((fid, name, cur_cat, pri, ai, module)) - return out - -# ---------------- 산출물 목록(표준 단계별) ---------------- -DELIVERABLES = [ - # (단계, 산출물명, 형식, 담당, 상태, 비고) - ("착수(계획)","사업수행계획서","DOCX","PMO","작성완료","본 산출물(gen_docx)"), - ("착수(계획)","WBS 및 마스터 일정","XLSX","PMO","작성완료","gen_xlsx(WBS/일정/투입/산출물)"), - ("착수(계획)","산출물 목록","XLSX","PMO","작성완료","gen_xlsx(산출물목록 시트)"), - ("착수(계획)","착수보고 자료","PPTX","PMO","작성완료","gen_pptx(착수보고)"), - ("분석","요구사항 정의서","DOCX","기획(planner)","작성완료","gen_reqspec(100대 기능 원천)"), - ("분석","요구사항 추적표(RTM)","XLSX","QA/기획","작성완료","gen_xlsx(RTM 시트)"), - ("분석","기획서(PLANNING)","MD","기획(planner)","기보유","docs/PLANNING.md v3.0"), - ("설계","개발계획서","DOCX","PMO/개발리드","작성완료","gen_devplan"), - ("설계","화면설계서·UI 스펙(design)","MD/DOCX","디자이너","기보유","docs/design.md"), - ("설계","아키텍처 설계서(앱/시스템/기술/데이터/네트워크)","MD","아키텍트","기보유","docs/architecture/*"), - ("설계","데이터 모델(전사 ERD·BI 데이터마트)","MD/SQL","DA/DB","진행","설계 트랙"), - ("설계","API 설계서(계약)","MD","AA/백엔드","기보유","docs/API_GUIDE.md"), - ("구현","소스코드(백엔드·프론트·워커)","Git","개발","진행","src/, tools/nanobanana"), - ("구현","DB 스키마·마이그레이션","SQL","DB","진행","Flyway"), - ("시험","시험계획서","DOCX","QA","작성완료","gen_testplan"), - ("시험","시험케이스·시험결과서","XLSX","QA","작성완료","gen_xlsx(시험케이스 시트)"), - ("이관","배포/운영 런북·이관 확인서","MD","DevOps","기보유","docs/OPS_RUNBOOK.md"), - ("교육","사용자 지침서·운영자 지침서","DOCX/PPTX","기술문서","예정","완성+QA 후"), - ("종료","완료(준공) 보고서","DOCX","PMO","예정","사업 종료 시"), -] - -# ---------------- 투입 인력 계획(M/M) ---------------- -STAFFING = [ - ("사업 PM/PMO","1","특급","A~E 상시","6.5"), - ("아키텍트(AA/SA/TA)","2","특급","A 집중","3.0"), - ("데이터 아키텍트(DA)","1","고급","A·D","2.0"), - ("네트워크 아키텍트(NA)","1","고급","A·E","1.0"), - ("공통/시스템관리 개발","2","중급","B 집중","4.0"), - ("백엔드 개발","3","중·고급","B~D","12.0"), - ("프론트엔드 개발","3","중·고급","B~E","12.0"), - ("DB 엔지니어(PostGIS)","1","고급","B~D","4.5"), - ("AI 개발","1","고급","C~D","3.0"), - ("시각화(나노바나나)","1","고급","C","2.0"), - ("도메인 개발(옥션/관람객/CMS/BI/관리자)","3","중·고급","D 집중","9.0"), - ("QA","2","중급","B~E 상시","8.0"), - ("DevOps","1","고급","B·E","2.5"), - ("디자이너","1","고급","A~E","3.0"), -] - -RISKS = [ - ("R1","홀 실측 CAD 원본 미확보","배치 정밀도","공개 규격 근사 도면으로 진행, 확보 시 대체"), - ("R2","이미지 생성(외부) 승인 게이트","M5 시각화","미승인 시 목/degraded, 승인 후 라이브"), - ("R3","외부 시스템(작업신고/PG/KT) 연동 미공개","M6/M9/M4b","제출용 파일·릴레이 방식, 협의 후 연동"), - ("R4","등록업체 옥션 참여율·응찰 품질","M15 실효성","등록업체 게이트·AI 자료 공정 비교 유인"), - ("R5","개인정보(리드·배지) 규정 위반","법적 리스크","동의·보존정책·감사·최소 수집"), - ("R6","AI 시각화 오용(계약 서류)","분쟁 리스크","워터마크·고지 강제(UI·응답·PDF)"), - ("R7","공통 레이어 지연","일정","Phase B 인력 선집중·병행 착수"), - ("R8","규정·요율 개정","정합성","룰셋 버전 관리·과거 버전 보존"), -] - -if __name__ == "__main__": - print("modules:", len(modules())) - ph = phases(); print("phases:", [(p[0], len(p[2])) for p in ph]) - print("features:", len(features())) - print("sample feature:", features()[0] if features() else None) diff --git a/docs/deliverables/개발계획서.pptx b/docs/deliverables/개발계획서.pptx deleted file mode 100644 index 882d1fc..0000000 Binary files a/docs/deliverables/개발계획서.pptx and /dev/null differ diff --git a/docs/deliverables/사업수행계획서.docx b/docs/deliverables/사업수행계획서.docx deleted file mode 100644 index 7a9eaad..0000000 Binary files a/docs/deliverables/사업수행계획서.docx and /dev/null differ diff --git a/docs/deliverables/제안서.pptx b/docs/deliverables/제안서.pptx deleted file mode 100644 index 687b162..0000000 Binary files a/docs/deliverables/제안서.pptx and /dev/null differ diff --git a/docs/deliverables/제안서/_gen/_qa_rtm.py b/docs/deliverables/제안서/_gen/_qa_rtm.py deleted file mode 100644 index ec42765..0000000 --- a/docs/deliverables/제안서/_gen/_qa_rtm.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- -import re, json, io, sys -sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') -slides = json.load(open(r"C:\GUARDiA\workspace\kintex\_workspace\proposal\_qa_slides.json", encoding="utf-8")) - -# Full REQ universe -UNIV = {} -for pfx, cnt in [("F", 81), ("A", 20), ("N", 21), ("S", 20)]: - for i in range(1, cnt + 1): - UNIV[f"{pfx}-{i:03d}"] = None # slide where covered - -# Parse each text line for badge tokens with prefix-context + range + continuation. -# Grammar we support in a run: -# REQ-

- single -# REQ-

-~ range (same prefix) -# ...·

- new prefix single continuation -# ...·

-~ new prefix range continuation -# ...· bare number continues LAST prefix (e.g. A-011·012) -# ...·~ bare range continues last prefix (e.g. 067~069) -cover = {} # req -> list of slides - -def add(pfx, a, b, sn): - for i in range(a, b + 1): - key = f"{pfx}-{i:03d}" - cover.setdefault(key, set()).add(sn) - -# token: optional prefix letter, number, optional ~ number -tok = re.compile(r"(?:REQ-)?(?:([FANS])-)?(\d{2,3})(?:\s*~\s*(?:[FANS]-)?(\d{2,3}))?") - -for s in slides: - sn = s["n"] - for t in s["texts"]: - # only scan segments that look like badge context (contain REQ- or letter-number dot chains) - for m in re.finditer(r"REQ-[FANS]-\d{2,3}(?:\s*~\s*\d{2,3})?(?:[·,\s]+(?:[FANS]-)?\d{2,3}(?:\s*~\s*\d{2,3})?)*", t): - seg = m.group(0) - last_pfx = None - for mm in tok.finditer(seg): - p, a, b = mm.group(1), mm.group(2), mm.group(3) - if p: - last_pfx = p - if last_pfx is None: - continue - ai = int(a) - bi = int(b) if b else ai - # sanity: within universe range - add(last_pfx, ai, bi, sn) - -missing = sorted(k for k in UNIV if k not in cover) -print("=== RANGE-EXPANDED BADGE COVERAGE ===") -print("COVERED (badge):", len(cover), "/ 142") -print("NOT badge-covered (", len(missing), "):", ", ".join(missing)) - -# priority 7 -prio = ["F-008", "F-010", "F-026", "F-032", "A-003", "A-005", "A-008"] -print("\n=== PRIORITY 7 BADGE STATUS ===") -for r in prio: - print(f"{r}: badge slides = {sorted(cover.get(r, []))}") - -# dump per-covered for the previously-flagged 18 -flagged = ["F-005","F-008","F-010","F-026","F-032","F-034","F-036","F-038","F-039","F-054","F-074","F-075","F-076","A-003","A-005","A-008","S-005","S-006"] -print("\n=== PREVIOUSLY-FLAGGED 18: badge coverage after range expansion ===") -for r in flagged: - sl = sorted(cover.get(r, [])) - print(f"{r}: {'BADGE ' + str(sl) if sl else 'NO BADGE'}") diff --git a/docs/deliverables/제안서/_gen/_qa_struct.py b/docs/deliverables/제안서/_gen/_qa_struct.py deleted file mode 100644 index 5218c56..0000000 --- a/docs/deliverables/제안서/_gen/_qa_struct.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -import json, io, sys, re -sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') -S = json.load(open('_qa_slides.json', encoding='utf-8')) - -ROMAN = ["Ⅰ","Ⅱ","Ⅲ","Ⅳ","Ⅴ","Ⅵ","Ⅶ","Ⅷ"] - -print("=== PER-SLIDE STRUCTURE (chapter line + first content) ===") -for s in S: - n = s["n"] - chap = "" - for t in s["texts"]: - # chapter header like "Ⅲ. 기술·기능 · ..." - if any(t.startswith(r + ".") or t.startswith(r + " ") for r in ROMAN): - chap = t - break - # headline = the longest early text that's not chapter/boilerplate - head = "" - for t in s["texts"][:6]: - if t == chap: continue - if "킨텍스" in t or "2026-07" in t or t in ROMAN: continue - if len(t) > len(head): head = t - print(f"{n:>3}| {chap[:46]:<46} | {head[:52]}") - -print("\n=== 배점 / SCORING TOKENS ===") -pats = ["80/20","35/80","90/10","70+20","90(","기술 90","기술 80","가격 10","가격 20","정성 70","정량 20","재정렬","공고 확정"] -for p in pats: - hits=[(s['n'],t[:80]) for s in S for t in s['texts'] if p in t] - print(f'--- {p!r}: {len(hits)}') - for n,t in hits[:8]: print(f' {n}: {t}') - -print("\n=== HONESTY LABELS ===") -for p in ["[검증됨]","[구현 계획]","[협의]","[가정]","[Non-Goal]","[공고"]: - hits=[(s['n'],t[:74]) for s in S for t in s['texts'] if p in t] - print(f'--- {p!r}: {len(hits)}') - for n,t in hits[:14]: print(f' {n}: {t}') - -print("\n=== WATERMARK / AI NOTICE ===") -for p in ["워터마크","AI 생성","AI-generated","watermark","합성 이미지","예상 이미지","시공 예"]: - hits=[(s['n'],t[:74]) for s in S for t in s['texts'] if p.lower() in t.lower()] - print(f'--- {p!r}: {len(hits)} : slides {sorted(set(n for n,_ in hits))}') diff --git a/docs/deliverables/제안서/_gen/build_deck.py b/docs/deliverables/제안서/_gen/build_deck.py deleted file mode 100644 index 7a8822a..0000000 --- a/docs/deliverables/제안서/_gen/build_deck.py +++ /dev/null @@ -1,1942 +0,0 @@ -# -*- coding: utf-8 -*- -""" -킨텍스 자동전시시스템 구축 — 제안서(기술제안서) PPTX 생성기 -- 소스: _workspace/proposal/{proposal_content.md, design_system.md, visual_concepts.md, tech_advisory.md, assets/icons/*.svg, assets/screens/*} -- 디자인: KINTEX Blue #0066B3 / AI Purple #6D4AFF / 맑은 고딕 / 16:9 · 개발계획서(gen_dev_plan_pptx.py)와 동일 팔레트·톤(산출물 패밀리) -- 도식 15종은 python-pptx 도형으로 직접 작도(visual_concepts 좌표·색·라벨 그대로). 선 아이콘은 SVG→PNG(icon_raster) 삽입. -- 부록 D: assets/screens/ + screens_manifest.json 재스캔 → 화면 설계(안) 갤러리 자동 재구성. -- 보안 불변: 키·비번·IP·SSH 미기재. AI 생성 이미지 워터마크·금지 고지 유지. -재실행: python build_deck.py -""" -import os, json, glob -from pptx import Presentation -from pptx.util import Emu, Pt -from pptx.dml.color import RGBColor -from pptx.enum.text import PP_ALIGN, MSO_ANCHOR -from pptx.enum.shapes import MSO_SHAPE -from pptx.oxml.ns import qn -from icon_raster import render_icon - -BASE = os.path.dirname(os.path.abspath(__file__)) -ICONPNG = os.path.join(BASE, "_iconpng") -SCREENS = os.path.join(BASE, "assets", "screens") -os.makedirs(ICONPNG, exist_ok=True) - -# ---------------- 팔레트 (design_system §1) ---------------- -BLUE = RGBColor(0x00, 0x66, 0xB3) -BLUE_DK = RGBColor(0x00, 0x44, 0x7A) -BLUE_LT = RGBColor(0xE1, 0xEF, 0xF9) -PURPLE = RGBColor(0x6D, 0x4A, 0xFF) -PURPLE_DK = RGBColor(0x4A, 0x2F, 0xCC) -PURPLE_LT = RGBColor(0xEC, 0xE8, 0xFF) -INK = RGBColor(0x10, 0x18, 0x28) -MUTED = RGBColor(0x66, 0x70, 0x85) -SLATE = RGBColor(0x98, 0xA2, 0xB3) -LINE = RGBColor(0xE4, 0xE7, 0xEC) -BG = RGBColor(0xF9, 0xFA, 0xFB) -HALL_BG = RGBColor(0xF2, 0xF4, 0xF7) -WHITE = RGBColor(0xFF, 0xFF, 0xFF) -CARD = WHITE -GREEN = RGBColor(0x12, 0x9E, 0x63) -AMBER = RGBColor(0xE0, 0x8A, 0x00) -RED = RGBColor(0xD9, 0x2D, 0x20) -CYAN = RGBColor(0x0B, 0xA5, 0xEC) -TEAL = RGBColor(0x0E, 0x93, 0x84) -ZEBRA = RGBColor(0xFC, 0xFC, 0xFD) -BLUEMID = RGBColor(0x4A, 0x90, 0xD9) -NAVY = RGBColor(0x2B, 0x4C, 0x7E) -POLY = RGBColor(0xD8, 0xDE, 0xE8) -SUBLT = RGBColor(0xC7, 0xDD, 0xF2) -FONT = "맑은 고딕" - -EMU_W = 12192000; EMU_H = 6858000 -IN = 914400 -def inch(v): return int(v * IN) - -prs = Presentation() -prs.slide_width = EMU_W; prs.slide_height = EMU_H -BLANK = prs.slide_layouts[6] - -def slide(): return prs.slides.add_slide(BLANK) - -def _fill(sp, color): - if color is None: sp.fill.background() - else: sp.fill.solid(); sp.fill.fore_color.rgb = color - -def rect(s, x, y, w, h, fill=None, line=None, line_w=1.0, shape=MSO_SHAPE.RECTANGLE, - shadow=False, dash=None): - sp = s.shapes.add_shape(shape, inch(x), inch(y), inch(w), inch(h)) - _fill(sp, fill) - if line is None: - sp.line.fill.background() - else: - sp.line.color.rgb = line; sp.line.width = Pt(line_w) - if dash: - ln = sp.line._get_or_add_ln() - d = ln.makeelement(qn('a:prstDash'), {'val': dash}); ln.append(d) - sp.shadow.inherit = False # creates an empty - if shadow: - el = sp._element.spPr - ef = el.find(qn('a:effectLst')) - if ef is None: - ef = el.makeelement(qn('a:effectLst'), {}); el.append(ef) - sh = ef.makeelement(qn('a:outerShdw'), - {'blurRad':'90000','dist':'38100','dir':'5400000','rotWithShape':'0'}) - clr = sh.makeelement(qn('a:srgbClr'), {'val':'101828'}) - alp = clr.makeelement(qn('a:alpha'), {'val':'16000'}) - clr.append(alp); sh.append(clr); ef.append(sh) - return sp - -def line_conn(s, x1, y1, x2, y2, color=SLATE, w=1.0, dash=None): - ln = s.shapes.add_connector(2, inch(x1), inch(y1), inch(x2), inch(y2)) - ln.line.color.rgb = color; ln.line.width = Pt(w) - ln.shadow.inherit = False - if dash: - el = ln.line._get_or_add_ln() - d = el.makeelement(qn('a:prstDash'), {'val': dash}); el.append(d) - return ln - -def text(s, x, y, w, h, content, size=11, color=INK, bold=False, align=PP_ALIGN.LEFT, - anchor=MSO_ANCHOR.TOP, line_spacing=1.0, space_after=2, wrap=True): - """content: str | list[para]; para = str | seg-dict | list[seg]; seg={'t','size','color','bold'}""" - tb = s.shapes.add_textbox(inch(x), inch(y), inch(w), inch(h)) - tf = tb.text_frame - tf.word_wrap = wrap - tf.vertical_anchor = anchor - tf.margin_left = 0; tf.margin_right = 0; tf.margin_top = 0; tf.margin_bottom = 0 - if isinstance(content, str): content = [content] - for i, para in enumerate(content): - p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() - p.alignment = align; p.line_spacing = line_spacing - p.space_after = Pt(space_after); p.space_before = Pt(0) - if isinstance(para, str): segs = [{"t": para}] - elif isinstance(para, dict): segs = [para] - else: segs = para - for seg in segs: - r = p.add_run(); r.text = seg.get("t","") - r.font.name = FONT; r.font.size = Pt(seg.get("size", size)) - r.font.bold = seg.get("bold", bold); r.font.color.rgb = seg.get("color", color) - rPr = r._r.get_or_add_rPr() - rPr.append(rPr.makeelement(qn('a:ea'), {'typeface':FONT})) - return tb - -def page_bg(s, color=BG): rect(s, -0.1, -0.1, 13.53, 7.7, fill=color) - -def chip(s, x, y, w, h, label, fill, tc=WHITE, size=9.5, bold=True, line=None): - rect(s, x, y, w, h, fill=fill, line=line, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, w, h, label, size=size, color=tc, bold=bold, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -def card(s, x, y, w, h, fill=CARD, line=LINE, shadow=True): - return rect(s, x, y, w, h, fill=fill, line=line, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=shadow) - -# ---- 선 아이콘(SVG→PNG) ---- -def icon(s, name, x, y, size, color=BLUE): - hexc = '%02X%02X%02X' % (color[0], color[1], color[2]) - out = os.path.join(ICONPNG, f"{name}__{hexc}.png") - if not os.path.exists(out): - render_icon(name, (color[0], color[1], color[2]), out, px=160) - s.shapes.add_picture(out, inch(x), inch(y), inch(size), inch(size)) - -# ---- 이미지 aspect-fit 삽입 ---- -def img_fit(s, path, x, y, w, h, frame=True, framecolor=LINE, bg=WHITE): - from PIL import Image - iw, ih = Image.open(path).size - ar = iw/ih; box = w/h - if ar > box: - dw = w; dh = w/ar - else: - dh = h; dw = h*ar - dx = x + (w-dw)/2; dy = y + (h-dh)/2 - if frame: - rect(s, x, y, w, h, fill=bg, line=framecolor, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - s.shapes.add_picture(path, inch(dx), inch(dy), inch(dw), inch(dh)) - -# ---- 헤더/푸터 ---- -FOOTER = "킨텍스(KINTEX) 자동전시시스템 구축 · 기술제안서" -def content_header(s, kicker, title, idx, req=None): - page_bg(s, BG) - rect(s, 0, 0, 13.333, 0.12, fill=BLUE) - rect(s, 0.55, 0.5, 0.62, 0.62, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.55, 0.5, 0.62, 0.62, f"{idx:02d}" if isinstance(idx,int) else idx, - size=20, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, 1.32, 0.5, 9.3, 0.28, kicker, size=11, color=PURPLE, bold=True) - tw = (11.46 - 0.082*len(req)) if req else 11.3 # REQ 배지와 겹치지 않도록 폭 축소(길면 자동 줄바꿈) - text(s, 1.32, 0.72, tw-1.32, 0.56, title, size=19.5, color=INK, bold=True, line_spacing=0.96) - rect(s, 0.55, 1.34, 12.23, 0.02, fill=LINE) - text(s, 0.55, 7.06, 8, 0.3, FOOTER, size=8.5, color=MUTED) - text(s, 11.0, 7.06, 1.78, 0.3, "2026-07-11", size=8.5, color=MUTED, align=PP_ALIGN.RIGHT) - if req: - rw = 0.15 + 0.082*len(req) - rect(s, 12.78-rw, 0.55, rw, 0.3, fill=RGBColor(0xEE,0xF0,0xF3), shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 12.78-rw, 0.55, rw, 0.3, req, size=8, color=MUTED, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -def caption(s, txt, y=6.62): - text(s, 0.55, y, 12.23, 0.3, txt, size=9.5, color=MUTED, align=PP_ALIGN.LEFT) - -# 라벨 컬러(검증됨/구현 계획/협의) -LABELC = {"[검증됨]":GREEN, "[구현 계획]":BLUE, "[협의]":AMBER, "[가정]":AMBER, "[Non-Goal]":SLATE} -def _segify(t): - """문자열에서 [라벨] 토큰을 색 run으로 분리.""" - segs=[]; i=0 - while i < len(t): - hit=None - for lab in LABELC: - if t.startswith(lab, i): hit=lab; break - if hit: - segs.append({"t":hit, "color":LABELC[hit], "bold":True}); i+=len(hit) - else: - j=i - while j < len(t): - if any(t.startswith(l, j) for l in LABELC): break - j+=1 - segs.append({"t":t[i:j]}); i=j - return segs - -def bullets(s, x, y, w, items, dot=BLUE, size=10.5, gap=0.46, lh=1.0, dotsize=0.1, tcolor=INK): - for i, it in enumerate(items): - yy = y + i*gap - rect(s, x, yy+0.07, dotsize, dotsize, fill=dot, shape=MSO_SHAPE.OVAL) - segs = _segify(it) if isinstance(it, str) else it - for sg in segs: - sg.setdefault("size", size); sg.setdefault("color", tcolor) - text(s, x+0.24, yy, w-0.24, gap, [segs], line_spacing=lh) - -def lead(s, x, y, w, h, txt, fill=BLUE, tc=WHITE, size=15): - rect(s, x, y, w, h, fill=fill, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.25, y, w-0.4, h, txt, size=size, color=tc, bold=True, anchor=MSO_ANCHOR.MIDDLE) - -def wm_badge(s, x, y, w, h=0.34, size=8.5): - rect(s, x, y, w, h, fill=RGBColor(0xFC,0xE9,0xE7), line=RED, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.12, y, w-0.2, h, "AI 생성 예상 이미지 — 실제 시공과 다를 수 있음. 계약·심사 서류 사용 금지.", - size=size, color=RED, bold=True, anchor=MSO_ANCHOR.MIDDLE) - -def arrow(s, x, y, big=False, color=SLATE): - text(s, x, y, 0.3, 0.5, "›" if not big else "→", size=17 if not big else 15, - color=color, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -def titlecard(s, x, y, w, h, title, tcolor, items, headfill=None, bodysize=9.5, hsize=12, igap=None): - card(s, x, y, w, h) - if headfill: - rect(s, x, y, w, 0.46, fill=headfill, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, w, 0.46, title, size=hsize, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - y0 = y+0.62 - else: - rect(s, x, y, 0.09, h, fill=tcolor, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.24, y+0.13, w-0.4, 0.32, title, size=hsize, color=tcolor, bold=True) - y0 = y+0.55 - g = igap if igap else min(0.4, (h-(y0-y)-0.1)/max(1,len(items))) - for i, it in enumerate(items): - yy = y0 + i*g - rect(s, x+0.22, yy+0.06, 0.09, 0.09, fill=tcolor, shape=MSO_SHAPE.OVAL) - segs=_segify(it) if isinstance(it,str) else it - for sg in segs: sg.setdefault("size",bodysize); sg.setdefault("color",INK) - text(s, x+0.42, yy, w-0.6, g, [segs], line_spacing=0.94) - -def table(s, x, y, w, cols, rows, colw, headfill=BLUE, rowh=0.4, hsize=9.5, bsize=9, - hl_rows=None, hl_fill=BLUE_LT): - hl_rows = hl_rows or set() - rect(s, x, y, w, rowh, fill=headfill, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - cx = x - for j, c in enumerate(cols): - text(s, cx+0.1, y, colw[j]-0.12, rowh, c, size=hsize, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE, - align=PP_ALIGN.CENTER if j>0 else PP_ALIGN.LEFT) - cx += colw[j] - for i, row in enumerate(rows): - ry = y + rowh + i*rowh - if i in hl_rows: - rect(s, x, ry, w, rowh, fill=hl_fill) - elif i % 2 == 1: - rect(s, x, ry, w, rowh, fill=ZEBRA) - rect(s, x, ry, w, 0.008, fill=LINE) - cx = x - for j, cell in enumerate(row): - b = (i in hl_rows) and j==0 - col = INK if j==0 else MUTED - if isinstance(cell, tuple): txt, col = cell - else: txt = cell - text(s, cx+0.1, ry, colw[j]-0.12, rowh, txt, size=bsize, color=(INK if i in hl_rows else col), - bold=(i in hl_rows), anchor=MSO_ANCHOR.MIDDLE, align=PP_ALIGN.CENTER if j>0 else PP_ALIGN.LEFT) - cx += colw[j] - -# ================================================================ -# COVER (S01) -# ================================================================ -def cover(): - s = slide() - rect(s, -0.1, -0.1, 13.53, 7.7, fill=BLUE_DK) - rect(s, 6.4, -1.5, 9, 11, fill=BLUE, shape=MSO_SHAPE.PARALLELOGRAM) - rect(s, 9.2, -2.0, 7, 12, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) - # 브랜드 - rect(s, 0.85, 0.8, 0.42, 0.42, fill=WHITE, shape=MSO_SHAPE.OVAL) - rect(s, 0.97, 0.92, 0.18, 0.18, fill=PURPLE, shape=MSO_SHAPE.OVAL) - text(s, 1.42, 0.78, 6, 0.46, "KINTEX · WISE AI", size=15, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - chip(s, 0.85, 2.3, 1.75, 0.42, "기술제안서", PURPLE, WHITE, size=12) - chip(s, 2.72, 2.3, 2.25, 0.42, "Technical Proposal", NAVY, WHITE, size=10.5) - text(s, 0.85, 2.88, 11, 1.9, [ - [{"t":"킨텍스 자동전시시스템","size":44,"color":WHITE,"bold":True}], - [{"t":"구축 기술제안서","size":44,"color":WHITE,"bold":True}], - ], line_spacing=1.02) - # 슬로건 배너 - rect(s, 0.85, 4.7, 11.4, 0.66, fill=RGBColor(0x14,0x35,0x63), shape=MSO_SHAPE.ROUNDED_RECTANGLE) - rect(s, 0.85, 4.7, 0.1, 0.66, fill=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, "camera-render", 1.06, 4.86, 0.34, WHITE) - text(s, 1.55, 4.7, 10.6, 0.66, "신청서를 내는 순간, 시공 후 사진을 먼저 본다.", - size=18, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 0.88, 5.55, 11.4, 0.4, - "설계 → 시각화 → 발주 → 시공 → 운영 → 분석. 킨텍스에서 한 흐름으로 끝난다.", - size=12.5, color=SUBLT) - rect(s, 0.85, 6.12, 11.63, 0.02, fill=RGBColor(0x3A,0x5A,0x8C)) - text(s, 0.85, 6.32, 8.5, 0.4, [[{"t":"사업명 ","size":10.5,"color":RGBColor(0x90,0xA6,0xC9)}, - {"t":"킨텍스(KINTEX) 자동전시시스템 구축","size":11,"color":WHITE,"bold":True}]]) - text(s, 0.85, 6.68, 9.2, 0.4, [[{"t":"기술스택 ","size":10.5,"color":RGBColor(0x90,0xA6,0xC9)}, - {"t":"React(Vite)+TS · Spring Boot 3.x(Java17)+MyBatis · PostgreSQL(PostGIS) · Redis · 나노바나나 워커 · Claude AI","size":9.5,"color":WHITE}]]) - text(s, 9.7, 6.32, 2.78, 0.4, "작성일 2026-07-11", size=11, color=WHITE, bold=True, align=PP_ALIGN.RIGHT) - text(s, 9.7, 6.68, 2.78, 0.4, "제안 · GUARDiA/WISE 팀", size=9, color=RGBColor(0x90,0xA6,0xC9), align=PP_ALIGN.RIGHT) - -# ================================================================ -# 간지 divider (배점 배지) -# ================================================================ -def divider(no, kr, en, points, score, score_fill=BLUE): - s = slide() - rect(s, -0.1, -0.1, 13.53, 7.7, fill=BLUE_DK) - rect(s, 8.6, -1.5, 8, 11, fill=BLUE, shape=MSO_SHAPE.PARALLELOGRAM) - rect(s, 10.9, -2, 6, 12, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) - text(s, 1.0, 2.05, 5, 2.2, no, size=150, color=RGBColor(0x2C,0x4E,0x82), bold=True) - rect(s, 1.15, 3.9, 0.9, 0.09, fill=PURPLE) - text(s, 1.15, 4.05, 7, 0.8, kr, size=34, color=WHITE, bold=True) - text(s, 1.18, 4.9, 7, 0.4, en, size=13, color=RGBColor(0x9C,0xB4,0xD8), bold=True) - for i, p in enumerate(points): - yy = 2.5 + i*0.62 - rect(s, 7.55, yy+0.07, 0.16, 0.16, fill=PURPLE, shape=MSO_SHAPE.OVAL) - text(s, 7.85, yy, 4.7, 0.5, p, size=12.5, color=WHITE, anchor=MSO_ANCHOR.MIDDLE) - # 배점 배지 - rect(s, 10.05, 0.7, 2.5, 0.6, fill=score_fill, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, 10.05, 0.7, 2.5, 0.6, score, size=13, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - return s - -# ================================================================ -# 도식 ① 3중 해자 벤다이어그램 -# ================================================================ -def venn(s, detail=False): - cx = 5.0 if not detail else 4.3 - A=(cx-0.0, 3.55); B=(cx+2.5, 3.55); C=(cx+1.25, 5.35); r=1.95 - # 원(연한 fill + 굵은 stroke) - rect(s, A[0]-r/2, A[1]-r/2, r, r, fill=PURPLE_LT, line=PURPLE, line_w=2.5, shape=MSO_SHAPE.OVAL) - rect(s, B[0]-r/2, B[1]-r/2, r, r, fill=BLUE_LT, line=BLUE, line_w=2.5, shape=MSO_SHAPE.OVAL) - rect(s, C[0]-r/2, C[1]-r/2, r, r, fill=RGBColor(0xE1,0xF3,0xF1), line=TEAL, line_w=2.5, shape=MSO_SHAPE.OVAL) - # 라벨 - icon(s, "ai-sparkle", A[0]-1.35, A[1]-1.15, 0.3, PURPLE) - text(s, A[0]-2.35, A[1]-1.5, 2.3, 0.3, "나노바나나 시공 예측", size=11.5, color=PURPLE, bold=True, align=PP_ALIGN.RIGHT) - text(s, A[0]-2.35, A[1]-1.2, 2.3, 0.3, "image-to-image·40초·S1~S7", size=8, color=MUTED, align=PP_ALIGN.RIGHT) - icon(s, "postgis-spatial", B[0]+1.05, B[1]-1.15, 0.3, BLUE) - text(s, B[0]+0.05, B[1]-1.5, 2.4, 0.3, "PostGIS 실측 공간데이터", size=11.5, color=BLUE, bold=True) - text(s, B[0]+0.05, B[1]-1.2, 2.5, 0.3, "폴리곤·트렌치·배선 단일 원천", size=8, color=MUTED) - icon(s, "closed-loop", C[0]-0.15, C[1]+0.95, 0.3, TEAL) - text(s, C[0]-2.6, C[1]+1.0, 2.3, 0.3, "공사 옥션 폐루프", size=11.5, color=TEAL, bold=True, align=PP_ALIGN.RIGHT) - text(s, C[0]+0.25, C[1]+1.0, 2.7, 0.3, "AI자료→응찰→낙찰→발주 자동전환", size=8, color=MUTED) - # 중앙 해자 배지 - mx=cx+1.25 - rect(s, mx-1.1, 3.95, 2.2, 0.78, fill=INK, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, mx-1.1, 4.0, 2.2, 0.34, "우리만의 해자(Moat)", size=11, color=WHITE, bold=True, align=PP_ALIGN.CENTER) - text(s, mx-1.1, 4.34, 2.2, 0.3, "재현 난이도 높은 결합", size=8.5, color=SUBLT, align=PP_ALIGN.CENTER) - if detail: - rc = titlecard(s, 8.95, 1.55, 3.83, 5.0, "3결합 재현이 어려운 이유", PURPLE, [ - "원A [검증됨] — G1 승인·나노바나나 워커 라이브, image-to-image 구조보존 파이프라인 상주", - "원B [검증됨] — M2~M5 PostGIS 매퍼 라이브, 부스 폴리곤·트렌치·배선 단일 공간 원천", - "원C [구현 계획] — Phase D·M15, AI 설계자료가 곧 응찰 근거(attached_refs)", - "결합의 희소성 — 단일 요소는 모방 가능하나 셋의 폐루프 결합은 경쟁 전시테크·범용 SI가 재현 곤란", - "실증 우위 — '말이 아니라 이미 돈다'(인프라·인증·PostGIS·나노바나나·자동배포 기동 중)", - ], headfill=PURPLE, bodysize=9.3, igap=0.86) - - -# ================================================================ -# 후행 정의 헬퍼 (조립 시 상단 hoist) -# ================================================================ - -def scr_path(scr): return os.path.join(SCREENS, scr+".png") -def screen_panel(s, scr, x, y, w, h, label): - p = scr_path(scr) - if os.path.exists(p): - img_fit(s, p, x, y, w, h) - chip(s, x+0.12, y+h-0.36, 0.95, 0.28, scr, PURPLE, size=8) - text(s, x+1.15, y+h-0.37, w-1.3, 0.3, label, size=8.5, color=MUTED, anchor=MSO_ANCHOR.MIDDLE) - else: - rect(s, x, y, w, h, fill=HALL_BG, line=LINE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, w, h, label, size=10, color=MUTED, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -def org(x,y,w,h,t,sub,fill,tc,ln=None,big=False): - rect(s,x,y,w,h,fill=fill,line=(ln or fill),line_w=1.2,shape=MSO_SHAPE.ROUNDED_RECTANGLE,shadow=big) - text(s,x,y+(0.08 if sub else 0),w,(0.3 if sub else h),t,size=(11.5 if big else 10),color=tc,bold=True, - align=PP_ALIGN.CENTER,anchor=(MSO_ANCHOR.TOP if sub else MSO_ANCHOR.MIDDLE)) - if sub: text(s,x,y+0.34,w,h-0.38,sub,size=7.8,color=(MUTED if fill==WHITE else WHITE),align=PP_ALIGN.CENTER) - -def _modbox(col, ry, rh, code, name, cl, icn, strong=False): - x=mx0+col*mcw+0.03 - rect(s, x, ry, mcw-0.12, rh, fill=(PURPLE_LT if cl==PURPLE else (BLUE_LT if cl==BLUE else HALL_BG)), - line=cl, line_w=(2.2 if strong else 1.0), shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.08, ry+0.05, mcw-0.24, 0.28, [[{"t":code+" ","size":9,"color":cl,"bold":True},{"t":name,"size":7.6,"color":INK}]], line_spacing=0.9) - if icn: icon(s, icn, x+mcw-0.44, ry+0.06, 0.26, cl) - -def band(y, h, fill, ln, label): - rect(s, 0.55, y, 10.6, h, fill=fill, line=ln, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - rect(s, 0.55, y, 1.45, h, fill=ln, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.55, y, 1.45, h, label, size=9, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.9) -def nd(x, y, w, h, t, fill, tc, ln=None, sz=8.2): - rect(s, x, y, w, h, fill=fill, line=(ln or LINE), line_w=0.8, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.04, y, w-0.08, h, t, size=sz, color=tc, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.88) - -# ================================================================ -# 슬라이드 시퀀스 (나라장터 표준 7대장) -# ================================================================ - -cover() - - -# ---- 목차 (표준 7대장) ---- -s = slide(); content_header(s, "CONTENTS", "목차 — 나라장터 정보시스템 표준 7대장", "―") -toc=[("Ⅰ","일반현황","제안사·수행조직·핵심 투입인력(M/M)·정량 대응",BLUE), - ("Ⅱ","전략 및 방법론","사업이해·추진전략·적용기술·표준FW·개발방법론",PURPLE), - ("Ⅲ","기술 및 기능","나노바나나·배치설계배선·옥션·관람·BI·데이터·보안",PURPLE), - ("Ⅳ","성능 및 품질","성능·SLA·품질·인터페이스·관측성",BLUE), - ("Ⅴ","프로젝트 관리","일정·관리방법론·위험이슈·품질형상",BLUE), - ("Ⅵ","프로젝트 지원","인수인계·교육·기술지원·유지보수(SM)·비상대책",BLUE), - ("Ⅶ","그 밖의 사항","확장·상생협력·하도급·기대효과",PURPLE), - ("부록","A·B·C·D","확인필요·REQ커버리지·성숙도·화면 갤러리",NAVY)] -colw=5.9 -for i,(no,t,d,cl) in enumerate(toc): - col=i//4; row=i%4 - x=0.6+col*(colw+0.33); y=1.55+row*1.24 - rect(s, x, y, colw, 1.08, fill=CARD, line=LINE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - rect(s, x, y, 0.09, 1.08, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.26, y, 1.0, 1.08, no, size=(20 if no!="부록" else 13), color=cl, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+1.35, y+0.2, colw-1.6, 0.4, t, size=13.5, color=INK, bold=True) - text(s, x+1.35, y+0.62, colw-1.6, 0.4, d, size=8.6, color=MUTED, line_spacing=0.95) -caption(s, "표준 7대장(Ⅰ~Ⅶ) + 부록 — 평가부문과 장 구조를 1:1 정렬. Ⅲ(기술·기능)에 제안 무게 집중.", y=6.68) - -# ---- 배점 대응 맵 (90/10) ---- -s = slide(); content_header(s, "표준 목차 · 평가배점 대응 맵", "정보화사업 표준 배점 — 기술 90(정성70+정량20) / 가격 10에 장별 정렬", "―") -strip=[("기술능력 90","정성 70 + 정량 20",BLUE,7.4),("가격 10","입찰가격평가",PURPLE,4.83)] -sx=0.55 -for name,sub,cl,ww in strip: - rect(s, sx, 1.5, ww, 0.72, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, sx+0.2, 1.55, ww-0.3, 0.36, name, size=13, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, sx+0.2, 1.92, ww-0.3, 0.26, sub, size=8.5, color=SUBLT, anchor=MSO_ANCHOR.MIDDLE) - sx+=ww+0.15 -heat=[("전략 및 방법론","Ⅱ","사업이해·추진전략·적용기술·표준FW·개발방법론",PURPLE), - ("기술 및 기능 (최다)","Ⅲ","나노바나나·배치설계배선·옥션·관람·BI·기술보안",PURPLE), - ("성능 및 품질","Ⅳ","성능·SLA·품질·인터페이스·관측성",BLUE), - ("프로젝트 관리","Ⅴ","일정·관리방법론·위험이슈·품질형상",BLUE), - ("프로젝트 지원","Ⅵ","인수인계·교육·기술지원·유지보수(SM)·비상",BLUE), - ("정량(보유인력·경영·실적·상생)","Ⅰ·Ⅶ","일반현황 증빙·상생협력·하도급",TEAL)] -y0=2.5 -for i,(name,ch,desc,cl) in enumerate(heat): - y=y0+i*0.62 - rect(s, 0.55, y, 4.0, 0.5, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.72, y, 2.95, 0.5, name, size=9.8, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - rect(s, 3.85, y+0.06, 0.62, 0.38, fill=WHITE, line=cl, line_w=1.2, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 3.85, y+0.05, 0.62, 0.4, ch, size=11, color=cl, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, 4.7, y, 8.0, 0.5, desc, size=9.2, color=INK, anchor=MSO_ANCHOR.MIDDLE) -caption(s, "배점은 수요기관 재량 — 정보화 지침 제18조 원칙값(기술90/가격10, 정성70+정량20) 표기. 실제 공고 확인 시 재정렬.", y=6.5) - -# ---- [가정] 고지 (표준 7대장) ---- -s = slide(); content_header(s, "표준 목차 · [가정] 고지 간지", "가정은 숨기지 않고 드러냅니다 — 실제 공고 확인 시 즉시 재정렬합니다", "―") -card(s, 0.55, 1.6, 12.23, 2.15) -rect(s, 0.55, 1.6, 0.1, 2.15, fill=AMBER, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -icon(s, "risk-warning", 0.85, 1.85, 0.55, AMBER) -text(s, 1.6, 1.85, 11.0, 0.4, "표준 7대장 재편 — 평가배점·목차 가정 고지", size=13, color=AMBER, bold=True) -bullets(s, 1.6, 2.4, 10.9, [ - "목차 — 조달청 별지 제5호서식(정보시스템 구축) 표준 7대장(Ⅰ~Ⅶ)으로 재편", - "배점 — 정보화사업 표준 기술 90(정성70+정량20)/가격 10 [가정], 실제 공고 확인 시 재정렬", - "상생협력·하도급·투입인력 실명·단가는 [공고·컨소시엄 확정 시 기재]", - "확인 필요(T1~T5)는 부록 A에 집약 → 정직성을 강점으로 전환", -], dot=AMBER, gap=0.42) -lead(s, 0.55, 4.05, 12.23, 0.56, "우리의 태도 — 약한 고리를 먼저 드러내고 방어 논리를 붙인다. 발주처 협업 자세로 신뢰를 얻는다.", fill=INK, size=13) -caption(s, "실제 공고가 확인되면 배점·목차·분량·투입계획을 즉시 재정렬한다.") - -# ================================================================ -# Ⅰ 일반현황 -# ================================================================ - -divider("Ⅰ", "일반현황", "GENERAL STATUS", ["제안사·수행조직", "핵심 투입인력 M/M", "정량평가 대응"], "정량 대응 · 20점", TEAL) - -# ---- S02 제안 개요 ---- -s = slide(); content_header(s, "Ⅰ. 일반현황 · 제안 개요", "도면이 아니라 사진으로 컨펌하고, 그 사진으로 발주까지 종결한다", "Ⅰ", "REQ-A-001") -assets = [("camera-render","나노바나나 시공 예측","도면을 못 읽어도 신청 시점에 '시공 후 사진'으로 의사결정", PURPLE), - ("postgis-spatial","PostGIS 실측 공간데이터","부스 폴리곤·트렌치·배선 LineString 단일 원천에서 검증·정산·BI", BLUE), - ("closed-loop","공사 옥션 폐루프","AI 설계자료→응찰→낙찰→발주 자동전환, 한 흐름에서 종결", TEAL)] -cw=3.95 -for i,(ic,t,d,cl) in enumerate(assets): - x=0.55+i*(cw+0.19) - card(s, x, 1.55, cw, 2.15) - rect(s, x, 1.55, cw, 0.08, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, ic, x+0.28, 1.85, 0.62, cl) - text(s, x+1.05, 1.9, cw-1.2, 0.5, t, size=13, color=cl, bold=True) - text(s, x+0.28, 2.68, cw-0.5, 0.9, d, size=10, color=INK, line_spacing=1.0) -lead(s, 0.55, 3.95, 12.23, 0.62, "차별점 한 줄 — “말이 아니라 이미 돈다”: 인프라·인증·PostGIS 코어·나노바나나·자동배포가 라이브로 기동 중", fill=BLUE, size=14) -bullets(s, 0.55, 4.85, 12.0, [ - "우리는 누구 — GUARDiA 표준 프레임워크(WISE/UIWS) 검증 자산 위에 전시 도메인 코어를 얹은 팀", - "왜 우리인가 — ①나노바나나 시공 예측 ②PostGIS 실측 공간데이터 ③공사 옥션 폐루프, 세 결합을 이미 라이브로 검증 [검증됨]", - "제안 무게 — 기술·기능(Ⅲ) 배점 비중이 가장 큼, 제안서 분량을 그에 맞춰 배분", -], dot=PURPLE, gap=0.5) -caption(s, "단일 요소는 모방할 수 있으나, 세 결합은 재현하기 어렵다 — 우리만의 해자.") - -# ---- Ⅰ 핵심 투입인력 (M/M) ---- -s = slide(); content_header(s, "Ⅰ. 일반현황 · 핵심 투입인력", "직군·등급·투입 M/M로 수행역량을 정량 제시 — 실명·단가는 공고 확정 시 확정", "Ⅰ") -mm_rows=[("총괄 PM","4","4","4","12"), - ("아키텍트(AA·SA·TA·DA·NA)","8","4","2","14"), - ("백엔드(공통·코어·도메인)","12","16","12","40"), - ("프론트엔드(역할별 6포털)","8","12","8","28"), - ("DB 엔지니어(PostGIS)","4","4","2","10"), - ("AI 개발(Claude·제약 솔버)","4","4","2","10"), - ("시각화(나노바나나 워커)","4","2","2","8"), - ("QA","4","6","6","16"), - ("DevOps/인프라","2","2","4","8"), - ("소계 (핵심 개발)","50","54","42","146")] -table(s, 0.55, 1.6, 8.7, ["직군","P1","P2","P3","합계(M/M)"], mm_rows, [3.4,1.05,1.05,1.05,2.15], rowh=0.42, hsize=9.5, bsize=9, hl_rows={9}) -titlecard(s, 9.45, 1.6, 3.33, 4.6, "투입 요약", PURPLE, [ - "핵심 개발 약 146 M/M + PMO·품질 오버헤드 4~8 → 총 ~150 M/M", - "12개월(Phase 1~3) 기준 · 상주 1.0 FTE PM", - "라이브 스캐폴드·인증·인프라·나노바나나로 착수 공수 실측 절감", - "실명 대신 직군·등급·M/M만 기재(개인정보·단가 미노출)", -], headfill=PURPLE, bodysize=9.0, igap=0.98) -chip(s, 0.55, 6.35, 5.7, 0.4, "가격제안 투입인력표와 1:1 매핑", BLUE, size=10) -caption(s, "직군별 공수(M/M) 산정 — 총 ~150 M/M. 예산·기간은 [가정](공공 SI 통례), 공고 확인 시 재정렬.", y=6.8) - -# S15 추진체계도 (도식 ⑬) -s = slide(); content_header(s, "Ⅰ. 일반현황 · 수행조직·추진체계", "거버넌스·아키텍트·코어·도메인·AI·QA·DevOps가 게이트로 맞물린다", "Ⅰ", "REQ-N-004") -org(4.9,1.45,3.55,0.7,"프로젝트 총괄 (PM)","범위·일정·리스크·게이트(G1·G2)",BLUE,WHITE,big=True) -line_conn(s,6.67,2.15,6.67,2.45,SLATE,1.2) -org(2.95,2.48,3.35,0.62,"개발 PM","WBS·스프린트·품질게이트",PURPLE,WHITE) -org(7.05,2.48,3.35,0.62,"PMO","관리표준·형상·QA 거버넌스",PURPLE,WHITE) -rect(s,0.55,3.35,12.23,0.38,fill=BLUE_LT,shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s,0.72,3.35,4,0.38,"아키텍트 그룹",size=10.5,color=BLUE_DK,bold=True,anchor=MSO_ANCHOR.MIDDLE) -for i,(a,d) in enumerate([("AA","앱 아키텍처"),("SA","시스템·NFR"),("TA","기술표준"),("DA","데이터·ERD"),("NA","네트워크·보안")]): - org(0.55+i*2.46,3.82,2.32,0.62,a,d,WHITE,BLUE,BLUE) -rect(s,0.55,4.6,12.23,0.38,fill=PURPLE_LT,shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s,0.72,4.6,4,0.38,"구현 그룹",size=10.5,color=PURPLE,bold=True,anchor=MSO_ANCHOR.MIDDLE) -for i,(a,d) in enumerate([("공통/시스템","common-dev"),("백엔드","backend-dev"),("프론트","frontend-dev"),("DB","db-engineer"),("도메인","옥션·관람·CMS·BI·관리자"),("AI·시각화","ai-dev·visualizer")]): - org(0.55+i*2.05,5.07,1.93,0.66,a,d,WHITE,PURPLE,PURPLE) -org(3.4,5.88,3.0,0.6,"품질 (kintex-qa)","점진 QA·경계면·보안 불변",GREEN,WHITE) -org(6.95,5.88,3.0,0.6,"배포 (devops-dev)","CI/CD·systemd·env",NAVY,WHITE) -caption(s, "선행 게이트(G1·G2) + Phase 게이트 QA 통과로 진행 — 거버넌스·아키텍트·실행 계층.") - -# ---- Ⅰ 정량평가 대응 ---- -s = slide(); content_header(s, "Ⅰ. 일반현황 · 정량평가 대응", "정량평가(보유인력·경영·실적·상생) 항목별 증빙 대응 — 총 20점 이내", "Ⅰ") -q=[("보유인력","투입 직군·등급·자격 증빙, 상주 PM·아키텍트 확보","org-people",BLUE), - ("경영상태","기업신용평가 등급 제출(공고 기준)","dashboard-chart",PURPLE), - ("수행실적","동일·유사 정보시스템 구축 실적 증빙","document-rtm",TEAL), - ("상생협력","중소기업 공동수급 참여지분율(별표11, 최대 5점)","tenant-building",BLUE_DK), - ("사회적책임·지리","가점 항목 대응(공고 지정 시)","check-verify",NAVY)] -for i,(t,d,ic,cl) in enumerate(q): - x=0.55+(i%3)*4.11; y=1.6+(i//3)*1.75 - card(s, x, y, 3.97, 1.55) - icon(s, ic, x+0.22, y+0.28, 0.5, cl) - text(s, x+0.9, y+0.28, 2.9, 0.4, t, size=12, color=cl, bold=True) - text(s, x+0.9, y+0.75, 2.9, 0.7, d, size=9, color=INK, line_spacing=1.0) -lead(s, 0.55, 5.35, 12.23, 0.6, "정량은 증빙 미제출 시 최하점 — 발주 확인 후 별첨(증빙)으로 100% 대응, 배점 20점 이내 확보.", fill=INK, size=12.5) -caption(s, "정량평가 항목 대응 맵 — 보유인력·경영상태·수행실적·상생협력·사회적책임. [가정] 공고 확인 시 확정.") - -# ================================================================ -# Ⅱ 전략 및 방법론 -# ================================================================ - -divider("Ⅱ", "전략 및 방법론", "STRATEGY & METHODOLOGY", ["사업이해·추진전략", "적용기술(나노바나나·AI)", "표준FW·개발방법론"], "정성 · 전략및방법론", PURPLE) - -# ---- S04 핵심 요약 (도식 ① 요약) ---- -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 제안 핵심 요약", "단일 요소는 모방할 수 있으나, 세 결합은 재현하기 어렵다 — 우리만의 해자", "Ⅱ", "REQ-A-001·002") -venn(s, detail=False) -# 우측 Win Theme 요약 -wl = ["WT-1 시공 예측", "WT-2 배치·설계·배선 3안+규정검증", "WT-3 옥션 폐루프", "WT-4 보안 3중 격리", "WT-5 멀티테넌트 SaaS"] -for i,w in enumerate(wl): - y=1.7+i*0.52 - rect(s, 9.1, y, 3.68, 0.44, fill=BLUE_LT, line=BLUE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 9.28, y, 3.5, 0.44, w, size=9.5, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) -caption(s, "셋의 결합은 경쟁 전시테크·범용 SI가 재현하기 어렵다. 최다 배점은 Ⅲ(기술·기능).") - -# S07 배경·목적 -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 사업 이해도", "108,011㎡의 아시아권 상위 전시장, 준비는 여전히 HWP·CAD 수작업이다", "Ⅱ") -mets = [("108,011㎡","현 전시면적",BLUE),("178,000㎡","2028 제3전시장 완공",PURPLE),("수일~수주","CAD 외주 배치 리드타임",AMBER),("HWP·육안","신고·검수 방식",SLATE)] -mw=2.94 -for i,(v,l,cl) in enumerate(mets): - x=0.55+i*(mw+0.16) - rect(s, x, 1.55, mw, 1.1, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, x, 1.68, mw, 0.5, v, size=22, color=WHITE, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 2.24, mw, 0.32, l, size=9.5, color=WHITE, align=PP_ALIGN.CENTER) -titlecard(s, 0.55, 2.9, 6.05, 3.3, "현행 병목 (As-Is)", SLATE, [ - "부스 배치 — CAD 외주 수작업, 수일~수주 소요", - "신고서류 — HWP 수기 작성·이메일 제출", - "배선·위치표시도 — 육안 작도, 수기 위치표시", - "규정 검수 — 홀매니저 육안 검토 병목", - "시공 결과 — 개장일에 처음 대면(사전 시각화 부재)", -], headfill=SLATE, bodysize=9.8, igap=0.5) -titlecard(s, 6.75, 2.9, 6.03, 3.3, "추진 목적 (To-Be)", PURPLE, [ - "전시 준비 전 과정을 AI로 자동화 — 설계·검증·시각화", - "리드타임·오류·외주비용을 동시 절감", - "핵심 차별화 — 시공 전 '결과 사진' 선(先)시각화", - "설계→시각화→발주→시공→운영→분석 폐루프 완성", - "KINTEX 기준 + 다중 전시관 멀티테넌트 SaaS 확장", -], headfill=PURPLE, bodysize=9.8, igap=0.5) -caption(s, "규모는 국내 최대·아시아권 상위이나, 준비 실무는 수작업에 정체 — 자동화 여지 큼.") - -# S08 As-Is 타임라인 -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 사업 이해도", "D-150부터 D-7까지, 마디마다 사람이 기다리고 CAD를 외주한다", "Ⅱ") -steps = [("D-150","판매·견적","견적을 담당자가 수기 회신 → 대기 발생",BLUE), - ("D-30","배치·설계","CAD 외주 수일~수주, 규정 위반 육안 검수",AMBER), - ("D-25","유틸리티","전기·네트워크·급배수 신청이 채널별 파편화",AMBER), - ("D-7","제출·검수","kxwp 제출·서류 육안 검토",RED)] -line_conn(s, 0.9, 3.1, 12.4, 3.1, color=SLATE, w=1.6) -bw=2.85 -for i,(d,t,pain,cl) in enumerate(steps): - x=0.7+i*(bw+0.18) - rect(s, x+bw/2-0.13, 2.97, 0.26, 0.26, fill=cl, shape=MSO_SHAPE.OVAL) - chip(s, x+bw/2-0.6, 2.3, 1.2, 0.42, d, cl, size=12) - card(s, x, 3.45, bw, 1.5) - text(s, x+0.2, 3.6, bw-0.4, 0.35, t, size=12, color=cl, bold=True) - text(s, x+0.2, 4.0, bw-0.4, 0.85, pain, size=9.5, color=INK, line_spacing=1.0) - icon(s, "risk-warning", x+bw-0.55, 3.58, 0.32, cl) -lead(s, 0.55, 5.3, 12.23, 0.62, "진단 — 각 마디의 대기·외주·육안 작업이 리드타임과 오류의 원천. To-Be 자동화의 근거.", fill=INK, size=13) -caption(s, "As-Is 프로세스 병목 — D-데이별 Pain Point.") - -# S09 페르소나 -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 사업 이해도", "6역할+2관리자, 각자의 과업·고통·기대를 정확히 안다", "Ⅱ", "REQ-F-081") -pers = [("org-people","주최자 (organizer)","배치·행사 총괄","CAD 수작업 고통",BLUE), - ("visitor-badge","참가업체 (exhibitor)","부스 신청·설계","도면 못 읽어 개장일 첫 대면",PURPLE), - ("auction-gavel","공사·장치업체","견적·시공","리스트+엑셀 매칭, 비교 부재",TEAL), - ("check-verify","홀매니저 (ops)","규정 검수·승인","육안 검수 병목",AMBER), - ("mobile","관람객 (visitor)","등록·현장","배지·체크인 대기",BLUE), - ("dashboard-chart","일반 대중 (public)","공개 열람","일정·플로어플랜 접근",SLATE)] -cw=3.95 -for i,(ic,role,task,pain,cl) in enumerate(pers): - x=0.55+(i%3)*(cw+0.19); y=1.55+(i//3)*2.35 - card(s, x, y, cw, 2.15) - rect(s, x, y, cw, 0.08, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, ic, x+0.24, y+0.28, 0.5, cl) - text(s, x+0.9, y+0.32, cw-1.0, 0.5, role, size=11.5, color=cl, bold=True, line_spacing=0.92) - text(s, x+0.24, y+1.0, cw-0.48, 0.32, [[{"t":"과업 ","color":MUTED,"size":9,"bold":True},{"t":task,"color":INK,"size":9}]]) - text(s, x+0.24, y+1.35, cw-0.48, 0.32, [[{"t":"Pain ","color":RED,"size":9,"bold":True},{"t":pain,"color":INK,"size":9}]]) - text(s, x+0.24, y+1.7, cw-0.48, 0.32, [[{"t":"기대 ","color":GREEN,"size":9,"bold":True},{"t":"각 과업의 자동화·가시화","color":INK,"size":9}]]) -caption(s, "+ 플랫폼 관리자·테넌트 관리자 2관리자 → 역할별 포털 분리 근거(REQ-F-081).") - -# S10 To-Be 정량 -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 사업 이해도", "견적은 즉시, 배치는 수 분, 검수는 자동, 결과는 사진으로", "Ⅱ", "REQ-F-002·007·009") -rows=[("홀 배정 견적","수일 대기","즉시 (규칙엔진 2,250원/㎡ 자동)"), - ("배치도 초안","수주 CAD 외주","수 분 3안 자동생성"), - ("규정 검수","육안 검토","제출 전 자동 플래깅"), - ("위치표시도","수기 작도","좌표 클릭 자동 작도"), - ("시공 결과","개장일 첫 대면","신청 시점 사진 예측")] -table(s, 0.55, 1.7, 12.23, ["개선 항목","As-Is","To-Be"], rows, [3.6,3.9,4.73], rowh=0.62, hsize=11, bsize=10.5) -for i in range(len(rows)): - text(s, 7.35, 2.32+i*0.62, 0.4, 0.5, "→", size=13, color=PURPLE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -caption(s, "As-Is→To-Be 정량 개선 5행 — 리드타임 수일→즉시·수 분, 검수 육안→자동.") - -# S11 비전·포지셔닝 -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 추진전략", "도면·표를 보여주는 전시테크가 아니라, 사진으로 컨펌하는 베뉴 운영 플랫폼", "Ⅱ") -lead(s, 0.55, 1.6, 12.23, 1.05, "“도면이 아니라 사진으로 컨펌한다”", fill=BLUE_DK, size=26) -titlecard(s, 0.55, 2.9, 3.95, 3.3, "사진급 의사결정", PURPLE, [ - "기존 전시테크 = 도면·3D 뷰어 수준", - "우리 = 사진급 시공 예측", - "신청 시점에 결과를 본다", -], headfill=PURPLE, bodysize=10, igap=0.55) -titlecard(s, 4.65, 2.9, 3.95, 3.3, "단일 폐루프", BLUE, [ - "설계 자료가 곧 발주 근거", - "발주까지 한 흐름에서 종결", - "재입력·중복 작업 제거", -], headfill=BLUE, bodysize=10, igap=0.55) -titlecard(s, 8.75, 2.9, 4.03, 3.3, "데이터 주권", TEAL, [ - "온프레미스 Ollama 기본", - "외부 AI(Claude·Gemini)는 승인 예외만", - "데이터존 아웃바운드 0", -], headfill=TEAL, bodysize=10, igap=0.55) -caption(s, "포지셔닝 — 사진 수준 의사결정 + 폐루프 종결 + 데이터 주권.") - -# S12 3중 해자 상세 (도식 ①) -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 추진전략", "셋 중 하나는 흉내낼 수 있어도, 셋의 결합은 재현 난이도가 높다", "Ⅱ", "REQ-A-001·002") -venn(s, detail=True) -caption(s, "3중 해자 — 나노바나나 시공 예측 ∩ PostGIS 실측 공간데이터 ∩ 공사 옥션 폐루프.") - -# S13 Win Theme 5 -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 추진전략", "다섯 개의 차별화 메시지가 배점 다섯 곳을 정확히 겨눈다", "Ⅱ", "REQ-A-001·S-009") -wts = [("WT-1","도면이 아니라 사진으로 컨펌한다","AI 시각화 핵심","20점",PURPLE,"camera-render"), - ("WT-2","수 분 내 3안, 제출 전 규정 자동검증","시각화+기능 교차","20+15",BLUE,"booth-layout"), - ("WT-3","사진 수준 자료로 응찰하고 발주까지","옥션 폐루프","15점",TEAL,"auction-gavel"), - ("WT-4","AI가 만든 것은 표시, 남의 데이터는 차단","보안 3중","10점",BLUE_DK,"shield-security"), - ("WT-5","코드 배포 없이 데이터 온보딩으로 확장","멀티테넌트","5점",BLUE,"tenant-building")] -cw=2.4 -for i,(no,msg,sub,sc,cl,ic) in enumerate(wts): - x=0.55+i*(cw+0.06) - card(s, x, 1.6, cw, 4.6) - rect(s, x, 1.6, cw, 0.5, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 1.6, cw, 0.5, no, size=13, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - icon(s, ic, x+cw/2-0.32, 2.35, 0.64, cl) - text(s, x+0.16, 3.2, cw-0.32, 1.4, msg, size=10.5, color=INK, bold=True, align=PP_ALIGN.CENTER, line_spacing=1.02) - text(s, x+0.16, 4.9, cw-0.32, 0.5, sub, size=9, color=MUTED, align=PP_ALIGN.CENTER) - chip(s, x+cw/2-0.6, 5.5, 1.2, 0.44, sc, cl, size=11) -caption(s, "5개 Win Theme가 평가배점 5개 항목(20·20+15·15·10·5)에 1:1 대응.") - -# S17 나노바나나 개요 -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 적용기술(나노바나나)", "시공 전에 사진으로 의사결정한다 — 경쟁 전시테크가 못 하는 차별화", "Ⅱ", "REQ-A-001") -bullets(s, 0.55, 1.6, 7.0, [ - "문제 — 참가업체는 도면을 못 읽어 개장일에야 결과를 처음 본다", - "해법 — image-to-image로 빈 부스(도면/실측)를 '시공 후 사진'으로 예측", - "실증 — Gemini 라이브(G1 소유자 승인 완료), 워커 상주 [검증됨]", - "효과 — 조감도 외주(비용·시간) 대체, 신청 시점 의사결정", -], dot=PURPLE, gap=0.62, size=11) -screen_panel(s, "SCR-12", 7.85, 1.6, 4.93, 3.5, "시각화 갤러리 — 표준 샷 · 워터마크 표기") -wm_badge(s, 7.85, 5.2, 4.93, 0.62) -lead(s, 0.55, 4.5, 7.0, 0.9, "“도면이 아니라 사진으로 컨펌한다” — 신청 시점에 결과 사진으로 의사결정하는 유일 차별화.", fill=PURPLE, size=13) -caption(s, "Gemini gemini-3.1-flash-image-preview 워커 라이브 [검증됨] · 모든 컷 워터마크 항상 포함.") - -# S19 image-to-image 구조보존 -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 적용기술(나노바나나)", "골격·치수·통로·카메라 앵글은 잠그고, 스타일·재질만 사실화한다", "Ⅱ", "REQ-A-002·003") -titlecard(s, 0.55, 1.6, 6.05, 3.4, "보존 (잠금) — 왜곡·과장 방지", BLUE, [ - "부스 골격·치수", - "통로·동선", - "카메라 앵글·시점", - "참조 이미지 inlineData 기준 구조 고정", -], headfill=BLUE_DK, bodysize=11, igap=0.58) -titlecard(s, 6.75, 1.6, 6.03, 3.4, "교체 (사실화) — 사진 리얼리티", PURPLE, [ - "스타일·컨셉", - "재질·마감", - "조명·분위기", - "구조화 프롬프트 사전(BOOTH_TYPES·STYLES·FIXTURE_LAYERS)", -], headfill=PURPLE, bodysize=11, igap=0.58) -lead(s, 0.55, 5.25, 12.23, 0.62, "검증 패턴 — ReRoomAI의 image-to-image 보존/교체 분리를 이식(reroomai-source.md).", fill=INK, size=13) -caption(s, "보존/교체 파라미터 분리로 왜곡·과장 방지 + 사진급 리얼리티 동시 달성.") - -# S20 파이프라인 (도식 ③) -s = slide(); content_header(s, "Ⅱ. 전략·방법론 · 적용기술(나노바나나)", "생성은 전면 비동기 — 사용자는 대기하지 않고, 완료는 WebSocket으로 밀어준다", "Ⅱ", "REQ-A-007·N-006") -boxes=[("사용자\n렌더 요청",BLUE_LT,BLUE,"mobile"),("Spring 백엔드\nRenderJob·쿼터",WHITE,BLUE,None), - ("Redis 큐\nleftPush",WHITE,BLUE,"pipeline-flow"),("워커\nBLPOP",PURPLE_LT,PURPLE,"ai-sparkle"), - ("Gemini\nimage-to-image",PURPLE_LT,PURPLE,None),("워터마크·S6\n로컬 PIL 래스터",PURPLE_LT,PURPLE,"watermark"), - ("오브젝트\n스토리지",WHITE,BLUE,None),("WebSocket\n완료 푸시",BLUE_LT,BLUE,None)] -bw=1.44; bx0=0.55; by=2.35; bh=1.0 -for i,(t,fl,cl,icn) in enumerate(boxes): - x=bx0+i*(bw+0.075) - rect(s, x, by, bw, bh, fill=fl, line=cl, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - if icn: icon(s, icn, x+bw/2-0.18, by+0.1, 0.36, cl) - text(s, x+0.06, by+(0.46 if icn else 0.14), bw-0.12, bh-0.5, t, size=8.3, color=(PURPLE_DK if cl==PURPLE else BLUE_DK), bold=True, align=PP_ALIGN.CENTER, line_spacing=0.9) - if i in(3,4): chip(s, x+bw/2-0.35, by-0.28, 0.7, 0.24, "라이브", GREEN, size=7.5) - if i point)","최단 경로 ST_Length · 통로 횡단 최소","SRID 0 홀 로컬 미터 · GiST 실시간"] -for i,nt in enumerate(notes): - yy=4.05+i*0.6 - rect(s, 9.6, yy+0.06, 0.09, 0.09, fill=BLUE, shape=MSO_SHAPE.OVAL) - text(s, 9.8, yy, 2.85, 0.55, nt, size=8.6, color=INK, line_spacing=0.95) -caption(s, "홀별 평면도 15장 + CAD 확보 [검증됨] · 미확보 구간은 '가정 좌표(실측 대기)' 라벨(R-2).") - -# S28 위치표시도·유틸리티 신청 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(위치표시도)", "좌표 클릭으로 위치표시도를 자동 작도하고, 신청·견적은 D-25 역산으로 챙긴다", "Ⅲ", "REQ-F-017·018") -bullets(s, 0.55, 1.6, 6.9, [ - "위치표시도 자동생성 — 좌표 클릭 자동 작도, 수기 작도 폐지(REQ-F-017)", - "네트워크·급배수·압축공기 신청 + 마감 리마인더(D-25 역산)(REQ-F-018)", - "자동 견적 연동 — 신청과 동시에 요율 기반 견적 산출", -], dot=BLUE, gap=0.6, size=11) -screen_panel(s, "SCR-08", 7.7, 1.6, 5.08, 3.7, "유틸리티 신청 요약·위치표시도") -caption(s, "수기 위치표시도 폐지 — 좌표 클릭 자동 작도로 오류·리드타임 제거.") - -# ---- P3-D 옥션 ---- -# S29 옥션 폐루프 (도식 ⑦) -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(옥션 폐루프)", "AI가 만든 설계 자료가 곧 응찰 근거이고, 한 흐름에서 발주까지 끝난다", "Ⅲ", "REQ-F-025·026·031") -# 좌측 AI 자료 스택 -rect(s, 0.55, 2.0, 2.7, 3.4, fill=PURPLE_LT, line=PURPLE, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) -icon(s, "ai-sparkle", 1.6, 2.15, 0.5, PURPLE) -text(s, 0.7, 2.72, 2.4, 0.3, "AI 자료 패키지", size=11, color=PURPLE, bold=True, align=PP_ALIGN.CENTER) -for i,t in enumerate(["M2 배치","M3 설계","M4 배선/BOQ","M5 시공예측 이미지"]): - yy=3.15+i*0.5 - rect(s, 0.75, yy, 2.3, 0.4, fill=WHITE, line=PURPLE, line_w=0.8, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.75, yy, 2.3, 0.4, t, size=9, color=INK, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -flow=[("옥션 개설\n(역경매/RFQ/고정가)",BLUE,"auction-gavel"), - ("등록업체 검증 게이트\n미등록 응찰 차단 403",RED,"shield-security"), - ("견적서 제출\nPDF·버전",BLUE,"document-rtm"), - ("실시간 순위·라운드 마감\nRedis 정렬셋·WS 델타",BLUE,"dashboard-chart"), - ("종합평가 낙찰\n가격+평판+납기",PURPLE,"check-verify"), - ("Award → 계약/발주 자동전환\nM6·M9",TEAL,"closed-loop")] -positions=[(3.55,2.0),(6.65,2.0),(9.75,2.0),(9.75,3.75),(6.65,3.75),(3.55,3.75)] -fw=2.9; fh=1.4 -for i,((t,cl,icn),(x,y)) in enumerate(zip(flow,positions)): - rect(s, x, y, fw, fh, fill=(PURPLE_LT if cl==PURPLE else (RGBColor(0xFC,0xE9,0xE7) if cl==RED else (RGBColor(0xE1,0xF3,0xF1) if cl==TEAL else BLUE_LT))), - line=cl, line_w=(2.0 if cl==RED else 1.2), shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - icon(s, icn, x+0.14, y+0.14, 0.34, cl) - text(s, x+0.56, y+0.12, fw-0.6, fh-0.2, t, size=9.3, color=(INK), bold=True, line_spacing=0.95, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+fw/2-0.12, y-0.02, 0.3, 0.4, str(i+1), size=9, color=WHITE, bold=True, align=PP_ALIGN.CENTER) - chip(s, x+fw/2-0.15, y-0.02, 0.3, 0.3, str(i+1), cl, size=8) -line_conn(s, 3.25, 3.0, 3.55, 2.7, color=PURPLE, w=1.5) -caption(s, "AI 설계자료가 곧 발주 근거. 사진 수준 자료로 응찰→발주까지 한 흐름 [구현 계획: Phase D-M15].") - -# S30 역경매 순위 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(역경매)", "라운드·마감·최저가·내 위치를 실시간으로, 자동마감까지", "Ⅲ", "REQ-F-027") -rows=[("A 장치","1,240만원","4.2점","D+3","1위"),("B 시공","1,285만원","4.5점","D+2","2위"), - ("C 종합","1,310만원","4.1점","D+4","3위"),("D 부스","1,360만원","3.9점","D+3","4위")] -table(s, 0.55, 1.7, 7.6, ["업체","현재 최저가","평판","납기","순위"], rows, [1.9,1.9,1.2,1.2,1.4], rowh=0.5, hl_rows={0}) -titlecard(s, 8.35, 1.7, 4.43, 3.5, "실시간 메커니즘", BLUE, [ - "실시간 순위 = Redis 정렬셋 + WebSocket 델타 푸시", - "라운드 마감 = Redis 타이머(자동마감)", - "익명 옵션 · 내 위치 표시", - "스캐폴드 4계층·큐·WS 패턴 확정 [구현 계획]", -], headfill=BLUE, bodysize=9.5, igap=0.6) -chip(s, 0.55, 4.4, 3.0, 0.5, "잔여 시간 02:14:37", RED, size=13) -caption(s, "실시간 순위 보드 — 업체 순위·최저가·잔여 시간, 라운드 자동마감.") - -# S31 종합평가 낙찰 (도식 ⑧) -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(종합평가 낙찰)", "가격만이 아니라 평판·납기까지 가중 스코어로 투명하게 낙찰한다", "Ⅲ", "REQ-F-028·030·031") -text(s, 0.55, 1.5, 12.2, 0.3, "가중치(예): 가격 0.6 / 평판 0.25 / 납기 0.15 — 옥션별 설정형(weight jsonb)", size=9.5, color=MUTED) -rows=[("B 시공","1,285만원(92)","4.5(90)","D+2(95)","91.6","1위"), - ("A 장치","1,240만원(96)","4.2(84)","D+3(88)","91.2","2위"), - ("C 종합","1,310만원(88)","4.1(82)","D+4(80)","85.3","3위"), - ("D 부스","1,360만원(80)","3.9(78)","D+3(88)","81.3","4위")] -table(s, 0.55, 1.9, 12.23, ["업체","견적금액(점수)","평판(점수)","납기(점수)","가중 종합점수","순위"], - rows, [2.2,3.0,2.2,2.2,1.83,0.78], rowh=0.56, hl_rows={0}) -# 낙찰 배지 -icon(s, "check-verify", 11.6, 2.5, 0.3, GREEN) -lead(s, 0.55, 4.8, 12.23, 0.62, "선정 사유·항목별 비교 근거 자동 산출 — 가격만이 아니라 평판·납기 가중으로 투명·최적 발주.", fill=INK, size=13) -bullets(s, 0.55, 5.7, 12.0, [ - "등록업체 AI 매칭 추천(REQ-F-030) · 물량서(BOQ) 자동산출 근거(REQ-F-031)", -], dot=PURPLE, gap=0.4, size=10) -caption(s, "1위(B 시공) = BLUE_LT 강조 + 낙찰(Award). 종합점수 가중합 시각화.", y=6.35) - -# S32 등록 게이트·발주 전환 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(등록 게이트·발주)", "미등록 업체는 응찰 자체가 차단(403)되고, 낙찰은 계약·발주로 자동 전환된다", "Ⅲ", "REQ-F-029·032·S-007") -seq=[("응찰 시도",BLUE),("M7 등록 검증\nTB_COMPANY.kintex_registered",PURPLE),("미등록?\nNOT_REGISTERED_COMPANY 403",RED),("등록 확인 → 응찰 허용",GREEN)] -for i,(t,cl) in enumerate(seq): - x=0.55+i*3.12 - rect(s, x, 1.7, 2.9, 1.1, fill=(RGBColor(0xFC,0xE9,0xE7) if cl==RED else (BLUE_LT if cl==BLUE else (PURPLE_LT if cl==PURPLE else RGBColor(0xE3,0xF6,0xEC)))), - line=cl, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, x+0.1, 1.7, 2.7, 1.1, t, size=9.5, color=INK, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.95) - if i<3: text(s, x+2.9-0.05, 1.7, 0.24, 1.1, "›", size=16, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -lead(s, 0.55, 3.2, 12.23, 0.7, "킨텍스 '등록업체 시공 필수' 규정을 시스템이 강제 — 미등록 = 응찰 원천 차단(403).", fill=RED, size=13) -titlecard(s, 0.55, 4.15, 12.23, 2.15, "발주 자동전환", TEAL, [ - "Award → 계약/발주 자동전환(M6 서류·M9 정산 연동)", - "이벤트 디커플 — 모듈 간 직접 호출 없이 이벤트로 연동, 순환 의존 금지", - "낙찰 확정 시점에 계약서·발주서·납부 스케줄 자동 생성 흐름으로 연결", -], headfill=TEAL, bodysize=10, igap=0.5) -caption(s, "등록 검증 시퀀스 + 발주 전환 — 규정 강제와 폐루프 완결.") - -# ---- P3-E 판매·서류 ---- -# S33 판매 M1 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(판매·서류)", "가용성은 실시간 캘린더로, 견적은 규칙엔진으로 즉시, 신청은 웹폼→HWP로", "Ⅲ", "REQ-F-001~006") -bullets(s, 0.55, 1.6, 6.9, [ - "홀/반홀 가용성 실시간 캘린더(REQ-F-001)", - "규칙엔진 자동 견적 — 2,250원/㎡ [협의: C4 요율 공식본](REQ-F-002)", - "배정신청 웹폼→HWP 자동생성(REQ-F-003)", - "부스 판매 인벤토리·온라인 셀프 선택(REQ-F-004·005)", - "임대계약 전자서명(선택, REQ-F-006)", -], dot=BLUE, gap=0.56, size=10.5) -screen_panel(s, "SCR-02", 7.7, 1.6, 5.08, 3.7, "주최자 대시보드 — 행사·가용성·신청 현황") -caption(s, "견적 수일 대기 → 즉시(규칙엔진). 신청 웹폼→HWP 자동생성으로 수작업 제거.") - -# S34 서류 M6 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(서류·마일스톤)", "D-150/30/25/7을 역산해 알리고, 웹폼은 HWP/PDF로, 검수는 AI가 돕는다", "Ⅲ", "REQ-F-020~024·A-011·012") -mile=[("D-150","판매·계약",BLUE),("D-30","배치·설계 마감",AMBER),("D-25","유틸리티 신청",AMBER),("D-7","제출·검수",RED)] -line_conn(s, 0.9, 2.35, 8.2, 2.35, color=SLATE, w=1.5) -for i,(d,t,cl) in enumerate(mile): - x=0.7+i*1.9 - rect(s, x+0.75, 2.22, 0.26, 0.26, fill=cl, shape=MSO_SHAPE.OVAL) - chip(s, x+0.3, 1.65, 1.15, 0.4, d, cl, size=11) - text(s, x, 2.6, 1.85, 0.5, t, size=9, color=INK, bold=True, align=PP_ALIGN.CENTER, line_spacing=0.9) -bullets(s, 0.55, 3.4, 8.0, [ - "마일스톤 자동생성·역산 알림(F-020) · 신고서류 웹폼→HWP/PDF 7종(F-021)", - "서류 버전·전자결재 워크플로(F-023) · OCR 계약·납품서 자동등록(F-024·A-020)", - "AI 서류 검수(누락·불일치)(A-011) · 규정 자연어 챗봇(근거·인용)(A-012)", - "kxwp 제출 릴레이 — 웹폼→HWP/PDF까지 완결, 릴레이만 킨텍스 IT 협의 [협의: C2, R-3](F-022)", -], dot=PURPLE, gap=0.55, size=10) -screen_panel(s, "SCR-10", 9.0, 3.35, 3.78, 2.95, "홀매니저 승인 큐 — 서류·전자결재") -caption(s, "D-데이 역산 알림 + 웹폼→HWP/PDF + AI 검수 — 서류 리드타임·오류 절감.") - -# ---- P3-F 관람·참가 ---- -# S35 관람 M10 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(관람·등록)", "사전등록→모바일 배지→현장 체크인→리드까지, 오프라인에서도 끊기지 않는다", "Ⅲ", "REQ-F-037~044·A-018") -journey=[("사전등록","유형별·중복검증",BLUE,"document-rtm"),("모바일 배지/QR","발급",PURPLE,"mobile"), - ("현장 체크인","QR·즉석 인쇄·집계",BLUE,"visitor-badge"),("리드 캡처","AI 스코어링·분류",PURPLE,"dashboard-chart")] -jw=2.9 -for i,(t,d,cl,icn) in enumerate(journey): - x=0.55+i*(jw+0.14) - card(s, x, 1.65, jw, 2.0) - rect(s, x, 1.65, jw, 0.08, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, icn, x+jw/2-0.28, 1.9, 0.56, cl) - text(s, x+0.1, 2.6, jw-0.2, 0.4, t, size=11.5, color=cl, bold=True, align=PP_ALIGN.CENTER) - text(s, x+0.1, 3.05, jw-0.2, 0.5, d, size=9, color=MUTED, align=PP_ALIGN.CENTER) - if i<3: text(s, x+jw-0.02, 1.65, 0.2, 2.0, "›", size=16, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -chip(s, 4.8, 3.85, 3.7, 0.44, "오프라인 체크인 폴백 (REQ-F-040)", GREEN, size=10) -bullets(s, 0.55, 4.55, 12.0, [ - "참가업체 리드캡처(F-041) · AI 리드 스코어링·분류(F-042·A-018) · 세그먼트 EDM(F-043)", - "티켓발권·AI폼빌더·스마트배지(선택, F-044)", -], dot=PURPLE, gap=0.45, size=10) -caption(s, "등록→체크인→리드 여정 — 오프라인에서도 끊기지 않는 현장 폴백.") - -# S36 비즈매칭·CMS·공개사이트 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(비즈매칭·공개)", "AI가 매칭을 추천하고, 다국어 공개사이트·마이크로사이트가 SEO로 관람객을 모은다", "Ⅲ", "REQ-F-045~051·067~069") -titlecard(s, 0.55, 1.6, 6.05, 3.1, "비즈매칭·마케팅 (M11)", PURPLE, [ - "AI 비즈매칭 추천(F-045·A-018)", - "미팅 슬롯·성과 리포트(F-046)", - "이벤트 모바일앱·세션·채팅·설문(F-047)", - "세그먼트 EDM·캠페인 자동화(F-050)·스폰서십(F-051)", -], headfill=PURPLE, bodysize=9.8, igap=0.52) -titlecard(s, 6.75, 1.6, 6.03, 3.1, "CMS·공개사이트 (M12·M17)", BLUE, [ - "공개 홍보 사이트(SEO·다국어)(F-048)", - "공개 인터랙티브 플로어플랜(F-049)", - "CMS 게시 워크플로·버전(F-067)·마이크로사이트(F-068)", - "다국어 콘텐츠·hreflang(F-069) · AI 카피·번역(A-015)", -], headfill=BLUE, bodysize=9.8, igap=0.52) -lead(s, 0.55, 5.0, 12.23, 0.62, "AI 매칭으로 성과를 높이고, 다국어 SEO 공개사이트로 관람객·바이어를 모은다.", fill=INK, size=13) -caption(s, "공개사이트 + 마이크로사이트 + AI 매칭 — 참가·관람 유입 확대.") - -# ---- P3-G 정산·BI·현장 ---- -# S37 정산 M9 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(정산·결제)", "납부는 스케줄로, 결제는 PG로, 예치금은 실사용까지 투명하게 정산한다", "Ⅲ", "REQ-F-056~060") -flow=[("납부 스케줄\n자동생성·알림",BLUE),("PG 결제\n유틸리티·부대",PURPLE),("예치금\n실사용 정산",TEAL),("세금계산서\n수수료 정산",BLUE)] -for i,(t,cl) in enumerate(flow): - x=0.55+i*3.12 - rect(s, x, 1.7, 2.9, 1.3, fill=(PURPLE_LT if cl==PURPLE else (RGBColor(0xE1,0xF3,0xF1) if cl==TEAL else BLUE_LT)), - line=cl, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, x+0.1, 1.7, 2.7, 1.3, t, size=11, color=INK, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE, line_spacing=1.0) - if i<3: text(s, x+2.9-0.05, 1.7, 0.24, 1.3, "›", size=16, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -bullets(s, 0.55, 3.4, 12.0, [ - "납부 스케줄 자동생성·알림(F-056) · 유틸리티·부대 PG 결제(F-057)", - "예치금 실사용 정산 투명화(F-058) · 세금계산서·옥션 수수료 정산(F-059)", - "환불·취소 규정 자동(선택, F-060)", -], dot=BLUE, gap=0.5, size=10.5) -lead(s, 0.55, 5.1, 12.23, 0.62, "예치금은 실사용까지 투명 정산 — PG 결제·세금계산서·옥션 수수료를 한 흐름에.", fill=INK, size=13) -caption(s, "정산 흐름 — 납부→결제→예치금 정산→세금계산서.") - -# S38 BI-1 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(경영분석 BI)", "가동률·매출구성·행사별 P&L·㎡당 수익을 운영사 관점 한 화면에", "Ⅲ", "REQ-F-061~063") -bullets(s, 0.55, 1.6, 6.9, [ - "홀·기간별 가동률 대시보드(REQ-F-061)", - "매출 구성·행사별 P&L(REQ-F-062)", - "전시장 ROI·RevPAD·㎡당 수익(REQ-F-063)", - "BI 집계·조회는 읽기복제 오프로드(운영 프라이머리 보호)", -], dot=BLUE, gap=0.58, size=10.5) -screen_panel(s, "SCR-13", 7.7, 1.6, 5.08, 3.7, "경영분석 대시보드 — 가동률·매출·P&L·RevPAD") -caption(s, "운영사 관점 한 화면 — Recharts 대시보드, 읽기복제로 운영 부하 격리.") - -# S39 BI-2 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(경영분석 BI)", "리텐션·LTV·참가사 ROI를 권한 격리로, AI가 수요를 예측하고 KPI를 브리핑한다", "Ⅲ", "REQ-F-064~066·A-013·014") -bullets(s, 0.55, 1.6, 7.0, [ - "참가사 리텐션·LTV 코호트(REQ-F-064)", - "참가업체 관점 ROI — 권한 격리(자사 데이터만)(REQ-F-065)", - "경영진 KPI 대시보드·드릴다운(REQ-F-066)", - "AI 수요예측·수율/동적가격(REQ-A-013)", - "AI KPI 브리핑·자연어 조회 Text-to-SQL(REQ-A-014)", -], dot=PURPLE, gap=0.55, size=10.5) -titlecard(s, 7.85, 1.6, 4.93, 3.4, "데이터 거버넌스", TEAL, [ - "BI 마트는 개인 식별자 비반입(집계만)", - "참가업체는 자사 데이터만(격리)", - "AI 인사이트는 근거·인용 병기", -], headfill=TEAL, bodysize=10, igap=0.6) -caption(s, "KPI 드릴다운 + AI 인사이트 — 권한 격리와 개인정보 비반입 원칙 준수.") - -# S40 현장운영·물류 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 기능(현장운영·물류)", "반입/반출 슬롯·통행증 QR·wayfinding·이상탐지 — 선택 기능은 시설 협의 전제로", "Ⅲ", "REQ-F-033~036·052~055") -bullets(s, 0.55, 1.6, 6.9, [ - "반입/반출 슬롯 예약·통행증 QR·중량물 우선(F-033·034)", - "지게차·부대장비 렌탈·철거 대기열 시뮬(F-035·036)", - "실내 wayfinding·POI·경로(F-052)", - "혼잡 모니터·예측·전력부하·주차(F-053·054·A-019)", - "안전 위반·이상탐지(F-055·A-019) · 사이니지 배포(F-070)", -], dot=BLUE, gap=0.55, size=10.5) -screen_panel(s, "SCR-14", 7.7, 1.6, 5.08, 3.0, "홀 현장운영 대시보드 — 혼잡·전력·안전·물류") -rect(s, 7.7, 4.75, 5.08, 0.85, fill=RGBColor(0xFF,0xF6,0xE8), line=AMBER, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 7.85, 4.75, 4.8, 0.85, "[협의] wayfinding 비콘·IoT·사이니지 HW는 킨텍스 시설 인프라 협의 전제(C7, R-7). 미협의 시에도 코어 가치(WT-1~4)는 HW 독립.", size=8.8, color=AMBER, bold=True, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.95) -caption(s, "현장 운영 상황판 — 선택 기능(비콘·IoT·사이니지)은 시설 협의 전제, 코어는 HW 독립.") - -# S41 전체 아키텍처 (도식 ⑨) -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 시스템 장비구성", "확정 스택으로 이미 기동 중 — 도상이 아니라 동작하는 아키텍처다", "Ⅲ", "REQ-A-016·F-081") -band(1.45, 0.5, BLUE_LT, BLUE, "엣지/DMZ") -nd(2.15,1.5,4.3,0.4,"CDN·정적캐시",WHITE,INK,BLUE); nd(6.55,1.5,4.5,0.4,"WAF/Reverse Proxy(nginx)·TLS·레이트리밋",WHITE,INK,BLUE) -band(2.02, 0.5, BLUE_LT, BLUE, "역할별\n프론트") -for i,p in enumerate(["organizer","exhibitor","contractor","ops","admin","public/visitor"]): - nd(2.15+i*1.5,2.07,1.42,0.4,p,WHITE,(MUTED if p=="admin" else INK),(SLATE if p=="admin" else BLUE),7.6) -band(2.59, 0.46, BLUE, BLUE_DK, "SSO·RBAC") -nd(2.15,2.62,8.9,0.4,"JWT SSO + TOTP 2FA · 이중 RBAC(플랫폼/행사)",BLUE_LT,BLUE_DK,BLUE) -band(3.13, 0.86, WHITE, BLUE, "공유\n백엔드") -nd(2.15,3.2,8.9,0.34,"REST + WebSocket / STOMP",BLUE_LT,BLUE_DK,BLUE) -for i,t in enumerate(["룰엔진","배치·배선·PostGIS","옥션 M15","BI M16","CMS M17","공개 API(RO)"]): - nd(2.15+i*1.49,3.58,1.42,0.36,t,WHITE,INK,BLUE,7.4) -band(4.07, 0.5, PURPLE_LT, PURPLE, "비동기") -nd(2.15,4.12,3.0,0.4,"Redis 큐·순위·타이머·쿼터",WHITE,INK,PURPLE) -nd(5.3,4.12,2.9,0.4,"나노바나나 워커",PURPLE_LT,PURPLE,PURPLE); nd(8.3,4.12,2.75,0.4,"서류·EDM 워커",WHITE,INK,PURPLE) -band(4.64, 0.5, HALL_BG, BLUE_DK, "데이터") -nd(2.15,4.69,3.3,0.4,"PostgreSQL + PostGIS",WHITE,INK,BLUE_DK); nd(5.55,4.69,2.7,0.4,"읽기복제(BI·공개)",WHITE,INK,BLUE_DK); nd(8.35,4.69,2.7,0.4,"오브젝트 스토리지",WHITE,INK,BLUE_DK) -# 우측 외부 게이트 컬럼 -rect(s, 11.35, 1.45, 1.43, 3.69, fill=INK, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 11.35, 1.52, 1.43, 0.3, "외부 게이트", size=8.5, color=WHITE, bold=True, align=PP_ALIGN.CENTER) -for i,(g,live) in enumerate([("Gemini",True),("Claude",False),("PG 결제",False),("kxwp 릴레이",False),("등록업체 DB",False)]): - yy=1.9+i*0.62 - rect(s, 11.45, yy, 1.23, 0.5, fill=WHITE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 11.45, yy+0.02, 1.23, 0.3, g, size=8, color=INK, bold=True, align=PP_ALIGN.CENTER) - if live: chip(s, 11.6, yy+0.26, 0.95, 0.2, "라이브", GREEN, size=6.5) -caption(s, "6밴드 계층 + 외부 게이트 — 인프라·CI/CD·인증·PostGIS 코어·나노바나나 [검증됨].") - -# S42 단일 공간원천 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 데이터 요구사항", "부스 폴리곤·트렌치 포인트·배선 LineString은 하나의 원천에서만 산다", "Ⅲ", "REQ-N-008·017") -rect(s, 4.4, 1.7, 4.5, 1.0, fill=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) -icon(s, "postgis-spatial", 4.6, 1.9, 0.6, WHITE) -text(s, 5.3, 1.78, 3.5, 0.5, "PostGIS 단일 공간 원천", size=13, color=WHITE, bold=True) -text(s, 5.3, 2.24, 3.5, 0.4, "SRID 0 홀 로컬 미터 · GiST 인덱스", size=9, color=SUBLT) -srcs=[("M2 부스 폴리곤","booth-layout"),("M4 트렌치 포인트","postgis-spatial"),("M4 배선 LineString","wiring-power")] -for i,(t,icn) in enumerate(srcs): - x=1.0+i*3.9 - rect(s, x, 3.4, 3.5, 0.9, fill=BLUE_LT, line=BLUE, line_w=1.2, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, icn, x+0.2, 3.6, 0.44, BLUE) - text(s, x+0.8, 3.4, 2.6, 0.9, t, size=10.5, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - line_conn(s, x+1.75, 3.4, 6.65, 2.7, color=SLATE, w=1.0) -bullets(s, 0.55, 4.7, 12.0, [ - "M2 폴리곤·M4 배선 매퍼가 권위 — 소비 모듈은 조회만·중복 저장 금지(REQ-N-017)", - "전 geom GiST 인덱스로 공간 쿼리 실시간(REQ-N-008)", - "검증 게이트 — ArchUnit·계약 검수로 중복 저장 방지", -], dot=PURPLE, gap=0.5, size=10.5) -caption(s, "공간 데이터 단일 원천 — 검증·시각화·정산·BI·wayfinding이 하나의 원천을 소비.") - -# S46 보안 개요 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 보안 요구사항", "AI가 만든 것은 표시하고, 남의 데이터는 차단한다 — REQ-S 20항목 전량 커버", "Ⅲ", "REQ-S 전량") -inv=[("외부 API 격리","Ollama 기본·Claude/Gemini만 승인 예외, egress DROP",BLUE_DK,"shield-security"), - ("워터마크 항상","AI 생성 이미지 watermarkRequired 불변 [검증됨]",PURPLE,"watermark"), - ("PII 격리","동의·마스킹·암호화·응답 제외·테넌트 격리",BLUE,"check-verify"), - ("테넌트 fail-closed","한 층이라도 불일치면 차단",TEAL,"tenant-building")] -cw=2.94 -for i,(t,d,cl,icn) in enumerate(inv): - x=0.55+i*(cw+0.16) - card(s, x, 1.6, cw, 2.5) - rect(s, x, 1.6, cw, 0.08, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, icn, x+cw/2-0.3, 1.85, 0.6, cl) - text(s, x+0.1, 2.6, cw-0.2, 0.5, t, size=11, color=cl, bold=True, align=PP_ALIGN.CENTER, line_spacing=0.9) - text(s, x+0.15, 3.15, cw-0.3, 0.9, d, size=8.8, color=INK, align=PP_ALIGN.CENTER, line_spacing=1.0) -lead(s, 0.55, 4.45, 12.23, 0.62, "보안 성숙도 — 인증·워터마크·admin env는 [검증됨](설계가 아니라 동작하는 통제).", fill=INK, size=13) -kcards=[("100%","REQ-S 커버리지",GREEN),("4대","불변 원칙",BLUE),("20","보안 요구항목",PURPLE)] -for i,(v,l,cl) in enumerate(kcards): - x=0.55+i*4.11 - rect(s, x, 5.35, 3.97, 0.9, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.3, 5.45, 1.5, 0.7, v, size=26, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+2.0, 5.35, 1.8, 0.9, l, size=11, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) -caption(s, "불변 원칙 4대 + REQ-S 20항목 커버리지 100%.", y=6.55) - -# S47 보안영역 분리 (도식 ⑩) -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 보안 요구사항", "공개 DMZ는 쓰기 권한이 없고, 데이터존은 아웃바운드가 0이다", "Ⅲ", "REQ-S-020·018·N-018") -zones=[("인터넷",SLATE),("엣지\nCDN·WAF·DDoS",BLUE_LT),("DMZ(공개)\n공개LB·visitor GW\n쓰기 없음",BLUE), - ("내부망(인증)\nSSO/2FA·공유 백엔드",BLUE_DK),("AI 워커존\nRedis·나노바나나·Ollama\negress 제한",PURPLE), - ("데이터존(최내곽)\nPostgreSQL+PostGIS\n아웃바운드 전면 차단",INK)] -zw=1.9 -for i,(t,cl) in enumerate(zones): - x=0.55+i*(zw+0.06) - tc = INK if cl in (BLUE_LT,) else WHITE - fl = cl if cl not in (SLATE,) else RGBColor(0xE9,0xEC,0xF1) - rect(s, x, 2.3, zw, 2.7, fill=(fl if cl!=SLATE else RGBColor(0xE9,0xEC,0xF1)), line=(cl if cl!=BLUE_LT else BLUE), line_w=1.0+i*0.2, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, x+0.08, 2.4, zw-0.16, 2.5, t, size=8.6, color=(INK if cl in (SLATE,BLUE_LT) else WHITE), bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE, line_spacing=1.0) - if i<5: text(s, x+zw-0.04, 3.4, 0.14, 0.5, "›", size=13, color=SLATE, bold=True, align=PP_ALIGN.CENTER) -# 상단 관리존 -rect(s, 0.55, 1.55, 12.23, 0.5, fill=NAVY, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.55, 1.55, 12.23, 0.5, "관리존 — 배스천·관측성 · VPN/허용IP+2FA · 인터넷 미도달", size=10, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -# 철칙 배지 -for i,t in enumerate(["DMZ → 데이터존 직접 접근 절대 금지","데이터존 아웃바운드 0 (유출경로 제거)","egress 승인 4목적지만 — Claude·Gemini·PG·SMTP, 그 외 DROP"]): - x=0.55+i*4.11 - rect(s, x, 5.25, 3.97, 0.7, fill=RGBColor(0xFC,0xE9,0xE7), line=RED, line_w=1.4, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.1, 5.25, 3.77, 0.7, t, size=8.8, color=RED, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.92) -caption(s, "공개/내부 물리·논리 분리 · 데이터존 무egress로 유출경로 자체 제거.") - -# S48 인증·접근통제 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 보안 요구사항", "JWT+2FA는 이미 라이브, admin 비번은 env로 재시드, RBAC는 3중이다", "Ⅲ", "REQ-S-004~008·F-071") -bullets(s, 0.55, 1.6, 7.0, [ - "JWT(HS256)+TOTP 2FA(RFC6238)+로그인 실패 잠금 [검증됨: Flyway V7/V8]", - "admin 비번 env 재시드(AES-256-GCM, admin123 금지) [검증됨: env 시더]", - "행사 RBAC 3중 — 테넌트 격리 ⊃ 행사 RBAC ⊃ 열람/편집 게이트", - "admin/internal 엔드포인트 게이트(REQ-S-008)", - "미등록 업체 차단(403)과 연계(REQ-S-007)", -], dot=BLUE, gap=0.56, size=10.5) -rect(s, 8.0, 1.6, 4.78, 1.5, fill=INK, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 8.15, 1.7, 4.5, 0.35, "RBAC 3중 구조", size=12, color=WHITE, bold=True, align=PP_ALIGN.CENTER) -text(s, 8.15, 2.15, 4.5, 0.9, "테넌트(전시관) 격리\n⊃ 행사(Event) RBAC\n⊃ 열람/편집 게이트", size=10, color=SUBLT, bold=True, align=PP_ALIGN.CENTER, line_spacing=1.05) -for i,(t,d,cl) in enumerate([("JWT + RBAC","6역할 게이트",BLUE),("2FA / OTP","TOTP RFC6238 [검증됨]",PURPLE),("RBAC·감사 백오피스","F-071",BLUE_DK)]): - y=3.4+i*0.9 - rect(s, 8.0, y, 4.78, 0.75, fill=WHITE, line=cl, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 8.2, y+0.1, 4.4, 0.3, t, size=11, color=cl, bold=True) - text(s, 8.2, y+0.42, 4.4, 0.28, [_segify(d)[0] if _segify(d) else {"t":d}] if False else _segify(d), size=9, color=MUTED) -caption(s, "JWT+2FA·admin env 재시드는 [검증됨: Flyway V7/V8] — 설계가 아니라 동작하는 통제.") - -# S49 AI·이미지 보안 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 보안 요구사항", "생성 이미지는 워터마크·금지 고지 불변, 외부 API 키는 워커 env에만 산다", "Ⅲ", "REQ-S-001·002·009") -titlecard(s, 0.55, 1.6, 6.05, 3.5, "워터마크 항상 [검증됨]", RED, [ - "M5 응답 watermarkRequired:true + text + notice", - "계약·심사 서류 사용 금지 고지 — 제거 불가", - "RAG 근거·인용·환각 차단(abstain)(A-017)", -], headfill=RED, bodysize=10, igap=0.6) -titlecard(s, 6.75, 1.6, 6.03, 3.5, "외부 API 격리", BLUE_DK, [ - "Ollama 기본 + Claude/Gemini만 승인 예외, egress 그 외 DROP(S-001)", - "GEMINI_API_KEY 워커 env only(백엔드 미취급)(S-002)", - "워커존 단일 egress — 유출 경로 최소화", -], headfill=BLUE_DK, bodysize=10, igap=0.6) -wm_badge(s, 0.55, 5.35, 12.23, 0.5) -caption(s, "워터마크 실물 + 외부 API 격리(워커존 단일 egress) — 시크릿·키값 미기재.", y=6.0) - -# S50 개인정보·감사 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 보안 요구사항", "PII는 동의·마스킹·암호화·응답 제외, 민감 액션은 전수 감사, 코드는 안전하게", "Ⅲ", "REQ-S-010~017·N-018") -titlecard(s, 0.55, 1.6, 6.05, 4.5, "개인정보(PII) 보호", BLUE, [ - "동의 분리(privacy/marketing/share)·최소수집(S-013)", - "*_enc AES-256-GCM·응답 완전 제외·보존/파기", - "자격증명·키 완전 제외(응답/로그/커밋 마스킹)(S-003)", - "스택트레이스 미노출(S-010)·DataAccessException 요약(S-011)", - "관측성 로그·메트릭·트레이스에 PII 미기록(N-018)", -], headfill=BLUE, bodysize=9.5, igap=0.56) -titlecard(s, 6.75, 1.6, 6.03, 4.5, "감사·안전코딩", PURPLE, [ - "민감 액션 감사 전수(TB_AUDIT_LOG tenant_id·@Audited AOP)(S-012)", - "입력 검증(S-014)·XSS/CSP(S-015)", - "인젝션 차단 MyBatis #{}(S-016)", - "IDOR/CSRF/JWT 회전(S-017)", - "의존성 SCA CI 게이트(S-019)", -], headfill=PURPLE, bodysize=9.5, igap=0.56) -caption(s, "PII 마스킹·암호화·응답 제외 + 민감 액션 전수 감사 + 안전코딩 통제.") - -# S56 격리 아키텍처 (도식 ⑪) -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 시스템 운영(테넌트 격리)", "한 층이라도 불일치하면 차단 — 컨텍스트→서비스→MyBatis→PostGIS 4층 방어", "Ⅲ", "REQ-S-018·N-020") -# 좌측 진입 -rect(s, 0.55, 2.0, 3.0, 1.3, fill=PURPLE_LT, line=PURPLE, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) -icon(s, "tenant-building", 0.75, 2.15, 0.44, PURPLE) -text(s, 0.75, 2.62, 2.7, 0.6, "요청 — 서브도메인(kintex./coex.) + JWT 소속 → 컨텍스트 해소(불일치 시 거부)", size=8.6, color=INK, bold=True, line_spacing=0.9) -line_conn(s, 3.55, 2.65, 4.1, 2.65, color=SLATE, w=1.5) -# 4층 스택 -layers=[("① 컨텍스트 필터","서브도메인+소속 불일치 거부",BLUE_LT,BLUE), - ("② 서비스 가드","tenant 스코프 검증",BLUE,WHITE), - ("③ MyBatis 인터셉터","tenant_id 자동 주입",BLUE_DK,WHITE), - ("④ PostGIS/쿼리","tenant_id 선두 복합 인덱스",PURPLE,WHITE)] -for i,(t,d,fl,tc) in enumerate(layers): - y=1.7+i*1.05 - rect(s, 4.1, y, 5.0, 0.9, fill=fl, line=(fl if tc==WHITE else BLUE), line_w=1.2, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, 4.35, y+0.12, 4.6, 0.35, t, size=11, color=tc, bold=True) - text(s, 4.35, y+0.48, 4.6, 0.32, d, size=9, color=(SUBLT if tc==WHITE else MUTED)) - if i<3: text(s, 6.5, y+0.88, 0.4, 0.22, "▼", size=11, color=SLATE, align=PP_ALIGN.CENTER) -# 우측 데이터 파티션 -rect(s, 9.35, 1.7, 3.43, 3.35, fill=WHITE, line=LINE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) -text(s, 9.5, 1.85, 3.1, 0.3, "TB_* WHERE tenant_id = ctx", size=9.5, color=INK, bold=True) -rect(s, 9.55, 2.3, 3.0, 1.0, fill=BLUE_LT, line=BLUE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 9.55, 2.3, 3.0, 1.0, "KINTEX (#1)", size=12, color=BLUE_DK, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -rect(s, 9.55, 3.45, 3.0, 1.0, fill=PURPLE_LT, line=PURPLE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 9.55, 3.45, 3.0, 1.0, "COEX (#2)", size=12, color=PURPLE_DK, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -rect(s, 0.55, 5.4, 12.23, 0.55, fill=RGBColor(0xFC,0xE9,0xE7), line=RED, line_w=1.4, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.55, 5.4, 12.23, 0.55, "fail-closed — 한 층이라도 불일치면 차단 [구현 계획: 백필 전 단일테넌트 무중단 → 검증 후 필터 활성화]", size=10.5, color=RED, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -caption(s, "KINTEX #1 검증 → COEX #2 코드 배포 없이 데이터 온보딩. 기능 불변, 데이터·권한만 분리.") - -# S57 온보딩·2계층 관리자 -s = slide(); content_header(s, "Ⅲ. 기술·기능 · 시스템 운영(온보딩)", "코드 배포 없이 데이터 온보딩 6단계로 새 전시관을 연다", "Ⅲ", "REQ-F-077~079") -steps=["테넌트 생성","홀·구역 입력","요율 룰셋","규정 룰셋","마스터데이터","컨텍스트 활성화"] -sw=1.95 -for i,st in enumerate(steps): - x=0.55+i*(sw+0.1) - rect(s, x, 1.7, sw, 1.0, fill=BLUE_LT, line=BLUE, line_w=1.2, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - chip(s, x+sw/2-0.22, 1.55, 0.44, 0.32, str(i+1), BLUE, size=11) - text(s, x+0.08, 1.95, sw-0.16, 0.7, st, size=10, color=BLUE_DK, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.9) - if i<5: text(s, x+sw-0.03, 1.7, 0.16, 1.0, "›", size=15, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -titlecard(s, 0.55, 3.15, 6.05, 3.0, "2계층 관리자", PURPLE, [ - "플랫폼 슈퍼관리자 — 테넌트 스위처(F-079)", - "테넌트 관리자 — 단일 스코프", - "테넌트 컨텍스트 해소(F-078)", -], headfill=PURPLE, bodysize=10, igap=0.6) -titlecard(s, 6.75, 3.15, 6.03, 3.0, "재사용 기반", BLUE, [ - "온보딩 = 데이터 입력 수준(코드 배포 없음)(F-077)", - "공통코드·메뉴 관리(§5B, F-073) 재사용", - "홀·요율·규정 룰셋·마스터데이터만 주입", -], headfill=BLUE, bodysize=10, igap=0.6) -caption(s, "온보딩 6단계 + 2계층 관리자 — 코드 배포 없이 새 전시관 개설.") - -# ================================================================ -# Ⅳ 성능 및 품질 -# ================================================================ - -divider("Ⅳ", "성능 및 품질", "PERFORMANCE & QUALITY", ["성능·SLA·확장성", "품질·관측성", "인터페이스"], "정성 · 성능·품질", BLUE) - -# S51 성능·가용성 -s = slide(); content_header(s, "Ⅳ. 성능·품질 · 성능 요구사항", "코어 SLO 99.5%(성수기 99.9% 지향), 무중단 롤링·PITR·degraded로 버틴다", "Ⅳ", "REQ-N-001~005·011") -mets=[("99.5%","코어 SLO(성수기 99.9%)",BLUE),("2","백엔드 인스턴스 롤링",PURPLE),("PITR","PG 스탠바이·failover",TEAL),("degraded","3중 폴백 모드",AMBER)] -mw=2.94 -for i,(v,l,cl) in enumerate(mets): - x=0.55+i*(mw+0.16) - rect(s, x, 1.6, mw, 1.1, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, x, 1.72, mw, 0.5, v, size=22, color=WHITE, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 2.28, mw, 0.34, l, size=9, color=WHITE, align=PP_ALIGN.CENTER) -bullets(s, 0.55, 3.0, 12.0, [ - "코어 SLO 99.5%(성수기 99.9% 지향), 무상태 백엔드+Redis 외부화(N-001)", - "백엔드 2인스턴스·헬스체크 LB·롤링 무중단(N-002) · PG 스탠바이 복제·자동 failover·PITR(N-003)", - "degraded — Gemini 장애=목 응답, Claude 장애=Ollama 폴백, PG 복제지연=프라이머리 폴백(N-005)", - "워커 장애 재큐(멱등)(N-011)", -], dot=BLUE, gap=0.55, size=10.5) -lead(s, 0.55, 5.4, 12.23, 0.6, "무중단·복원 — 롤링 배포·PITR·degraded 3중 폴백으로 성수기 부하를 버틴다.", fill=INK, size=13) -caption(s, "SLA 수치 + HA 구성 — 프론트·백엔드·Redis·PG·워커 다중화.") - -# S52 나노바나나 성능 -s = slide(); content_header(s, "Ⅳ. 성능·품질 · 성능 요구사항", "단건 평균 40초, 1024px 전처리·자동생성 한정·캐시·쿼터로 대량을 제어한다", "Ⅳ", "REQ-N-006·007") -mets=[("≤40s","단건 평균(전면 비동기)",PURPLE),("1024px","Canvas 다운스케일·JPEG 0.85",BLUE), - ("S1·S7","자동생성 한정",TEAL),("쿼터","성공 시에만 차감",GREEN)] -mw=2.94 -for i,(v,l,cl) in enumerate(mets): - x=0.55+i*(mw+0.16) - card(s, x, 1.6, mw, 2.0) - rect(s, x, 1.6, mw, 0.08, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 2.0, mw, 0.7, v, size=24, color=cl, bold=True, align=PP_ALIGN.CENTER) - text(s, x+0.12, 2.8, mw-0.24, 0.7, l, size=9.5, color=INK, align=PP_ALIGN.CENTER, line_spacing=1.0) -bullets(s, 0.55, 4.0, 12.0, [ - "단건 평균 ≤40s, 전면 비동기(WS 완료 푸시)(N-006)", - "전처리 — 클라 Canvas 1024px 다운스케일 + JPEG 0.85", - "대량 제어 — 자동생성 S1·S7 한정 + 동일 스키마해시 캐시 + 행사별 쿼터(N-007)", -], dot=PURPLE, gap=0.55, size=11) -caption(s, "렌더 성능 지표 — 40초·1024px·캐시 히트·쿼터로 비용/지연 제어.") - -# S43 확장성·비동기 -s = slide(); content_header(s, "Ⅳ. 성능·품질 · 성능·확장성", "워커는 큐 깊이로 독립 확장하고, BI·공개는 읽기복제로 오프로드한다", "Ⅳ", "REQ-N-009·010·011") -titlecard(s, 0.55, 1.6, 6.05, 4.5, "독립 확장 (워커)", PURPLE, [ - "나노바나나·서류·EDM 워커 = 큐 깊이 기반 증감(N-009)", - "워커 장애 재큐 — 멱등 Job(N-011)", - "GEMINI 워커존 단일 egress", - "역할별 프론트 번들 분리(F-081, 방식 확정 대기 [구현 계획])", -], headfill=PURPLE, bodysize=10, igap=0.62) -titlecard(s, 6.75, 1.6, 6.03, 4.5, "오프로드 (읽기복제·CDN)", BLUE, [ - "BI 집계·공개 조회·플로어플랜 열람 = 읽기복제 라우팅(N-010)", - "공개영역 CDN·요청률 상한(N-019)", - "무상태 백엔드 2인스턴스 롤링(N-002)", - "운영 프라이머리 보호 — 읽기 부하 분리", -], headfill=BLUE, bodysize=10, igap=0.62) -caption(s, "배포 토폴로지 — 프론트·백엔드 2인스턴스·워커 N·읽기복제·CDN.") - -# S53 접근성·다국어·테마·반응형 -s = slide(); content_header(s, "Ⅳ. 성능·품질 · 품질 요구사항", "WCAG 2.1 AA+KWCAG 2.2, 한/영/중/일, 라이트/다크, 데스크톱/모바일 전부", "Ⅳ", "REQ-N-012~016") -items=[("웹 접근성","WCAG 2.1 AA + 공공 KWCAG 2.2 병행(N-012)",BLUE,"check-verify"), - ("비시각 대안","캔버스/맵 데이터 테이블·키보드 부스 선택(N-013)",BLUE,"mobile"), - ("다국어(i18n)","한 기본+영/중/일·hreflang·로케일 포맷(N-014)",PURPLE,"document-rtm"), - ("라이트/다크","CSS 토큰 2벌·prefers-color-scheme(N-015)",PURPLE,"dashboard-chart"), - ("반응형","데스크톱=설계/에디터·모바일=현장/조회/승인(N-016)",TEAL,"mobile")] -for i,(t,d,cl,icn) in enumerate(items): - x=0.55+(i%3)*4.11; y=1.6+(i//3)*1.7 - card(s, x, y, 3.97, 1.5) - icon(s, icn, x+0.2, y+0.25, 0.44, cl) - text(s, x+0.8, y+0.2, 3.0, 0.4, t, size=11, color=cl, bold=True) - text(s, x+0.8, y+0.62, 3.0, 0.8, d, size=8.8, color=INK, line_spacing=1.0) -chip(s, 8.75, 3.3, 3.97, 1.5, "[구현 계획: 전면 스윕 대기]", SLATE, size=11) -caption(s, "접근성·다국어·테마·반응형 매트릭스 — 공공 KWCAG 병행.") - -# S54 관측성·데이터 -s = slide(); content_header(s, "Ⅳ. 성능·품질 · 품질·관측성", "로그·메트릭·트레이스에 PII·스택트레이스는 남기지 않고, BI는 배치 ETL로 정합한다", "Ⅳ", "REQ-N-018·019·021") -obs=[("수집","로그·메트릭·트레이스",BLUE),("저장","관리존 · PII·스택트레이스 미기록",PURPLE),("대시보드","관측성 · include-stacktrace: never",TEAL)] -for i,(t,d,cl) in enumerate(obs): - x=0.55+i*4.11 - rect(s, x, 1.7, 3.85, 1.2, fill=(BLUE_LT if cl==BLUE else (PURPLE_LT if cl==PURPLE else RGBColor(0xE1,0xF3,0xF1))), line=cl, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, x+0.15, 1.82, 3.55, 0.35, t, size=12, color=cl, bold=True, align=PP_ALIGN.CENTER) - text(s, x+0.15, 2.2, 3.55, 0.6, d, size=9, color=INK, align=PP_ALIGN.CENTER, line_spacing=0.95) - if i<2: text(s, x+3.85, 1.7, 0.26, 1.2, "›", size=16, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -bullets(s, 0.55, 3.4, 12.0, [ - "관측성 — 자격증명·PII·스택트레이스 미기록(include-stacktrace: never)(N-018)", - "BI 데이터마트 — 스타 스키마(FACT/DIM) 야간 배치 ETL 또는 읽기복제 적재(N-021)", - "KpiSnapshot(원천-집계 정합 오차 0) · 공개영역 CDN·요청률 상한(N-019)", -], dot=BLUE, gap=0.55, size=10.5) -caption(s, "관측성 파이프라인(수집→저장→대시보드) — PII·스택트레이스 미기록.") - -# S45 연동·마스터데이터 -s = slide(); content_header(s, "Ⅳ. 성능·품질 · 인터페이스 요구사항", "외부는 게이트웨이로, 룰셋·요율은 버전 데이터로 무중단 개정한다", "Ⅳ", "REQ-F-072·080") -titlecard(s, 0.55, 1.6, 6.05, 4.5, "연동 게이트웨이", BLUE, [ - "API·웹훅·CRM/ERP 연동(선택, F-080)", - "외부 게이트 — Gemini(워커존)·Claude(백엔드)", - "PG 결제·kxwp 릴레이·등록업체 DB 739", - "내부↔외부 경계는 게이트웨이 단일 통로", -], headfill=BLUE, bodysize=10, igap=0.62) -titlecard(s, 6.75, 1.6, 6.03, 4.5, "마스터데이터·룰셋 버전", PURPLE, [ - "룰셋·마스터데이터 버전관리(요율·규정 룰셋)", - "M18 백오피스·감사 기록으로 무중단 개정(F-072)", - "규정 개정 = 코드 배포 없이 데이터 버전 교체", - "요율 공식본은 [협의: C4]", -], headfill=PURPLE, bodysize=10, igap=0.62) -caption(s, "연동 인터페이스 맵 — 내부↔외부 게이트웨이 + 버전 데이터로 무중단 개정.") - -# ================================================================ -# Ⅴ 프로젝트 관리 -# ================================================================ - -divider("Ⅴ", "프로젝트 관리", "PROJECT MANAGEMENT", ["일정계획", "관리방법론·역량", "위험·이슈·품질형상"], "정성 · 사업관리", BLUE) - -# S58 일정 간트 (도식 ⑫) -s = slide(); content_header(s, "Ⅴ. 프로젝트 관리 · 일정계획", "Phase 1 코어 → Phase 2 워크플로/운영 → Phase 3 현장/확장, 게이트로 진행", "Ⅴ") -for i,(lb,cl) in enumerate([("완료",GREEN),("진행",AMBER),("예정",SLATE)]): - rect(s, 9.9+i*1.0, 1.4, 0.24, 0.24, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 10.18+i*1.0, 1.38, 0.9, 0.28, lb, size=9, color=INK, anchor=MSO_ANCHOR.MIDDLE) -gx=4.3; gw=8.4; units=12; uw=gw/units -for i in range(units+1): - rect(s, gx+i*uw, 1.95, 0.008, 4.05, fill=RGBColor(0xF0,0xF1,0xF4)) -for i,lab in enumerate(["Phase 1 (~4M)","Phase 2 (~4M)","Phase 3 (~4M+)"]): - text(s, gx+i*4*uw, 1.7, 2.8, 0.24, lab, size=8.5, color=BLUE_DK, bold=True) -rows=[("P1","아키텍처(A)",0,3,GREEN,"완료"), - ("P1","공통레이어 B (2FA·시스템관리)",0.5,3,GREEN,"완료"), - ("P1","P0 코어 M2·M3·M4·M5",1.5,2.5,AMBER,"진행"), - ("P1"," ├ 스캐폴드·인증·매퍼·룰엔진·나노바나나",1.5,1.5,GREEN,"완료"), - ("P1"," └ 3안·병합·실데이터",3.0,1.0,AMBER,"진행"), - ("P2","옥션 M15·관람 M10",4,2,SLATE,"예정"), - ("P2","BI M16·관리자 M18",4.3,2,SLATE,"예정"), - ("P2","판매·서류·정산 M1·M6·M7·M9·CMS M12·M17",4.5,2.5,SLATE,"예정"), - ("P3","멀티테넌시·M11·M13·M14",8,2.5,SLATE,"예정"), - ("P3","배포 안정화·SM 이관",8.5,3,AMBER,"진행")] -ry=2.02; rh=0.375 -for i,(ph,nm,st,dur,cl,lab) in enumerate(rows): - y=ry+i*rh - if i%2==0: rect(s, 0.55, y-0.01, 12.23, rh-0.01, fill=ZEBRA) - text(s, 0.6, y, 0.5, rh, ph, size=7.5, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 1.1, y, 3.2, rh, nm, size=7.6, color=INK, anchor=MSO_ANCHOR.MIDDLE) - bx=gx+st*uw; bw=max(dur*uw,0.28) - rect(s, bx, y+0.05, bw, rh-0.14, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, bx, y+0.03, bw, rh-0.1, lab, size=6.8, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -# 마일스톤 -for mx,ml in [(gx+0.5*uw,"G1"),(gx+0.9*uw,"G2"),(gx+4*uw,"P게이트"),(gx+8*uw,"QA"),(gx+12*uw,"오픈")]: - rect(s, mx-0.09, 5.85, 0.18, 0.18, fill=(GREEN if ml in("G1","G2") else BLUE), shape=MSO_SHAPE.DIAMOND) - text(s, mx-0.4, 6.02, 0.8, 0.2, ml, size=7, color=INK, bold=True, align=PP_ALIGN.CENTER) -nowx=gx+2.7*uw -rect(s, nowx, 1.95, 0.02, 4.05, fill=PURPLE) -text(s, nowx-0.4, 6.02, 0.8, 0.2, "현재", size=7.5, color=PURPLE, bold=True, align=PP_ALIGN.CENTER) -caption(s, "선행 게이트 통제 + Phase 게이트 QA 통과 후 진행. G1·G2 완료 — 실증 기반. 총 ~150 M/M.", y=6.45) - -# ---- Ⅴ 관리방법론 ---- -s = slide(); content_header(s, "Ⅴ. 프로젝트 관리 · 관리방법론", "PMBOK 기반 통합관리 + ITSM 운영 프로세스로 일정·품질·리스크를 통제한다", "Ⅴ") -mm=[("통합·범위관리","PMBOK 기반 착수→계획→실행→감시→종료, 범위기술서·WBS","booth-layout",BLUE), - ("일정·자원관리","Phase 게이트·마일스톤·번다운, 직군별 M/M 투입계획","dashboard-chart",PURPLE), - ("의사소통·보고","주간/월간 보고·이슈 에스컬레이션·주관기관 정례 협의","document-rtm",TEAL), - ("형상·산출물관리","Gitea 형상관리·산출물 목록·변경관리·감리 대응","check-verify",BLUE_DK)] -for i,(t,d,ic,cl) in enumerate(mm): - x=0.55+(i%2)*6.19; y=1.6+(i//2)*2.15 - card(s, x, y, 6.05, 1.9) - rect(s, x, y, 0.1, 1.9, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, ic, x+0.3, y+0.35, 0.55, cl) - text(s, x+1.15, y+0.33, 4.7, 0.4, t, size=12.5, color=cl, bold=True) - text(s, x+1.15, y+0.9, 4.7, 0.85, d, size=9.5, color=INK, line_spacing=1.05) -lead(s, 0.55, 6.0, 12.23, 0.5, "관리방법론·관리역량 — 통합·범위·일정·의사소통·형상관리 표준 체계로 실행력 확보.", fill=INK, size=11.5) -caption(s, "관리방법론·관리역량 독립 절 — 표준 평가부문(프로젝트 관리) 정렬.", y=6.62) - -# S59 리스크 매트릭스 (도식 ⑭) -s = slide(); content_header(s, "Ⅴ. 프로젝트 관리 · 위험·이슈 관리", "약한 고리를 숨기지 않고 드러내 방어한다 — 선제 대응이 곧 신뢰다", "Ⅴ") -# 매트릭스 -mxx,myy,mww,mhh=2.2,1.7,6.3,4.3 -rect(s, mxx, myy, mww, mhh, fill=WHITE, line=LINE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -rect(s, mxx+mww/2, myy, mww/2, mhh/2, fill=RGBColor(0xFC,0xE9,0xE7)) # 우상 위험 -rect(s, mxx, myy+mhh/2, mww/2, mhh/2, fill=RGBColor(0xE3,0xF6,0xEC)) # 좌하 안전 -rect(s, mxx+mww/2, myy, 0.006, mhh, fill=LINE); rect(s, mxx, myy+mhh/2, mww, 0.006, fill=LINE) -text(s, mxx, myy+mhh+0.05, mww, 0.24, "발생가능성 →", size=8.5, color=MUTED, align=PP_ALIGN.CENTER) -text(s, mxx-0.55, myy, 0.5, mhh, "영향도 →", size=8.5, color=MUTED, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -bubbles=[("R-1",0.62,0.78,AMBER),("R-2",0.58,0.8,AMBER),("R-3",0.5,0.5,SLATE),("R-4",0.5,0.45,SLATE), - ("R-5",0.48,0.5,SLATE),("R-6",0.28,0.82,AMBER),("R-7",0.35,0.35,SLATE),("R-8",0.22,0.22,GREEN)] -for lb,px,py,cl in bubbles: - bx=mxx+px*mww-0.22; by=myy+(1-py)*mhh-0.22 - rect(s, bx, by, 0.44, 0.44, fill=cl, shape=MSO_SHAPE.OVAL, shadow=True) - text(s, bx, by, 0.44, 0.44, lb, size=8, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -# 우측 게이트·대응 -titlecard(s, 8.75, 1.7, 4.03, 4.3, "선행 게이트·대응", GREEN, [ - "G1 나노바나나 승인 [검증됨: 완료]", - "G2 배포서버 [검증됨: 완료]", - "R-1/2 외부API·CAD — 승인 게이트+degraded·가정 라벨", - "R-3 kxwp — 단계적 접근 · R-5 간판 — 후처리 합성", - "R-6 AI 오판 — 사람 확정 · R-8 확장 — 격리 레이어만", -], headfill=GREEN, bodysize=8.8, igap=0.62) -caption(s, "발생×영향 매트릭스에 R-1~R-8 배치 — 약한 고리를 선제 노출·방어(숨기지 않아 신뢰).") - -# S60 품질·형상관리 -s = slide(); content_header(s, "Ⅴ. 프로젝트 관리 · 품질·형상관리", "배포는 헬스체크로 지키고 실패하면 롤백, 의존성은 SCA 게이트로 막는다", "Ⅴ", "REQ-N-004·S-019") -gate=[("빌드","compileJava·tsc",BLUE),("테스트","점진 경계면 검증",PURPLE),("SCA","OWASP Dep-Check",AMBER), - ("헬스체크","health 200",GREEN),("롤백","실패 시 이전 jar",RED)] -for i,(t,d,cl) in enumerate(gate): - x=0.55+i*2.5 - rect(s, x, 1.8, 2.3, 1.1, fill=(BLUE_LT if cl==BLUE else (PURPLE_LT if cl==PURPLE else (RGBColor(0xFF,0xF6,0xE8) if cl==AMBER else (RGBColor(0xE3,0xF6,0xEC) if cl==GREEN else RGBColor(0xFC,0xE9,0xE7))))), - line=cl, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, x+0.1, 1.9, 2.1, 0.4, t, size=11, color=cl, bold=True, align=PP_ALIGN.CENTER) - text(s, x+0.1, 2.32, 2.1, 0.5, d, size=8.8, color=INK, align=PP_ALIGN.CENTER, line_spacing=0.9) - if i<4: text(s, x+2.3-0.05, 1.8, 0.2, 1.1, "›", size=15, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -bullets(s, 0.55, 3.3, 12.0, [ - "Fail-Safe 배포 — 백업→배포→헬스체크 200→실패 시 롤백(이전 jar 유지) [검증됨: deploy_kintex.sh](N-004)", - "QA 게이트 — 점진 경계면 검증·보안 불변·빌드 회귀(Phase 게이트 통과 후 진행)", - "SCA CI 게이트 — OWASP Dependency-Check 정기 스캔·패치(S-019)", - "형상관리 — Gitea zio/kintex·Conventional Commits·main 보호·환경 분리(dev/staging/prod)", -], dot=BLUE, gap=0.55, size=10.5) -caption(s, "품질 게이트 흐름 — 빌드→테스트→SCA→헬스체크→롤백.") - -# ================================================================ -# Ⅵ 프로젝트 지원 -# ================================================================ - -divider("Ⅵ", "프로젝트 지원", "PROJECT SUPPORT", ["인수인계·교육·기술지원", "유지보수(SM)·SLA", "장애대응·기밀보안"], "정성 · 사업지원", NAVY) - -# ---- Ⅵ 인수인계·교육·기술지원 ---- -s = slide(); content_header(s, "Ⅵ. 프로젝트 지원 · 인수인계·교육·기술지원", "구축 후 지속가능성 — 이관·교육·지원·하자보수를 표준 절차로 보장한다", "Ⅵ") -hv=[("인수인계","이관계획·검사 대상/방법·충족조건, 지식이전(운영자·개발자 지침서 PPT·프로그램 사양서)","closed-loop",BLUE), - ("교육훈련","운영자·관리자·사용자 대상 교육 — 내용·방법·기간·인원·횟수, 실습·매뉴얼 제공","org-people",PURPLE), - ("기술지원","지원 범위·수준·기술 매뉴얼, 서비스데스크(SR·문의) 채널·응답 기준","shield-security",TEAL), - ("하자보수","하자담보 기간 내 하자보수 범위·조치절차·대응(계약 조건 준수)","check-verify",BLUE_DK)] -for i,(t,d,ic,cl) in enumerate(hv): - x=0.55+(i%2)*6.19; y=1.6+(i//2)*2.15 - card(s, x, y, 6.05, 1.9) - rect(s, x, y, 0.1, 1.9, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, ic, x+0.3, y+0.35, 0.55, cl) - text(s, x+1.15, y+0.33, 4.7, 0.4, t, size=12.5, color=cl, bold=True) - text(s, x+1.15, y+0.9, 4.7, 0.85, d, size=9.3, color=INK, line_spacing=1.05) -caption(s, "프로젝트 지원 4영역 — 인수인계·교육훈련·기술지원·하자보수. WORK_STATUS 인수인계 체계 활용.") - -# ---- Ⅵ 유지보수(SM)·SLA ---- -s = slide(); content_header(s, "Ⅵ. 프로젝트 지원 · 유지보수(SM)·SLA", "라이브 자동배포를 실적으로 — SLA·장애대응·정기점검을 ITSM 프로세스로 운영한다", "Ⅵ", "REQ-N-004") -sla=[("코어 서비스 가용성","99.5% (성수기 99.9% 지향)","월 가용성·health"), - ("장애 응답시간","P1 즉시·P2 30분·P3 4시간","티켓 타임스탬프"), - ("장애 복구시간(MTTR)","P1 ≤2h·P2 ≤1영업일","복구 완료 기록"), - ("배포 성공률","≥99% (Fail-Safe 자동 롤백)","배포 로그 게이트"), - ("나노바나나 큐 소진","best-effort(워커 스케일아웃)","큐 적체·처리량")] -table(s, 0.55, 1.6, 8.7, ["SLA 지표","목표","측정"], sla, [3.1,3.0,2.6], rowh=0.5, hsize=10, bsize=8.8) -titlecard(s, 9.45, 1.6, 3.33, 3.55, "운영 체계·조직", TEAL, [ - "ITSM — 요청(SR)·장애·변경·배포·문제(RCA)", - "조직 — 운영총괄+백/프론트+DB/인프라+AI워커+QA", - "근무 — 평일 상주 + 성수기(3~5·9~11월) 온콜", - "CI/CD 자동배포 [검증됨: deploy_kintex.sh]", -], headfill=TEAL, bodysize=8.8, igap=0.7) -lead(s, 0.55, 4.7, 8.7, 0.55, "월간 SLA 리포트(가용성·MTTR·티켓·배포) — 페널티 체계는 계약 조건에 따름(공고 확인).", fill=INK, size=11) -caption(s, "SM 운영 — SLA 5지표 + ITSM 조직. 자동배포는 라이브 실적(과장 아님).", y=6.5) - -# ---- Ⅵ 장애대응·비상대책 ---- -s = slide(); content_header(s, "Ⅵ. 프로젝트 지원 · 장애대응·비상대책", "등급분류→에스컬레이션→RCA, degraded·PITR·롤백으로 비상 상황을 흡수한다", "Ⅵ", "REQ-N-005") -inc=[("장애 등급·에스컬레이션","P1(중단)~P4(경미) 분류, 자동 알림(WS·알림센터)·온콜 에스컬레이션",RED,"risk-warning"), - ("RCA·재발방지","근본원인 분석 리포트·재발방지 티켓화, 감사로그·관측성 추적(PII 미기록)",PURPLE,"check-verify"), - ("degraded / BCP·DR","Gemini=목응답·Claude=Ollama 폴백·PG=failover+PITR·배포=자동 롤백",TEAL,"shield-security"), - ("정기점검·예방정비","DB 인덱스/복제·백업(PITR)·Redis AOF·워커 하트비트·TLS 만료·용량 점검",BLUE,"pipeline-flow")] -for i,(t,d,cl,ic) in enumerate(inc): - y=1.62+i*1.16 - card(s, 0.55, y, 12.23, 1.02) - rect(s, 0.55, y, 0.1, 1.02, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, ic, 0.85, y+0.28, 0.48, cl) - text(s, 1.55, y+0.14, 4.0, 0.74, t, size=12, color=cl, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 5.7, y+0.14, 6.9, 0.74, d, size=9.5, color=INK, anchor=MSO_ANCHOR.MIDDLE, line_spacing=1.0) -caption(s, "장애대응·비상대책(BCP/DR) — RPO 최소·RTO 분 단위, degraded 3중 폴백으로 무중단 지향.") - -# ---- Ⅵ 기밀보안(관리적) ---- -s = slide(); content_header(s, "Ⅵ. 프로젝트 지원 · 기밀보안(관리적)", "기술적 통제(Ⅲ) 위에 관리적·물리적 기밀보안 체계를 얹는다", "Ⅵ", "REQ-S-012") -sec=[("보안 조직·서약","보안책임자 지정·보안서약서·보안교육 정례",BLUE,"org-people"), - ("문서·산출물 보안","산출물 분류·접근통제·반출입 통제·형상 이력",PURPLE,"document-rtm"), - ("인력·물리 보안","출입통제·개발환경 망분리·퇴사자 권한 회수",TEAL,"tenant-building"), - ("감사·점검","민감 액션 전수 감사(TB_AUDIT_LOG)·정기 보안점검·SCA",BLUE_DK,"check-verify")] -for i,(t,d,cl,ic) in enumerate(sec): - x=0.55+(i%2)*6.19; y=1.6+(i//2)*1.95 - card(s, x, y, 6.05, 1.72) - rect(s, x, y, 6.05, 0.5, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, 6.05, 0.5, t, size=12, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - icon(s, ic, x+0.3, y+0.72, 0.5, cl) - text(s, x+1.0, y+0.66, 4.85, 0.95, d, size=9.8, color=INK, anchor=MSO_ANCHOR.MIDDLE, line_spacing=1.05) -lead(s, 0.55, 5.72, 12.23, 0.5, "관리적 보안 — 기술 보안(Ⅲ §3 보안 요구사항)과 분리 배치, 표준 채점 구조 정렬.", fill=INK, size=11.5) -caption(s, "기밀보안관리 — 관리적·물리적 통제(조직·문서·인력·감사). 기술적 보안은 Ⅲ §3.", y=6.55) - -# ================================================================ -# Ⅶ 그 밖의 사항 -# ================================================================ - -divider("Ⅶ", "그 밖의 사항", "OTHERS", ["멀티테넌시·확장", "상생협력·하도급", "기대효과"], "가점 · 상생·부가", PURPLE) - -# S55 멀티테넌트 비전 -s = slide(); content_header(s, "Ⅶ. 그 밖의 사항 · 멀티테넌시·확장", "KINTEX #1에서 검증하고, COEX #2로 확장하며, 제3전시장을 표준으로 맞는다", "Ⅶ") -road=[("KINTEX","기준 테넌트 #1 · 검증 완료",GREEN),("COEX","테넌트 #2 · 온보딩 대상",BLUE), - ("제3전시장","2028 · 178,000㎡",PURPLE),("타 전시관","국내 전시산업 표준 SaaS",SLATE)] -line_conn(s, 1.3, 2.9, 11.9, 2.9, color=SLATE, w=1.6) -for i,(t,d,cl) in enumerate(road): - x=0.7+i*3.1 - rect(s, x+1.15, 2.77, 0.26, 0.26, fill=cl, shape=MSO_SHAPE.OVAL) - card(s, x, 3.3, 2.85, 1.5) - rect(s, x, 3.3, 2.85, 0.08, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, "tenant-building", x+0.2, 3.5, 0.44, cl) - text(s, x+0.8, 3.5, 1.9, 0.4, t, size=12, color=cl, bold=True) - text(s, x+0.2, 4.05, 2.5, 0.65, d, size=9, color=INK, line_spacing=1.0) -bullets(s, 0.55, 5.2, 12.0, [ - "개발 부담 최소화 — 멀티테넌시=격리 레이어만 추가, 기능 로직 불변(R-8)", -], dot=PURPLE, gap=0.4, size=11) -caption(s, "테넌트 확장 로드맵 — KINTEX→COEX→제3전시장→타 전시관 SaaS.") - -# ---- Ⅶ 상생협력·하도급 ---- -s = slide(); content_header(s, "Ⅶ. 그 밖의 사항 · 상생협력·하도급", "중소기업 참여·하도급 계획을 원칙으로 제시 — 구체 수치는 공고·컨소시엄 확정 시 기재", "Ⅶ") -titlecard(s, 0.55, 1.6, 6.05, 3.5, "상생협력 (정량 별표11)", BLUE, [ - "중소기업 공동수급체 참여 — 참여지분율 배점(최대 5점) 대응", - "지분율 50%↑=최고등급 지향 [공고·컨소 확정 시 기재]", - "중소기업 기술·인력 참여로 상생 생태계", - "구체 지분·업체는 [공고 확정 시 기재]", -], headfill=BLUE, bodysize=9.5, igap=0.66) -titlecard(s, 6.75, 1.6, 6.03, 3.5, "하도급 계획 (정성 적정성)", PURPLE, [ - "하도급 금액/계약금액 비율 적정성(배점 5점 이상)", - "20억원 이상 SW사업은 하도급 평가 제외 불가 — 계획서 필수", - "하도급 승인·관리·대금 직불 등 표준 준수", - "하도급 범위·업체·비율은 [공고 확정 시 기재]", -], headfill=PURPLE, bodysize=9.5, igap=0.66) -lead(s, 0.55, 5.3, 12.23, 0.62, "상생협력·하도급 — 일반 원칙 수준 제시, 구체 수치는 공고·컨소시엄 확정 후 확정 기재.", fill=INK, size=13) -caption(s, "정량 별표11(상생협력)·정성 하도급 적정성 대응. 구체 수치는 [공고 확정 시 기재](T3·T5).") - -# S61 정량 기대효과 (도식 ⑮) -s = slide(); content_header(s, "Ⅶ. 그 밖의 사항 · 기대효과", "견적 수일→즉시, 배치 수주→수 분, 검수 육안→자동, 결과는 사진으로", "Ⅶ", "REQ-F-002·007·009·017") -quant=[("홀 배정 견적","수일","즉시","document-rtm"),("배치도 초안","수일~수주","수 분","booth-layout"), - ("규정 검수","육안 검토","자동 플래깅","check-verify"),("유틸리티 신청","수기 작도","클릭+자동견적","wiring-power"), - ("시공 결과 예측","불가(외주)","표준 샷 자동생성","camera-render")] -qw=2.4 -for i,(k,a,b,icn) in enumerate(quant): - x=0.55+i*(qw+0.06) - card(s, x, 1.6, qw, 2.15) - icon(s, icn, x+qw/2-0.24, 1.75, 0.48, PURPLE) - text(s, x, 2.32, qw, 0.3, k, size=10, color=INK, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 2.68, qw, 0.24, a, size=9.5, color=SLATE, align=PP_ALIGN.CENTER) - text(s, x, 2.94, qw, 0.24, "↓", size=13, color=PURPLE, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 3.2, qw, 0.3, b, size=10.5, color=BLUE, bold=True, align=PP_ALIGN.CENTER) -imp=[("40초","단건 렌더",BLUE),("99.5%","코어 SLO",PURPLE),("142","REQ 커버",BLUE),("18","도메인 모듈",PURPLE)] -mw=2.94 -for i,(v,l,cl) in enumerate(imp): - x=0.55+i*(mw+0.16) - rect(s, x, 4.05, mw, 1.05, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, x+0.25, 4.15, 1.4, 0.85, v, size=26, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+1.65, 4.05, mw-1.7, 1.05, l, size=10.5, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) -lead(s, 0.55, 5.4, 12.23, 0.6, "리드타임 수일→즉시·수 분, 검수 육안→자동 — 정량 개선을 숫자로 각인.", fill=INK, size=13) -caption(s, "5지표 Before/After + 임팩트 지표 스트립.") - -# S62 정성·전략 효과 -s = slide(); content_header(s, "Ⅶ. 그 밖의 사항 · 기대효과", "참가업체는 영업력을, 홀매니저는 시간을, 킨텍스는 데이터 주권을 얻는다", "Ⅶ") -qual=[("참가업체","사진급 자료로 영업력·수주력 강화",PURPLE,"camera-render"), - ("홀매니저","규정 검수 병목 해소로 처리량 증대",BLUE,"check-verify"), - ("킨텍스(운영사)","온프레미스 우선·외부 AI 격리로 데이터 주권 확보",TEAL,"shield-security"), - ("전시산업","국내 전시산업 표준 플랫폼화",NAVY,"tenant-building")] -for i,(who,d,cl,icn) in enumerate(qual): - x=0.55+(i%2)*6.19; y=1.65+(i//2)*2.25 - card(s, x, y, 6.05, 2.0) - rect(s, x, y, 0.1, 2.0, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, icn, x+0.3, y+0.35, 0.6, cl) - text(s, x+1.15, y+0.35, 4.7, 0.5, who, size=14, color=cl, bold=True) - text(s, x+1.15, y+0.95, 4.7, 0.9, d, size=10.5, color=INK, line_spacing=1.05) -caption(s, "이해관계자별 정성·전략 효과 — 영업력·시간·데이터 주권·산업 표준화.") - -# S63 확장 비전·마무리 -s = slide(); content_header(s, "Ⅶ. 그 밖의 사항 · 기대효과·마무리", "킨텍스에서 검증하고, 코엑스로 확장하며, 다시 사진으로 컨펌한다", "Ⅶ", "REQ-F-077") -road=["KINTEX 검증","COEX 확장","제3전시장 표준","타 전시관 SaaS"] -for i,r in enumerate(road): - x=0.55+i*3.12 - rect(s, x, 1.6, 2.9, 0.7, fill=(PURPLE if i%2 else BLUE), shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - text(s, x, 1.6, 2.9, 0.7, r, size=11, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - if i<3: text(s, x+2.9-0.05, 1.6, 0.24, 0.7, "›", size=16, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -bullets(s, 0.55, 2.7, 12.0, [ - "확장 — COEX·제3전시장·타 전시관 SaaS 확장", - "재사용 — 검증된 공통 프레임워크(WISE/UIWS) 위에 도메인 코어 이식", -], dot=PURPLE, gap=0.5, size=11) -rect(s, 0.55, 3.9, 12.23, 1.7, fill=BLUE_DK, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) -rect(s, 8.5, 3.9, 4.28, 1.7, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) -icon(s, "camera-render", 0.95, 4.35, 0.7, WHITE) -text(s, 1.9, 4.15, 8.0, 1.2, "“신청서를 내는 순간,\n시공 후 사진을 먼저 본다.”", size=24, color=WHITE, bold=True, line_spacing=1.05, anchor=MSO_ANCHOR.MIDDLE) -caption(s, "확장 비전 로드맵 + 클로징 — 킨텍스를 전시 운영 표준 플랫폼으로 전환.") - -# ================================================================ -# 부록 A/B/C -# ================================================================ - -divider("A", "부록", "APPENDIX", - ["확인 필요·협의", "REQ 커버리지", "100대 기능 성숙도"], "부록", NAVY) - -# 부록 A 확인필요 -s = slide(); content_header(s, "부록 A · 확인 필요·협의 사항", "가정·협의를 1장에 집약 — 발주처 협업 자세", "A") -rows=[("C1","평가배점·제출규격 (가정)","공공 SI 통례 기준, 공고 확인 시 재정렬"), - ("C2","kxwp API (REQ-F-022)","웹폼→HWP/PDF 완결, 릴레이만 킨텍스 IT 협의"), - ("C3","CAD·트렌치 실측 (REQ-F-016)","평면도 15장+CAD 확보, 미확보 구간 실측 협의"), - ("C4","인터넷 요율 공식본 (REQ-F-002)","2,250원/㎡ 가정, 공식 요율표 확인 필요"), - ("C5","나노바나나 G1 (완료)","소유자 승인·워커 라이브 [검증됨]"), - ("C6","코엑스 실측 (REQ-F-077)","COEX 온보딩 시 홀·요율·규정 데이터"), - ("C7","사이니지·비콘·IoT HW (F-070·052~055)","킨텍스 시설 인프라 협의 전제"), - ("C8","기타 협의","연동·데이터·운영 세부 협의 항목")] -table(s, 0.55, 1.6, 12.23, ["ID","확인·협의 항목","비고"], rows, [0.9,5.0,6.33], rowh=0.55, hsize=10) -caption(s, "모든 [가정]·[협의]를 1장 집약 → 정직성을 강점으로 전환(리스크 R-4 방어).", y=6.5) - -# 부록 B REQ 커버리지 -s = slide(); content_header(s, "부록 B · REQ 커버리지 매트릭스", "142 REQ ↔ 슬라이드 1:1 — 미반영 0", "B") -cov=[("REQ-F (기능)","81","81","0",BLUE),("REQ-A (AI)","20","20","0",PURPLE), - ("REQ-N (비기능)","21","21","0",TEAL),("REQ-S (보안)","20","20","0",BLUE_DK)] -cw=2.94 -for i,(t,tot,cvd,miss,cl) in enumerate(cov): - x=0.55+i*(cw+0.16) - card(s, x, 1.6, cw, 2.3) - rect(s, x, 1.6, cw, 0.5, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 1.6, cw, 0.5, t, size=11, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, x, 2.25, cw, 0.7, cvd, size=34, color=cl, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 3.05, cw, 0.3, f"/ {tot} 반영", size=10, color=INK, align=PP_ALIGN.CENTER) - chip(s, x+cw/2-0.7, 3.4, 1.4, 0.32, f"미반영 {miss}", GREEN, size=9) -rect(s, 0.55, 4.2, 12.23, 1.0, fill=GREEN, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) -text(s, 0.75, 4.32, 3.0, 0.76, "142", size=40, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) -text(s, 3.0, 4.2, 9.5, 1.0, "총 142 REQ 전량 반영 · 미반영 0 — 상세 RTM(REQ→주 반영 슬라이드)은 proposal_content.md RTM 반영표 참조", size=12, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE, line_spacing=1.05) -caption(s, "QA는 이 표로 142 REQ 반영상태를 최종 검증 — 미반영 0.") - -# 부록 C 100대 기능 성숙도 -s = slide(); content_header(s, "부록 C · 100대 기능 성숙도", "기구현 63 · 부분 18 · 신규 19 — 정량 성숙도", "C") -mat=[("기구현","63","라이브·검증 완료",GREEN),("부분 구현","18","일부 완성·보강 필요",AMBER),("신규","19","설계 확정·구현 대기",SLATE)] -for i,(t,v,d,cl) in enumerate(mat): - x=0.55+i*4.11 - card(s, x, 1.7, 3.97, 2.6) - rect(s, x, 1.7, 3.97, 0.08, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 2.05, 3.97, 1.0, v, size=56, color=cl, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 3.25, 3.97, 0.4, t, size=14, color=INK, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 3.7, 3.97, 0.4, d, size=9.5, color=MUTED, align=PP_ALIGN.CENTER) -# stacked bar -rect(s, 0.55, 4.7, 12.23, 0.6, fill=HALL_BG, line=LINE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -total=100; x0=0.65; wtot=12.03 -segs=[(63,GREEN),(18,AMBER),(19,SLATE)] -cx=x0 -for v,cl in segs: - w=wtot*v/total - rect(s, cx, 4.78, w-0.03, 0.44, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, cx, 4.78, w-0.03, 0.44, str(v), size=12, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - cx+=w -caption(s, "라이브 확인분(인프라·CI/CD·나노바나나·인증·워터마크·PostGIS 코어·평면도 자산)만 [검증됨].", y=5.7) - -# ================================================================ -# 부록 D · 화면 설계(안) 갤러리 -# ================================================================ - -def build_gallery(): - manifest_path = os.path.join(SCREENS, "screens_manifest.json") - scr_files = sorted(glob.glob(os.path.join(SCREENS, "*.png"))) - if not scr_files: - return 0 - meta = {} - group_order = [] - if os.path.exists(manifest_path): - data = json.load(open(manifest_path, encoding="utf-8")) - group_order = data.get("group_order", []) - meta = data.get("screens", {}) - # 폴더 재스캔 — 매니페스트에 없는 파일도 수록(파일명 기반 캡션) - by_group = {} - for fp in scr_files: - fn = os.path.basename(fp) - m = meta.get(fn) - if not m: - scrid = os.path.splitext(fn)[0] - m = {"scr":scrid, "module":"", "name":"", "group":"기타 화면", "device":"DESKTOP", "order":900} - by_group.setdefault(m["group"], []).append((fp, m)) - for g in by_group: by_group[g].sort(key=lambda t: t[1].get("order", 900)) - order = [g for g in group_order if g in by_group] + [g for g in by_group if g not in group_order] - total = sum(len(v) for v in by_group.values()) - - divider("D", "화면 설계(안) 갤러리", "SCREEN DESIGN GALLERY", - ["역할·모듈별 화면", "Google Stitch AI 시안", f"수록 {total}화면"], "부록 D", PURPLE) - # intro 고지 - s = slide(); content_header(s, "부록 D · 화면 설계(안) 갤러리", "역할·모듈별 화면 설계 시안 — 구현 시 조정될 수 있음", "D") - rect(s, 0.55, 1.6, 12.23, 1.3, fill=RGBColor(0xFF,0xF6,0xE8), line=AMBER, line_w=1.2, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - icon(s, "risk-warning", 0.85, 1.9, 0.55, AMBER) - text(s, 1.6, 1.78, 11.0, 0.4, "고지", size=13, color=AMBER, bold=True) - text(s, 1.6, 2.2, 11.0, 0.6, "본 화면은 Google Stitch 기반 AI 생성 디자인 시안이며, 실제 구현 시 조정될 수 있음. 화면 문자열·수치·데이터는 예시입니다.", size=11, color=INK, bold=True, line_spacing=1.05) - # 그룹 인덱스 - text(s, 0.55, 3.2, 12.0, 0.3, "수록 그룹", size=12, color=BLUE, bold=True) - for i, g in enumerate(order): - x=0.55+(i%3)*4.11; y=3.6+(i//3)*0.62 - cnt=len(by_group[g]) - rect(s, x, y, 3.97, 0.5, fill=CARD, line=LINE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - rect(s, x, y, 0.09, 0.5, fill=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.25, y, 3.0, 0.5, g, size=10, color=INK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - chip(s, x+3.35, y+0.1, 0.5, 0.3, str(cnt), PURPLE, size=8.5) - caption(s, f"총 {total}개 화면을 역할·모듈 그룹으로 수록 — SCR ID·화면명 캡션 병기.", y=6.55) - - # 그룹별 갤러리 슬라이드 - for g in order: - items = by_group[g] - device = items[0][1].get("device", "DESKTOP") - per = 4 if device == "MOBILE" else 3 - for c in range(0, len(items), per): - chunk = items[c:c+per] - s = slide(); content_header(s, "부록 D · 화면 설계(안) 갤러리", g, "D") - n=len(chunk) - if device == "MOBILE": - cw=2.9; gap=0.19; cardh=4.6; x0=(13.333-(n*cw+(n-1)*gap))/2 - for i,(fp,m) in enumerate(chunk): - x=x0+i*(cw+gap) - card(s, x, 1.55, cw, cardh) - img_fit(s, fp, x+0.12, 1.67, cw-0.24, cardh-0.7, frame=False) - chip(s, x+0.12, 1.55+cardh-0.42, 0.9, 0.3, m.get("scr",""), PURPLE, size=8) - text(s, x+1.1, 1.55+cardh-0.43, cw-1.2, 0.32, m.get("name",""), size=8.5, color=INK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - else: - cw=3.95; gap=0.19; cardh=3.9; x0=(13.333-(n*cw+(n-1)*gap))/2 - for i,(fp,m) in enumerate(chunk): - x=x0+i*(cw+gap); y=1.65 - card(s, x, y, cw, cardh) - img_fit(s, fp, x+0.12, y+0.12, cw-0.24, cardh-0.75, frame=False) - chip(s, x+0.12, y+cardh-0.5, 1.0, 0.32, m.get("scr",""), PURPLE, size=9) - modtxt = m.get("module","") - text(s, x+1.2, y+cardh-0.51, cw-1.3, 0.34, [[{"t":m.get("name",""),"size":9.5,"color":INK,"bold":True}]], anchor=MSO_ANCHOR.MIDDLE) - if modtxt: - text(s, x+0.14, y+cardh-0.15, cw-0.28, 0.2, modtxt+" · 대상: "+g, size=7.5, color=MUTED) - caption(s, "Google Stitch AI 생성 디자인 시안 — 실제 구현 시 조정될 수 있음.", y=6.7) - return total - -gallery_count = build_gallery() - -# ================================================================ -# 저장 -# ================================================================ -OUT = r"C:\GUARDiA\workspace\kintex\docs\deliverables\제안서.pptx" -os.makedirs(os.path.dirname(OUT), exist_ok=True) -prs.save(OUT) -print("SAVED:", OUT) -print("TOTAL SLIDES:", len(prs.slides._sldIdLst)) -print("GALLERY SCREENS:", gallery_count) diff --git a/docs/deliverables/제안서/_gen/design_system.md b/docs/deliverables/제안서/_gen/design_system.md deleted file mode 100644 index c30c600..0000000 --- a/docs/deliverables/제안서/_gen/design_system.md +++ /dev/null @@ -1,242 +0,0 @@ -# 킨텍스 자동전시시스템 구축 — 제안서 디자인 시스템 (Design System) - -> 작성: proposal-visual-designer · 작성일: 2026-07-11 · 버전 v1.0 -> 짝 문서: `visual_concepts.md`(도식 12종·표지·간지 시각 컨셉) · `assets/icons/*.svg`(선 아이콘 20종) · `proposal_outline.md`(63슬라이드) · `tech_advisory.md` §1-4(도식 사양) -> 목적: **디자인 템플릿 PPTX 부재** → KINTEX 브랜드 기반 디자인 시스템을 직접 정의. deck-designer(`proposal_deck.pptx` python-pptx)가 이 스펙을 벗어나지 않는다. -> 스타일 정합: 같은 사업 산출물 패밀리(`docs/deliverables/_gen/gen_dev_plan_pptx.py` 개발계획서)와 **동일 팔레트·폰트·16:9·도형 직접 작도** 톤 유지. 제안서는 개발계획서보다 **표지·간지 시각 임팩트를 1단계 상향**(수주 문서 특성). -> 원칙: 시크릿(키·비번·내부IP) 미기재. 과도한 장식 금지 — **평가위원 가독성 우선**. 명료성·위계·일관성 > 장식. - ---- - -## 0. 아트디렉션 한 줄 - -**"도면이 아니라 사진으로 컨펌한다"** — 전시장의 공간감(블루)과 AI 생성의 지능감(퍼플)을 대비시키되, 본문은 흰 여백과 카드로 **읽기 쉬운 공공 제안서**를 유지한다. 표지·간지·핵심 도식(3중 해자·나노바나나 파이프라인)에만 시각 에너지를 집중하고, 나머지는 절제한다. - ---- - -## 1. 컬러 팔레트 - -### 1-1. 주조·강조색 (Brand) - -| 토큰 | HEX | RGB | 역할 | WCAG(흰 배경 대비) | -|---|---|---|---|---| -| `BLUE` (KINTEX Blue) | **#0066B3** | 0,102,179 | **주조색** — 헤더 액센트바·섹션칩·주요 강조·표 헤더 | 5.3:1 (AA 본문/AAA 대형) | -| `BLUE_DK` (Deep) | **#00447A** | 0,68,122 | 표지/간지 배경·최심층 데이터존·강조 대비 | 8.9:1 (AAA) | -| `BLUE_LT` (Tint) | **#E1EFF9** | 225,239,249 | 블루 계열 카드 배경·밴드 배경·연한 강조 | 배경 전용 | -| `PURPLE` (AI Purple) | **#6D4AFF** | 109,74,255 | **강조색** — AI/나노바나나/지능 요소 전용·간지 포인트·2차 액센트 | 5.0:1 (AA) | -| `PURPLE_DK` | **#4A2FCC** | 74,47,204 | 퍼플 위 텍스트 대비 보강·AI 심층 | 7.4:1 (AAA) | -| `PURPLE_LT` (Tint) | **#ECE8FF** | 236,232,255 | AI 카드 배경·퍼플 밴드 배경 | 배경 전용 | - -> **색 규율(불변)**: 퍼플은 **AI·나노바나나·지능·예측** 맥락에만 쓴다(남발 금지). 블루=구조/공간/시스템/신뢰. 이 이원 대비가 "결정적 알고리즘(블루) + AI 보조(퍼플)" 3층 신뢰 프레이밍을 색으로 각인한다. - -### 1-2. 중립 그레이 스케일 (Neutral) - -| 토큰 | HEX | 명도 단계 | 역할 | -|---|---|---|---| -| `INK` | **#101828** | 950 | 본문 기본 텍스트·제목 | -| `MUTED` | **#667085** | 500 | 보조 텍스트·캡션·부제 | (흰 배경 4.6:1 AA) | -| `SLATE` | **#98A2B3** | 400 | 비활성·예정 상태·눈금·화살표 | -| `LINE` | **#E4E7EC** | 200 | 카드 테두리·구분선 | -| `BG` | **#F9FAFB** | 50 | 본문 슬라이드 페이지 배경 | -| `HALL_BG` | **#F2F4F7** | 100 | 데이터/평면도 영역 배경·표 zebra 짝수행 | -| `WHITE` | **#FFFFFF** | 0 | 카드·본문 배경·다크 배경 위 텍스트 | - -### 1-3. 상태색 (Status / Semantic) - -| 토큰 | HEX | 의미 | 사용처 | -|---|---|---|---| -| `GREEN` | **#129E63** | 완료·라이브·검증됨·통과 | 간트 완료바·"이미 돈다" 배지·QA 통과·라이브 실적 | -| `AMBER` | **#E08A00** | 진행·주의·중간 리스크 | 간트 진행바·리스크 매트릭스 중위험·확인필요 | -| `RED` | **#D92D20** | 차단·위험·실격 리스크·전기배선 | 등록 게이트 403·고위험·**S6 전기 배선 범례(적)** | -| `CYAN` | **#0BA5EC** | 정보·네트워크 배선 | **S6 네트워크 배선 범례(청)**·정보 배지 | -| `TEAL` | **#0E9384** | 급배수·부가 | **S6 급배수 배선 범례(녹)** — GREEN과 구분 위해 청록 | - -> **배선 3색 규약(도식 ⑥ PostGIS 오버레이·④ Before/After 배선 범례 고정)**: 전기=`RED #D92D20`, 네트워크=`CYAN #0BA5EC`, 급배수=`TEAL #0E9384`. tech_advisory §1-4 ⑥의 "적/청/녹"을 색맹 안전(적-청-청록 조합, 명도차 확보)으로 확정. 상태 GREEN(완료)과 배선 TEAL(급배수)을 분리해 혼동 방지. - -### 1-4. 접근성·색맹 안전 - -- 본문 텍스트(INK/MUTED)는 모두 흰/연한 배경에서 WCAG AA 이상. -- 다크 배경(BLUE_DK/BLUE) 위 텍스트는 WHITE 또는 `#C7DDF2`(표지 부제, 대비 확보) 사용. -- 상태·배선은 **색 단독 의존 금지** — 항상 텍스트 라벨/아이콘/패턴 병기(완료=✓·진행=●·차단=✕, 배선=색+실선/파선/점선 스타일 병기). -- 색맹(적록) 안전: 완료/위험을 GREEN/RED만으로 구분하지 않고 아이콘·위치로 이중 인코딩. - ---- - -## 2. 타이포그래피 - -### 2-1. 폰트 패밀리 - -| 용도 | 폰트 | 대체(fallback) | -|---|---|---| -| 국문 기본 | **맑은 고딕 (Malgun Gothic)** | Pretendard, 본고딕(Noto Sans KR) | -| 영문·숫자 | 맑은 고딕(동일 지정) | Segoe UI, Arial | - -> deck-designer는 개발계획서와 동일하게 `FONT = "맑은 고딕"`을 전 run에 지정하고, ``(East Asian) 명시로 한글 렌더 일관성 확보. 외부 폰트 임베드 없이 시스템 폰트로 결정론적 생성. - -### 2-2. 타입 위계 (16:9 · pt 기준) - -| 위계 | 크기(pt) | 굵기 | 색 | 자간·행간 | 사용처 | -|---|---|---|---|---|---| -| 표지 대제목 (Display) | **44** | Bold | WHITE | line 1.02 | 표지 사업명 2줄 | -| 간지 넘버 (Section No.) | **150** | Bold | BLUE_DK(음영) | — | 간지 배경 대형 숫자 | -| 간지 제목 (Divider Title) | **34** | Bold | WHITE | — | 간지 섹션 국문명 | -| 본문 슬라이드 제목 (H1) | **22** | Bold | INK | line 1.0 | content_header 타이틀 | -| 소제목 (H2) | **13~14** | Bold | BLUE/PURPLE | — | 카드 헤더·블록 제목 | -| 강조 배너 (Lead) | **16~18** | Bold | WHITE/INK | line 1.0 | 비전 배너·핵심 문장 | -| 본문 (Body) | **10.5~11** | Regular | INK | line 1.0 | 불릿·설명 | -| 표 셀 (Table) | **8.5~10** | Regular/Bold | INK/MUTED | line 0.92 | 표 내용 | -| 캡션·부제 (Caption) | **8~9.5** | Regular | MUTED | — | 도표 캡션·부가설명 | -| 킥커 (Kicker) | **11** | Bold | PURPLE | — | 헤더 상단 섹션 라벨(대문자 영문 병기) | -| 배지·칩 (Chip) | **8~10** | Bold | WHITE | — | 배점 배지·상태칩·우선순위 | -| 지표 수치 (Metric) | **26~46** | Bold | WHITE/BLUE | — | 기대효과·통계 대형 숫자 | -| 푸터 (Footer) | **8.5** | Regular | MUTED | — | 슬라이드 하단 | - -> **본문 최소 크기 10pt 하한**(투사 가독성). 표 셀만 예외적으로 8.5pt까지 허용하되 정보 밀도 높은 표에 한정. 캡션 8pt 하한. - ---- - -## 3. 그리드·여백 (16:9) - -### 3-1. 캔버스 - -- 슬라이드: **13.333 in × 7.5 in** (12192000 × 6858000 EMU), 와이드 16:9. -- deck-designer 좌표계는 개발계획서와 동일 `inch(v)=v*914400` 헬퍼 사용. - -### 3-2. 안전 마진·그리드 - -| 항목 | 값(in) | 비고 | -|---|---|---| -| 좌우 마진 | **0.55** | 콘텐츠 좌측단=0.55, 우측단=12.78 (폭 12.23) | -| 상단 (본문) | 헤더 하단 구분선 **1.28**, 콘텐츠 시작 **1.45~1.5** | content_header 아래 | -| 하단 | 푸터 **7.06**, 콘텐츠 하한 **6.9** | | -| 상단 액센트바 | y=0, 높이 **0.12**, BLUE | 전 본문 공통 | -| 컬럼 그리드 | **12 컬럼** 개념 (실전: 2·3·4·5·6분할) | 카드 폭=(12.23 - gap×(n-1))/n | -| 표준 gutter | **0.16~0.35** | 카드 간격 | - -### 3-3. 정렬·화이트스페이스 원칙 - -- **좌측 정렬 기본**. 중앙 정렬은 칩·배지·지표 수치·간지 한정. -- 카드는 항상 `LINE` 테두리 1pt + 라운드(ROUNDED_RECTANGLE) + 옅은 그림자(alpha 18%). 그림자는 카드·주요 노드에만(남발 금지). -- **한 슬라이드 1 메시지** — 도식 1개 또는 카드 2~4개 + 하단 캡션 1줄. 밀도 상한 준수(P3 나노바나나 슬라이드도 도식+캡션+방어배지까지). -- 여백은 위계 신호 — 섹션 간 0.3in 이상 공백 확보. 꽉 채우지 않는다. - ---- - -## 4. 슬라이드 4종 레이아웃 규격 - -### 4-1. 표지 (Cover) — S01 - -- 배경: `BLUE_DK` 풀블리드 + 우측 겹침 평행사변형 2매(`BLUE` 큰 것, `PURPLE` 작은 것)로 대각 에너지. **여기에 나노바나나 시공 예측 히어로 이미지(워터마크)를 우측 평행사변형 영역에 반투명 합성**(개발계획서 대비 상향 포인트, `visual_concepts.md` §표지 참조). -- 좌상: 원형 브랜드 마크(WHITE 원+PURPLE 코어) + "KINTEX · WISE AI" 15pt WHITE Bold. -- 문서종류 배지 2개: "기술제안서"(PURPLE) + "Technical Proposal"(딥블루). -- 대제목: 사업명 2줄 44pt WHITE Bold. 그 아래 슬로건 배너 강조. -- 하단 메타바: 사업명·기술스택·작성일 (라인 구분). -- **슬로건 각인**: "신청서를 내는 순간, 시공 후 사진을 먼저 본다." — 부제로 대형 배치. - -### 4-2. 간지 (Section Divider) — 각 P1~P9 앞 - -- 배경: `BLUE_DK` 풀블리드 + 우측 평행사변형 2매(BLUE/PURPLE). -- 좌측: 대형 섹션 넘버 **150pt**(BLUE_DK보다 살짝 밝은 `#2C4E82` 음영톤) + PURPLE 언더바 + 국문 제목 34pt + 영문 13pt. -- 우측: 해당 섹션 핵심 포인트 3개(PURPLE 원 불릿 + WHITE 12.5pt). -- **배점 배지**: 간지 우상단에 해당 섹션 평가배점 배지(예 "P3 · 기술제안 35점")를 추가 — 배점 규율 시각화(개발계획서엔 없던 제안서 전용 요소). - -### 4-3. 본문 (Content) — 표준 - -- `content_header(kicker, title, idx)`: 상단 액센트바(BLUE 0.12) + 섹션번호 칩(라운드, BLUE) + 킥커(PURPLE 11pt) + 제목(INK 22pt) + 구분선(1.28) + 푸터. -- 콘텐츠 영역: y 1.45~6.9. 카드/표/도식 배치. -- 하단 **캡션 1줄** 필수(도식 슬라이드) — MUTED 9pt, tech_advisory 각 도식 "캡션" 텍스트 그대로. -- REQ 배지(선택): 우상단에 해당 슬라이드 대응 REQ-ID 소형 배지(회색) — RTM 정합 가시화. - -### 4-4. 도식 (Diagram) — 핵심 12종 - -- 본문 헤더 동일 + 콘텐츠 전체를 단일 도식에 할애. -- 도형은 **python-pptx 직접 작도**(외부 이미지 금지, 개발계획서 원칙 계승). 노드=ROUNDED_RECTANGLE, 연결=화살표 텍스트("→"/"›") 또는 얇은 라인 rect, 아이콘=본 시스템 SVG(단색 stroke, 슬라이드에선 도형 근사 또는 SVG를 EMF/PNG 없이 도형으로 재현). -- 각 도식 상세는 `visual_concepts.md`에 좌표·색·라벨 수준으로 명세. - ---- - -## 5. 컴포넌트 스타일 - -| 컴포넌트 | 규칙 | -|---|---| -| **카드 (Card)** | ROUNDED_RECTANGLE, fill WHITE, line LINE 1pt, 그림자(alpha 18%, blur 90k, dist 38k). 헤더 있으면 상단 컬러 바(0.07~0.5) 또는 좌측 컬러 스트립(0.08~0.09). | -| **섹션 넘버 칩** | 0.62×0.62 라운드, BLUE(또는 PURPLE), WHITE 20pt Bold 중앙. | -| **배점 배지** | 라운드칩, fill=섹션색, WHITE 8~10pt Bold. "20점"/"35점" 등. 배점 큰 P3는 PURPLE 강조. | -| **상태 칩** | 라운드, 완료=GREEN·진행=AMBER·예정=SLATE·차단=RED. 텍스트+색 이중. | -| **불릿** | 작은 OVAL(0.09~0.11) 컬러 점 + 텍스트. 컬러=맥락색(블루/퍼플/상태). 이모지 불릿 금지 — **선 아이콘 또는 도형 점만**. | -| **콜아웃/배너** | 풀폭 라운드 rect, fill=BLUE(비전)/INK(권한구조)/PURPLE(AI강조), WHITE Bold 중앙. | -| **번호 배지** | 작은 OVAL, fill=색, WHITE 8pt Bold 숫자. 프로세스 스텝. | -| **프로세스 스텝** | 카드 나열 + 사이 화살표("›" 16pt SLATE). 좌→우 흐름. | -| **표 (Table)** | 헤더=BLUE 풀폭 라운드바 WHITE Bold, 본문 zebra(짝수행 `#FCFCFD`/HALL_BG), 행 구분 LINE 0.01, 셀 8.5~10pt. 강조행(낙찰 등)=BLUE_LT + Bold. | -| **아이콘** | 본 시스템 `assets/icons/*.svg` — 선(stroke) 스타일, 24×24, stroke-width 1.75, currentColor. 슬라이드 내 색=맥락색. | - ---- - -## 6. 차트 스타일 (BI 도식 · S38·S39·S61) - -- 라이브러리 톤: Recharts 목업 재현(막대·라인·도넛·KPI 카드). -- **시퀀셜(단일 계열)**: BLUE 계열 명도 그라데이션(#00447A→#0066B3→#4A90D9→#9CC3E8). -- **카테고리(다계열)**: BLUE / PURPLE / TEAL / AMBER / SLATE 순환(최대 5, 색맹 안전 순서). 6번째부터 명도 변주. -- **비교(Before/After·As-Is/To-Be)**: As-Is=SLATE(회색, 과거), To-Be=BLUE(현재/개선). 개선 화살표=PURPLE. -- **강조 수치**: 대형 Metric(26~46pt) + 단위 소형. KPI 카드는 BLUE/PURPLE 교차 fill + WHITE 수치. -- 축·그리드: SLATE 얇은 선, 라벨 8pt MUTED. 범례는 색+텍스트+마커 형태 병기. -- 도넛/파이: 최대 5조각, 나머지 "기타" 회색 통합. 중앙에 총계 수치. - ---- - -## 7. 아이콘 시스템 (선 스타일 SVG — 직접 제작) - -### 7-1. 공통 제작 규격 (전 20종 통일) - -- **viewBox**: `0 0 24 24` -- **fill**: `none` (면 채움 절대 금지) -- **stroke**: `currentColor` (슬라이드에서 맥락색 상속) -- **stroke-width**: `1.75` (전 아이콘 동일 시각 무게) -- **stroke-linecap**: `round`, **stroke-linejoin**: `round` -- **그리드 정렬**: 24 그리드, 여백 2(콘텐츠 영역 2~22), 코너 반경 통일감. -- **외부 라이브러리·폰트아이콘·온라인 생성 금지** — 전량 직접 패스 작도. -- 한 세트 통일 메타포: 기하 단순·동일 디테일 밀도(오버피팅 금지). - -### 7-2. 아이콘 20종 (최소 16종 요구 → 20종 제작) - -| # | 파일명 | 의미 | 주 사용 슬라이드 | -|---|---|---|---| -| 1 | `ai-sparkle.svg` | AI/스파클/지능 | 나노바나나·AI 전 슬라이드·간지 | -| 2 | `booth-layout.svg` | 부스/레이아웃/배치 | M2 플로어플랜·모듈맵 | -| 3 | `wiring-power.svg` | 배선/전기/유틸리티 | M4 배선·아키텍처 | -| 4 | `camera-render.svg` | 카메라/렌더링/시각화 | 나노바나나 샷세트·표지 | -| 5 | `auction-gavel.svg` | 옥션/경매 망치 | M15 옥션 폐루프 | -| 6 | `visitor-badge.svg` | 관람객/배지 | M10 관람·등록 | -| 7 | `shield-security.svg` | 보안/방패 | P5 보안·불변원칙 | -| 8 | `watermark.svg` | 워터마크 | AI 이미지 보안·워터마크 | -| 9 | `tenant-building.svg` | 테넌트/전시관/빌딩 | 멀티테넌시·확장 | -| 10 | `schedule-gantt.svg` | 일정/간트 | P8 일정·마일스톤 | -| 11 | `org-people.svg` | 조직/사람 | 추진체계도·페르소나 | -| 12 | `check-verify.svg` | 체크/검증/규정 | 규정검증·QA·커버리지 | -| 13 | `dashboard-chart.svg` | 대시보드/차트/BI | M16 경영분석·기대효과 | -| 14 | `mobile.svg` | 모바일/반응형 | 현장·모바일·접근성 | -| 15 | `document-rtm.svg` | 문서/서류/RTM | M6 서류·RTM·커버리지 | -| 16 | `risk-warning.svg` | 리스크/경고 | 리스크 매트릭스·확인필요 | -| 17 | `postgis-spatial.svg` | 공간데이터/PostGIS/폴리곤 | 단일 공간원천·배선 | -| 18 | `pipeline-flow.svg` | 파이프라인/큐/비동기 | 나노바나나 파이프라인·아키텍처 | -| 19 | `network-zone.svg` | 망분리/보안영역 | 보안영역 분리도 | -| 20 | `closed-loop.svg` | 폐루프/순환 | 생애주기 폐루프·옥션 | - -> 16종 요구 대비 **20종 제작**(공간데이터·파이프라인·망분리·폐루프 4종 추가 — 킨텍스 도식 핵심 메타포 보강). - -### 7-3. deck-designer 아이콘 사용 지침 - -- python-pptx는 SVG 직접 임베드가 어려우므로, 각 아이콘은 **(a) 슬라이드 헤더/칩 옆 시각 앵커**로 쓸 때는 대응 도형(원+선) 근사 작도, **(b) 아이콘 자체가 콘텐츠일 때**는 SVG를 사전 PNG 변환(cairosvg/inkscape) 후 삽입 가능. 변환 시 stroke 색=맥락색으로 렌더. -- 색 상속: 아이콘 stroke는 배치 맥락색(블루=구조, 퍼플=AI, 상태색=상태)으로 지정. - ---- - -## 8. deck-designer 전달 요약 (준수 계약) - -1. **팔레트 상수**를 §1 표 그대로 코드 상단에 정의(개발계획서 `gen_dev_plan_pptx.py`와 동일 변수명 재사용 + PURPLE_DK·RED·CYAN·TEAL 추가). -2. **폰트** 맑은 고딕 전 run + East Asian 지정. -3. **레이아웃 4종**(§4) 헬퍼 함수화: `cover()`, `divider(no,kr,en,points,score)`, `content_header()`, 도식별 함수. -4. **12종 도식 + 표준 3종**(추진체계·리스크·기대효과)은 `visual_concepts.md` 좌표·라벨대로 작도. 재해석 금지. -5. **색 규율**: 퍼플=AI 전용, 배선 3색 고정(적/청/청록), 상태색 의미 고정. -6. **가독성 우선**: 본문 10pt 하한, 한 슬라이드 1메시지, 캡션 1줄, 여백 확보. -7. **보안**: 시크릿·내부IP 도식/라벨에 미기재. 외부 API는 "승인 예외"로만 표기. diff --git a/docs/deliverables/제안서/_gen/fetch_screens.py b/docs/deliverables/제안서/_gen/fetch_screens.py deleted file mode 100644 index 02aa4c3..0000000 --- a/docs/deliverables/제안서/_gen/fetch_screens.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Stitch 화면 스크린샷 다운로드 + 매니페스트 생성 -- MCP list_screens 결과(제목→SCR/모듈/그룹 매핑)의 screenshot.downloadUrl을 assets/screens/ 로 저장 -- screens_manifest.json 갱신(파일명→title·scr·module·group·device·order) -- build_deck.py 는 assets/screens/ + manifest 를 재스캔해 갤러리 자동 재구성 -재실행: 새 화면이 생기면 MCP 재조회 후 SCREENS 갱신하여 다시 실행. -""" -import os, json, urllib.request, ssl - -BASE = os.path.dirname(os.path.abspath(__file__)) -OUT = os.path.join(BASE, "assets", "screens") -os.makedirs(OUT, exist_ok=True) - -# title -> (scr, module, korean_name, group, device) -META = { - "Login & Workspace Selection - KINTEX AI System": ("SCR-01","공통","로그인 & 행사 워크스페이스 선택","공통·인증","DESKTOP"), - "Organizer Dashboard - KINTEX AI System": ("SCR-02","M1","주최자 대시보드","주최자","DESKTOP"), - "Booth Layout Editor - KINTEX AI System": ("SCR-03","M2","부스 배치 에디터","주최자","DESKTOP"), - "Layout Comparison - KINTEX AI System": ("SCR-04","M2","배치안 비교(S7 조감)","주최자","DESKTOP"), - "Exhibition Schedule - KINTEX AI System (Updated Icons)": ("SCR-15","M1·M6","전시 일정 관리","주최자","DESKTOP"), - "Exhibitor Home - KINTEX AI System": ("SCR-05","M1","참가업체 부스 홈","참가업체","DESKTOP"), - "Booth Design Studio - KINTEX AI System": ("SCR-06","M3","부스 설계 스튜디오","참가업체","DESKTOP"), - "Utility Wiring View - KINTEX AI System": ("SCR-07","M4","유틸리티 배선 뷰","참가업체","DESKTOP"), - "Utility Order Summary - KINTEX AI System": ("SCR-08","M4","유틸리티 신청 요약·위치표시도","참가업체","DESKTOP"), - "Compliance Report - KINTEX AI System": ("SCR-09","M2","규정 검증 리포트","장치업체","DESKTOP"), - "Manager Approval Queue - KINTEX AI System": ("SCR-10","M6","홀매니저 승인 큐","홀매니저·운영","DESKTOP"), - "Review Details - KINTEX AI System": ("SCR-11","M6","홀매니저 검수 상세","홀매니저·운영","DESKTOP"), - "Hall Operations - KINTEX AI System (Updated Icons)": ("SCR-14","M14","홀 현장운영 대시보드","홀매니저·운영","DESKTOP"), - "Visualization Gallery - KINTEX AI System": ("SCR-12","M5","시각화 갤러리","시각화·전역","DESKTOP"), - "Business Intelligence - KINTEX AI System (Updated Icons)": ("SCR-13","M16","경영분석 대시보드","경영분석","DESKTOP"), - "Admin Dashboard - KINTEX AI System (Updated Icons)": ("SCR-16","M18","관리자 대시보드","관리자 백오피스","DESKTOP"), - "Component Guide - KINTEX AI System (Updated Icons)": ("SCR-17","디자인","컴포넌트 가이드(스타일가이드)","스타일가이드","DESKTOP"), - "Contractor On-site Checklist - KINTEX AI System": ("SCR-M1","현장","시공업체 현장 체크리스트","모바일 현장","MOBILE"), - "Manager Field Inspection - KINTEX AI System": ("SCR-M2","현장","홀매니저 현장 검수","모바일 현장","MOBILE"), -} - -# title -> screenshot downloadUrl (from MCP list_screens; 최신 조회분) -URLS = { - "Login & Workspace Selection - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLtUqdYm2030_UAU-An_JAmVNGl2eTumZ5vNnz9T5WeXoyFc7pvkO4RQPoHLdsFqg4shnjDOxWNOMGVPtMLH8A_SUNvd7GFISwRGX1vZ8hMkZIsiU4DPDg-6qV6EpX1j0qHjpu9zHPyWtO_6nWcRDGaOsweZTkjvZ2UeTD0GgyqCETLufFh69ZQvlcuOpM0VSH7spqLDwPq-hpoWme6S_WBSg9b6ygHB3lUvfkOgs5wWtPOgIcZ6bCn1ZB8", - "Organizer Dashboard - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLtAyvwUEakGIhFdDB_6cQqB8DzBPnb7TdrSUBehA1gVEzNKg-ppC3sF1LcgF_vr9bceJ3voOhAcUcC5CYbSiPb0IjMoHHpwfCqm5rqI9vTqHjdvIpg69oMO09Rx_wF0k_XCY6P9doDNj8SaW5TjcsbuvGT4NHO-5uBDeT65_MF-ZTdRZ_jF2mugRi0KqBS5dnAvnJhQH-UFdIO3v_Wtaj8hkXkvS--sth05JgI1hNMUZJrjHGiROzBGxw", - "Booth Layout Editor - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLvOGbKVWZuHy5ALELagNRlBkIwl8byuXEe1LcPgKuP7o5XaRZrsE6UTVJC821aK01hdrs19oson_NDDMnoo7nqU6kgu5NgJJdxK6cdF5lmzr2jziNxWEwHDPoibYcIeAFsCaIZy49a6mYUzrY2ZhQDqAT0AZ3YV8Cfq_dMkuyaGaHvKRBt_1cbRRFa0MgZsN9l1Ed6w6GPKLjKF7Sw3fOpdYxTwhLz6BrpIBBkNYsk97t7nzA9Hv2mTHw", - "Layout Comparison - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLvUxlOFlFr0MwnaD8KN10xT9gsFq0_wyilol_aWYLj2Ev_kHhNKJ5PHug_6KwEemQtsQjZDEP8zEPpkdZvUq-TTOubFN3NStmGaIlf1-GvpPzq5uLMGrneKXsp7ccThjFjq556VQhhr2WHlyqlSEhtx37cd0i9Iggo3GfPwcNYNUMd6kizoWzKTy_LMvTjhMAdMGwblLvMN1bYDhoQ75Aty2eSwf92NSzsLccQPk5EGw_GicpZRenkeDQ", - "Exhibition Schedule - KINTEX AI System (Updated Icons)":"https://lh3.googleusercontent.com/aida/AP1WRLthlJCkcQzW168tISpRl5BVhGWN6aZ-dTxxpnJYLqrWUQ132vn-w0x23XA_y8JF7e-q28ZqqJy_Z9tqc2J_OK99-RPYpT7nbFmoD2H-NC7MBHRp7RSegRK-hE5WKT3r8MNtFPG1PvcDIdvSiNPqy_YYcdOCYDvjwVZAPmhgBSiWFyZTZjB6Plwq6et0F5SIZQThhmK_lFcy0K9Jcodwk2YiCcIET47cTYj02BpEieNO5RK7gj6fsL3OonI", - "Exhibitor Home - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLvzY0N-4aD4E4apccqfR3yNCRkuFKEOc4O4uJIbkaHQQrDi-gDwKS5pFJPEvcAKBfrGgi10XY32J6VSfgiX_AzVomJDuoinMjWzTi_XSXSzJgravcI-ABUo-ep_p9rdNP2lX7T01WqML0uhOYGD2jlhjhusVpqsYBYsRmFhi6NhQMYqUZdBH_ndL9YW6g6wAOObXXVGKgMUBr0d-YHI0FyhKM0uAi1CYQ2QQiNDSJtZdtOHS6yn44MwUhs", - "Booth Design Studio - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLu1TWc00TspuC4jpGfgrvt9_kRQIWg84Zr4FW1l_YVePzu5jTqkYR7UmO1Xw9nuydukCbwIh3OiOqpedk9Tt4fwrTQkqrxJEZYNmM3dr-GRhvbYhO2UFrmlJzhdMeFtXuTKnv3nTqzWyTgBySTbXYncfySbTSzC06dleCMKamOHEgGI0M60g_9LBT-STycoHohAFWuUY2Z4XIlkN0gJJ0B0P3y9JTEX7S3RTiiO8RNhAoBskY2ZjQId7TI", - "Utility Wiring View - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLt0_fkmB3ACojGDXezlhbpya-s-re7WgfEAXim10VWfAMNWet_cpx3addY23p2Kzcd-1NXVbgmQh-5tGokg19E0ui7H5yU2IVbU28GOquQu-ngiRv6CpAcMtQfe-zjzfrSOA1DT2mT-zQTUYd068i6HwJJv5734uU4hFuvLU0xCExlfe8kxnp5Ct8IfC11Aoh_jLvPMCqsMmOux0mtYRf4Sp4hEaBfJr1BMwIX4dAlvpTV-PJxiJ_M5Ymo", - "Utility Order Summary - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLtyOGoy4l69iClGRq0bO8uTbAjZx54sF890NtTmAKPztU6OwKtfsUDZ8c_KcynLP1btwWj1VMXUu0MHKCx75n5gCNpxuRRpwAgdoQM6Mxzwt-TuWLyAhM4CW7MXKJEpVVA0vQmXjqV6hM177yZ0kTJ6aN0u2C-mvzELug_hDensEwivvFZn4OHfK1UF3HY7GxuPLZB4smGXiMqHH3vRGD1feNV1USWxfTGq0G5JhIu79TI1mMyTbmrtvg4", - "Compliance Report - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLuh3R_GPiSwqoywspJfxh9c8o-4qNRFGsRQJ6hNtS4OyvqArAB3AVT4ZDkQrJjZPutTFFG0k7JNr_egmfBAfUn_LjOO5AB-eqTA3MoAdGxmqEa7qThF7hCIz2lfKInkL4N-GSrNdZtBnJMb95f9sRUDzNaLk-wQZZcUv2ee6Uo1CvWEu5mdYScTal09vaepqghDUi2Td8oxzTjQ_dcRGUHDcZoncSuvPg2fPhHECW99CiTwD3RMU2es470", - "Manager Approval Queue - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLt0jyggUFD_uIa47eZ71yDp7_r-yDJxUvGJcxuBhlHcjPsilp_Su2XDR17WyNcxPWdd5t_6nJCIkd9JjblfA8ENMW2QvuszG7RawM33gvFJG5uS8_JFoctdLz8Oq1WDAcdJH3ObRgv3EDDfqFY9LXvAnzKA8d2qsR31fz5ulTfulgUyCUdckbSdheECcc1lrtx4TLwafiwUggI1y8zDNVHtOcJuw4cG9OWqxMxreCghnJ3D4YthwnWUAQ", - "Review Details - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLtZn9UwuYCpWjWC9ZlUKYKZO6-8LWF92u8BZhUpgNrIjPQVa2NiS6zv5Zz9k2NQSPYCWdj2OQBRpB7u1hNi3AmMvObMtvvewMBeeDrDInB22eFS6DcbuT_Goa5bsxMTIWp0jx5PKR2rGZtp-78nHn2NNi7p96d_2ddXJ3eBtwAq-NRFL7jbuFbGWrIW0wFkJ8ThMazQ33UxOFsLmD_PXCrnWyt_NcNO-ans631jlKL-VdL_FfKBNDaxylw", - "Hall Operations - KINTEX AI System (Updated Icons)":"https://lh3.googleusercontent.com/aida/AP1WRLtVuCoXA2RksHSQq2vVewDWWsoduCoY9CD0n9rVvsD-ShiGsQCPBCFJWakyIjNNPqF9GASb3u5rs3bqHSUd_kk15d_014FfzpYuiWyrjTleULMsFDsnhaW9n-jpO7_ayRndbZWh8zBjpZ2zVuUReCYRFP_BVwr2O3RYyKCEVXcY6l6dzMywa3EozvEhV87GSS-CxKhm4cBb5RcI7j_5j4IgFj9RYxDzq89agjhTxjsWA9DIx5lq32BSof0", - "Visualization Gallery - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLuuyZPo--qWn5qMTYI0fWcrBj1LuMzudztIvctYqof9RweLOLVb1Xu7a-xTUB615pRKZp-AAOPQZgRcqQUN6bT_XIyykoLT4yFQXXOxMqIXI-FulW61CdiBOfo6U43Z9YI37oXm_aFtV9PsEgPrsjj3aIzNYxxtSAkOJzVx-G215-uAVdcikNeYoPvnSkgoBP5OjzR5futn48YmVMNsEmbVXe0YVIZbwQOvlIa31TDNPiw2xzJyvdGUwrg", - "Business Intelligence - KINTEX AI System (Updated Icons)":"https://lh3.googleusercontent.com/aida/AP1WRLubNXF75G8McZ1gryw7VDigsKOl63B9nuVSi6_tJCXAYF4ZIhEuJlNfptxhTWSxP5CwN5cUWxHVnnviC-qO8pxChg_Xf5GpImd-UTXb_JIdqzGT2dtr0qkNN15FqPMvk4WenixdezsLNsU4moxl72lOoqJvxHi0xuBp4SmRzJNTd1Z7XK29ycdk-80LCfvPBHZrbT2iGZGreshY63kpwu4dlZarjt_xQO1qqTm_ZYkpIM79xJPMT-jMNyQ", - "Admin Dashboard - KINTEX AI System (Updated Icons)":"https://lh3.googleusercontent.com/aida/AP1WRLu3zdb3wXg6Dvq77ymBp4iT7wU7uo9G5t8cfb5X2_TZr0XNtST2AnmFqsrZO56E1p1Xz6xbQesdd_59tfcpsoUMkB5WO1fja-HFLK06JtFeGkZxitnG-87qCgauSPphlsjOtSHaEiZmPfbg5yh-a5PelIKe2bHjM-FmX4hm7_UItkgaC-FoEBEfsNwHnALPBbWvz6u8ZZhuXceh8G0V_SSH0ks9t75boEooOPg15xEJ4KszZHdlb2OFCl0", - "Component Guide - KINTEX AI System (Updated Icons)":"https://lh3.googleusercontent.com/aida/AP1WRLsLW88myjTGHETfGth2ywdFnjE3y692dNnxaDKncS_DwVuqegjWfznVhrKE8VXJLSWkmIiqREk13m-FKKAK7nwJGAtUX2r7f54KrcEd0Na2B-xFqEqzQovlfFi2tbxletF3WKJUqNGp_IHkvlghOymIz7IBUOeA5UABGGqfJdSBGIso2Fqh1jX5Pu0LEfP1HsfL167lTgS4t1oe94hDp9QC-MQgD2U-KOYdoUaZ_WINLtFeZ5-3Uvo05YI", - "Contractor On-site Checklist - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLtA_bsgM_D2rk4HbtM-snlKWWvXP8Lm4dGEvLQcGFgf6tFBkHXiK5fiT-T2UCzS46RkEwV8GDjM3sHFRfh65PQ4Ssnj-p5DVhZO0JCx3_jmTDumyl3kYbL5ssEU15psKPJ8tU_yX-MlUelq-Yhh6lgkGYqbTFUy8aQYD5M3F-FUAKQLUjSTb_SqHHg4dae9RqB6otbfl5Y0KiA4QDkAURFWBgJnJeOmuONOr-r6BTKS4ZPmu7DnAXQPYK0", - "Manager Field Inspection - KINTEX AI System":"https://lh3.googleusercontent.com/aida/AP1WRLtStbjv9lr4kBFrhu9LuUu7yXXGrFg_8HKk4DPRjSpxcslk4GPz85moG6zJzCQrt_tVWRi255BLH9NKp8wH-W_kBcPEy9_T4xVBPbYkytnT6oHIMW5HXz0OAkhq1Izk-EI7Xj_-FfAhKUpjgAXhg_7sGZMrkD6sXgVk57W6-fZNmN-OqYY2872tL-JK_EgVAdY7joxaUoqVOizVZB2Mw_ElFBRTFCPz3iO8UTwhQs2q55OYSqP-n0in7pc", -} - -# 갤러리 그룹 순서 -GROUP_ORDER = ["공통·인증","주최자","참가업체","장치업체","홀매니저·운영","시각화·전역","경영분석","관리자 백오피스","스타일가이드","모바일 현장"] - -def scr_num(scr): - s = scr.replace("SCR-","") - if s.startswith("M"): return 100+int(s[1:]) - return int(s.split("·")[0]) if s[0].isdigit() else 999 - -ctx = ssl.create_default_context() -ctx.check_hostname = False -ctx.verify_mode = ssl.CERT_NONE - -def slug(scr, kor): - return scr.replace("·","_") + ".png" - -manifest = {} -ok = fail = 0 -for title, meta in META.items(): - scr, module, kor, group, device = meta - url = URLS.get(title) - fn = slug(scr, kor) - fp = os.path.join(OUT, fn) - if url: - try: - req = urllib.request.Request(url, headers={"User-Agent":"Mozilla/5.0"}) - data = urllib.request.urlopen(req, context=ctx, timeout=60).read() - with open(fp, "wb") as f: - f.write(data) - ok += 1 - except Exception as e: - print("FAIL", scr, title, repr(e)[:80]); fail += 1 - continue - elif not os.path.exists(fp): - print("SKIP(no url, no file)", scr); continue - manifest[fn] = {"scr":scr,"module":module,"name":kor,"group":group,"device":device, - "title":title,"order":scr_num(scr)} - -with open(os.path.join(OUT, "screens_manifest.json"), "w", encoding="utf-8") as f: - json.dump({"group_order":GROUP_ORDER,"screens":manifest}, f, ensure_ascii=False, indent=1) - -print(f"DOWNLOADED ok={ok} fail={fail} manifest={len(manifest)} -> {OUT}") diff --git a/docs/deliverables/제안서/_gen/g2b_standard_toc.md b/docs/deliverables/제안서/_gen/g2b_standard_toc.md deleted file mode 100644 index ad6e27c..0000000 --- a/docs/deliverables/제안서/_gen/g2b_standard_toc.md +++ /dev/null @@ -1,342 +0,0 @@ -# 나라장터(g2b) 정보시스템 구축 부문 — 표준 제안서 목차·평가배점 조사 및 킨텍스 제안서 재편 매핑 - -> 작성: rfp-analyst · 작성일: 2026-07-12 · 목적: 킨텍스 제안서(P1~P9, 63슬라이드) 목차 재편 근거 -> 원칙: **확인된 1차 출처(법령·실제 RFP)만 사실로 기재**, 그 외는 [가정]/[통례] 표기. 시크릿 미기재. -> 대상 제안서 파일은 **읽기만**(수정 금지) — 본 문서는 매핑·권고만 산출. -> 짝 문서: `proposal_outline.md`(현행 P1~P9) · `rfp_analysis.md`(§4 배점 가정) · `tech_advisory.md`(§5 WBS·§6 SM) · `rtm.md` - ---- - -## 0. 핵심 결론 (요약) - -1. **표준 목차의 단일 출처**는 조달청 「협상에 의한 계약 제안서평가 세부기준」 **[별지 제5호서식] 제안서 서식(예시: 소프트웨어개발·유지관리사업)** — 7개 대장(Ⅰ 일반현황 ~ Ⅶ 그 밖의 사항)이다. 이 목차가 **평가부문 = 제안서 장(章)**으로 1:1 정렬된다. -2. **정성평가 평가항목의 단일 출처**는 같은 세부기준 **[별표 3의2] 소프트웨어 및 시스템 개발사업** — "정보시스템 구축"에 해당하는 표준 평가표. 부문: **전략 및 방법론 / 수행계획(기술·기능) / 수행기반 / 프로젝트 관리 / 프로젝트 지원 / 하도급 / 필수 제안**. -3. **배점은 세부기준이 고정하지 않는다.** 제9조⑤에 따라 **수요기관이 항목별 배점한도를 결정**(항목당 30점 초과 금지, 하도급 5점 이상, 국산제품 활용 2점 이내). 정보화사업 총 배점은 「행정기관·공공기관 정보시스템 구축·운영 지침」 **제18조**에 따라 **기술 90(정성 70 + 정량 20) / 가격 10**(원칙), HW 비중 50% 이상 사업은 **기술 80 / 가격 20** 허용. -4. **현행 킨텍스 제안서의 80/20 배점 가정(rfp_analysis §4)은 정보화 표준(90/10)과 다르다** — 실제 공고 확인 전까지 [가정]이며, SW 중심 사업이면 90/10이 표준. (본 문서는 지적만, proposal 파일 수정 없음.) -5. 현행 63슬라이드는 **표준 7대장에 전부 매핑 가능**하나, **신설이 필요한 장 4개**(Ⅰ 일반현황 / Ⅴ·Ⅵ 프로젝트 관리·지원 보강 / 상생협력·하도급 / 정량평가 대응)가 있다. 미배치 슬라이드 0. - ---- - -## 1. 조달청 기술평가 표준 항목·배점 (출처·연도 명시) - -### 1-1. 전체 배점 체계 (법령 확정 사실) - -| 구분 | 배점 | 출처(조항) | -|---|---|---| -| 종합 = 기술능력평가 + 입찰가격평가 | 100 | 세부기준 제9조① | -| 기술능력평가 | **80점** (세부기준 원칙) / 정보화사업은 **90점**(지침 제18조) | 세부기준 제9조①1 · 지침 제18조 | -| 입찰가격평가 | 20점(원칙) / 정보화 10점 | 세부기준 제9조①2 | -| ─ 정성평가 | (기술 90 기준) **70점** | 실제 RFP 표본 §1 | -| ─ 정량평가 | **20점 이내**(총 배점한도) | 세부기준 제9조④ | -| HW 비중 50%↑ 예외 | 기술 80 / 가격 20 허용 | 지침 제18조 | - -> **출처 정본**: 조달청 「협상에 의한 계약 제안서평가 세부기준」(조달청지침, 최신 개정 **2024. 9. 6. 시행**; 2017. 6. 1. 최초 시행). 정보화 총배점은 「행정기관 및 공공기관 정보시스템 구축·운영 지침」(행정안전부고시 제2024-66호, **2024. 8. 28. 시행**) 제18조. - -### 1-2. 정성평가 표준 평가표 — [별표 3의2] 소프트웨어 및 시스템 개발사업 (정보시스템 구축 표준) - -> **세부기준은 각 세부항목에 고정 점수를 부여하지 않는다**(수요기관이 배점한도 결정). 아래는 **평가항목 ↔ 세부평가항목 구조**(법령 원문 그대로). 배점 예시는 §1-3 실제 RFP 표본 참조. - -| 평가항목(부문) | 세부평가항목 | 채점 관점(평가위원이 무엇을 보는가) | -|---|---|---| -| **전략 및 방법론** | 사업이해도 | 사업 목표·특성 부합, 구체성·적절성. (기획용역 ISP 수행자 참여 시 1등급 하향) | -| | 추진전략 | 수행 일정·위험요소 고려한 추진전략 타당성 | -| | 추진체계 | 수행조직(전문업체·기술지원·공동수급체·하도급) 역할·책임·품질확보·협력 방안 | -| | 사업추진방법론 | 방법론 구체성·적절성, 적용사례·경험, 단계별 산출물 제시 | -| **수행계획**(≈기술 및 기능) | 시스템 요구사항 | 장비 규격 충족·인터페이스·확장성·공급/설치·유지관리 | -| | 국산제품 활용 기여도 | 국산장비 도입방안 적절성·국내기업 지원 (배점 **2점 이내**, 국내입찰 한정) | -| | 기능 요구사항 | 기능·기대·제약 대비 구현방안 구체성·실현가능성 | -| | 성능 요구사항 | 성능충족 구현·테스트·방법론·분석도구 | -| | 인터페이스 요구사항 | 타 시스템 연계 방안·사용자 UI 분석/설계/구현/테스트 | -| | 유지관리 정책 | 제조사 유지관리(기간·비용·부품·보증) | -| **수행기반** | 적용기술 | 적용 기술의 확장·실현 가능성 | -| | 개발환경 | 장비·작업장소 등 인프라 준비·대응 | -| | 보안 요구사항 | 기술적 보안 구현(설계→구현→검증 단계 반영) | -| | 제약사항 | 기술·표준(**표준 프레임워크 적용 포함**)·언어·방법론·법제도·안전관리 제약 대응 | -| **프로젝트 관리** | 일정관리 | 수행기간·세부일정·단계별 산출물 연계 | -| | 품질관리 | 품질관리 범위·절차·점검, 품질보증 인증 근거 | -| | 기밀보안관리 | 물리적·관리적 보안 체계·대책 | -| | 위험 및 이슈 관리 | 위험·이슈 식별·분석·절차·대응 | -| **프로젝트 지원** | 인수인계 | 인계 전략·검사 대상/방법·충족조건·인계계획 | -| | 교육훈련 | 운영자·관리자·사용자 교육 내용·방법·기간·인원·횟수 | -| | 기술지원 | 지원 범위·수준·기술 매뉴얼 | -| | 하자보수 계획 | 하자담보 기간 내 하자보수 범위·조치절차·대응 | -| **하도급** | 하도급 계획의 적정성 | 하도급 금액/계약금액 비율 적정성 (배점 **5점 이상 필수**; **20억원 이상 SW사업은 제외 불가**) | -| **필수 제안** | (수요기관 지정) | 제안요청 필수사항 반영 여부 | - -### 1-3. 정량평가 항목 (별표 8~14, 총 20점 이내) - -| 정량 항목 | 출처 | 비고 | -|---|---|---| -| 경영상태 | 별표8 | 기업신용평가 | -| 수행실적 | 별표9 | 동일·유사 사업 실적 | -| 책임성·성실성 | 별표10 | | -| **상생협력** | **별표11** | 중소기업 공동수급체 참여 지분율 — 50%↑=5.0점 … 35%미만=1.0점 (**최대 5점**). 중소기업 단독=최고등급, 참여지분 0=0점 | -| 상용SW 유지관리 하도급금액 적정성 | 별표12 | | -| SW기술자료 임치 | 별표13 | | -| 정량 필수제안 | 별표14 | | - -### 1-4. 실제 RFP 배점 표본 (구체 숫자 예시) — g2b R25BK00879799 (정보시스템 구축, HW 포함형, 30p 이내) - -> 표본은 90/10(기술 90+가격 10) 채택. 부문 세분은 수요기관 재량임을 실증. - -| 구분 | 평가항목 | 배점 | -|---|---|---| -| 정량(20) | 보유인력 5 · 경영상태 5 · 수행실적 5 · 사회적책임 3 · 지리적거리 2 | 20 | -| 정성(70) | 사업의 이해도(10) · 시스템 구성(20) · 시스템 기능(20) · 운영방안(10) · 사업관리 및 지원(10) | 70 | -| 가격(10) | 입찰가격평가 | 10 | - ---- - -## 2. 정보시스템 부문 표준 제안서 목차 (장·절 구조) - -### 2-1. 표준 목차 정본 — [별지 제5호서식] 제안서 서식(예시: 소프트웨어개발·유지관리사업) - -> 출처: 세부기준 별지 제5호서식(법령 원문). **평가부문과 장 구조가 일치**하도록 설계됨. 각 절 옆은 대응 평가항목(별표 3의2). - -``` -Ⅰ. 일반현황 - 1. 입찰자 일반현황 (일반현황·주요 연혁·자본금·매출액) - 2. 해당 사업의 수행조직 및 업무분장 → [정성] 추진체계 / [정량] 경영·실적 - 3. 해당 사업의 핵심투입인력 및 이력사항 → [정량] 보유인력 -Ⅱ. 전략 및 방법론 - 1. 사업 이해도 → 전략및방법론·사업이해도 - 2. 추진전략 → 추진전략·추진체계 - 3. 적용기술 → 수행기반·적용기술 - 4. 표준 프레임워크 적용 → 수행기반·제약사항(표준FW) - 5. 개발 방법론 → 사업추진방법론 -Ⅲ. 기술 및 기능 - 1. 시스템 장비구성 요구사항 → 수행계획·시스템 요구사항 - 2. 기능 요구사항 → 수행계획·기능 요구사항 - 3. 보안 요구사항 → 수행기반·보안 요구사항 - 4. 데이터 요구사항 → (기능/데이터) - 5. 시스템 운영 요구사항 - 6. 제약사항 → 수행기반·제약사항 -Ⅳ. 성능 및 품질 - 1. 성능 요구사항 → 수행계획·성능 요구사항 - 2. 품질 요구사항 → 프로젝트관리·품질관리 - 3. 인터페이스 요구사항 → 수행계획·인터페이스 요구사항 - 4. 테스트 요구사항 -Ⅴ. 프로젝트 관리 - 1. 관리방법론 - 2. 관리역량 - 3. 일정계획 → 프로젝트관리·일정관리 - 4. 개발장비 -Ⅵ. 프로젝트 지원 - 1. 품질보증 → 프로젝트관리·품질관리 - 2. 시험운영 - 3. 교육훈련 → 프로젝트지원·교육훈련 - 4. 유지보수 → 프로젝트지원·기술지원/하자보수 - 5. 기밀보안 → 프로젝트관리·기밀보안관리 - 6. 비상대책 -Ⅶ. 그 밖의 사항 → 기대효과·확장·상생협력 등 -``` - -### 2-2. 실제 RFP 표본의 목차 요구 (간소형 예시) — g2b R25BK00879799 (30p 이내) - -``` -Ⅰ. 사업의 이해 : 과제수행목표·제안개요·제안사현황·추진체계및전략·제안의특징및장점 -Ⅱ. 시스템 관련사항: 총괄시스템구성전략·기능별시스템구성및기능·성능구현 사전운영방안·향후활용방안 -Ⅲ. 사업관리 및 지원: 사업관리(보고·일정·위험)·시스템지원방안(교육·기술지원·유지보수) -Ⅳ. 첨부자료 -※ "발주처 요청 외 제안사가 중요하다고 판단한 목차·내용은 별도 기술하고 근거자료 첨부" -``` - -> **판독**: 소형·HW형 RFP는 별지 5호서식을 3~4장으로 압축한다. **대형 SW 구축**은 별지 5호서식 7대장을 그대로 쓰는 경향. 킨텍스 규모(전 전시 파이프라인)는 **7대장 채택 권장**. - -### 2-3. 장별 "무엇을 담는가" (평가위원 채점 관점 요약) - -| 장 | 담을 내용 | 채점 포인트 | -|---|---|---| -| Ⅰ 일반현황 | 제안사 개요·실적·재무, 수행조직도·R&R, 핵심인력 이력 | 정량(보유인력·경영·실적) 근거 + 조직 신뢰 | -| Ⅱ 전략·방법론 | 사업이해도, 추진전략/체계, 적용기술, 표준FW, 개발방법론 | "우리가 사업을 제대로 이해했고, 검증된 방법으로 한다"는 확신. **차별화(Win Theme) 메시지의 1차 노출 지점** | -| Ⅲ 기술·기능 | 요구기능 구현방안, 시스템 구성, 보안·데이터, 제약 대응 | 요구사항 대비 구현 구체성·실현가능성. **최다 배점 통례 → 기능 차별화 총력** | -| Ⅳ 성능·품질 | 성능충족·테스트, 품질보증, 인터페이스, 가용성/SLA | 정량 목표치·검증방법 제시 | -| Ⅴ 프로젝트 관리 | 관리방법론·역량, 일정계획, 위험·이슈, 품질관리 | 일정 준수·리스크 통제 실행력 | -| Ⅵ 프로젝트 지원 | 인수인계, 교육, 기술지원, **유지보수(SM)**, 기밀보안, 비상대책 | 구축 후 지속가능성·이관·운영 | -| Ⅶ 그 밖의 사항 | 기대효과, 확장, 상생협력·하도급, 부가제안 | 정성 가점·정책부합 | - ---- - -## 3. 재편 매핑표 — 현행 킨텍스 P1~P9 (63슬라이드) → 표준 7대장 - -> 규칙: 각 슬라이드를 표준 장·절로 이동. **차별화(나노바나나·AI 폐루프)는 배점 높은 Ⅲ(기술·기능) + Ⅱ(전략·방법론) 접점에 배치**해 노출 극대화. 미배치 0. - -### 3-1. 전면부 (표지·요약) - -| 현행 | 표준 배치 | 비고 | -|---|---|---| -| S01 표지 | 표지 | 유지 | -| S02 제안 개요 | **Ⅰ 일반현황 §1 앞 or 제안개요 간지** | 제안사 소개는 Ⅰ으로 흡수 | -| S03 목차 | 목차 | 7대장 인덱스로 재구성 | -| S04 제안 핵심요약(3중 해자) | **Ⅱ 전략·방법론 §2 추진전략** 도입(요약 간지) | Win Theme 조기 각인 | -| S05 배점 대응 맵 | 목차 직후 간지 | 표준 배점(90/10)으로 갱신 권고 | -| S06 [가정] 고지 간지 | 부록 or 서두 노트 | 유지 | - -### 3-2. P1 사업이해 → Ⅱ 전략·방법론 §1 - -| 현행 | 표준 배치 | -|---|---| -| S07 사업 배경·목적 | **Ⅱ §1 사업 이해도** | -| S08 As-Is 프로세스 병목 | **Ⅱ §1 사업 이해도** | -| S09 이해관계자·페르소나 | **Ⅱ §1 사업 이해도** | -| S10 To-Be 목표(정량) | **Ⅱ §1 사업 이해도** (→ 효과 지표는 Ⅶ와 연계) | - -### 3-3. P2 추진전략 → Ⅱ 전략·방법론 §2·§5 + Ⅰ - -| 현행 | 표준 배치 | -|---|---| -| S11 비전·포지셔닝 | **Ⅱ §2 추진전략** | -| S12 3중 해자 전략 | **Ⅱ §2 추진전략** (차별화 앵커) | -| S13 Win Theme 5선 | **Ⅱ §2 추진전략** (배점 정렬 배지 병기) | -| S14 전시 생애주기 폐루프 | **Ⅱ §5 개발 방법론**(생애주기 방법론) | -| S15 추진체계·수행조직 | **Ⅰ 일반현황 §2 수행조직·업무분장** (+ Ⅱ §2 추진체계 교차) | - -### 3-4. P3 기술제안 (25슬라이드) → Ⅲ 기술·기능 중심 + Ⅱ 적용기술 + Ⅳ 일부 - -> **핵심 전략**: 나노바나나·AI 폐루프는 **Ⅱ §3 적용기술**(방법론·차별화 관점)과 **Ⅲ §2 기능 요구사항**(구현 관점) **두 장에 걸쳐 노출** → 전략및방법론·기술및기능 양쪽 배점 동시 획득. - -| 현행 | 표준 배치 | -|---|---| -| S16 전체 모듈맵 | **Ⅲ 기술·기능 도입부**(기능 조망) | -| **S17~S23 나노바나나 시공예측(7)** | **Ⅱ §3 적용기술**(S17 개요·S19 구조보존·S20 파이프라인·S23 AI신뢰) + **Ⅲ §2 기능 요구사항**(S18 샷세트·S21 Before/After·S22 RenderJob 방어). **7슬라이드 절대 유지** | -| S24 플로어플랜 스튜디오(M2) | **Ⅲ §2 기능 요구사항** (+ 적용기술 교차) | -| S25 배치 규정 자동검증 | **Ⅲ §2 기능 요구사항** | -| S26 부스 설계 스튜디오(M3) | **Ⅲ §2 기능 요구사항** | -| S27 유틸리티 설계(M4)·PostGIS 배선 | **Ⅲ §2 기능 요구사항** (+ Ⅲ §4 데이터 요구사항 교차) | -| S28 위치표시도·유틸리티 신청 | **Ⅲ §2 기능 요구사항** | -| S29 옥션 개요·폐루프 | **Ⅲ §2 기능 요구사항** (차별화 폐루프) | -| S30 역경매·실시간 순위 | **Ⅲ §2 기능 요구사항** | -| S31 종합평가 낙찰 | **Ⅲ §2 기능 요구사항** | -| S32 등록 게이트·발주 전환 | **Ⅲ §2 기능 요구사항** (+ Ⅲ §3 보안 교차) | -| S33 판매·홀배정·견적(M1) | **Ⅲ §2 기능 요구사항** | -| S34 서류·마일스톤(M6) | **Ⅲ §2 기능 요구사항** (+ Ⅲ §5 시스템 운영 교차) | -| S35 관람객 등록·배지·리드(M10) | **Ⅲ §2 기능 요구사항** | -| S36 비즈매칭·마케팅·공개사이트(M11·12·17) | **Ⅲ §2 기능 요구사항** | -| S37 정산·결제(M9) | **Ⅲ §2 기능 요구사항** | -| S38 경영분석 BI-1(M16) | **Ⅲ §2 기능 요구사항** (+ Ⅲ §4 데이터 교차) | -| S39 경영분석 BI-2(M16) | **Ⅲ §2 기능 요구사항** | -| S40 현장운영·물류(M8·13·14) | **Ⅲ §2 기능 요구사항** | - -### 3-5. P4 아키텍처 → Ⅱ §3·§4 + Ⅲ §1·§4·§5 - -| 현행 | 표준 배치 | -|---|---| -| S41 전체 아키텍처 | **Ⅲ §1 시스템 장비구성 요구사항** (+ Ⅱ §3 적용기술) | -| S42 단일 공간 데이터 원천(PostGIS) | **Ⅲ §4 데이터 요구사항** | -| S43 확장성·비동기 | **Ⅳ §1 성능 요구사항** (+ Ⅶ 확장 연계) | -| S44 AI 플랫폼 라우팅(Claude→Ollama) | **Ⅱ §3 적용기술** | -| S45 연동·마스터데이터 | **Ⅳ §3 인터페이스 요구사항** (+ Ⅲ §5 시스템 운영) | - -### 3-6. P5 보안·개인정보 → Ⅲ §3 + Ⅴ·Ⅵ 기밀보안 - -> 표준은 **기술적 보안 = Ⅲ §3 보안 요구사항**, **관리적 기밀보안 = Ⅴ 기밀보안관리 / Ⅵ §5 기밀보안**으로 분리. 현행 P5를 분할 배치. - -| 현행 | 표준 배치 | -|---|---| -| S46 보안 개요·불변 원칙 | **Ⅲ §3 보안 요구사항** (개요) | -| S47 보안영역 분리(망 구성) | **Ⅲ §3 보안 요구사항** (+ Ⅲ §1 구성) | -| S48 인증·접근통제(2FA·RBAC) | **Ⅲ §3 보안 요구사항** | -| S49 AI·이미지 보안(워터마크) | **Ⅲ §3 보안 요구사항** (차별화 보안) | -| S50 개인정보·감사·안전코딩 | **Ⅲ §3 보안 요구사항** + **Ⅴ §? 기밀보안관리 / Ⅵ §5 기밀보안** (관리적 요소 이관) | - -### 3-7. P6 비기능 → Ⅳ 성능·품질 - -| 현행 | 표준 배치 | -|---|---| -| S51 성능·가용성 SLA | **Ⅳ §1 성능 요구사항** | -| S52 나노바나나 성능 | **Ⅳ §1 성능 요구사항** | -| S53 접근성·다국어·테마·반응형 | **Ⅳ §2 품질 요구사항** | -| S54 관측성·데이터 | **Ⅳ §2 품질 요구사항** (+ Ⅴ 관리 연계) | - -### 3-8. P7 멀티테넌시·확장 → Ⅲ §5 + Ⅶ - -| 현행 | 표준 배치 | -|---|---| -| S55 멀티테넌트 SaaS 비전 | **Ⅶ 그 밖의 사항**(확장 비전) | -| S56 테넌트 격리 아키텍처 | **Ⅲ §5 시스템 운영 요구사항** (+ Ⅲ §3 보안 격리) | -| S57 온보딩·2계층 관리자 | **Ⅲ §5 시스템 운영 요구사항** | - -### 3-9. P8 사업관리·일정·조직 → Ⅴ 프로젝트 관리 + Ⅵ - -| 현행 | 표준 배치 | -|---|---| -| S58 추진 일정·Phase | **Ⅴ §3 일정계획** | -| S59 선행 게이트·리스크 관리 | **Ⅴ §1 관리방법론 / 위험·이슈 관리** | -| S60 품질·형상관리 | **Ⅴ §2 관리역량(품질관리)** + **Ⅵ §1 품질보증** | - -### 3-10. P9 기대효과 → Ⅶ 그 밖의 사항 - -| 현행 | 표준 배치 | -|---|---| -| S61 정량 기대효과 | **Ⅶ 그 밖의 사항** | -| S62 정성·전략 효과 | **Ⅶ 그 밖의 사항** | -| S63 확장 비전·마무리 | **Ⅶ 그 밖의 사항** | - -### 3-11. 매핑 커버리지 요약 - -| 표준 장 | 배치된 현행 슬라이드 | 소계 | -|---|---|---| -| 표지·목차 | S01·S03·S05·S06 | 4 | -| Ⅰ 일반현황 | S02·S15 (+ **신설 필요**) | 2 | -| Ⅱ 전략·방법론 | S04·S07~S14·S17·S19·S20·S23·S41(교차)·S44 | 14 | -| Ⅲ 기술·기능 | S16·S18·S21·S22·S24~S40·S42·S45(교차)·S46~S50·S56·S57 | 34 | -| Ⅳ 성능·품질 | S43·S51~S54 | 5 | -| Ⅴ 프로젝트 관리 | S58·S59·S60 | 3 | -| Ⅵ 프로젝트 지원 | S50(관리적 일부)·S60(품질보증) (+ **대폭 신설 필요**) | ~1 | -| Ⅶ 그 밖의 사항 | S55·S61·S62·S63 | 4 | -| **미배치** | — | **0** | - ---- - -## 4. 표준 목차 채택 시 신설 필요 장(章)과 내용 소스 - -> 현행 63슬라이드가 **표준 대비 취약하거나 부재**한 영역. 실제 공고 시 감점·실격 방지를 위해 신설. - -| 신설/보강 대상 | 이유 | 내용 소스(지목) | -|---|---|---| -| **Ⅰ 일반현황 (신설)** | 제안사 일반현황·연혁·자본금·매출, 수행조직도·R&R, **핵심투입인력 이력**은 정량평가(보유인력·경영·실적) 근거. 현행에 사실상 부재(S02·S15 부분). | `tech_advisory §5-2` 직군별 M/M 산정 → 투입인력표. 제안사 실적은 발주 확인 후 확보(⚠확인필요) | -| **Ⅵ 프로젝트 지원 (대폭 신설)** | 인수인계·교육훈련·기술지원·**유지보수(SM)**·비상대책은 표준 정성 부문. 현행 P8이 품질/형상만 얇게 다룸. | `tech_advisory §6 SM 운영 체계`(SLA·장애대응·정기점검·CI/CD 운영·ITSM 프로세스·조직 R&R §6-1) · §5-1 WBS(SM 이관·인수인계) | -| **Ⅴ 프로젝트 관리 보강** | 표준은 관리방법론·관리역량·위험/이슈를 독립 부문으로 배점. 현행 배점 2점 가정으로 과소. | `tech_advisory §5 WBS·공수` · `proposal_strategy §4 리스크` | -| **상생협력·하도급 (신설)** | 정량 별표11(중소기업 참여지분율, 최대 5점)·정성 하도급 계획 적정성(5점 이상). **20억↑ SW사업은 하도급 평가 제외 불가**. 현행 없음. | 공동수급/중소기업 참여 계획·하도급 계획서(별지 제14호서식) — 컨소시엄 구성 확정 후(⚠확인필요) | -| **정량평가 대응 절 (신설)** | 보유인력·경영상태·수행실적·사회적책임은 필수 증빙 미제출 시 최하점. 별첨 근거 필요. | Ⅰ 일반현황 + 별첨(증빙) — 발주 확인 후 | -| **표준 프레임워크 적용 절 (Ⅱ §4)** | 별표3의2 제약사항에 "표준 프레임워크 적용 포함" 명시. 공공 SW 필수 관점. | 확정 스택(Spring Boot/React) — 전자정부 표준FW 적용 여부는 ⚠확인필요(현재 스택은 GUARDiA 표준) | - ---- - -## 5. 재편 권고 (Win Theme 노출 극대화) - -1. **7대장 채택**: 킨텍스 규모(전 전시 생애주기)는 별지 5호서식 7대장 구조가 정합. 현행 P1~P9를 §3 매핑대로 재배열. -2. **나노바나나(S17~S23) 이중 노출**: Ⅱ §3 적용기술(방법론·차별화)과 Ⅲ §2 기능 요구사항(구현) 양쪽에 배치 → **전략및방법론 + 기술및기능 배점 동시 겨냥**. 7슬라이드 축소 금지. -3. **AI 폐루프(배치·설계·배선·옥션 S24~S32)**: Ⅲ §2 기능 요구사항에 집중 배치(최다 배점 장). 폐루프 차별화 메시지를 기능 채점표에 직접 정렬. -4. **보안 분할**: 기술적 보안 → Ⅲ §3, 관리적 기밀보안 → Ⅴ·Ⅵ. 표준 채점 구조에 맞춰 배점 누수 방지. -5. **Ⅵ 프로젝트 지원 신설**로 "구축 후 지속가능성"(SM·교육·이관) 확보 — 공공 평가위원 필수 확인 항목. -6. **배점 표기 갱신**: 현행 80/20 가정 → 정보화 표준 90/10(정성70+정량20)로 갱신 권고(실제 공고 확인 시 확정). *proposal 파일 수정은 별도 지시 필요 — 본 문서는 근거만 제공.* - ---- - -## 6. 출처 URL 전체 목록 - -**1차 출처 (법령·실제 문서 — 본문 근거)** -- 조달청 협상에 의한 계약 제안서평가 세부기준 (국가법령정보센터): https://law.go.kr/LSW/admRulLsInfoP.do?admRulSeq=2100000088849 -- 조달청 협상에 의한 계약 제안서평가 세부기준 PDF (별표3의2·별표11·별지5호서식 원문 추출): https://bidform.co.kr/updata/notifile/ab689c32028cee2185e44dcd24c0fb89.pdf -- 실제 나라장터 정보시스템 구축 RFP 제안서 작성요령(배점·목차 표본, g2b R25BK00879799): https://www.g2b.go.kr/pn/pnp/pnpe/UntyAtchFile/downloadFile.do?bidPbancNo=R25BK00879799&bidPbancOrd=000&fileType=&fileSeq=3 -- 행정기관 및 공공기관 정보시스템 구축·운영 지침 제18조(평가배점) (국가법령정보센터): https://www.law.go.kr/LSW//admRulInfoP.do?admRulSeq=2100000246404&chrClsCd=010201 -- 정보시스템 구축·운영 지침 제18조(평가배점) — 국민건강보험공단 법령DB: https://www.nhis.or.kr/lm/lmxsrv/law/joHistoryContent.do?SEQ=1541&SEQ_CONTENTS=4250027&DATE_START=20240828&DATE_END=20190823 - -**참고 출처 (통례·가이드)** -- KOSA 소프트웨어 사업 가이드북 v3.0: https://www.sw.or.kr/upload/common/027d281c-2a7a-4886-b96b-e9ad04c7a309.pdf -- 공공 SW사업 제안요청서 작성 예시(SW 법제도 반영): https://smallake.kr/wp-content/uploads/2023/03/붙임공공SW사업-제안요청서-작성-예시.pdf -- NIPA SW사업 단계별 발주 가이드: https://www.nipa.kr/home/2-7-1-1/7089 -- 조달청 협상에 의한 계약 제안서평가 세부기준(2026.1.26. 시행 안내, 도로교통공단): https://www.koroad.or.kr/main/board/22/305400/board_view.do - ---- - -## 7. 확인 필요 (삭제 금지) - -| # | 항목 | 사유 | -|---|---|---| -| T1 | 실제 킨텍스 공고의 배점·목차 강제 여부 | 본 문서는 표준·표본 근거. 실제 공고가 별지 5호서식과 다른 목차를 강제할 수 있음 | -| T2 | 정보화(SW중심) vs HW중심 판정 | 90/10 vs 80/20 결정. 킨텍스 시스템은 SW 중심 → 90/10 추정, 공고 확인 필요 | -| T3 | 컨소시엄·중소기업 참여·하도급 구성 | 상생협력(별표11)·하도급(별표3의2) 대응은 컨소시엄 확정 후 작성 | -| T4 | 전자정부 표준프레임워크 적용 요구 여부 | 별표3의2 제약사항에 표준FW 명시 — 현행 스택(GUARDiA 표준)과 정합성 공고 확인 | -| T5 | 사업금액 20억원 이상 여부 | 20억↑이면 하도급 평가항목 제외 불가(세부기준 제9조⑤ 단서) | diff --git a/docs/deliverables/제안서/_gen/icon_raster.py b/docs/deliverables/제안서/_gen/icon_raster.py deleted file mode 100644 index cdd7028..0000000 --- a/docs/deliverables/제안서/_gen/icon_raster.py +++ /dev/null @@ -1,239 +0,0 @@ -# -*- coding: utf-8 -*- -""" -경량 SVG(stroke) -> PNG 래스터라이저 (cairo 불필요, PIL만 사용) -- assets/icons/*.svg (viewBox 0 0 24 24, fill none, stroke currentColor, stroke-width 1.75, round cap/join) -- 지원: M/m L/l H/h V/v C/c S/s A/a Z/z, , -- 렌더: 고배율 supersample 후 LANCZOS 다운스케일, 라운드 캡/조인 = 두꺼운 라인 + 정점 원 -- 색: stroke=맥락색(RGB). fill 없음(선 아이콘). -재사용: from icon_raster import render_icon; render_icon('ai-sparkle', (r,g,b), out_png, px=192) -""" -import os, re, math -from PIL import Image, ImageDraw - -ICON_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets", "icons") - -_num = re.compile(r'[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?') - -def _nums(s): - return [float(x) for x in _num.findall(s)] - -# ---- cubic / arc flattening ---- -def _cubic(p0, p1, p2, p3, n=18): - pts = [] - for i in range(1, n+1): - t = i/n; mt = 1-t - x = mt**3*p0[0] + 3*mt*mt*t*p1[0] + 3*mt*t*t*p2[0] + t**3*p3[0] - y = mt**3*p0[1] + 3*mt*mt*t*p1[1] + 3*mt*t*t*p2[1] + t**3*p3[1] - pts.append((x, y)) - return pts - -def _arc(p0, rx, ry, phi, large, sweep, p1, n=24): - # endpoint -> center parameterization (SVG spec) - if rx == 0 or ry == 0: - return [p1] - phi = math.radians(phi) - cosp, sinp = math.cos(phi), math.sin(phi) - dx = (p0[0]-p1[0])/2.0; dy = (p0[1]-p1[1])/2.0 - x1p = cosp*dx + sinp*dy; y1p = -sinp*dx + cosp*dy - rx, ry = abs(rx), abs(ry) - lam = x1p*x1p/(rx*rx) + y1p*y1p/(ry*ry) - if lam > 1: - s = math.sqrt(lam); rx *= s; ry *= s - num = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p - den = rx*rx*y1p*y1p + ry*ry*x1p*x1p - co = math.sqrt(max(0.0, num/den)) if den else 0.0 - if large == sweep: - co = -co - cxp = co*rx*y1p/ry; cyp = -co*ry*x1p/rx - cx = cosp*cxp - sinp*cyp + (p0[0]+p1[0])/2.0 - cy = sinp*cxp + cosp*cyp + (p0[1]+p1[1])/2.0 - def ang(ux, uy, vx, vy): - d = math.sqrt((ux*ux+uy*uy)*(vx*vx+vy*vy)) - c = max(-1.0, min(1.0, (ux*vx+uy*vy)/d)) if d else 1.0 - a = math.acos(c) - if ux*vy - uy*vx < 0: a = -a - return a - th1 = ang(1, 0, (x1p-cxp)/rx, (y1p-cyp)/ry) - dth = ang((x1p-cxp)/rx, (y1p-cyp)/ry, (-x1p-cxp)/rx, (-y1p-cyp)/ry) - if not sweep and dth > 0: dth -= 2*math.pi - if sweep and dth < 0: dth += 2*math.pi - pts = [] - for i in range(1, n+1): - th = th1 + dth*i/n - x = cosp*rx*math.cos(th) - sinp*ry*math.sin(th) + cx - y = sinp*rx*math.cos(th) + cosp*ry*math.sin(th) + cy - pts.append((x, y)) - return pts - -# flag-aware arc arg regex: rx ry rot large sweep(single 0/1) x y (repeatable) -_NN = r'[-+]?(?:\d*\.\d+|\d+\.?)' -_ARC = re.compile( - r'(' + _NN + r')[,\s]*(' + _NN + r')[,\s]*(' + _NN + r')[,\s]*' - r'([01])[,\s]*([01])[,\s]*(' + _NN + r')[,\s]*(' + _NN + r')') - -def _parse_path(d): - """반환: list of subpaths, each subpath = list of (x,y) polyline points.""" - chunks = re.findall(r'([MmLlHhVvCcSsAaZz])([^MmLlHhVvCcSsAaZz]*)', d) - subs = []; cur = [] - x = y = 0.0; sx = sy = 0.0 - prev_ctrl = None; prev_cmd = None - for cmd, arg in chunks: - rel = cmd.islower(); c = cmd.upper() - if c == 'A': - for m in _ARC.finditer(arg): - rx, ry, rot, large, sweep, nx, ny = (float(m.group(k)) for k in range(1, 8)) - if rel: nx += x; ny += y - cur += _arc((x, y), rx, ry, rot, int(large), int(sweep), (nx, ny)) - x, y = nx, ny - prev_cmd = c; prev_ctrl = None; continue - vals = _nums(arg) - if c == 'Z': - cur.append((sx, sy)); x, y = sx, sy - subs.append(cur); cur = [] - prev_cmd = c; prev_ctrl = None; continue - j = 0 - first_pair = True - while j < len(vals) or (c in ('M', 'L', 'C', 'S', 'H', 'V') and False): - if c == 'M': - nx, ny = vals[j], vals[j+1]; j += 2 - if rel: nx += x; ny += y - if first_pair: - if cur: subs.append(cur) - cur = [(nx, ny)]; sx, sy = nx, ny - else: - cur.append((nx, ny)) - x, y = nx, ny; first_pair = False - elif c == 'L': - nx, ny = vals[j], vals[j+1]; j += 2 - if rel: nx += x; ny += y - cur.append((nx, ny)); x, y = nx, ny - elif c == 'H': - nx = vals[j]; j += 1 - if rel: nx += x - cur.append((nx, y)); x = nx - elif c == 'V': - ny = vals[j]; j += 1 - if rel: ny += y - cur.append((x, ny)); y = ny - elif c == 'C': - x1, y1, x2, y2, nx, ny = vals[j:j+6]; j += 6 - if rel: x1+=x; y1+=y; x2+=x; y2+=y; nx+=x; ny+=y - cur += _cubic((x, y), (x1, y1), (x2, y2), (nx, ny)) - prev_ctrl = (x2, y2); x, y = nx, ny - elif c == 'S': - x2, y2, nx, ny = vals[j:j+4]; j += 4 - if rel: x2+=x; y2+=y; nx+=x; ny+=y - if prev_cmd in ('C', 'S') and prev_ctrl: - x1 = 2*x - prev_ctrl[0]; y1 = 2*y - prev_ctrl[1] - else: - x1, y1 = x, y - cur += _cubic((x, y), (x1, y1), (x2, y2), (nx, ny)) - prev_ctrl = (x2, y2); x, y = nx, ny - else: - break - if j >= len(vals): - break - prev_cmd = c - if c not in ('C', 'S'): - prev_ctrl = None - if cur: subs.append(cur) - return subs - -def _circle_pts(cx, cy, r, n=48): - return [(cx + r*math.cos(2*math.pi*i/n), cy + r*math.sin(2*math.pi*i/n)) for i in range(n+1)] - -def _rect_pts(x, y, w, h, rx=0, transform=None, n=6): - rx = min(rx, w/2, h/2) if rx else 0 - pts = [] - if rx <= 0: - pts = [(x, y), (x+w, y), (x+w, y+h), (x, y+h), (x, y)] - else: - def arc(cx, cy, a0, a1): - return [(cx+rx*math.cos(math.radians(a)), cy+rx*math.sin(math.radians(a))) - for a in [a0 + (a1-a0)*k/n for k in range(n+1)]] - pts += [(x+rx, y)] - pts += [(x+w-rx, y)] - pts += arc(x+w-rx, y+rx, -90, 0) - pts += [(x+w, y+h-rx)] - pts += arc(x+w-rx, y+h-rx, 0, 90) - pts += [(x+rx, y+h)] - pts += arc(x+rx, y+h-rx, 90, 180) - pts += [(x, y+rx)] - pts += arc(x+rx, y+rx, 180, 270) - if transform: - m = re.match(r'rotate\(\s*([-\d.]+)\s+([-\d.]+)\s+([-\d.]+)\s*\)', transform) - if m: - ang = math.radians(float(m.group(1))); ox = float(m.group(2)); oy = float(m.group(3)) - ca, sa = math.cos(ang), math.sin(ang) - pts = [((px-ox)*ca-(py-oy)*sa+ox, (px-ox)*sa+(py-oy)*ca+oy) for px, py in pts] - return pts - -def _extract(svg_text): - """SVG -> list of polylines (in 24-unit viewBox coords).""" - polys = [] - for m in re.finditer(r']*\bd="([^"]+)"', svg_text): - polys += _parse_path(m.group(1)) - for m in re.finditer(r']*>', svg_text): - a = m.group(0) - cx = re.search(r'cx="([-\d.]+)"', a); cy = re.search(r'cy="([-\d.]+)"', a); r = re.search(r'r="([-\d.]+)"', a) - if cx and cy and r: - polys.append(_circle_pts(float(cx.group(1)), float(cy.group(1)), float(r.group(1)))) - for m in re.finditer(r']*>', svg_text): - a = m.group(0) - def g(k, d=0.0): - mm = re.search(k+r'="([-\d.]+)"', a); return float(mm.group(1)) if mm else d - tr = re.search(r'transform="([^"]+)"', a) - polys.append(_rect_pts(g('x'), g('y'), g('width'), g('height'), g('rx', 0), - tr.group(1) if tr else None)) - return polys - -_cache = {} -def render_icon(name, rgb, out_png, px=192, stroke_w=1.75, pad=1.0): - """name: 파일명(확장자 무관). rgb: (r,g,b) 0-255. out_png 저장. 이미 있으면 재사용.""" - key = (name, tuple(rgb), px) - if key in _cache and os.path.exists(out_png): - return out_png - path = os.path.join(ICON_DIR, name if name.endswith('.svg') else name+'.svg') - with open(path, 'r', encoding='utf-8') as f: - svg = f.read() - polys = _extract(svg) - SS = 4 # supersample - W = px*SS - scale = W/24.0 - img = Image.new('RGBA', (W, W), (0, 0, 0, 0)) - dr = ImageDraw.Draw(img) - col = (int(rgb[0]), int(rgb[1]), int(rgb[2]), 255) - lw = max(1, int(round(stroke_w*scale))) - rcap = lw/2.0 - for poly in polys: - if len(poly) < 2: - continue - sp = [(px_*scale, py_*scale) for px_, py_ in poly] - dr.line(sp, fill=col, width=lw, joint='curve') - # round caps/joins via dots at each vertex - for (vx, vy) in sp: - dr.ellipse([vx-rcap, vy-rcap, vx+rcap, vy+rcap], fill=col) - img = img.resize((px, px), Image.LANCZOS) - img.save(out_png) - _cache[key] = out_png - return out_png - -if __name__ == "__main__": - # 테스트: 20종 x 1색 몽타주 - outdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "_iconpng") - os.makedirs(outdir, exist_ok=True) - names = sorted(f[:-4] for f in os.listdir(ICON_DIR) if f.endswith('.svg')) - cols = 5 - cell = 96 - mont = Image.new('RGBA', (cols*cell, ((len(names)+cols-1)//cols)*cell), (255, 255, 255, 255)) - from PIL import ImageDraw as _ID - md = _ID.Draw(mont) - for i, nm in enumerate(names): - outp = os.path.join(outdir, nm+".png") - render_icon(nm, (0x00, 0x66, 0xB3), outp, px=72) - ic = Image.open(outp) - cx = (i % cols)*cell + 12; cy = (i//cols)*cell + 6 - mont.alpha_composite(ic, (cx, cy)) - md.text((cx, cy+74), nm[:14], fill=(60, 60, 60)) - mont.convert('RGB').save(os.path.join(outdir, "_montage.png")) - print("OK", len(names), "icons ->", os.path.join(outdir, "_montage.png")) diff --git a/docs/deliverables/제안서/_gen/proposal_content.md b/docs/deliverables/제안서/_gen/proposal_content.md deleted file mode 100644 index 47babbf..0000000 --- a/docs/deliverables/제안서/_gen/proposal_content.md +++ /dev/null @@ -1,998 +0,0 @@ -# 킨텍스 자동전시시스템 구축 — 제안서 본문 (Proposal Content · 슬라이드 단위) - -> 작성: proposal-writer · 작성일: 2026-07-11 · 버전 v1.0 -> 입력: `proposal_outline.md`(63슬라이드 배점 가중 목차) · `proposal_strategy.md`(Win Theme 5·리스크 R-1~R-8) · `tech_advisory.md`(§0 라이브 실적/구현 계획 구분·도식 12종 사양·NFR 21·보안 20·공수 ~150M/M·SM) · `rfp_analysis.md`+`rtm.md`(REQ 142) -> 산출 규격: 슬라이드별 [번호·섹션·헤드라인(3초 메시지)·본문 불릿(근거)·도표 지시(tech_advisory 도식 ID)·대응 REQ·출처]. 문체 = **주장 → 근거(라이브 실적/설계 문서) → 효과**. 과장 금지 — tech_advisory §0 구분표에 따라 **[검증됨]**(구축 완료·라이브)과 **[구현 계획]**(설계 확정·미완)을 표기 라벨로 명확 구분. -> 보안 불변: 시크릿(키·비번·IP·SSH) 미기재. AI 생성 이미지는 워터마크·고지 정책 명시. -> deck-designer 지침: 도식 지시의 "도식ID"는 `tech_advisory.md §1-4`의 번호(①~⑫)와 1:1. 아이콘 전량 선(stroke) SVG. - ---- - -## 표기 범례 (전 슬라이드 공통) - -- **[검증됨]** = WORK_STATUS(2026-07-11) 라이브 확인분. "이미 기동 중". -- **[구현 계획]** = 설계·데이터모델 확정, 구현 대기. "표준대로 채운다". -- **[협의]** = 발주처 협의·확인 필요(⚠) 항목. 정직 고지 대상. -- **REQ** 열 = 해당 슬라이드가 충족하는 요구사항 ID(RTM 반영 근거). - ---- - -# 0. 표지·목차 (S01~S06) - -## S01 · 표지 -- **섹션**: 표지 -- **헤드라인**: **"신청서를 내는 순간, 시공 후 사진을 먼저 본다."** -- **본문** - - 사업명: 킨텍스(KINTEX) 자동전시시스템 구축 - - 서브 슬로건: "설계 → 시각화 → 발주 → 시공 → 운영 → 분석. 킨텍스에서 한 흐름으로 끝난다." - - 제안사·제안일 표기 영역(로고 자리) -- **도표 지시**: 배경 = 나노바나나 시공 예측 히어로 이미지(Before 빈 부스 → After 시공 사진). **워터마크 항상 표기**("AI 생성 예상 이미지 — 실제 시공과 다를 수 있음. 계약·심사 서류 사용 금지"). 도식ID 없음(히어로). -- **REQ**: REQ-A-001, REQ-S-009 -- **출처**: strategy §0, PLANNING §1-2 - -## S02 · 제안 개요 -- **섹션**: 표지 -- **헤드라인**: "우리는 도면이 아니라 사진으로 컨펌하고, 그 사진으로 발주까지 종결한다." -- **본문** - - 우리는 누구: GUARDiA 표준 프레임워크(WISE/UIWS) 검증 자산 위에 전시 도메인 코어를 얹은 팀 - - 왜 우리인가: ①나노바나나 시공 예측 ②PostGIS 실측 공간데이터 ③공사 옥션 폐루프 — **세 결합을 이미 라이브로 검증** [검증됨] - - 차별점 1줄: "말이 아니라 이미 돈다" — 인프라·인증·PostGIS 코어·나노바나나·자동배포가 기동 중 -- **도표 지시**: 제안사 핵심 자산 아이콘 3종(선 SVG) — 시공예측·공간데이터·옥션폐루프. -- **REQ**: REQ-A-001, REQ-N-008, REQ-F-025 -- **출처**: tech_advisory §0-1, strategy §0 - -## S03 · 목차 -- **섹션**: 표지 -- **헤드라인**: "배점을 따라 읽으십시오 — 제안서 무게가 곧 배점 배분입니다." -- **본문** - - P1 사업이해 · P2 추진전략 · P3 기술제안 · P4 아키텍처 · P5 보안·개인정보 · P6 비기능 · P7 멀티테넌시·확장 · P8 사업관리·일정·조직 · P9 기대효과 - - P3(기술제안)이 전체 25슬라이드(≈40%) — 배점 35점 정렬 -- **도표 지시**: 목차 인덱스(9섹션) + 각 섹션 배점 배지 병기. 도식ID 없음. -- **REQ**: (내비게이션 — 개별 REQ 없음) -- **출처**: outline 배분 총괄표 - -## S04 · 제안 핵심 요약(1p) -- **섹션**: 표지 -- **헤드라인**: "단일 요소는 모방할 수 있으나, 세 결합은 재현하기 어렵다 — 우리만의 해자." -- **본문** - - Win Theme 5선: WT-1 시공 예측 · WT-2 배치·설계·배선 3안+규정검증 · WT-3 옥션 폐루프 · WT-4 보안 3중 격리 · WT-5 멀티테넌트 SaaS - - 3중 해자: 나노바나나 시공 예측 ∩ PostGIS 실측 공간데이터 ∩ 공사 옥션 폐루프 - - 배점의 44%(35/80)가 P3에 집중 → 제안 무게 집중 근거 -- **도표 지시**: **도식① 3중 해자 벤다이어그램**(3원 60% 겹침, 중앙 교집합 "재현 난이도 높은 결합 = 우리만의 해자(Moat)"). tech_advisory §1-4 ①. -- **REQ**: REQ-A-001·002, REQ-N-008·017, REQ-F-025 -- **출처**: strategy §3-3, tech_advisory §1-4 ① - -## S05 · 배점 대응 맵 -- **섹션**: 표지 -- **헤드라인**: "우리 제안은 배점이 큰 곳에 정확히 무게를 실었다." -- **본문** - - AI 시각화·설계(핵심) 20점 → WT-1·WT-2 전면(P3 나노바나나 7슬라이드+배치·설계·배선 5슬라이드) - - 기능 구현 15점 → 모듈맵 1장 조망 + 옥션·관람·BI 상세 - - 아키텍처 10·보안 10·비기능 8·사업이해·전략 10·멀티테넌시 5·사업관리 2 -- **도표 지시**: 배점-섹션 정렬 매트릭스(80점 히트맵). 도식ID 없음(인포그래픽). -- **REQ**: (배점 정렬 — 전 REQ 계열 조망) -- **출처**: strategy §2, outline 배분 총괄 - -## S06 · [가정] 고지 간지 -- **섹션**: 표지 -- **헤드라인**: "가정은 숨기지 않고 드러냅니다 — 실제 공고 확인 시 즉시 재정렬합니다." -- **본문** - - 실제 RFP 부재 → 평가배점·제출규격은 공공 SI 통례 기준 **[가정]** (리스크 R-4 정면 방어) - - 목차는 안전 정렬(사업이해→전략→기술→아키→보안→비기능→확장→관리→기대효과) - - 확인 필요 8항목(C1~C8)은 부록 A에 집약 → 정직성을 강점화 -- **도표 지시**: 간단 노트 카드(리스크 R-4 방어 문구). 도식ID 없음. -- **REQ**: (투명 고지 — 개별 REQ 없음) -- **출처**: strategy §4 R-4, rfp_analysis §4·§5 - ---- - -# P1. 사업이해 (S07~S10) - -## S07 · 사업 배경·목적 -- **섹션**: P1 사업이해 -- **헤드라인**: "108,011㎡의 아시아 3위 전시장, 준비는 여전히 HWP·CAD 수작업이다." -- **본문** - - 킨텍스 규모: 현 108,011㎡ → 2028년 제3전시장 완공 시 178,000㎡(국내 최대·아시아권 상위) - - 현행 병목: 부스 배치는 CAD 외주 수작업, 신고서류는 HWP 수기, 배선·위치표시도 육안 작도 - - 목적: 전시 준비 전 과정을 AI로 자동화해 리드타임·오류·외주비용을 동시 절감 -- **도표 지시**: 킨텍스 규모 인포그래픽(면적·홀 수·연간 행사 규모 지표 카드). 도식ID 없음. -- **REQ**: (사업 맥락 — 개별 REQ 없음) -- **출처**: PLANNING §1-1 - -## S08 · As-Is 프로세스 병목 -- **섹션**: P1 사업이해 -- **헤드라인**: "D-150부터 D-7까지, 마디마다 사람이 기다리고 CAD를 외주한다." -- **본문** - - D-150 판매·견적: 견적 산출을 담당자가 수기 회신 → 대기 발생 - - D-30 배치·설계: CAD 외주 수일~수주, 규정 위반은 홀매니저 육안 검수 병목 - - D-25 유틸리티: 전기·네트워크·급배수 신청이 개별 채널로 파편화 - - D-7 제출·검수: kxwp 제출·서류 육안 검토 -- **도표 지시**: **As-Is 타임라인 다이어그램**(D-데이별 Pain Point 표시). 도식ID 없음(표준 타임라인). -- **REQ**: (병목 진단 — 개별 REQ 없음, To-Be 근거) -- **출처**: PLANNING §3-1·3-2 - -## S09 · 이해관계자·페르소나 -- **섹션**: P1 사업이해 -- **헤드라인**: "6역할+2관리자, 각자의 과업·고통·기대를 정확히 안다." -- **본문** - - 주최자(organizer): 배치·행사 총괄 — CAD 수작업 고통 - - 참가업체(exhibitor): 도면을 못 읽어 개장일에 결과 첫 대면 — 시공 예측 니즈 - - 공사·장치업체(contractor): 리스트+엑셀 매칭, 견적 비교 부재 - - 홀매니저(ops): 규정 육안 검수 병목 - - 관람객(visitor): 등록·배지·현장 체크인 - - 일반 대중(public): 공개 홍보·플로어플랜 열람 - - +플랫폼 관리자·테넌트 관리자 -- **도표 지시**: 페르소나 카드 6종(과업·Pain·기대 3행). 도식ID 없음. -- **REQ**: REQ-F-081(역할별 포털의 근거) -- **출처**: PLANNING §2 - -## S10 · To-Be 목표(정량) -- **섹션**: P1 사업이해 -- **헤드라인**: "견적은 즉시, 배치는 수 분, 검수는 자동, 결과는 사진으로." -- **본문** - - 견적: 수일 대기 → **즉시**(규칙엔진 2,250원/㎡ 자동 계산) - - 배치: 수주 CAD 외주 → **수 분 3안 자동생성** - - 규정 검수: 육안 → **제출 전 자동 플래깅** - - 위치표시도: 수기 작도 → **좌표 클릭 자동 작도** - - 시공 결과: 개장일 첫 대면 → **신청 시점 사진 예측** -- **도표 지시**: As-Is→To-Be 대비표(정량 개선 5행). 도식ID 없음(표). -- **REQ**: REQ-F-002, REQ-F-007, REQ-F-009, REQ-F-017, REQ-A-001 -- **출처**: PLANNING §1-3 - ---- - -# P2. 추진전략 (S11~S15) - -## S11 · 비전·포지셔닝 -- **섹션**: P2 추진전략 -- **헤드라인**: "도면과 표를 보여주는 전시테크가 아니라, 사진으로 컨펌하는 베뉴 운영 플랫폼." -- **본문** - - 기존 전시테크는 도면·3D 뷰어 수준 → 우리는 **사진급 시공 예측**으로 의사결정 - - 단일 폐루프: 설계 자료가 곧 발주 근거 → 발주까지 한 흐름에서 종결 - - 데이터 주권: 온프레미스 Ollama 기본, 외부 AI는 승인 예외만 -- **도표 지시**: 비전 슬로건 대형 타이포. 도식ID 없음. -- **REQ**: (포지셔닝 — 개별 REQ 없음) -- **출처**: PLANNING §1-2, strategy §0 - -## S12 · 3중 해자 전략 -- **섹션**: P2 추진전략 -- **헤드라인**: "셋 중 하나는 흉내낼 수 있어도, 셋의 결합은 재현 난이도가 높다." -- **본문** - - 원A 나노바나나 시공 예측: image-to-image 구조보존·40초·S1~S7 [검증됨: G1 승인·워커 라이브] - - 원B PostGIS 실측 공간데이터: 부스 폴리곤·트렌치 포인트·배선 LineString 단일 원천 [검증됨: M2~M5 매퍼 라이브] - - 원C 공사 옥션 폐루프: AI 자료→응찰→낙찰→발주 자동전환 [구현 계획: Phase D] -- **도표 지시**: **도식① 3중 해자 벤다이어그램**(S04와 동일·상세 재강조). tech_advisory §1-4 ①. -- **REQ**: REQ-A-001·002, REQ-N-008·017, REQ-F-025·032 -- **출처**: strategy §3-3, tech_advisory §0-1·§1-4 ① - -## S13 · Win Theme 5선 -- **섹션**: P2 추진전략 -- **헤드라인**: "다섯 개의 차별화 메시지가 배점 다섯 곳을 정확히 겨눈다." -- **본문** - - WT-1 "도면이 아니라 사진으로 컨펌한다" — 배점 20(AI 시각화 핵심) - - WT-2 "수 분 내 3안, 제출 전 규정 자동검증" — 배점 20+15 교차 - - WT-3 "사진 수준 자료로 응찰하고 발주까지 끝낸다" — 배점 15(폐루프) - - WT-4 "AI가 만든 것은 표시하고, 남의 데이터는 차단한다" — 배점 10(보안) - - WT-5 "코드 배포 없이 데이터 온보딩만으로 새 전시관을 연다" — 배점 5(확장) -- **도표 지시**: Win Theme 5카드(각 카드에 배점 배지). 도식ID 없음. -- **REQ**: REQ-A-001, REQ-F-007·025, REQ-S-009·018, REQ-F-077 -- **출처**: strategy §1 - -## S14 · 전시 생애주기 폐루프 -- **섹션**: P2 추진전략 -- **헤드라인**: "설계 → 시각화 → 발주 → 시공 → 운영 → 분석이 하나의 순환으로 닫힌다." -- **본문** - - 판매·기획(M1) → 설계·시각화(M2~M5) → 발주·계약(M15·M6·M9) → 참가·관람(M10·M11) → 현장운영(M8·M13·M14) → 사후·경영(M16) - - 각 단계 산출물이 다음 단계 입력 → 재입력·중복 작업 제거 - - 분석(M16)이 다음 행사 기획(M1)으로 환류 -- **도표 지시**: 생애주기 폐루프 순환도(M모듈 매핑). 도식ID 없음(순환도). -- **REQ**: (생애주기 조망 — 상세 REQ는 P3에서 반영) -- **출처**: PLANNING §1-2-1 - -## S15 · 추진체계·수행조직 -- **섹션**: P2 추진전략 -- **헤드라인**: "거버넌스·아키텍트·코어·도메인·AI·QA·DevOps가 게이트로 맞물린다." -- **본문** - - 거버넌스: 총괄 PM · PMO · 개발 PM - - 아키텍트 풀: AA · SA · TA · DA · NA(5종 아키텍처 확정 완료) - - 실행: 공통·코어(백엔드·프론트·DB) · 도메인(옥션·관람·CMS·BI·관리자) · AI·시각화 · QA · DevOps - - 게이트: 선행 게이트(G1·G2) + Phase 게이트 QA 통과 -- **도표 지시**: **추진체계도(조직도)** — 거버넌스+아키텍트+실행팀 계층. tech_advisory §1-4 말미(추가 표준 도식). -- **REQ**: REQ-N-004(품질 게이트) -- **출처**: 하네스 팀 구성, tech_advisory §5-1 - ---- - -# P3. 기술제안 (S16~S40, 25슬라이드 · 배점 35 · 최다 집중) - -## P3-A. 모듈 조망 - -## S16 · 전체 모듈맵 -- **섹션**: P3 기술제안 -- **헤드라인**: "18개 모듈+공통 레이어를 한 장으로 조망 — P0 코어 4개가 심장이다." -- **본문** - - P0 코어(강조): M2 플로어플랜·M3 부스설계·M4 유틸리티·M5 나노바나나 - - P1 도메인: M1 판매·M6 서류·M7 등록·M9 정산·M15 옥션·M10 관람·M12/M17 CMS·M16 BI·M18 관리자 - - P2 확장: M8 물류·M11 비즈매칭·M13 wayfinding·M14 현장 IoT - - **공통/시스템관리 레이어(§5B) — 전 모듈 선행 기반** [검증됨: WISE 이식 Flyway V1~V9]: 공통코드·메뉴(F-073), 공통 업무모듈 일지·일정·쪽지·공지·검색(F-074), 회의록 STT·업무보고 통계 PDF(F-075), 통합 알림센터 WebSocket(F-076) - - 멀티테넌시(§1A): tenant_id 격리 기반 [구현 계획] - - 100대 기능 성숙도: 기구현 63·부분 18·신규 19(정량) -- **도표 지시**: **도식② 모듈맵(생애주기 6단계 × 우선순위 P0/P1/P2 매트릭스)** + 하단 공통 레이어 띠. tech_advisory §1-4 ②. -- **REQ**: REQ-F-073·074·075·076, 전 모듈 조망 -- **출처**: rfp_analysis §2-1, tech_advisory §1-4 ②·§0 - -## P3-B. 나노바나나 시공 예측 — WT-1 (S17~S23, 7슬라이드 · 축소 금지) - -## S17 · 나노바나나 개요 -- **섹션**: P3 기술제안(WT-1) -- **헤드라인**: "시공 전에 사진으로 의사결정한다 — 경쟁 전시테크가 못 하는 차별화." -- **본문** - - 문제: 참가업체는 도면을 못 읽어 개장일에야 결과를 처음 본다(PLANNING §2) - - 해법: image-to-image로 빈 부스(도면/실측)를 시공 후 사진으로 예측 - - 실증: Gemini 라이브(G1 소유자 승인 완료), `gemini-3.1-flash-image-preview` 워커 상주 [검증됨] - - 효과: 조감도 외주(비용·시간) 대체, 신청 시점 의사결정 -- **도표 지시**: 히어로 Before/After 실물(워터마크 표기). 도식ID 없음(히어로). -- **REQ**: REQ-A-001 -- **출처**: strategy §1 WT-1, tech_advisory §0-1·§2 WT-1 - -## S18 · 표준 샷세트 S1~S7 -- **섹션**: P3 기술제안(WT-1) -- **헤드라인**: "정면·야간·통로·내부·Before/After·배선·홀전경 — 7컷을 평균 40초에." -- **본문** - - S1 정면 부스 전경 / S2 야간 조명 연출(조도 배치안 반영) / S3 통로 시점 / S4 부스 내부 / S5 Before/After / S6 배선 오버레이 / S7 홀 전경 - - 자동생성은 비용·품질 제어 위해 **S1·S7 한정**, 나머지는 온디맨드 [구현 계획: 샷세트 파이프라인] - - 모든 컷에 워터마크·고지 항상 포함 -- **도표 지시**: 샷세트 7컷 그리드(각 컷 워터마크 배지). 도식ID 없음(그리드). -- **REQ**: REQ-A-001, REQ-F-019(조명 조도 배치안) -- **출처**: tech_advisory §2 WT-1, PLANNING §6 - -## S19 · image-to-image 구조보존 -- **섹션**: P3 기술제안(WT-1) -- **헤드라인**: "골격·치수·통로·카메라 앵글은 잠그고, 스타일·재질만 사실화한다." -- **본문** - - 참조 이미지 `inlineData` + 구조화 프롬프트 사전(BOOTH_TYPES·BOOTH_STYLES·FIXTURE_LAYERS) - - **보존(잠금)**: 부스 골격·치수·통로·카메라 앵글 → 왜곡·과장 방지 - - **교체(사실화)**: 스타일·재질·마감만 → 사진 리얼리티 - - 검증 패턴: ReRoomAI image-to-image 보존/교체 분리 이식(reroomai-source.md) -- **도표 지시**: 보존/교체 파라미터 도식(잠금 항목 vs 교체 항목 2열). 도식ID 없음. -- **REQ**: REQ-A-002, REQ-A-003 -- **출처**: tech_advisory §2 WT-1, docs/analysis/reroomai-source.md - -## S20 · AI 파이프라인 아키텍처 -- **섹션**: P3 기술제안(WT-1) -- **헤드라인**: "생성은 전면 비동기 — 사용자는 대기하지 않고, 완료는 WebSocket으로 밀어준다." -- **본문** - - 흐름: 사용자 요청 → Spring RenderJob 생성·쿼터 확인 → Redis 큐(leftPush) → Python 워커(BLPOP) → Gemini image-to-image → 워터마크 후처리·S6 로컬 PIL 래스터 → 오브젝트 스토리지 → pub/sub → 백엔드 relay → WebSocket `/topic/render/{jobId}` → 프론트 완료 푸시 - - degraded 분기: G1 미승인/Gemini 장애 시 워커 목(mock) 응답·구조·워터마크 유지(REQ-N-005) - - 성공 시에만 쿼터 차감, `GEMINI_API_KEY`는 워커 env only -- **도표 지시**: **도식③ 나노바나나 파이프라인 플로우(8박스 좌→우)**. tech_advisory §1-4 ③. -- **REQ**: REQ-A-007, REQ-N-006 -- **출처**: tech_advisory §1-4 ③·§2 WT-1 - -## S21 · Before/After·배선 오버레이 -- **섹션**: P3 기술제안(WT-1) -- **헤드라인**: "드래그 한 번으로 빈 부스와 시공 후를 비교하고, 배선을 색으로 겹쳐 본다." -- **본문** - - Before/After: 좌우 분할 카드 + 중앙 드래그 핸들, ARIA slider·키보드 좌우 조작(WCAG AA) - - S6 배선 오버레이: Gemini 미경유, 백엔드/워커 로컬 PIL **결정적 래스터 합성**(좌표 정확성·비용/지연 제외) - - 색상 규약: 전기=적색, 네트워크=청색, 급배수=녹색 -- **도표 지시**: **도식④ Before/After 슬라이더**(좌 "Before: 빈 부스", 우 "After: 시공 후 예상(AI 생성·워터마크)", 하단 워터마크 배지). tech_advisory §1-4 ④. -- **REQ**: REQ-A-004, REQ-A-005 -- **출처**: tech_advisory §1-4 ④·§2 WT-1 - -## S22 · RenderJob 방어·품질 -- **섹션**: P3 기술제안(WT-1) -- **헤드라인**: "다층 방어로 실패를 흡수하고, 성공했을 때만 쿼터를 쓴다." -- **본문** - - 크기가드·mimeType 자동감지·SAFETY 분기·친화적 한글 에러 - - 간판 한글 렌더 실패 시 **간판영역 후처리 합성**(리스크 R-5 방어) - - 대량 생성 제어: 자동생성 S1·S7 한정 + 동일 스키마해시 캐시 + 행사별 쿼터(성공 시만 차감) -- **도표 지시**: RenderJob 상태 시퀀스(방어 계층 표시). 도식ID 없음(시퀀스). -- **REQ**: REQ-A-006, REQ-A-008, REQ-N-007 -- **출처**: tech_advisory §2 WT-1, strategy §4 R-5 - -## S23 · AI 신뢰 원칙 -- **섹션**: P3 기술제안(WT-1) -- **헤드라인**: "결정적 알고리즘이 중심, LLM은 보조, 최종 확정은 사람 — 워터마크는 불변." -- **본문** - - 3층 신뢰: 결정적 알고리즘(제약 솔버·PostGIS) 중심 + LLM 보조 + 사람 최종 확정 - - 워터마크 항상 포함(`watermarkRequired:true`), 계약·심사 서류 사용 금지 고지 제거 불가 - - AI 답변은 RAG 근거·인용·환각 차단(abstain)으로 오판 리스크 차단(리스크 R-6 방어) -- **도표 지시**: AI 3층 신뢰 구조 도식(결정론→LLM→사람). 도식ID 없음. -- **REQ**: REQ-S-009, REQ-A-017 -- **출처**: tech_advisory §2 WT-2·WT-4, strategy §4 R-6 - -## P3-C. 배치·설계·배선 AI 자동화 — WT-2 (S24~S28, 5슬라이드) - -## S24 · 플로어플랜 스튜디오(M2) -- **섹션**: P3 기술제안(WT-2) -- **헤드라인**: "제약 솔버가 상이한 목표로 정확히 3안 — 사람이 선택·병합·확정한다." -- **본문** - - 3안 자동생성: **결정적 제약 솔버**(비-LLM)가 통로 효율/프리미엄 부스/균형 등 목표별 생성, LLM은 조건해석만(REQ-A-009) - - 선택/병합 편집 + 최종안 버전기록·부스좌표 확정·초대링크(REQ-F-008·010) - - M2 매퍼(PostGIS 공간 SQL) [검증됨], 3안 생성·병합·집계 API [구현 계획] -- **도표 지시**: **도식⑤ 3안 비교 화면**(3열 카드·각 카드 최적화 목표·미니 배치도·지표, 하단 액션바 선택/병합/재검증). tech_advisory §1-4 ⑤. -- **REQ**: REQ-F-007·008·010, REQ-A-009 -- **출처**: tech_advisory §1-4 ⑤·§2 WT-2 - -## S25 · 배치 규정 자동검증 -- **섹션**: P3 기술제안(WT-2) -- **헤드라인**: "피난통로·바닥하중·비상구를 제출 전에 자동으로 플래깅한다." -- **본문** - - 룰엔진(`compliance-v*.json` 버전 데이터) + PostGIS 공간 판정: 통로폭 `ST_Distance/ST_Buffer`, 비상구 `ST_Intersects`, 홀 이탈 `ST_Contains`, 바닥하중 홀별 - - 병합 후 재검증 필수, 위반 시 `COMPLIANCE_BLOCKED`(422) - - 원칙: 규정검증=플래깅까지, 구조안전 판정은 구조기술사/킨텍스(Non-Goal 명시, 리스크 R-6) -- **도표 지시**: 규정 위반 플래그 대시보드(위반 항목·홀별 하중·비상구 교차 목록). 도식ID 없음(대시보드). -- **REQ**: REQ-F-009, REQ-A-010 -- **출처**: tech_advisory §2 WT-2, strategy §4 R-6 - -## S26 · 부스 설계 스튜디오(M3) -- **섹션**: P3 기술제안(WT-2) -- **헤드라인**: "조립부스 3D 프리뷰·독립부스 3안, 높이·리깅·방염·이격은 사전 검증." -- **본문** - - 조립부스 옵션·3D 프리뷰(REQ-F-011), 독립부스 설계 3안·선택/병합·버전(REQ-F-012) - - 설계 규정 사전검증: 높이 5m·리깅 6.5~8.5m·방염·이격(REQ-F-013) - - 도면 업로드 비전 추출·검증(REQ-F-014·A-020) -- **도표 지시**: 설계 3안 + 규정 체크리스트(높이·리깅·방염·이격). 도식ID 없음. -- **REQ**: REQ-F-011·012·013·014, REQ-A-020 -- **출처**: tech_advisory §2 WT-2, PLANNING §M3 - -## S27 · 유틸리티 설계(M4) -- **섹션**: P3 기술제안(WT-2) -- **헤드라인**: "기기→kW→분전반을 자동 산출하고, 트렌치→분전반 최단 배선을 PostGIS가 그린다." -- **본문** - - 전기 용량·분전반 자동산출: 기기목록→kW 합산→분전반 산출(REQ-F-015) - - 배선 경로: 최근접 트렌치 KNN(`geom <-> point`)→최단 배선 LineString(`ST_Length`, 통로 횡단 최소 휴리스틱)(REQ-F-016) - - 실측 근거: 홀별 평면도 JPG 15장 + CAD "평면,트렌치.dwg" 확보 [검증됨], 미확보 구간은 "가정 좌표(실측 대기)" 라벨(리스크 R-2) -- **도표 지시**: **도식⑥ PostGIS 배선 오버레이**(부스 폴리곤·트렌치 포인트◆·분전반▣·색상별 LineString, 전기 적/네트 청/급배수 녹). tech_advisory §1-4 ⑥. -- **REQ**: REQ-F-015, REQ-F-016 -- **출처**: tech_advisory §1-4 ⑥·§2 WT-2, strategy §4 R-2 - -## S28 · 위치표시도·유틸리티 신청 -- **섹션**: P3 기술제안(WT-2) -- **헤드라인**: "좌표 클릭으로 위치표시도를 자동 작도하고, 신청·견적은 D-25 역산으로 챙긴다." -- **본문** - - 위치표시도 자동생성: 좌표 클릭 자동 작도, 수기 작도 폐지(REQ-F-017) - - 네트워크·급배수·압축공기 신청 + 마감 리마인더(D-25 역산)(REQ-F-018) - - 자동 견적 연동 -- **도표 지시**: 위치표시도 자동생성 예시(좌표 클릭 → 작도 결과). 도식ID 없음. -- **REQ**: REQ-F-017, REQ-F-018 -- **출처**: tech_advisory §2 WT-2, PLANNING §M4 - -## P3-D. 공사 옥션 폐루프 — WT-3 (S29~S32, 4슬라이드) - -## S29 · 옥션 개요·폐루프 -- **섹션**: P3 기술제안(WT-3) -- **헤드라인**: "AI가 만든 설계 자료가 곧 응찰 근거이고, 한 흐름에서 발주까지 끝난다." -- **본문** - - AI 자료 패키지(M2 배치+M3 설계+M4 배선/BOQ+M5 시공예측 이미지)가 응찰 근거(`attached_refs`) - - 옥션 유형 설정형(REVERSE 역경매/RFQ/FIXED), 물량서(BOQ) 자동산출(REQ-F-031) - - 데이터모델: `TB_AUCTION 1─N TB_QUOTATION ─ TB_AWARD` [구현 계획: Phase D-M15] -- **도표 지시**: **도식⑦ 옥션 폐루프 흐름도**(AI 자료 패키지→옥션 개설→등록 게이트→견적 제출→실시간 순위→종합평가 낙찰→발주 자동전환). tech_advisory §1-4 ⑦. -- **REQ**: REQ-F-025·026, REQ-F-031 -- **출처**: tech_advisory §1-4 ⑦·§2 WT-3 - -## S30 · 역경매·실시간 순위 -- **섹션**: P3 기술제안(WT-3) -- **헤드라인**: "라운드·마감·최저가·내 위치를 실시간으로, 자동마감까지." -- **본문** - - 실시간 순위 = Redis 정렬셋 + WebSocket `/topic/auction/{auctionId}` 델타 푸시 - - 라운드 마감 = Redis 타이머(자동마감) - - 익명 옵션·내 위치 표시(REQ-F-027) - - 스캐폴드 4계층·큐·WS 패턴 확정으로 실현성 입증 [구현 계획] -- **도표 지시**: 실시간 순위 보드 목업(업체 순위·최저가·잔여 시간). 도식ID 없음(목업). -- **REQ**: REQ-F-027 -- **출처**: tech_advisory §2 WT-3 - -## S31 · 종합평가 낙찰 -- **섹션**: P3 기술제안(WT-3) -- **헤드라인**: "가격만이 아니라 평판·납기까지 가중 스코어로 투명하게 낙찰한다." -- **본문** - - 가중 스코어: 가격+평판+납기(`weight` jsonb, 옥션별 설정형, 예: 가격 0.6/평판 0.25/납기 0.15)(REQ-F-028) - - 항목별 비교표·선정 사유 제시 - - 등록업체 AI 매칭 추천(REQ-F-030), 물량서(BOQ) 근거(REQ-F-031) -- **도표 지시**: **도식⑧ 종합평가 스코어 비교표**(업체 행 × 평가항목 열 + 가중 종합점수 + 낙찰 하이라이트). tech_advisory §1-4 ⑧. -- **REQ**: REQ-F-028, REQ-F-030, REQ-F-031 -- **출처**: tech_advisory §1-4 ⑧·§2 WT-3 - -## S32 · 등록 게이트·발주 전환 -- **섹션**: P3 기술제안(WT-3) -- **헤드라인**: "미등록 업체는 응찰 자체가 차단(403)되고, 낙찰은 계약·발주로 자동 전환된다." -- **본문** - - 등록 게이트: 미등록 업체 응찰 원천 차단 `NOT_REGISTERED_COMPANY`(403), M7이 `TB_COMPANY.kintex_registered` 검증 권위 → 킨텍스 "등록업체 시공 필수" 규정을 시스템이 강제(REQ-S-007) - - 발주 전환: Award → 계약/발주 자동전환(M6·M9 연동, 이벤트 디커플·순환 금지)(REQ-F-032) -- **도표 지시**: 등록 검증 시퀀스 + 발주 전환 흐름. 도식ID 없음(시퀀스). -- **REQ**: REQ-F-029, REQ-F-032, REQ-S-007 -- **출처**: tech_advisory §2 WT-3·§4 REQ-S-007 - -## P3-E. 판매·서류·워크플로 (S33~S34) - -## S33 · 판매·홀배정·견적(M1) -- **섹션**: P3 기술제안 -- **헤드라인**: "가용성은 실시간 캘린더로, 견적은 규칙엔진으로 즉시, 신청은 웹폼→HWP로." -- **본문** - - 홀/반홀 가용성 실시간 캘린더(REQ-F-001) - - 규칙엔진 자동 견적(2,250원/㎡, 요율 공식본 [협의: C4])(REQ-F-002) - - 배정신청 웹폼→HWP 자동생성(REQ-F-003), 부스 판매 인벤토리(REQ-F-004), 온라인 셀프 선택·판매(REQ-F-005) - - 임대계약 전자서명(선택, REQ-F-006) -- **도표 지시**: 견적 규칙엔진 계산 흐름(면적×요율→견적). 도식ID 없음. -- **REQ**: REQ-F-001·002·003·004·005·006 -- **출처**: PLANNING §M1, rfp_analysis(C4 요율) - -## S34 · 서류·마일스톤(M6) -- **섹션**: P3 기술제안 -- **헤드라인**: "D-150/30/25/7을 역산해 알리고, 웹폼은 HWP/PDF로, 검수는 AI가 돕는다." -- **본문** - - 마일스톤 자동생성·역산 알림(REQ-F-020), 신고서류 웹폼→HWP/PDF 자동생성 7종(REQ-F-021) - - 서류 버전·전자결재 워크플로(REQ-F-023), OCR 계약·납품서 자동등록(REQ-F-024·A-020) - - AI 서류 검수(누락·불일치)(REQ-A-011), 규정 자연어 챗봇(근거·인용)(REQ-A-012) - - kxwp 제출 릴레이: 웹폼→HWP/PDF까지 API 무관하게 완결, 릴레이 단계만 킨텍스 IT 협의 [협의: C2, 리스크 R-3](REQ-F-022) -- **도표 지시**: 마일스톤 역산 타임라인(D-데이별 서류·알림). 도식ID 없음(타임라인). -- **REQ**: REQ-F-020·021·022·023·024, REQ-A-011·012 -- **출처**: tech_advisory §2, strategy §4 R-3 - -## P3-F. 관람·참가 (S35~S36) - -## S35 · 관람객 등록·배지·리드(M10) -- **섹션**: P3 기술제안 -- **헤드라인**: "사전등록→모바일 배지→현장 체크인→리드까지, 오프라인에서도 끊기지 않는다." -- **본문** - - 온라인 사전등록(유형별·중복검증)(REQ-F-037), 모바일 배지/QR 발급(REQ-F-038) - - 현장 QR 체크인·즉석 인쇄·집계(REQ-F-039), **오프라인 체크인 폴백**(REQ-F-040) - - 참가업체 리드캡처(REQ-F-041), AI 리드 스코어링·분류(REQ-F-042·A-018), 세그먼트 EDM(REQ-F-043) - - 티켓발권·AI폼빌더·스마트배지(선택, REQ-F-044) -- **도표 지시**: 등록→체크인→리드 여정도. 도식ID 없음(여정도). -- **REQ**: REQ-F-037·038·039·040·041·042·043·044, REQ-A-018 -- **출처**: PLANNING §M10, tech_advisory §7 - -## S36 · 비즈매칭·마케팅·공개사이트(M11·M12·M17) -- **섹션**: P3 기술제안 -- **헤드라인**: "AI가 매칭을 추천하고, 다국어 공개사이트·마이크로사이트가 SEO로 관람객을 모은다." -- **본문** - - AI 비즈매칭 추천(REQ-F-045·A-018), 미팅 슬롯·성과 리포트(REQ-F-046), 이벤트 모바일앱·세션·채팅·설문(REQ-F-047) - - 공개 홍보 사이트(SEO·다국어)(REQ-F-048), 공개 인터랙티브 플로어플랜(REQ-F-049), 세그먼트 EDM·캠페인 자동화(REQ-F-050), 스폰서십 패키지·대시보드(REQ-F-051) - - CMS 게시 워크플로·버전(REQ-F-067), 마이크로사이트(REQ-F-068), 다국어 콘텐츠·hreflang(REQ-F-069) - - AI 카피·이미지 초안·다국어 번역(REQ-A-015) -- **도표 지시**: 공개사이트 + 마이크로사이트 목업. 도식ID 없음(목업). -- **REQ**: REQ-F-045·046·047·048·049·050·051·067·068·069, REQ-A-015·018 -- **출처**: PLANNING §M11·M12·M17, tech_advisory §1-2 - -## P3-G. 정산·경영분석·현장 (S37~S40) - -## S37 · 정산·결제(M9) -- **섹션**: P3 기술제안 -- **헤드라인**: "납부는 스케줄로, 결제는 PG로, 예치금은 실사용까지 투명하게 정산한다." -- **본문** - - 납부 스케줄 자동생성·알림(REQ-F-056), 유틸리티·부대 PG 결제(REQ-F-057) - - 예치금 실사용 정산 투명화(REQ-F-058), 세금계산서·옥션 수수료 정산(REQ-F-059) - - 환불·취소 규정 자동(선택, REQ-F-060) -- **도표 지시**: 정산 흐름도(납부→결제→예치금 정산→세금계산서). 도식ID 없음. -- **REQ**: REQ-F-056·057·058·059·060 -- **출처**: PLANNING §M9 - -## S38 · 경영분석 BI(M16)-1 -- **섹션**: P3 기술제안 -- **헤드라인**: "가동률·매출구성·행사별 P&L·㎡당 수익을 운영사 관점 한 화면에." -- **본문** - - 홀·기간별 가동률 대시보드(REQ-F-061), 매출 구성·행사별 P&L(REQ-F-062) - - 전시장 ROI·RevPAD·㎡당 수익(REQ-F-063) - - 데이터: BI 집계·조회는 읽기복제 오프로드(운영 프라이머리 보호) -- **도표 지시**: BI 대시보드 목업(Recharts — 가동률·매출구성·P&L·RevPAD). 도식ID 없음(목업). -- **REQ**: REQ-F-061·062·063 -- **출처**: PLANNING §M16, tech_advisory §3 REQ-N-010 - -## S39 · 경영분석 BI(M16)-2 -- **섹션**: P3 기술제안 -- **헤드라인**: "리텐션·LTV·참가사 ROI를 권한 격리로, AI가 수요를 예측하고 KPI를 브리핑한다." -- **본문** - - 참가사 리텐션·LTV 코호트(REQ-F-064), 참가업체 관점 ROI(권한 격리)(REQ-F-065), 경영진 KPI 대시보드·드릴다운(REQ-F-066) - - AI 수요예측·수율/동적가격(REQ-A-013), AI KPI 브리핑·자연어 조회 Text-to-SQL(REQ-A-014) - - BI 마트는 개인 식별자 비반입(집계만), 참가업체는 자사 데이터만(격리) -- **도표 지시**: KPI 드릴다운 + AI 인사이트 카드. 도식ID 없음. -- **REQ**: REQ-F-064·065·066, REQ-A-013·014 -- **출처**: PLANNING §M16, tech_advisory §4 REQ-S-013 - -## S40 · 현장운영·물류(M8·M13·M14) -- **섹션**: P3 기술제안 -- **헤드라인**: "반입/반출 슬롯·통행증 QR·wayfinding·이상탐지 — 선택 기능은 시설 협의 전제로." -- **본문** - - 반입/반출 슬롯 예약(REQ-F-033), 통행증 QR·중량물 우선(REQ-F-034), 지게차·부대장비 렌탈(REQ-F-035), 철거 대기열 시뮬레이션(REQ-F-036) - - 실내 wayfinding·POI·경로(REQ-F-052), 실시간 혼잡 모니터·예측(REQ-F-053·A-019), 홀 전력부하·주차 점유(REQ-F-054), 안전 위반·이상탐지(REQ-F-055·A-019) - - 사이니지 콘텐츠 배포(REQ-F-070) - - **[협의]**: wayfinding 비콘·IoT·사이니지 HW는 킨텍스 시설 인프라 협의 전제(C7, 리스크 R-7). 미협의 시에도 코어 가치(WT-1~4)는 HW 독립 -- **도표 지시**: 현장 운영 상황판(혼잡·전력·안전·물류 슬롯). 도식ID 없음(상황판). -- **REQ**: REQ-F-033·034·035·036·052·053·054·055·070, REQ-A-019 -- **출처**: PLANNING §M8·M13·M14, strategy §4 R-7 - ---- - -# P4. 아키텍처·기술스택 (S41~S45, 배점 10) - -## S41 · 전체 아키텍처 -- **섹션**: P4 아키텍처 -- **헤드라인**: "확정 스택으로 이미 기동 중 — 도상이 아니라 동작하는 아키텍처다." -- **본문** - - 4-Tier + 사이드카: React 18/19(Vite·TS) 역할별 분리 → SSO(JWT+2FA)·이중 RBAC → 공유 Spring Boot 3.2.5(Java 17)+MyBatis 모듈러 모놀리스 → PostgreSQL+PostGIS(+읽기복제)/Redis/오브젝트 스토리지 - - 사이드카: 나노바나나 Python 워커(이미지) + Claude AI(AiTextRouter·Ollama 폴백)(텍스트) - - 실증: 인프라·CI/CD·인증·PostGIS 코어·나노바나나 [검증됨] -- **도표 지시**: **도식⑨ 시스템 아키텍처 다이어그램(6밴드 계층 + 우측 외부 게이트 컬럼)**. tech_advisory §1-4 ⑨. -- **REQ**: REQ-A-016, REQ-F-081(역할별 프론트) -- **출처**: tech_advisory §1-1·§1-4 ⑨·§0-1 - -## S42 · 단일 공간 데이터 원천 -- **섹션**: P4 아키텍처 -- **헤드라인**: "부스 폴리곤·트렌치 포인트·배선 LineString은 하나의 원천에서만 산다." -- **본문** - - M2 폴리곤·M4 배선 매퍼가 권위, 소비 모듈은 조회만·중복 저장 금지(REQ-N-017) - - PostGIS 단일 공간 원천(SRID 0 홀 로컬 미터), 전 `geom` GiST 인덱스 실시간(REQ-N-008) - - 검증 게이트: ArchUnit·계약 검수로 중복 저장 방지 -- **도표 지시**: 공간 데이터 원천 모델(폴리곤·포인트·LineString 단일 원천). 도식ID 없음(개념 모델). -- **REQ**: REQ-N-008, REQ-N-017 -- **출처**: tech_advisory §3(N-008·017), data.md §0-2 - -## S43 · 확장성·비동기 -- **섹션**: P4 아키텍처 -- **헤드라인**: "워커는 큐 깊이로 독립 확장하고, BI·공개는 읽기복제로 오프로드한다." -- **본문** - - 워커 독립 스케일: 나노바나나·서류·EDM 워커 큐 깊이 기반 증감(REQ-N-009) - - 읽기복제 오프로드: BI 집계·공개 조회·플로어플랜 열람 읽기복제 라우팅(REQ-N-010) - - 공개영역 CDN·요청률 상한(REQ-N-019), 워커 장애 재큐(멱등 Job)(REQ-N-011) - - 역할별 프론트 번들 분리(REQ-F-081, 방식 확정 대기 [구현 계획]) -- **도표 지시**: 배포 토폴로지도(프론트·백엔드 2인스턴스·워커 N·읽기복제·CDN). 도식ID 없음(토폴로지). -- **REQ**: REQ-N-009·010·011·019, REQ-F-081 -- **출처**: tech_advisory §3, system.md §3-1 - -## S44 · AI 플랫폼 라우팅 -- **섹션**: P4 아키텍처 -- **헤드라인**: "Claude 기본, 장애·폐쇄망이면 Ollama 폴백 — LLM 직접호출은 금지, 중앙 경유만." -- **본문** - - `AiTextRouter`: Claude 기본(`api.anthropic.com` 승인 예외) → Ollama 폴백(REQ-A-016) - - 나노바나나는 큐/콜백만(백엔드는 이미지 미취급), degraded 시 목 응답(REQ-A-007, N-005) - - AiTextRouter/AiConfig는 UIWS 패턴 [구현 계획: 착수 대기] -- **도표 지시**: AI 라우터 폴백 흐름(Claude→실패→Ollama). 도식ID 없음. -- **REQ**: REQ-A-007, REQ-A-016 -- **출처**: tech_advisory §1-2 AI(텍스트)·§6 - -## S45 · 연동·마스터데이터 -- **섹션**: P4 아키텍처 -- **헤드라인**: "외부는 게이트웨이로, 룰셋·요율은 버전 데이터로 무중단 개정한다." -- **본문** - - API·웹훅·CRM/ERP 연동 게이트웨이(선택, REQ-F-080) - - 룰셋·마스터데이터 버전관리(요율·규정 룰셋), M18 백오피스·감사 기록으로 무중단 개정(REQ-F-072) - - 외부 게이트: Gemini(워커존)·Claude(백엔드)·PG 결제·kxwp 릴레이·등록업체 DB 739 -- **도표 지시**: 연동 인터페이스 맵(내부↔외부 게이트웨이). 도식ID 없음. -- **REQ**: REQ-F-072, REQ-F-080 -- **출처**: tech_advisory §1-2, §6-3 - ---- - -# P5. 보안·개인정보 (S46~S50, 배점 10 · WT-4) - -## S46 · 보안 개요·불변 원칙 -- **섹션**: P5 보안·개인정보 -- **헤드라인**: "AI가 만든 것은 표시하고, 남의 데이터는 차단한다 — REQ-S 20항목 전량 커버." -- **본문** - - 불변 원칙 4대: 외부 API 격리 · 워터마크 항상 · PII 격리 · 테넌트 fail-closed - - 보안 성숙도: 인증·워터마크·admin env는 [검증됨](설계가 아니라 동작하는 통제) - - REQ-S 20항목 커버리지 100% -- **도표 지시**: REQ-S 20항목 커버리지 표(항목·충족방안·상태). 도식ID 없음(표). -- **REQ**: REQ-S 전량(001~020) -- **출처**: tech_advisory §4·§0-1 - -## S47 · 보안영역 분리 -- **섹션**: P5 보안·개인정보 -- **헤드라인**: "공개 DMZ는 쓰기 권한이 없고, 데이터존은 아웃바운드가 0이다." -- **본문** - - 존 계층: 인터넷→엣지(CDN·WAF)→DMZ(공개, 쓰기 없음)→내부망(인증)→AI 워커존(egress 제한)→데이터존(최내곽, 아웃바운드 전면 차단) - - 철칙: DMZ→데이터존 직접 접근 절대 금지, 데이터존 아웃바운드 0(유출 경로 제거) - - egress 화이트리스트: 승인 4목적지만(Claude·Gemini·PG·SMTP), 그 외 DROP - - fail-closed 다층 테넌트 격리와 정합(REQ-S-018) -- **도표 지시**: **도식⑩ 보안영역 분리도(존 다이어그램 + 관리존 + egress 컬럼)**. tech_advisory §1-4 ⑩. -- **REQ**: REQ-S-020, REQ-S-018, REQ-N-018 -- **출처**: tech_advisory §1-4 ⑩·§4, network.md §2 - -## S48 · 인증·접근통제 -- **섹션**: P5 보안·개인정보 -- **헤드라인**: "JWT+2FA는 이미 라이브, admin 비번은 env로 재시드, RBAC는 3중이다." -- **본문** - - JWT(HS256)+TOTP 2FA(RFC6238)+로그인 실패 잠금 [검증됨: Flyway V7/V8](REQ-S-005) - - admin 비번 env 재시드(AES-256-GCM, admin123 금지) [검증됨: env 시더](REQ-S-004) - - 행사 RBAC 3중(테넌트 격리 ⊃ 행사 RBAC, 열람/편집 게이트)(REQ-S-006), admin/internal 엔드포인트 게이트(REQ-S-008) - - RBAC·감사·시스템설정 백오피스(REQ-F-071) - - 미등록 업체 차단(403)과 연계(REQ-S-007) -- **도표 지시**: 인증 흐름 + RBAC 3중 구조. 도식ID 없음. -- **REQ**: REQ-S-004·005·006·007·008, REQ-F-071 -- **출처**: tech_advisory §4·§0-1(라이브) - -## S49 · AI·이미지 보안 -- **섹션**: P5 보안·개인정보 -- **헤드라인**: "생성 이미지는 워터마크·금지 고지 불변, 외부 API 키는 워커 env에만 산다." -- **본문** - - 워터마크 항상: M5 응답 `watermarkRequired:true`+text+notice(계약·심사 서류 금지), 제거 불가 [검증됨](REQ-S-009) - - 외부 API 정책: Ollama 기본 + Claude/Gemini만 승인 예외, egress 그 외 DROP(REQ-S-001) - - `GEMINI_API_KEY` 워커 env only(백엔드 미취급, 워커존 단일 egress)(REQ-S-002) - - RAG 근거·인용·환각 차단(abstain)(REQ-A-017) -- **도표 지시**: 워터마크 실물 + 외부 API 격리(워커존 단일 egress). 도식ID 없음. -- **REQ**: REQ-S-001·002·009, REQ-A-017 -- **출처**: tech_advisory §4·§2 WT-4 - -## S50 · 개인정보·감사·안전코딩 -- **섹션**: P5 보안·개인정보 -- **헤드라인**: "PII는 동의·마스킹·암호화·응답 제외, 민감 액션은 전수 감사, 코드는 안전하게." -- **본문** - - PII: 동의 분리(privacy/marketing/share)·최소수집·`*_enc` AES-256-GCM·응답 완전 제외·보존/파기(REQ-S-013) - - 자격증명·키 완전 제외(응답/로그/커밋 마스킹)(REQ-S-003), 스택트레이스 미노출(REQ-S-010), DataAccessException 요약 매핑(REQ-S-011) - - 민감 액션 감사 전수(`TB_AUDIT_LOG` tenant_id 포함, `@Audited` AOP)(REQ-S-012) - - 안전코딩: 입력 검증(REQ-S-014)·XSS/CSP(REQ-S-015)·인젝션 차단 MyBatis `#{}`(REQ-S-016)·IDOR/CSRF/JWT 회전(REQ-S-017) - - 관측성: 로그·메트릭·트레이스에 PII/스택트레이스 미기록(REQ-N-018) -- **도표 지시**: PII 마스킹 예시 + 감사로그 표. 도식ID 없음. -- **REQ**: REQ-S-003·010·011·012·013·014·015·016·017, REQ-N-018 -- **출처**: tech_advisory §4, data.md §5 - ---- - -# P6. 비기능 (S51~S54, 배점 8) - -## S51 · 성능·가용성 SLA -- **섹션**: P6 비기능 -- **헤드라인**: "코어 SLO 99.5%(성수기 99.9% 지향), 무중단 롤링·PITR·degraded로 버틴다." -- **본문** - - 코어 SLO 99.5%(성수기 99.9% 지향), 무상태 백엔드+Redis 외부화(REQ-N-001) - - 백엔드 2인스턴스·헬스체크 LB·롤링 무중단(REQ-N-002), PG 스탠바이 복제·자동 failover·PITR(REQ-N-003) - - degraded 모드: Gemini 장애=목 응답, Claude 장애=Ollama 폴백, PG 복제지연=프라이머리 폴백(REQ-N-005) - - 워커 장애 재큐(멱등)(REQ-N-011) -- **도표 지시**: SLA 수치표 + HA 구성도(프론트·백엔드·Redis·PG·워커 다중화). 도식ID 없음(표+구성도). -- **REQ**: REQ-N-001·002·003·005·011 -- **출처**: tech_advisory §3·§6-2, system.md §3-2 - -## S52 · 나노바나나 성능 -- **섹션**: P6 비기능 -- **헤드라인**: "단건 평균 40초, 1024px 전처리·자동생성 한정·캐시·쿼터로 대량을 제어한다." -- **본문** - - 단건 평균 ≤40s, 전면 비동기(WS 완료 푸시)(REQ-N-006) - - 전처리: 클라 Canvas 1024px 다운스케일 + JPEG 0.85 - - 대량 제어: 자동생성 S1·S7 한정 + 동일 스키마해시 캐시 + 행사별 쿼터(성공 시만 차감)(REQ-N-007) -- **도표 지시**: 렌더 성능 지표 카드(40초·1024px·캐시 히트·쿼터). 도식ID 없음. -- **REQ**: REQ-N-006, REQ-N-007 -- **출처**: tech_advisory §3, system.md §3-3 - -## S53 · 접근성·다국어·테마·반응형 -- **섹션**: P6 비기능 -- **헤드라인**: "WCAG 2.1 AA+KWCAG 2.2, 한/영/중/일, 라이트/다크, 데스크톱/모바일 전부." -- **본문** - - 접근성: WCAG 2.1 AA + 공공 KWCAG 2.2 병행(REQ-N-012), 캔버스/맵 비시각 대안(데이터 테이블·키보드 부스 선택)(REQ-N-013) - - 다국어: i18n 외부화(한 기본+영/중/일)·hreflang·로케일 포맷(REQ-N-014) - - 테마: 라이트/다크 CSS 토큰 2벌·prefers-color-scheme(REQ-N-015) - - 반응형: 데스크톱=설계/에디터, 모바일=현장/조회/승인(REQ-N-016) - - [구현 계획: 전면 스윕 대기] -- **도표 지시**: 접근성·다국어 매트릭스. 도식ID 없음(매트릭스). -- **REQ**: REQ-N-012·013·014·015·016, REQ-F-069(다국어 정합) -- **출처**: tech_advisory §3, PLANNING §8 - -## S54 · 관측성·데이터 -- **섹션**: P6 비기능 -- **헤드라인**: "로그·메트릭·트레이스에 PII·스택트레이스는 남기지 않고, BI는 배치 ETL로 정합한다." -- **본문** - - 관측성: 로그·메트릭·트레이스 관리존 수집, 자격증명·PII·스택트레이스 미기록(`include-stacktrace: never`)(REQ-N-018) - - BI 데이터마트: 스타 스키마(FACT/DIM) 야간 배치 ETL 또는 읽기복제 적재, KpiSnapshot(원천-집계 정합 오차 0)(REQ-N-021) - - 공개영역 CDN·요청률 상한과 정합(REQ-N-019) -- **도표 지시**: 관측성 파이프라인(수집→저장→대시보드). 도식ID 없음. -- **REQ**: REQ-N-018, REQ-N-021, REQ-N-019 -- **출처**: tech_advisory §3, data.md §6, tech.md §5 - ---- - -# P7. 멀티테넌시·확장 (S55~S57, 배점 5 · WT-5) - -## S55 · 멀티테넌트 SaaS 비전 -- **섹션**: P7 멀티테넌시·확장 -- **헤드라인**: "KINTEX #1에서 검증하고, COEX #2로 확장하며, 제3전시장을 표준으로 맞는다." -- **본문** - - KINTEX=기준 테넌트 #1, COEX=테넌트 #2(온보딩 대상) - - 제3전시장(2028, 178,000㎡) 확장 + 국내 전시산업 표준 플랫폼화 - - 개발 부담 최소화: 멀티테넌시=격리 레이어만 추가, 기능 로직 불변(리스크 R-8) -- **도표 지시**: 테넌트 확장 로드맵(KINTEX→COEX→제3전시장→타 전시관). 도식ID 없음(로드맵). -- **REQ**: (비전 — 상세 REQ는 S56·S57) -- **출처**: strategy §1 WT-5·§4 R-8, PLANNING §1A - -## S56 · 테넌트 격리 아키텍처 -- **섹션**: P7 멀티테넌시·확장 -- **헤드라인**: "한 층이라도 불일치하면 차단 — 컨텍스트→서비스→MyBatis→PostGIS 4층 방어." -- **본문** - - 격리 축: `tenant_id`(전시관) ⊃ `event_id`(행사) ⊃ 행사 RBAC 3중 - - fail-closed 4층: ①컨텍스트 필터(서브도메인+소속, 불일치 거부)→②서비스 가드→③MyBatis 인터셉터(tenant_id 자동 주입)→④PostGIS(tenant_id 선두 복합 인덱스)(REQ-S-018) - - tenant_id 선두 복합 인덱스로 격리 쿼리 성능(REQ-N-020) - - [구현 계획: 스키마·컨텍스트 구현 대기, 백필 전 단일테넌트 무중단→검증 후 필터 활성화] -- **도표 지시**: **도식⑪ 멀티테넌시 격리 아키텍처도(수직 4층 방어 스택 + 좌측 테넌트 컨텍스트 진입)**. tech_advisory §1-4 ⑪. -- **REQ**: REQ-F-077·078, REQ-S-018, REQ-N-020 -- **출처**: tech_advisory §1-3·§1-4 ⑪ - -## S57 · 온보딩·2계층 관리자 -- **섹션**: P7 멀티테넌시·확장 -- **헤드라인**: "코드 배포 없이 데이터 온보딩 6단계로 새 전시관을 연다." -- **본문** - - 온보딩: 코드 배포 없이 데이터 온보딩(홀·요율·규정 룰셋·마스터데이터 입력 수준)(REQ-F-077) - - 2계층 관리자: 플랫폼 슈퍼관리자(테넌트 스위처) / 테넌트 관리자(단일 스코프)(REQ-F-079) - - 테넌트 컨텍스트 해소(REQ-F-078) - - 공통코드·메뉴 관리(§5B, REQ-F-073) 기반 재사용 -- **도표 지시**: 온보딩 6단계 도식 + 테넌트 스위처 화면. 도식ID 없음(단계 도식). -- **REQ**: REQ-F-077·078·079, REQ-F-073 -- **출처**: tech_advisory §1-3, strategy §1 WT-5 - ---- - -# P8. 사업관리·일정·조직 (S58~S60, 배점 2) - -## S58 · 추진 일정·Phase -- **섹션**: P8 사업관리·일정·조직 -- **헤드라인**: "Phase 1 코어 → Phase 2 워크플로/운영 → Phase 3 현장/확장, 게이트로 진행." -- **본문** - - Phase 1(~4M): 아키텍처(A)·공통레이어(B, 2FA/시스템관리)·P0 코어(C: M2·M3·M4·M5) - - Phase 2(~4M): 옥션 M15·관람 M10·BI M16·관리자 M18·판매/서류/정산 M1/6/7/9·CMS M12/17 - - Phase 3(~4M+): 멀티테넌시·M11·M13·M14·배포 안정화·SM 이관 - - 공수: 총 ~150 M/M 규모(핵심 146 + PMO/QA 4~8), 라이브 스캐폴드로 Phase A/B 공수 실측 절감 - - [주의: 예산·사업기간은 실제 RFP 부재로 가정, 공고 확인 시 재정렬] -- **도표 지시**: **도식⑫ 일정 간트(Phase × 모듈 + 마일스톤 다이아몬드 G1·G2·Phase 게이트·QA 게이트·오픈)**. tech_advisory §1-4 ⑫. -- **REQ**: (일정 — 실행력 근거) -- **출처**: tech_advisory §1-4 ⑫·§5, PLANNING §9 - -## S59 · 선행 게이트·리스크 관리 -- **섹션**: P8 사업관리·일정·조직 -- **헤드라인**: "약한 고리를 숨기지 않고 드러내 방어한다 — 선제 대응이 곧 신뢰다." -- **본문** - - 선행 게이트: G1 나노바나나 승인 [검증됨: 완료]·G2 배포서버 [검증됨: 완료] - - 리스크 R-1~R-8 정면 방어: 외부 API(승인 게이트+degraded 폴백)·CAD 실측(가정 라벨+협의 선행)·kxwp(단계적 접근)·배점 가정(투명 고지)·이미지 품질(후처리 합성)·AI 오판(사람 확정)·HW 연동(협의 전제)·확장 부담(격리 레이어만) - - 확인 필요 8항목(C1~C8) 관리 -- **도표 지시**: **리스크 매트릭스(발생×영향 2×2에 R-1~R-8·C1~C8 배치)** + 게이트. tech_advisory §1-4 말미(추가 표준 도식). -- **REQ**: (리스크 관리 — 방어 논리) -- **출처**: strategy §4, rfp_analysis §6, tech_advisory §0 - -## S60 · 품질·형상관리 -- **섹션**: P8 사업관리·일정·조직 -- **헤드라인**: "배포는 헬스체크로 지키고 실패하면 롤백, 의존성은 SCA 게이트로 막는다." -- **본문** - - Fail-Safe 배포: 백업→배포→헬스체크 200→실패 시 롤백(이전 jar 유지) [검증됨: deploy_kintex.sh](REQ-N-004) - - QA 게이트: 점진 경계면 검증·보안 불변·빌드 회귀(Phase 게이트 통과 후 진행) - - SCA CI 게이트: OWASP Dependency-Check 정기 스캔·패치(REQ-S-019) - - 형상관리: Gitea `zio/kintex`·Conventional Commits·main 보호·환경 분리(dev/staging/prod) -- **도표 지시**: 품질 게이트 흐름(빌드→테스트→SCA→헬스체크→롤백). 도식ID 없음. -- **REQ**: REQ-N-004, REQ-S-019 -- **출처**: tech_advisory §6-5·§0-1(라이브), §4 REQ-S-019 - ---- - -# P9. 기대효과 (S61~S63, 가점) - -## S61 · 정량 기대효과 -- **섹션**: P9 기대효과 -- **헤드라인**: "견적 수일→즉시, 배치 수주→수 분, 검수 육안→자동, 결과는 사진으로." -- **본문** - - 견적: 수일 대기 → 즉시(규칙엔진) - - 배치: 수주 CAD 외주 → 수 분 3안 자동생성 - - 규정 검수: 육안 → 제출 전 자동 플래깅 - - 위치표시도: 수기 → 좌표 클릭 자동 작도 - - 시공 결과: 개장일 첫 대면 → 신청 시점 사진 예측 -- **도표 지시**: **기대효과 정량 인포그래픽(5지표 Before/After)**. tech_advisory §1-4 말미(추가 표준 도식). -- **REQ**: REQ-F-002·007·009·017, REQ-A-001 -- **출처**: PLANNING §1-3, strategy §5 - -## S62 · 정성·전략 효과 -- **섹션**: P9 기대효과 -- **헤드라인**: "참가업체는 영업력을, 홀매니저는 시간을, 킨텍스는 데이터 주권을 얻는다." -- **본문** - - 참가업체: 사진급 자료로 영업력·수주력 강화 - - 홀매니저: 규정 검수 병목 해소로 처리량 증대 - - 킨텍스: 온프레미스 우선·외부 AI 격리로 데이터 주권 확보 - - 산업: 국내 전시산업 표준 플랫폼화 -- **도표 지시**: 이해관계자별 효과 카드. 도식ID 없음. -- **REQ**: (정성 효과 — 전략) -- **출처**: strategy §3 - -## S63 · 확장 비전·마무리 -- **섹션**: P9 기대효과 -- **헤드라인**: "킨텍스에서 검증하고, 코엑스로 확장하며, 다시 사진으로 컨펌한다." -- **본문** - - 확장: COEX·제3전시장·타 전시관 SaaS 확장 - - 재사용: 검증된 공통 프레임워크(WISE/UIWS) 위에 도메인 코어 이식 - - 클로징: "신청서를 내는 순간, 시공 후 사진을 먼저 본다." -- **도표 지시**: 확장 비전 로드맵 + 클로징 슬로건 대형 타이포. 도식ID 없음. -- **REQ**: REQ-F-077(확장 근거) -- **출처**: strategy §1 WT-5 - ---- - -# 부록 (선택 삽입) - -## 부록 A · 확인 필요·협의 사항 -- C1 평가배점·제출규격(가정) · C2 kxwp API(REQ-F-022) · C3 CAD·트렌치 실측(REQ-F-016 정확도) · C4 인터넷 요율 공식본(REQ-F-002) · C5 나노바나나 G1(완료) · C6 코엑스 실측(REQ-F-077) · C7 사이니지·비콘·IoT HW(REQ-F-070·052~055) · C8 기타 협의 -- 정직성 어필: 모든 [가정]·[협의]를 1장 집약 → 발주처 협업 자세 - -## 부록 B · REQ 커버리지 매트릭스 -- 아래 "RTM 반영표" 참조(142 REQ ↔ 슬라이드 1:1) - -## 부록 C · 100대 기능 성숙도 -- 기구현 63 · 부분 18 · 신규 19(정량, rfp_analysis §4) - ---- - -# RTM 반영표 (REQ → 슬라이드 · 142 REQ 전량) - -> 규칙: 각 REQ의 **주 반영 슬라이드**(굵게) + 보조 슬라이드. QA는 이 표로 142 REQ 반영상태 ☑ 최종 검증. 미반영 0. - -## REQ-F (기능 81) - -| REQ | 요약 | 반영 슬라이드 | -|---|---|---| -| REQ-F-001 | 홀 가용성 캘린더 | **S33** | -| REQ-F-002 | 규칙엔진 견적 | **S33** · S10·S61 | -| REQ-F-003 | 웹폼→HWP | **S33** | -| REQ-F-004 | 부스판매 인벤토리 | **S33** | -| REQ-F-005 | 온라인 셀프선택 | **S33** | -| REQ-F-006 | 임대계약 전자서명 | **S33** | -| REQ-F-007 | 배치 3안 자동생성 | **S24** · S10·S61·S13 | -| REQ-F-008 | 배치안 선택/병합 | **S24** | -| REQ-F-009 | 배치 규정 검증·재검증 | **S25** · S10·S61 | -| REQ-F-010 | 최종안 버전·좌표·초대링크 | **S24** | -| REQ-F-011 | 조립부스 3D 프리뷰 | **S26** | -| REQ-F-012 | 독립부스 3안·버전 | **S26** | -| REQ-F-013 | 설계 규정 사전검증 | **S26** | -| REQ-F-014 | 도면 비전 추출·검증 | **S26** | -| REQ-F-015 | 전기용량·분전반 산출 | **S27** | -| REQ-F-016 | 배선경로 PostGIS 최단 | **S27** · S42 | -| REQ-F-017 | 위치표시도 자동생성 | **S28** · S10·S61 | -| REQ-F-018 | 네트워크·급배수·압축공기 신청 | **S28** | -| REQ-F-019 | 조명 조도 배치안 | **S18** | -| REQ-F-020 | 마일스톤 역산 알림 | **S34** | -| REQ-F-021 | 신고서류 웹폼→HWP/PDF | **S34** | -| REQ-F-022 | kxwp 릴레이 ⚠ | **S34** · S59(리스크 R-3) | -| REQ-F-023 | 서류 버전·전자결재 | **S34** | -| REQ-F-024 | OCR 계약·납품서 | **S34** | -| REQ-F-025 | 역경매 옥션 개설 | **S29** · S12·S13 | -| REQ-F-026 | AI 자료 기반 응찰·PDF | **S29** | -| REQ-F-027 | 실시간 순위·자동마감 | **S30** | -| REQ-F-028 | 종합평가 낙찰 스코어 | **S31** | -| REQ-F-029 | 등록업체 게이트 | **S32** | -| REQ-F-030 | 등록업체 AI 매칭 | **S31** | -| REQ-F-031 | 물량서 BOQ 산출 | **S31** · S29 | -| REQ-F-032 | 낙찰→발주 자동전환 | **S32** · S12 | -| REQ-F-033 | 반입/반출 슬롯 | **S40** | -| REQ-F-034 | 통행증 QR | **S40** | -| REQ-F-035 | 지게차·렌탈 | **S40** | -| REQ-F-036 | 철거 대기열 시뮬 | **S40** | -| REQ-F-037 | 사전등록 | **S35** | -| REQ-F-038 | 모바일 배지/QR | **S35** | -| REQ-F-039 | 현장 체크인·집계 | **S35** | -| REQ-F-040 | 오프라인 체크인 폴백 | **S35** · S51 | -| REQ-F-041 | 리드캡처 | **S35** | -| REQ-F-042 | AI 리드 스코어링 | **S35** | -| REQ-F-043 | 세그먼트 EDM | **S35** | -| REQ-F-044 | 티켓발권·스마트배지 | **S35** | -| REQ-F-045 | AI 비즈매칭 | **S36** | -| REQ-F-046 | 미팅 슬롯·리포트 | **S36** | -| REQ-F-047 | 이벤트 모바일앱 | **S36** | -| REQ-F-048 | 공개 홍보 사이트 | **S36** | -| REQ-F-049 | 인터랙티브 플로어플랜 | **S36** | -| REQ-F-050 | 세그먼트 EDM 캠페인 | **S36** | -| REQ-F-051 | 스폰서십 패키지 | **S36** | -| REQ-F-052 | wayfinding | **S40** | -| REQ-F-053 | 혼잡 모니터·예측 | **S40** | -| REQ-F-054 | 전력부하·주차 | **S40** | -| REQ-F-055 | 안전 이상탐지 | **S40** | -| REQ-F-056 | 납부 스케줄 | **S37** | -| REQ-F-057 | PG 결제 | **S37** | -| REQ-F-058 | 예치금 정산 | **S37** | -| REQ-F-059 | 세금계산서·수수료 | **S37** | -| REQ-F-060 | 환불·취소 규정 | **S37** | -| REQ-F-061 | 가동률 대시보드 | **S38** | -| REQ-F-062 | 매출·P&L | **S38** | -| REQ-F-063 | ROI·RevPAD·㎡당 수익 | **S38** | -| REQ-F-064 | 리텐션·LTV | **S39** | -| REQ-F-065 | 참가업체 ROI 격리 | **S39** | -| REQ-F-066 | 경영진 KPI 드릴다운 | **S39** | -| REQ-F-067 | CMS 게시 워크플로 | **S36** | -| REQ-F-068 | 마이크로사이트 | **S36** | -| REQ-F-069 | 다국어 콘텐츠·hreflang | **S36** · S53 | -| REQ-F-070 | 사이니지 배포 ⚠ | **S40** · S59 | -| REQ-F-071 | RBAC·감사 백오피스 | **S48** | -| REQ-F-072 | 룰셋·마스터데이터 버전 | **S45** | -| REQ-F-073 | 공통코드·메뉴 | **S16** · S57 | -| REQ-F-074 | 공통 업무모듈 | **S16** | -| REQ-F-075 | 회의록 STT·업무보고 PDF | **S16** | -| REQ-F-076 | 통합 알림센터 WebSocket | **S16** | -| REQ-F-077 | 멀티테넌트 격리·온보딩 | **S56·S57** · S55·S63 | -| REQ-F-078 | 테넌트 컨텍스트 해소 | **S56** · S57 | -| REQ-F-079 | 2계층 관리자 | **S57** | -| REQ-F-080 | API·웹훅·CRM/ERP 연동 | **S45** | -| REQ-F-081 | 역할별 프론트 포털 | **S43** · S41·S09 | - -## REQ-A (AI 20) - -| REQ | 요약 | 반영 슬라이드 | -|---|---|---| -| REQ-A-001 | 샷세트 S1~S7 | **S17·S18** · S01·S12·S61 | -| REQ-A-002 | image-to-image 구조보존 | **S19** · S12 | -| REQ-A-003 | 구조화 프롬프트 사전 | **S19** | -| REQ-A-004 | S6 배선 오버레이 래스터 | **S21** | -| REQ-A-005 | Before/After 슬라이더 | **S21** | -| REQ-A-006 | RenderJob 방어 | **S22** | -| REQ-A-007 | 비동기 큐·WebSocket | **S20** · S44 | -| REQ-A-008 | 간판 텍스트 검수·후처리 | **S22** | -| REQ-A-009 | 배치 3안(제약 솔버+LLM) | **S24** | -| REQ-A-010 | AI 규정 검증 | **S25** · S26 | -| REQ-A-011 | AI 서류 검수 | **S34** | -| REQ-A-012 | 규정 챗봇(근거·인용) | **S34** | -| REQ-A-013 | AI 수요예측·동적가격 | **S39** | -| REQ-A-014 | KPI 브리핑·Text-to-SQL | **S39** | -| REQ-A-015 | AI 카피·번역 | **S36** | -| REQ-A-016 | AI 라우터(Claude→Ollama) | **S44** · S41 | -| REQ-A-017 | RAG·인용·환각 차단 | **S49** · S23 | -| REQ-A-018 | AI 리드 스코어·매칭 | **S35·S36** | -| REQ-A-019 | 현장 이상탐지·예측 | **S40** | -| REQ-A-020 | 도면 비전·OCR 등록 | **S26·S34** | - -## REQ-N (비기능 21) - -| REQ | 요약 | 반영 슬라이드 | -|---|---|---| -| REQ-N-001 | SLO 99.5% | **S51** | -| REQ-N-002 | 2인스턴스 롤링 무중단 | **S51** · S43 | -| REQ-N-003 | PG 스탠바이·PITR | **S51** | -| REQ-N-004 | Fail-Safe 배포 | **S60** | -| REQ-N-005 | degraded 모드 | **S51** · S20·S44 | -| REQ-N-006 | 나노바나나 40초·1024px | **S52** · S20 | -| REQ-N-007 | 대량 생성 제어 | **S52** · S22 | -| REQ-N-008 | 공간쿼리 GiST 실시간 | **S42** · S12 | -| REQ-N-009 | 워커 독립 스케일 | **S43** | -| REQ-N-010 | BI/공개 읽기복제 | **S43** · S38 | -| REQ-N-011 | 워커 재큐(멱등) | **S43** · S51 | -| REQ-N-012 | WCAG 2.1 AA·KWCAG 2.2 | **S53** | -| REQ-N-013 | 캔버스 비시각 대안 | **S53** | -| REQ-N-014 | 다국어 i18n·hreflang | **S53** | -| REQ-N-015 | 라이트/다크 테마 | **S53** | -| REQ-N-016 | 반응형 | **S53** | -| REQ-N-017 | 단일 공간 원천 | **S42** | -| REQ-N-018 | 관측성·미기록 | **S54** · S50 | -| REQ-N-019 | 공개 CDN·요청률 상한 | **S43** · S54 | -| REQ-N-020 | tenant_id 선두 복합 인덱스 | **S56** | -| REQ-N-021 | BI 데이터마트 배치 ETL | **S54** · S38 | - -## REQ-S (보안 20) - -| REQ | 요약 | 반영 슬라이드 | -|---|---|---| -| REQ-S-001 | 외부 API 정책 | **S49** · S47 | -| REQ-S-002 | GEMINI 키 워커 env only | **S49** | -| REQ-S-003 | 자격증명·키 완전 제외 | **S50** | -| REQ-S-004 | admin 비번 env 재시드 | **S48** | -| REQ-S-005 | JWT+2FA·실패 잠금 | **S48** | -| REQ-S-006 | 행사 RBAC 3중 | **S48** | -| REQ-S-007 | 미등록 업체 차단(403) | **S32** · S48 | -| REQ-S-008 | admin/internal 게이트 | **S48** | -| REQ-S-009 | 이미지 워터마크 항상 | **S49** · S23·S01 | -| REQ-S-010 | 스택트레이스 미노출 | **S50** | -| REQ-S-011 | DataAccessException 요약 | **S50** | -| REQ-S-012 | 감사로그 전수 | **S50** | -| REQ-S-013 | PII 동의·마스킹·암호화 | **S50** · S39 | -| REQ-S-014 | 입력 검증 | **S50** | -| REQ-S-015 | XSS·CSP | **S50** | -| REQ-S-016 | 인젝션 차단 | **S50** | -| REQ-S-017 | IDOR·CSRF·JWT 회전 | **S50** | -| REQ-S-018 | fail-closed 테넌트 격리 | **S56** · S47 | -| REQ-S-019 | 의존성 SCA CI 게이트 | **S60** · S50 | -| REQ-S-020 | 보안영역 분리 | **S47** | - ---- - -## 커버리지 요약 - -| 유형 | REQ 수 | 반영 | 미반영 | -|---|---|---|---| -| REQ-F (기능) | 81 | 81 | 0 | -| REQ-A (AI) | 20 | 20 | 0 | -| REQ-N (비기능) | 21 | 21 | 0 | -| REQ-S (보안) | 20 | 20 | 0 | -| **총계** | **142** | **142** | **0** | - -**슬라이드 총계**: 63(S01~S63) + 부록 3(A·B·C) -**RTM 커버리지**: 142/142 반영, **미반영 0** -**과장 방지 준수**: 라이브 확인분(인프라·CI/CD·나노바나나·인증·워터마크·PostGIS 코어·평면도 자산)만 [검증됨], 도메인 모듈·멀티테넌시·집계 API·i18n/접근성 전면 적용은 [구현 계획]으로 구분 표기. 시크릿(키·비번·IP·SSH) 전면 미기재. AI 생성 이미지는 전 슬라이드에서 워터마크·금지 고지 정책 명시. diff --git a/docs/deliverables/제안서/_gen/proposal_outline.md b/docs/deliverables/제안서/_gen/proposal_outline.md deleted file mode 100644 index ec46235..0000000 --- a/docs/deliverables/제안서/_gen/proposal_outline.md +++ /dev/null @@ -1,210 +0,0 @@ -# 킨텍스 자동전시시스템 구축 — 제안서 목차 (Proposal Outline · PPT) - -> 작성: proposal-strategist · 작성일: 2026-07-11 · 버전 v1.0 -> 짝 문서: `proposal_strategy.md`(Win Theme·평가대응·리스크) · `rtm.md`(REQ↔섹션 매핑) · `rfp_analysis.md`(배점·규격) -> 형식: **PowerPoint(.pptx) 기술제안서**, 분량 자율(권장 40~80p) → **목표 ~63슬라이드**. 실제 공고 확인 시 재정렬(rfp_analysis C1). -> 목차 코드: P1 사업이해 · P2 추진전략 · P3 기술제안 · P4 아키텍처 · P5 보안·개인정보 · P6 비기능 · P7 멀티테넌시·확장 · P8 사업관리·일정·조직 · P9 기대효과 (RTM 정합). -> 배분 원칙: **배점 가중** — P3(35점) ≈ 40%, P4·P5(각 10점) 각 ~8%, P6(8점) ~6%, P1+P2(10점) ~16%, P7(5점) ~5%, P8(2점) ~5%, P9 ~5%. - ---- - -## 슬라이드 배분 총괄 (배점 정렬) - -| 구간 | 목차 | 평가배점(가정) | 슬라이드 수 | 비중 | 핵심 Win Theme | -|---|---|---|---|---|---| -| 표지·목차·오버헤드 | — | — | 6 | 10% | 슬로건 각인 | -| P1 | 사업이해 | 10(P1+P2 공유) | 4 | 6% | 도메인 이해 | -| P2 | 추진전략 | 10(P1+P2 공유) | 5 | 8% | 3중 해자 | -| **P3** | **기술제안** | **20+15=35** | **25** | **40%** | **WT-1·2·3 총력** | -| P4 | 아키텍처·기술스택 | 10 | 5 | 8% | 확정 스택·공간원천 | -| P5 | 보안·개인정보 | 10 | 5 | 8% | WT-4 | -| P6 | 비기능 | 8 | 4 | 6% | SLA 수치 | -| P7 | 멀티테넌시·확장 | 5 | 3 | 5% | WT-5 | -| P8 | 사업관리·일정·조직 | 2 | 3 | 5% | 실행력·게이트 | -| P9 | 기대효과 | 가점 | 3 | 5% | 정량 ROI | -| **합계** | | **80(+가격20)** | **63** | 100% | | - ---- - -## 0. 표지·목차 (6슬라이드) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 기획 | -|---|---|---|---| -| S01 | 표지 | 사업명 + 슬로건 "신청서를 내는 순간, 시공 후 사진을 먼저 본다" | 나노바나나 시공 예측 히어로 이미지(워터마크) 배경 | -| S02 | 제안 개요 | 우리는 누구·왜 우리인가 3줄 | 제안사 로고·핵심 자산 아이콘(선 SVG) | -| S03 | 목차 | P1~P9 네비게이션 | 목차 인덱스(9섹션 배점 배지 병기) | -| S04 | 제안 핵심 요약(1p) | Win Theme 5선 + 3중 해자 한눈에 | **3중 해자 벤다이어그램**(시공예측∩PostGIS∩옥션폐루프) | -| S05 | 배점 대응 맵 | 우리 제안이 배점 어디를 어떻게 겨냥하는가 | **배점-섹션 정렬 매트릭스**(80점 히트맵) | -| S06 | [가정] 고지 간지 | 배점·규격 가정 투명 고지·공고 시 재정렬 | 간단 노트 카드(리스크 R-4 방어) | - ---- - -## P1. 사업이해 (4슬라이드 · 배점 사업이해·추진전략 10) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ/근거 | -|---|---|---|---|---| -| S07 | 사업 배경·목적 | 킨텍스 108,011㎡(2028년 178,000㎡), 전시 준비 여전히 HWP·CAD 수작업 | 킨텍스 규모 인포그래픽 | PLANNING §1-1 | -| S08 | As-Is 프로세스 병목 | D-150/30/25/7 타임라인의 Pain Point(견적 대기·CAD 외주·kxwp 육안검수·유틸리티 파편화) | **As-Is 타임라인 다이어그램**(D-데이별 병목 표시) | PLANNING §3-1·3-2 | -| S09 | 이해관계자·페르소나 | 6역할+2관리자 계층의 과업·고통·기대 | **페르소나 카드 6종**(주최자·참가·업체·홀매니저·관람객·대중) | PLANNING §2 | -| S10 | To-Be 목표(정량) | 견적 즉시·배치 수분·규정 자동검증·위치표시도 자동·시공 예측 | **As-Is→To-Be 대비표**(정량 개선 5행) | PLANNING §1-3 | - ---- - -## P2. 추진전략 (5슬라이드 · 배점 P1과 공유) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ/근거 | -|---|---|---|---|---| -| S11 | 비전·포지셔닝 | "도면이 아니라 사진으로 컨펌한다" 베뉴 운영 플랫폼 | 비전 슬로건 대형 타이포 | PLANNING §1-2 | -| S12 | **3중 해자 전략** | 시공예측∩PostGIS∩옥션폐루프 결합의 재현 난이도 | **3원 벤다이어그램(재강조·상세)** | strategy §3-3 | -| S13 | Win Theme 5선 | WT-1~5 슬로건·배점 정렬 | **Win Theme 5카드**(배점 배지 병기) | strategy §1 | -| S14 | 전시 생애주기 폐루프 | 설계→시각화→발주→시공→운영→분석 한 흐름 | **생애주기 폐루프 순환도**(M모듈 매핑) | PLANNING §1-2-1 | -| S15 | 추진체계·수행조직 | 아키텍트·코어·도메인·AI·QA·DevOps 팀 + 게이트 | **추진체계도(조직도)** | 하네스 팀 구성 | - ---- - -## P3. 기술제안 (25슬라이드 · 배점 AI시각화·설계 20 + 기능구현 15 = 35 · **최다 집중**) - -### P3-A. 모듈 조망 (1) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S16 | 전체 모듈맵 | M1~M18+§5B+§1A 한 장 조망, P0 코어 강조 | **모듈맵(생애주기×우선순위 매트릭스)** | rfp_analysis §2-1 | - -### P3-B. 나노바나나 시공 예측 — WT-1 최우선 (7) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S17 | 나노바나나 개요 | "시공 전에 사진으로 의사결정" 경쟁 미보유 차별화 | 히어로 Before/After 실물 | REQ-A-001 | -| S18 | 표준 샷세트 S1~S7 | 정면·야간·통로·내부·Before/After·배선·홀전경 | **샷세트 7컷 그리드**(워터마크) | REQ-A-001, F-019 | -| S19 | image-to-image 구조보존 | 골격·치수·통로·앵글 잠금, 스타일만 사실화 | **보존/교체 파라미터 도식** | REQ-A-002·003 | -| S20 | AI 파이프라인 아키텍처 | Spring 큐→Redis→Python 워커→오브젝트스토리지→WebSocket | **나노바나나 파이프라인 플로우** | REQ-A-007, N-006 | -| S21 | Before/After·배선 오버레이 | 드래그 슬라이더(ARIA), S6 백엔드 래스터 합성(전기적·네트청·급배수녹) | Before/After 슬라이더 + 배선 색상 범례 | REQ-A-004·005 | -| S22 | RenderJob 방어·품질 | 다층 크기가드·SAFETY 분기·간판 한글 후처리·성공 시만 쿼터 | **RenderJob 상태 시퀀스**(방어 계층) | REQ-A-006·008 | -| S23 | AI 신뢰 원칙 | 결정적 알고리즘+LLM 보조+사람 확정, 워터마크 불변 | AI 3층 신뢰 구조 도식 | REQ-S-009, A-017 | - -### P3-C. 배치·설계·배선 AI 자동화 — WT-2 (5) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S24 | 플로어플랜 스튜디오(M2) | 부스 배치 3안 자동생성·선택/병합·버전 | **3안 비교 화면 목업** | REQ-F-007·008·010, A-009 | -| S25 | 배치 규정 자동검증 | 피난통로·바닥하중 홀별·비상구·복층·소방 병합 후 재검증 | **규정 위반 플래그 대시보드** | REQ-F-009, A-010 | -| S26 | 부스 설계 스튜디오(M3) | 조립 3D 프리뷰·독립부스 3안·규정 사전검증(높이·리깅·방염·이격)·도면 비전추출 | 설계 3안+규정 체크리스트 | REQ-F-011~014, A-020 | -| S27 | 유틸리티 설계(M4) | 기기→kW→분전반 자동산출, 트렌치→분전반 PostGIS 최단 배선 | **PostGIS 배선 경로 오버레이** | REQ-F-015·016 | -| S28 | 위치표시도·유틸리티 신청 | 좌표 클릭 자동 작도(수기 폐지)·자동 견적·D-25 역산 | 위치표시도 자동생성 예시 | REQ-F-017·018 | - -### P3-D. 공사 옥션 폐루프 — WT-3 (4) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S29 | 옥션 개요·폐루프 | AI 설계자료→응찰 근거→발주까지 한 흐름 | **옥션 폐루프 흐름도**(M2~M5→M15→M6·M9) | REQ-F-025·026 | -| S30 | 역경매·실시간 순위 | 라운드·마감·최저가·내 위치(익명 옵션)·자동마감 | **실시간 순위 보드 목업** | REQ-F-027 | -| S31 | 종합평가 낙찰 | 가격+평판+납기 가중 스코어·항목별 비교표 | **종합평가 스코어 비교표** | REQ-F-028·030·031 | -| S32 | 등록 게이트·발주 전환 | 미등록 업체 응찰 원천 차단(403)·낙찰→계약/발주 자동전환 | **등록 검증 시퀀스** + 발주 전환 | REQ-F-029·032, S-007 | - -### P3-E. 판매·서류·워크플로 (2) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S33 | 판매·홀배정·견적(M1) | 가용성 캘린더·규칙엔진 견적(2,250원/㎡)·웹폼→HWP·셀프 부스선택 | 견적 규칙엔진 계산 흐름 | REQ-F-001~005 | -| S34 | 서류·마일스톤(M6) | D-150/30/25/7 역산·웹폼→HWP/PDF 7종·AI 서류검수·kxwp 릴레이(⚠협의)·전자결재·OCR | 마일스톤 역산 타임라인 | REQ-F-020~024, A-011 | - -### P3-F. 관람·참가 (2) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S35 | 관람객 등록·배지·리드(M10) | 사전등록·모바일 배지/QR·현장 체크인·오프라인 폴백·리드캡처·세그먼트 EDM | 등록→체크인→리드 여정도 | REQ-F-037~043 | -| S36 | 비즈매칭·마케팅·공개사이트(M11·M12·M17) | AI 비즈매칭·SEO 다국어 공개사이트·인터랙티브 플로어플랜·마이크로사이트·CMS 게시 워크플로 | 공개사이트+마이크로사이트 목업 | REQ-F-045·048~051·067~069 | - -### P3-G. 정산·경영분석·현장 (4) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S37 | 정산·결제(M9) | 납부 스케줄·PG 결제·예치금 실사용 투명 정산·세금계산서·옥션 수수료 | 정산 흐름도 | REQ-F-056~059 | -| S38 | 경영분석 BI(M16)-1 | 가동률·매출구성·행사별 P&L·RevPAD·㎡당 수익(운영사 관점) | **BI 대시보드 목업(Recharts)** | REQ-F-061~063 | -| S39 | 경영분석 BI(M16)-2 | 리텐션·LTV 코호트·참가사 ROI(권한 격리)·경영진 KPI·AI 수요예측·KPI 브리핑·Text-to-SQL | KPI 드릴다운 + AI 인사이트 카드 | REQ-F-064~066, A-013·014 | -| S40 | 현장운영·물류(M8·M13·M14) | 반입/반출 슬롯·통행증 QR·wayfinding·혼잡/전력/안전 이상탐지(선택·협의 전제) | 현장 운영 상황판 | REQ-F-033~036·052~055, A-019 | - ---- - -## P4. 아키텍처·기술스택 (5슬라이드 · 배점 10) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S41 | 전체 아키텍처 | 확정 스택 React18/19+Spring Boot 3.x+MyBatis+PostgreSQL/PostGIS+Redis+나노바나나 Python 워커 | **시스템 아키텍처 다이어그램**(전 계층) | rfp_analysis §1, REQ-A-016 | -| S42 | 단일 공간 데이터 원천 | M2폴리곤·M4배선 매퍼 권위, 소비 모듈 조회만·중복 금지, PostGIS GiST 실시간 | **공간 데이터 원천 모델**(폴리곤·포인트·LineString) | REQ-N-008·017 | -| S43 | 확장성·비동기 | 워커 독립스케일(큐 깊이)·읽기복제 BI 오프로드·공개영역 CDN·역할별 프론트 분리 | **배포 토폴로지도** | REQ-N-009·010·019, F-081 | -| S44 | AI 플랫폼 라우팅 | Claude 기본→Ollama 폴백·중앙경유·LLM 직접호출 금지, 나노바나나 큐/콜백 | **AI 라우터 폴백 흐름** | REQ-A-007·016 | -| S45 | 연동·마스터데이터 | API·웹훅·CRM/ERP 게이트웨이·룰셋/마스터데이터 버전관리·무중단 개정 | 연동 인터페이스 맵 | REQ-F-072·080 | - ---- - -## P5. 보안·개인정보 (5슬라이드 · 배점 10 · WT-4) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S46 | 보안 개요·불변 원칙 | "AI가 만든 것은 표시, 남의 데이터는 차단" REQ-S 20 커버리지 | **REQ-S 20항목 커버리지 표** | REQ-S 전량 | -| S47 | 보안영역 분리 | 공개 DMZ 쓰기 불가·데이터존 직결 불가·DMZ↔내부 HTTPS만 | **보안영역 분리도(망 구성)** | REQ-S-020, N-018 | -| S48 | 인증·접근통제 | JWT+2FA OTP(TOTP)·실패 잠금·admin 비번 env 재시드·RBAC 3중·admin/internal 게이트 | 인증 흐름 + RBAC 3중 구조 | REQ-S-004~008 | -| S49 | AI·이미지 보안 | 나노바나나 워터마크 항상·계약/심사 금지 고지·외부 API 워커 env only·RAG 환각차단 | **워터마크 실물** + 외부 API 격리 | REQ-S-001·002·009, A-017 | -| S50 | 개인정보·감사·안전코딩 | PII 동의·마스킹·AES-256-GCM·응답 제외·감사로그 전수·XSS/인젝션/IDOR·SCA 게이트·스택트레이스 미노출 | PII 마스킹 예시 + 감사로그 표 | REQ-S-010~017·019 | - ---- - -## P6. 비기능 (4슬라이드 · 배점 8) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S51 | 성능·가용성 SLA | SLO 99.5%(성수기 99.9%)·백엔드 2인스턴스 롤링·PG 스탠바이+PITR·degraded 모드 | **SLA 수치표** + HA 구성도 | REQ-N-001~005·011 | -| S52 | 나노바나나 성능 | 평균 40초·1024px+JPEG 0.85 전처리·자동생성 S1·S7 한정·캐시·쿼터 | 렌더 성능 지표 카드 | REQ-N-006·007 | -| S53 | 접근성·다국어·테마·반응형 | WCAG 2.1 AA+KWCAG 2.2·비시각 대안·i18n 한/영/중/일·hreflang·라이트/다크·데스크톱/모바일 | 접근성·다국어 매트릭스 | REQ-N-012~016 | -| S54 | 관측성·데이터 | 로그·메트릭·트레이스(PII/스택트레이스 미기록)·BI 데이터마트 배치 ETL/읽기복제 | 관측성 파이프라인 | REQ-N-018·021 | - ---- - -## P7. 멀티테넌시·확장 (3슬라이드 · 배점 5 · WT-5) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S55 | 멀티테넌트 SaaS 비전 | KINTEX #1 검증→COEX 확장, 제3전시장 대비 표준 플랫폼 | 테넌트 확장 로드맵 | §1A, WT-5 | -| S56 | 테넌트 격리 아키텍처 | tenant_id 전파·fail-closed 다층 격리(컨텍스트→서비스→MyBatis→PostGIS)·선두 복합 인덱스 | **멀티테넌시 격리 아키텍처도** | REQ-F-077·078, S-018, N-020 | -| S57 | 온보딩·2계층 관리자 | 코드 배포 없이 데이터 온보딩 6단계·플랫폼/테넌트 관리자·테넌트 스위처 | **온보딩 6단계 도식** | REQ-F-077·079 | - ---- - -## P8. 사업관리·일정·조직 (3슬라이드 · 배점 2) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ | -|---|---|---|---|---| -| S58 | 추진 일정·Phase | Phase 1 코어(~4M)·2 워크플로/운영(~4M)·3 현장/확장(~4M+) | **일정 간트 차트**(Phase×모듈) | PLANNING §9 | -| S59 | 선행 게이트·리스크 관리 | G1 나노바나나 승인·G2 배포서버·확인필요 8항목(C1~C8) 관리 | **리스크 매트릭스**(발생×영향) + 게이트 | strategy §4, rfp_analysis §6 | -| S60 | 품질·형상관리 | Fail-Safe 배포(헬스체크·롤백)·QA 게이트·SCA CI·경계면 검증 | 품질 게이트 흐름 | REQ-N-004, S-019 | - ---- - -## P9. 기대효과 (3슬라이드 · 가점) - -| # | 슬라이드 | 핵심 메시지 | 그래픽/도표 | REQ/근거 | -|---|---|---|---|---| -| S61 | 정량 기대효과 | 견적 수일→즉시·배치 수주→수분·검수 육안→자동·위치표시도 자동·시공 예측 가능 | **기대효과 정량 인포그래픽**(5지표 Before/After) | PLANNING §1-3 | -| S62 | 정성·전략 효과 | 참가업체 영업력·홀매니저 병목 해소·데이터 주권·전시산업 표준화 | 이해관계자별 효과 카드 | strategy §3 | -| S63 | 확장 비전·마무리 | COEX·제3전시장·타 전시관 SaaS 확장, 슬로건 재각인 | 확장 비전 로드맵 + 클로징 슬로건 | WT-5 | - ---- - -## 부록 (선택 삽입) - -| 항목 | 내용 | 근거 | -|---|---|---| -| A. 확인 필요·협의 사항 | C1~C8([가정]·⚠확인필요) 집약 — 정직성 어필, 리스크 R-1~R-8 방어 요약 | rfp_analysis §6, strategy §4 | -| B. REQ 커버리지 매트릭스 | 142 REQ(F81·A20·N21·S20)↔P1~P9 반영 표(RTM 요약) | rtm.md | -| C. 100대 기능 성숙도 | 기구현 63·부분 18·신규 19 정량 | rfp_analysis §4 | - ---- - -## deck-designer·writer 전달 핵심 - -- **필수 도식 12종**(반드시 제작): ①3중 해자 벤다이어그램(S04·S12) ②모듈맵(S16) ③나노바나나 파이프라인 플로우(S20) ④Before/After 슬라이더(S21) ⑤3안 비교 화면(S24) ⑥PostGIS 배선 오버레이(S27) ⑦옥션 폐루프 흐름도(S29) ⑧종합평가 스코어 비교표(S31) ⑨시스템 아키텍처 다이어그램(S41) ⑩보안영역 분리도(S47) ⑪멀티테넌시 격리 아키텍처도(S56) ⑫일정 간트(S58). -- **추진체계도**(S15)·**리스크 매트릭스**(S59)·**기대효과 인포그래픽**(S61)은 공공 제안 표준 필수. -- **아이콘**: 전량 선(line/stroke) 스타일 SVG 직접 제작(외부 라이브러리 금지) — proposal-visual-designer 산출 준수. -- **RTM 정합 검증**: 각 슬라이드 REQ 매핑을 rtm.md 반영상태(☐→☑)로 갱신. 142 REQ 누락 0 — qa가 본 목차의 REQ 열로 교차 점검. -- **배점 규율**: P3(S16~S40, 25슬라이드)가 전체의 40%. 나노바나나(S17~S23) 7슬라이드는 절대 축소 금지(최고 배점 20). 선택 기능(S40 등)은 압축 유지. diff --git a/docs/deliverables/제안서/_gen/proposal_qa.md b/docs/deliverables/제안서/_gen/proposal_qa.md deleted file mode 100644 index 4ea23e6..0000000 --- a/docs/deliverables/제안서/_gen/proposal_qa.md +++ /dev/null @@ -1,117 +0,0 @@ -# 제안서 최종 QA 보고 — 킨텍스 자동전시시스템 (나라장터 표준 7대장 재조립판) - -> QA: proposal-qa · 일자: 2026-07-12 · 대상: `docs/deliverables/제안서.pptx` (97슬라이드) + `_workspace/proposal/build_deck.py` -> 검증 방법: python-pptx 텍스트 추출(`_qa_extract.py`) + REQ range 전개 대조(`_qa_rtm.py`) + 구조/배점/라벨 스캔(`_qa_struct.py`) + PowerPoint COM PNG 렌더(`render_check.py`) 시각 확인. -> **최종 판정: 승인(PASS)** — 중대 결함 3건 수정 완료·재빌드·재검증. 잔여 이슈는 발주처 확인 항목([가정]/[협의])뿐(정직하게 라벨링됨). - ---- - -## 종합 결과 - -| # | 검증 항목 | 판정 | 비고 | -|---|---|---|---| -| 1 | 표준 목차 정합(7대장·신설 9슬라이드) | ✅ PASS | Ⅰ~Ⅶ 순차, 신설 9종 전량 실재 | -| 2 | RTM 커버리지(142 REQ) | ✅ PASS(수정 후) | 142/142. 결함 3건(F-008·F-010·F-026) 수정 | -| 3 | 배점 정렬(90/10) | ✅ PASS | 80/20·35/80 잔재 0, 재정렬 각주 존재 | -| 4 | 정직성 라벨 | ✅ PASS | tech_advisory §0과 일치 | -| 5 | 보안(노출/워터마크) | ✅ PASS | 자격증명 노출 0, AI 고지 존재 | -| 6 | 구조 품질(목차·간지·번호) | ✅ PASS | 목차↔장 순서·배점 배지 정합 | - -**수정 건수: 3건**(build_deck.py 3개 편집 → 재빌드 → COM 재렌더 → 통과) - ---- - -## 1. 표준 목차 정합 — PASS - -7대장이 `g2b_standard_toc.md` §2-1(별지 제5호서식)과 순서·구조 일치. 각 장 앞 섹션 간지(7개) 배치. - -| 장 | 슬라이드 | 섹션 간지 | -|---|---|---| -| Ⅰ 일반현황 | S6~S9 | S5 GENERAL STATUS | -| Ⅱ 전략·방법론 | S11~S25 | S10 STRATEGY & METHODOLOGY | -| Ⅲ 기술·기능 | S27~S56 | S26 TECHNOLOGY & FUNCTION | -| Ⅳ 성능·품질 | S58~S63 | S57 PERFORMANCE & QUALITY | -| Ⅴ 프로젝트 관리 | S65~S68 | S64 PROJECT MANAGEMENT | -| Ⅵ 프로젝트 지원 | S70~S73 | S69 PROJECT SUPPORT | -| Ⅶ 그 밖의 사항 | S75~S79 | S74 멀티테넌시·확장 | - -**신설 9슬라이드 전량 실재·내용 소스 정합:** -투입인력 M/M(S7·tech_advisory §5-2) · 정량평가(S9) · 표준FW(S24) · 관리방법론(S66 PMBOK) · 인수인계교육(S70) · SM/SLA(S71 §6) · 장애비상(S72) · 기밀보안(S73) · 상생협력(S76 별표11). - ---- - -## 2. RTM 커버리지 — PASS(수정 후 142/142) - -REQ 배지가 range 표기(예: `REQ-F-073~076`, 연속 `REQ-F-045~051·067~069`)를 포함하므로 range 전개 후 대조. 이전 QA에서 모호했던 7건 우선 확정: - -| REQ | 최초 상태 | 확정 결과 | -|---|---|---| -| F-008 배치안 선택/병합 편집 | 콘텐츠 O(S31)·배지 X | **수정**: S31 배지에 008 추가 | -| F-010 최종안 버전기록·좌표확정·초대링크 | 콘텐츠 X·배지 X (진성 누락) | **수정**: S31에 4번째 플로우 칩 "[최종안 확정·초대링크]" + 캡션 + 배지 010 추가 | -| F-026 AI자료 기반 견적서 응찰·PDF·버전 | 콘텐츠 O(S36)·배지 X | **수정**: S36 배지에 026 추가 | -| F-032 낙찰→발주 자동전환 | 배지 O(S39) | 이상 없음 | -| A-003 구조화 프롬프트 보존/교체 | 배지 O(S20) | 이상 없음 | -| A-005 Before/After 슬라이더 | 배지 O(S29) | 이상 없음 | -| A-008 간판 텍스트 비전 검수 | 배지 O(S30) | 이상 없음 | - -**range 전개 후 잔여 유일 진성 누락 = F-010**(초대링크/최종안 확정 콘텐츠·배지 모두 부재) → 콘텐츠+배지 동시 보강으로 해소. - -수정 후 union(단독 토큰 + range 전개) 대조: **142/142 전량 반영, 미반영 0.** 부록 B(S82) "미반영 0" 표기가 사실과 일치함(수정 전에는 F-008/010/026 미배지로 과장 소지 → 해소). - ---- - -## 3. 배점 정렬 — PASS - -- 표기 일관: **기술 90(정성70+정량20) / 가격 10** (S3 배점맵, S4 [가정] 간지). `기술 80`·`가격 20`·`80/20`·`35/80` 잔재 **0건**. -- 재정렬 각주 존재: S3 "실제 공고 확인 시 재정렬", S4·S7·S81 동일 취지. 정보화 지침 제18조 원칙값 근거 명기. -- S3 배점맵이 각 부문↔장(Ⅱ·Ⅲ최다·Ⅳ·Ⅴ·Ⅵ·Ⅰ·Ⅶ) 정확 매핑. - ---- - -## 4. 정직성 라벨 — PASS - -표본: [검증됨] 16 · [구현 계획] 4 · [협의] 2 · [가정] 5 · [Non-Goal] 1. `tech_advisory §0`(라이브 실적 vs 구현 계획)과 일치: -- **[검증됨]**(라이브): 나노바나나 Gemini 워커(S17·19), PostGIS 매퍼(S17), JWT+2FA(S52), CI/CD Fail-Safe(S24), 인프라·코어(S48) — §0-1과 일치. -- **[구현 계획]**: 옥션 M15 폐루프(S17·36·37 "Phase D-M15"), 멀티테넌시(S27), F-081 프론트 분리(S60) — §0-2와 일치. 과장(구현 완료 오기) 0. - ---- - -## 5. 보안 — PASS - -- 전 슬라이드 텍스트 스캔: IPv4/서버IP · `sk-`/`AIza` API키값 · SSH계정 · 비밀번호 값 노출 **0건**. -- AI 생성/워터마크 고지: 갤러리 도입부(S85) "본 화면은 Google Stitch 기반 AI 생성 디자인 시안…" 고지 박스 존재. M5 이미지 워터마크 불변(REQ-S-009) [검증됨] S22·50·53. 갤러리 전 시안(S85~97) "AI 생성" 고지 병기. - ---- - -## 6. 구조 품질 — PASS - -- 목차(S2 "나라장터 정보시스템 표준 7대장")↔실제 장 순서·슬라이드 번호 정합. -- 간지 배점 배지(S3): 부문↔장 매핑·배점 숫자 정확. -- 부록 A(100대 기능 성숙도)·B(REQ 매트릭스)·C(가정·협의 집약)·D(화면 갤러리) 순 구성. - ---- - -## 수정 내역 (build_deck.py) - -| 슬라이드 | 편집 | 커버 REQ | -|---|---|---| -| S31 배치 3안 | 배지 `REQ-F-007·A-009`→`REQ-F-007·008·010·A-009`; 액션칩 3→4([최종안 확정·초대링크] 추가); 캡션에 F-008·F-010 근거 명시 | F-008, F-010 | -| S36 옥션 폐루프 | 배지 `REQ-F-025·031`→`REQ-F-025·026·031` (콘텐츠 "견적서 제출 PDF·버전" 기존재) | F-026 | - -재빌드(97슬라이드) → COM 렌더 → S31/S36 시각 확인(오버플로·깨짐 없음, 4칩 균등 배치). - ---- - -## 잔여 이슈 (결함 아님 — 발주처 확인 항목) - -`g2b_standard_toc.md §7` T1~T5 및 부록 C 집약대로 정직 라벨링됨. QA 통과를 막지 않음: -- 배점 90/10, 예산·M/M(~150), 투입인력 실명·단가 → [가정], 공고 확정 시 재정렬/기재 -- 상생협력·하도급 구체 지분 → [공고·컨소 확정 시 기재] -- wayfinding 비콘·IoT·사이니지 HW(F-070·052~055) → [협의] 시설 인프라 전제 -- kxwp 연동(F-022) → ⚠확인필요 - ---- - -## 최종 판정: **승인(PASS)** - -7대장 표준 목차·신설 9슬라이드 완비, RTM 142/142(수정 3건 후), 배점 90/10 일관, 정직성 라벨 정합, 보안 노출 0, AI 고지 존재. 실제 공고 확인 시 배점·목차·투입계획 재정렬만 반영하면 제출 가능. diff --git a/docs/deliverables/제안서/_gen/proposal_strategy.md b/docs/deliverables/제안서/_gen/proposal_strategy.md deleted file mode 100644 index c9141ea..0000000 --- a/docs/deliverables/제안서/_gen/proposal_strategy.md +++ /dev/null @@ -1,150 +0,0 @@ -# 킨텍스 자동전시시스템 구축 — 수주 전략서 (Proposal Strategy) - -> 작성: proposal-strategist · 작성일: 2026-07-11 · 버전 v1.0 -> 입력: `rfp_analysis.md`(REQ 142 · 배점 가정 기술80/가격20) · `rtm.md`(P1~P9 매핑) · `docs/PLANNING.md` §1~§2 -> 원칙: **배점이 전략을 지배한다.** 과장·허위 금지 — rfp_analysis에 근거 있는 주장만 기재. 시크릿(키·비번·내부IP) 미기재. -> 성격: 실제 RFP 부재 → 평가배점·제출규격은 **[가정]**(공공 SI 통례). 실제 공고 확인 시 교체. - ---- - -## 0. 전략 요약 (Executive Summary) - -**한 문장 포지셔닝**: 킨텍스 자동전시시스템은 "도면과 표를 보여주는" 기존 전시테크가 아니라, **"신청서를 내는 순간 시공 후 사진을 먼저 보여주고, 그 사진으로 발주까지 종결하는" 유일한 폐루프 플랫폼**이다. - -**수주 논리 3단**: -1. **배점의 44%(35/80)가 P3 기술제안**(AI 시각화·설계 20 + 기능 구현 15)에 몰려 있다 → 제안서 분량·메시지·그래픽의 40% 이상을 P3에, 그중 절반을 나노바나나+M2~M5 폐루프에 집중한다. -2. 경쟁(글로벌 전시테크·기존 공공 SI)은 **나노바나나 시공 예측 + PostGIS 실측 공간데이터 + 옥션 폐루프**의 결합을 재현할 수 없다 → 이 3결합을 "우리만의 해자(moat)"로 전면화한다. -3. 공공 발주처의 최대 리스크(외부 API·개인정보·안전서류 오용)를 **선제 방어 논리**(G1 승인 게이트+degraded 폴백, AI 워터마크 불변, PII·테넌트 3중 격리)로 무장해 감점을 원천 차단한다. - -**슬로건 후보(표지·간지용)**: -- 메인: **"신청서를 내는 순간, 시공 후 사진을 먼저 본다."** (PLANNING §1-2 비전 직인용) -- 서브: **"설계 → 시각화 → 발주 → 시공 → 운영 → 분석. 킨텍스에서 한 흐름으로 끝난다."** - ---- - -## 1. Win Theme (차별화 메시지 5선) - -> 각 Win Theme = ①발주처 핵심 니즈 ②우리 차별화 메시지(슬로건) ③배점 정렬 ④근거(REQ/PLANNING) ⑤증빙 방식. 배점 가중 순으로 배열. - -### WT-1. 시공 전에 사진으로 의사결정한다 — 나노바나나 시공 예측 (최우선 · 배점 20) - -- **발주처 니즈**: 참가업체·주최자가 "도면을 읽지 못해 개장일에야 결과를 처음 본다"(PLANNING §2 참가업체 Pain). 조감도 외주는 비용·시간. -- **슬로건**: **"도면이 아니라 사진으로 컨펌한다."** -- **차별화 메시지**: image-to-image 구조보존(골격·치수·통로·카메라 앵글 잠금, 스타일만 사실화)으로 표준 샷세트 S1~S7(정면·야간조명·통로·내부·Before/After·배선 오버레이·홀전경)을 평균 40초에 자동 생성. **경쟁 전시테크가 보유하지 못한 기능**(rfp_analysis §4 win 우선순위 ①). -- **배점 정렬**: "AI 시각화·설계 자동화(핵심 차별화) 20점" 직격. 대응 REQ-A-001~008, REQ-F-019. -- **증빙**: Before/After 드래그 슬라이더 실물 캡처, 샷세트 예시 이미지(워터마크 포함), RenderJob 파이프라인 도식. - -### WT-2. 배치·설계·배선을 AI가 3안 자동생성하고 규정을 자동검증한다 (배점 20+15 교차) - -- **발주처 니즈**: 주최자 CAD 수작업 수일~수주, 홀매니저 육안 규정 검수 병목(PLANNING §1-3, §3-2). -- **슬로건**: **"수 분 내 3안, 제출 전 규정 자동검증."** -- **차별화 메시지**: 제약 솔버+LLM 조건해석으로 부스 배치/부스 설계 각 3안 자동생성·선택/병합·버전관리(REQ-F-007~013). 규정(피난통로·바닥하중 홀별·높이 5m·리깅 6.5~8.5m·방염·이격)을 제출 전 자동 플래깅. PostGIS 최단경로로 트렌치→분전반 배선·위치표시도 자동 작도(수기 폐지, REQ-F-015~017). -- **배점 정렬**: AI 시각화·설계(핵심) 20 + 기능 구현 15의 코어. 대응 REQ-A-009·010·020, REQ-F-007~018. -- **증빙**: 3안 비교 화면, 규정 위반 플래그 대시보드, PostGIS 배선 경로 오버레이, "사람이 최종 확정"(AI는 보조) 명시. - -### WT-3. AI 설계자료가 곧 발주 근거가 된다 — 공사 옥션 폐루프 (배점 15 · 폐루프 핵심) - -- **발주처 니즈**: 등록업체 739개가 단순 리스트+엑셀, 매칭·견적 비교 부재(PLANNING §3-2). -- **슬로건**: **"사진 수준 자료로 응찰하고, 한 흐름에서 발주까지 끝낸다."** -- **차별화 메시지**: M2~M5가 생성한 배치·설계·배선/BOQ·시공 예측 이미지가 곧 역경매 응찰 근거. 실시간 순위·종합평가 낙찰(가격+평판+납기)→계약/발주 자동전환(REQ-F-025~032). **미등록 업체 응찰 원천 차단(403)** 으로 킨텍스 "등록업체 시공 필수" 규정을 시스템이 강제. -- **배점 정렬**: 기능 구현 15 + 보안(등록 게이트). 대응 REQ-F-025~032, REQ-S-007. -- **증빙**: 옥션 흐름도(설계자료→응찰→실시간순위→낙찰→발주), 종합평가 스코어 비교표, 등록 게이트 차단 시퀀스. - -### WT-4. 공공이 안심하는 AI — 워터마크·PII·테넌트 3중 격리 불변 (배점 10) - -- **발주처 니즈**: 생성 이미지의 계약·심사 서류 오용 우려, 관람객/리드 개인정보 보호, 다전시관 데이터 혼입 방지. -- **슬로건**: **"AI가 만든 것은 반드시 표시하고, 남의 데이터는 원천 차단한다."** -- **차별화 메시지**: M5 이미지 응답에 워터마크 **항상** 포함(watermarkRequired:true·계약/심사 서류 금지 고지, REQ-S-009). PII 동의·마스킹·AES-256-GCM·응답 완전 제외(REQ-S-013). fail-closed 다층 테넌트 격리(컨텍스트 필터→서비스 가드→MyBatis 인터셉터→PostGIS, REQ-S-018). 외부 API는 Ollama 기본+Claude/Gemini만 승인 예외(REQ-S-001). -- **배점 정렬**: 보안·개인정보 10점. 대응 REQ-S 20항목 전량. -- **증빙**: 보안영역 분리도(DMZ/내부망), 워터마크 실물, 감사로그 전수 표, PII 마스킹 예시. - -### WT-5. 킨텍스에서 검증하고 코엑스로 확장한다 — 멀티테넌트 SaaS (배점 5 · 미래가치) - -- **발주처 니즈**: 킨텍스 제3전시장(2028, 178,000㎡) 확장 + 국내 전시산업 표준 플랫폼화. -- **슬로건**: **"코드 배포 없이 데이터 온보딩만으로 새 전시관을 연다."** -- **차별화 메시지**: KINTEX=기준 테넌트 #1, COEX=테넌트 #2 온보딩 6단계(마스터데이터 입력 수준). tenant_id 전파·2계층 관리자(플랫폼/테넌트)·컨텍스트 해소(REQ-F-077~079). 기능은 전부 보존, 데이터·권한만 전시관 단위 분리. -- **배점 정렬**: 멀티테넌시·확장성 5점 + 사업 미래 비전. 대응 REQ-F-077~079, REQ-N-020, REQ-S-018. -- **증빙**: 멀티테넌시 격리 아키텍처, 온보딩 6단계 도식, 테넌트 스위처 화면. - ---- - -## 2. 평가항목별 대응 전략 (배점 가중) - -> 각 평가부문에 "무엇을·어떻게 보여줄지". 정량 항목=수치, 정성 항목=스토리. 배점 큰 순. - -| # | 평가부문 | 배점 | 대응 전략 (증빙·실적·방법론·도표) | 주요 REQ | 목차 | -|---|---|---|---|---|---| -| 1 | **AI 시각화·설계 자동화(핵심)** | **20** | WT-1·WT-2 전면. 나노바나나 파이프라인 도식 + Before/After 실물 + 3안 생성·규정검증·PostGIS 배선 화면. "결정적 알고리즘 중심(제약 솔버) + LLM 보조 + 사람 최종확정" 3층 구조로 **AI 신뢰성** 강조 | REQ-A-001~010·020, F-007~019 | P3 | -| 2 | **기능 구현(M1~M18)** | 15 | 모듈맵 1장으로 전 모듈 조망 → 배점 큰 옥션(WT-3)·관람·BI를 상세, 선택(P2) 기능은 "구현 로드맵"으로 간결. 100대 기능 성숙도(기구현 63·부분 18·신규 19) **정량 제시** | REQ-F 전량, 특히 F-025~032 | P3 | -| 3 | **AI 설계 자동화(폐루프)** | (20 내) | WT-3. 옥션 흐름도 + 종합평가 스코어 비교표 + 발주 자동전환. AI 자료→응찰 근거 연결이 **경쟁이 못 하는 폐루프** | REQ-F-025~032, S-007 | P3 | -| 4 | 아키텍처·기술스택 | 10 | 확정 스택(React18/19+Spring Boot 3.x+MyBatis+PostgreSQL/PostGIS+Redis+나노바나나 Python 워커) 정합. 단일 공간 원천(M2폴리곤·M4배선 매퍼 권위)·워커 독립스케일·읽기복제 오프로드·역할별 프론트 분리 도식 | REQ-N-008~010·017·019, A-007·016, F-072·080·081 | P4 | -| 5 | 보안·개인정보(불변) | 10 | WT-4. REQ-S 20항목 커버리지 표 + 보안영역 분리도 + 워터마크·PII·감사로그·2FA. **감점/실격 리스크를 정면 방어** | REQ-S 20 전량, A-006·017 | P5 | -| 6 | 비기능(성능·가용성·접근성·다국어) | 8 | SLO 99.5%(성수기 99.9% 지향)·백엔드 2인스턴스 롤링 무중단·PG 스탠바이+PITR·나노바나나 40초/1024px·WCAG 2.1 AA+KWCAG 2.2·i18n 한/영/중/일·라이트/다크. **수치 SLA 표** | REQ-N 21 전량 | P6 | -| 7 | 멀티테넌시·확장성(COEX) | 5 | WT-5. 격리 아키텍처 + 온보딩 6단계 + tenant_id 선두 복합 인덱스. 제3전시장·타 전시관 확장 미래가치 | REQ-F-077~079, N-020, S-018 | P7 | -| 8 | 사업이해·추진전략 | 10 | As-Is 병목(HWP·CAD 수작업·kxwp·유틸리티 파편화) → To-Be 정량목표(견적 즉시·배치 수분·위치표시도 자동) 대비표. **킨텍스 실무 프로세스 정확 이해**(D-150/30/25/7 타임라인)로 도메인 신뢰 확보 | 전체 맥락 | P1·P2 | -| 9 | 사업관리·품질·일정 | 2 | Phase 1~3(코어→워크플로/운영→현장/확장) 간트 + QA 게이트 + Fail-Safe 배포 + 선행게이트(G1·G2) 관리. 배점 낮으나 실행력 신뢰 | REQ-N-004, S-019 | P8 | -| — | 기대효과 | (가점) | To-Be 정량 개선(견적 수일→즉시, 배치 수주→수분, 검수 육안→자동) + ROI·표준화·확장 스토리 | 목표표(§1-3) | P9 | - -**분량 배분 원칙**: P3(35점)에 전체 슬라이드의 ~40%, 그중 절반을 나노바나나+M2~M5. P4·P5(각 10점) 각 ~8%, P6(8점) ~6%, P1+P2(10점) ~16%, P7(5점) ~5%, P8(2점) ~5%, P9 ~5%. 선택 기능은 압축. - ---- - -## 3. 경쟁사 대비 강점 (Competitive Positioning) - -> 근거 있는 비교만. 경쟁을 폄하하지 않고 "우리가 결합한 것"을 강조. - -### 3-1. 글로벌 전시테크(ExpoPlatform·VenueSight·Swapcard·RainFocus 등) 대비 - -| 축 | 글로벌 전시테크 | 킨텍스 자동전시시스템 | 근거 | -|---|---|---|---| -| 시공 결과 시각화 | 도면·3D 뷰어 수준, 사진급 시공 예측 **미보유** | 나노바나나 image-to-image 구조보존 사진 생성 | PLANNING §1-2·§6, WT-1 | -| 공간 데이터 | 좌표·구역 관리 | **PostGIS 실측**(부스 폴리곤·트렌치 포인트·배선 LineString) 단일 원천 | REQ-N-008·017, data.md | -| 발주 연결 | 리드/매칭까지, 공사 발주 폐루프 약함 | AI 설계자료→역경매 응찰→낙찰→발주 자동전환 폐루프 | WT-3, §M15 | -| 킨텍스 도메인 정합 | 범용 글로벌 규격 | 킨텍스 규정(높이 5m·리깅·방염·바닥하중)·요율(2,250원/㎡)·D-데이 타임라인 내재화 | PLANNING §3, §7-1 | -| 데이터 주권 | 해외 SaaS·데이터 국외 | 온프레미스 Ollama 기본, 외부 AI는 승인 예외만·워커 env 격리 | REQ-S-001·002 | - -**메시지**: "글로벌 전시테크의 기능(부스판매·리드·비즈매칭·BI)은 이식하되, 그들이 못 하는 **실측 공간데이터×시공 예측 이미지×옥션 폐루프**를 관통시켰다." - -### 3-2. 기존 공공 SI(범용 웹시스템 구축) 대비 - -| 축 | 범용 SI | 킨텍스 자동전시시스템 | 근거 | -|---|---|---|---| -| AI 내재화 | 챗봇·검색 부가 수준 | 배치 3안·규정검증·시공 예측·수요예측이 **핵심 업무 자동화** | REQ-A 20항목 | -| 공간 연산 | 일반 CRUD | PostGIS GiST 실시간 공간쿼리(트렌치 KNN·통로버퍼·비상구 교차) | REQ-N-008 | -| 표준 프레임워크 | 개별 구축 | GUARDiA 표준(WISE/UIWS) 공통·시스템관리·JWT+2FA 이식으로 검증된 자산 재사용 | REQ-F-071~076, §5B | -| 확장성 | 단일 기관 | 멀티테넌트 SaaS(COEX 온보딩)로 재사용·표준화 | WT-5, §1A | - -**메시지**: "범용 SI가 아니라 전시 도메인 전용 AI 자동화 플랫폼. 검증된 공통 프레임워크(UIWS/WISE) 위에 도메인 코어(M2~M5)와 폐루프(M15)를 얹어 **개발 리스크와 기간을 동시에 단축**한다." - -### 3-3. 우리만의 3중 해자(Moat) — 한 장 요약 - -**나노바나나 시공 예측 이미지 ∩ PostGIS 실측 공간데이터 ∩ 공사 옥션 폐루프** — 이 세 가지의 결합은 단일 요소로는 모방 가능하나 **셋의 결합은 재현 난이도가 높다**. 제안서 P3 도입부에 "3원 벤다이어그램" 1장으로 각인. - ---- - -## 4. 리스크 방어 논리 ([가정]·⚠확인필요 정면 대응) - -> 평가위원이 반드시 파고들 "약한 고리"를 **선제적으로 드러내고 방어**한다. 숨기면 감점, 정면 대응하면 신뢰. - -| # | 리스크(발주처 우려) | 방어 논리 (정면 대응) | 근거 | 제안서 위치 | -|---|---|---|---|---| -| R-1 | **G1 나노바나나 Gemini 외부 API 미승인 시 핵심 기능 붕괴?** | "승인 게이트 + degraded 폴백" 2중 설계. G1 미승인/Gemini 장애 시 워커 **목(mock) 응답·구조·워터마크 유지**로 화면·워크플로 계속 동작(REQ-N-005). 승인 확보를 **선행 게이트로 프로젝트 관리**(kintex-pm 통제). 외부 API는 워커 env only·백엔드 미취급으로 보안 격리 | REQ-S-001·002, N-005, C5 | P3 도입·P5·P8 | -| R-2 | **CAD·트렌치 실측 데이터 미확보로 배선·배치 정확도 의심?** | 미확보 정직 고지 + "공개 스펙 기반 가정 그리드 + '가정' 라벨"로 데모, **실측 반영을 킨텍스 협의 선행작업으로 명시**. 확보 자산(홀별 평면도 JPG 15장·CAD DWG "평면,트렌치.dwg")으로 실현성 입증 | C3, PLANNING R4, CLAUDE.md 확보자산 | P3 배선·P8 | -| R-3 | **kxwp/kxfp 폐쇄형 API 미공개로 제출 연동 불가?** | "초기 수동 릴레이 → 목표 API 연동" 단계적 접근. 웹폼→HWP/PDF 자동생성까지는 API 무관하게 완결, 릴레이 단계만 킨텍스 IT 협의(⚠확인필요 명시). 리스크를 범위·일정에 반영 | REQ-F-022, C2 | P3 서류·P8 | -| R-4 | **평가배점·제출규격이 가정이라 목차 정합성 의심?** | 배점 가정을 **투명 고지**하고 "실제 공고 확인 시 목차·분량 즉시 재정렬" 유연성 제시. 목차는 공공 SI 통례(사업이해→전략→기술→아키→보안→비기능→확장→관리→기대효과)로 안전 정렬 | rfp_analysis §4·§5, C1 | P1·목차 간지 | -| R-5 | **간판 한글 텍스트 등 생성 이미지 품질 편차?** | 간판 텍스트 비전 오탈자 검수 + 한글 렌더 실패 시 **간판영역 후처리 합성**(REQ-A-008). 자동생성은 S1·S7 한정·캐시·쿼터로 품질/비용 제어(REQ-N-007) | REQ-A-008, N-007, R5 | P3 AI·P6 | -| R-6 | **AI 환각·오판(규정 오검증·설계 오류) 책임?** | "AI는 보조, 사람이 최종 확정" 원칙 불변. 규정검증=플래깅까지, 구조안전 판정은 구조기술사/킨텍스(Non-Goal 명시). AI 답변은 RAG 근거·인용·환각 차단(abstain)(REQ-A-017) | Non-Goal §1-4, REQ-A-017 | P3·P5 | -| R-7 | **사이니지 HW·비콘·IoT 센서 연동 불확실?** | 선택(P2) 기능으로 분류, "킨텍스 시설 인프라 협의 전제" 명시. 미협의 시에도 코어 가치 불변(WT-1~4는 HW 독립) | REQ-F-070·052~055, C7 | P3 현장·P8 | -| R-8 | **다전시관 확장이 개발 부담을 키우지 않나?** | 멀티테넌시=격리 레이어만 추가(tenant_id 전파), 기능 로직 불변. 백필 전 단일테넌트 무중단 동작, 검증 후 필터 활성화(회귀 방지) | §1A-5, REQ-F-077 | P7 | - -**방어 원칙**: 모든 [가정]·⚠확인필요 항목은 제안서 부록 "확인 필요·협의 사항" 1장으로 집약해 **정직성**을 오히려 강점화(발주처와의 협업 자세 어필). - ---- - -## 5. writer·deck-designer 전달 지침 - -- **메시지 우선순위**: WT-1 > WT-2 > WT-3 > WT-4 > WT-5 순으로 슬라이드 무게. P3 도입부에 "3중 해자" 벤다이어그램 필수. -- **정량 강조**: 견적 수일→즉시, 배치 수주→수분, 검수 육안→자동, 40초 렌더, SLO 99.5%, REQ 142 커버리지 등 숫자를 표지·간지·기대효과에 반복 노출. -- **AI 신뢰 프레이밍**: "결정적 알고리즘(제약 솔버·PostGIS) 중심 + LLM 보조 + 사람 최종확정" 3층을 모든 AI 슬라이드 캡션에 삽입 → 공공 AI 불안 해소. -- **RTM 정합**: 전 142 REQ가 P1~P9 어딘가에 반영되도록 목차(§proposal_outline.md)의 REQ 매핑을 준수. 누락 0. -- **금지**: 시크릿·내부IP·미확보 데이터의 확정적 서술 금지. 미확보는 "가정/협의 전제" 라벨 필수. diff --git a/docs/deliverables/제안서/_gen/render_check.py b/docs/deliverables/제안서/_gen/render_check.py deleted file mode 100644 index 55c907c..0000000 --- a/docs/deliverables/제안서/_gen/render_check.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -"""PowerPoint COM 렌더링 검증 — 전 슬라이드 PNG export.""" -import sys, os, glob -import win32com.client as win32 - -PPTX = sys.argv[1] if len(sys.argv) > 1 else r"C:\GUARDiA\workspace\kintex\docs\deliverables\제안서.pptx" -OUT = sys.argv[2] if len(sys.argv) > 2 else r"C:\Users\ython\AppData\Local\Temp\claude\C--GUARDiA-workspace-kintex\85f77606-5d61-424b-8a38-f8b61b56b54c\scratchpad\render" -os.makedirs(OUT, exist_ok=True) -for f in glob.glob(os.path.join(OUT, "*.png")): - try: os.remove(f) - except: pass - -app = win32.Dispatch("PowerPoint.Application") -try: - app.Visible = True -except Exception: - pass -pres = app.Presentations.Open(PPTX, ReadOnly=True, Untitled=False, WithWindow=False) -n = pres.Slides.Count -for i in range(1, n+1): - pres.Slides(i).Export(os.path.join(OUT, f"s{i:03d}.png"), "PNG", 1280, 720) -pres.Close(); app.Quit() -print("RENDERED", n, "slides ->", OUT) diff --git a/docs/deliverables/제안서/_gen/rfp_analysis.md b/docs/deliverables/제안서/_gen/rfp_analysis.md deleted file mode 100644 index 4a858d0..0000000 --- a/docs/deliverables/제안서/_gen/rfp_analysis.md +++ /dev/null @@ -1,366 +0,0 @@ -# 킨텍스 자동전시시스템 구축 — 요구사항 분석서 (가상 RFP 기반) - -> 작성: rfp-analyst · 작성일: 2026-07-11 · 버전 v1.0 -> **성격**: 실제 RFP 문서는 부재하며, 발주처(킨텍스, 한국국제전시장)의 내부 기획 문서를 "가상 RFP"의 요구사항 원천으로 삼아 제안서 작성·평가 대응용으로 구조화 추출한 것이다. -> **근거 문서(출처)**: `docs/PLANNING.md`(v3.0), `docs/FEATURE_BACKLOG_100.md`(v1.1), `docs/SECURITY.md`, `docs/architecture/{app,system,tech,data,network}.md`, `CLAUDE.md`(kintex 섹션). -> **원칙**: 문서에 없는 요구는 발명하지 않는다(각 REQ에 출처 병기). 비밀번호·키·IP 등 시크릿은 미기재. 가정(assumption)은 "[가정]"으로 명시. - ---- - -## 1. 사업 개요 (가정 포함) - -| 항목 | 내용 | 근거/비고 | -|---|---|---| -| 사업명 | 킨텍스 자동전시시스템(Exhibition Automation Platform) 구축 | PLANNING §1-2-1 | -| 발주처(가정) | 킨텍스(KINTEX, 한국국제전시장) — 기준 테넌트 #1 | PLANNING §1A-1 | -| 사업 목적 | 전시장 운영 전 주기(홀 배정→부스 배치→장치공사 설계→전기/조명→네트워크/유틸리티 배선→반입/반출→관람객·정산·경영분석)를 AI로 자동 설계·검증하고, 시공 결과를 나노바나나(Gemini 이미지 생성)로 "공사 후 사진"처럼 사전 시각화 | PLANNING §1-2 | -| 정체성 | 부스 시공 도구를 넘어 전시 기획·판매·시공·운영·관람·사후분석 전 주기를 아우르는 베뉴 운영 플랫폼 + 다중 전시관(Venue) 멀티테넌트 SaaS(KINTEX #1, COEX 온보딩) | PLANNING §1-2-1·§1A | -| 범위 모듈 | 코어 M2~M5(P0), 판매·발주·정산 M1·M6·M7·M9·M15, 관람·참가 M8·M10~M14, 경영·콘텐츠·관리 M16~M18, 공통/시스템관리 레이어 §5B, 멀티테넌시 §1A | PLANNING §4-1 | -| 확정 기술스택 | React 18/19(Vite·TS) + Spring Boot 3.x(Java 17) + MyBatis + PostgreSQL(PostGIS) + Redis + 나노바나나 Python 워커 사이드카 | PLANNING §8, CLAUDE.md | -| AI 플랫폼 | 텍스트=Claude 기본(AiTextRouter/AiConfig, `ANTHROPIC_API_KEY` env) → Ollama 폴백 / 이미지=나노바나나(Gemini `gemini-3.1-flash-image-preview`) | FEATURE_BACKLOG §0, PLANNING §6-4 | -| 사용자 역할 | 6 행사역할(주최자·참가업체·장치/시공업체·홀매니저·관람객·일반대중) + 2 관리자계층(테넌트 관리자·플랫폼 슈퍼관리자) | PLANNING §2·§1A-4 | -| 로드맵(가정) | Phase 1 코어(~4개월)·Phase 2 워크플로/운영(~4개월)·Phase 3 현장/확장(~4개월+) | PLANNING §9 | -| 선행 게이트 | G1 나노바나나 Gemini 승인(외부 API), G2 개발/배포 서버(kintex.zioinfo.co.kr, PostGIS+Redis) | CLAUDE.md, PLANNING §10 R12 | - -**[가정] 명시 항목**: 예산·계약방식·정확한 사업기간·평가배점·제출규격은 실제 RFP가 없어 공공 SI 통례를 가정한다(§4·§5). - ---- - -## 2. 과업 범위 - -### 2-1. 기능 과업 범위 (모듈별) - -| 계열 | 모듈 | 우선순위 | 범위 요지 | 출처 | -|---|---|---|---|---| -| 코어(불변) | M2 플로어플랜 스튜디오 | P0 | 부스 배치 3안 자동생성·선택/병합·규정 자동검증·버전 | PLANNING §M2 | -| 코어 | M3 부스 설계 스튜디오 | P0 | 조립/독립부스 설계 3안·규정 사전검증·도면 비전추출 | PLANNING §M3 | -| 코어 | M4 유틸리티 설계 | P0 | 전기·조명 용량/분전반/배선(M4a), 네트워크·급배수·압축공기·위치표시도(M4b) | PLANNING §M4 | -| 코어 | M5 나노바나나 시각화 | P0 | 시공 후 예상사진 표준 샷세트(S1~S7)·Before/After·워터마크 | PLANNING §M5·§6 | -| 판매·발주 | M1 홀 배정·자동견적 | P1 | 가용성 캘린더·규칙엔진 견적·배정신청 서식 | PLANNING §M1 | -| 판매·발주 | M6 서류·마일스톤 | P1 | D-150/30/25/7 역산·웹폼→HWP/PDF·AI 서류검수·kxwp 릴레이 | PLANNING §M6 | -| 판매·발주 | M7 등록업체 매칭 | P1 | 14분류×739개 업체 추천·RFQ·등록업체 검증 | PLANNING §M7 | -| 판매·발주 | M15 공사/장치 옥션 | P1(핵심) | 역경매·견적서(Quotation) 응찰·실시간 순위·종합평가 낙찰·발주전환 | PLANNING §M15 | -| 판매·발주 | M9 정산·결제 | P1 | 납부스케줄·PG결제·예치금 실사용 정산·세금계산서 | PLANNING §M9 | -| 물류 | M8 반입/반출 물류 | P2 | 슬롯예약·통행증 QR·중량물 우선·지게차 | PLANNING §M8 | -| 관람 | M10 관람객 등록·배지·리드 | P1 | 사전등록·모바일 배지/QR·현장 체크인·리드캡처·오프라인 폴백 | PLANNING §M10 | -| 관람 | M11 비즈니스 매칭 | P2 | AI 미팅추천·슬롯예약·성과 리포트 | PLANNING §M11 | -| 관람 | M12 마케팅·공개 홍보사이트 | P1 | SEO·다국어 공개사이트·인터랙티브 플로어플랜·세그먼트 EDM | PLANNING §M12 | -| 관람 | M13 wayfinding | P2 | 실내 내비(블루닷/존레벨)·POI 검색·경로 | PLANNING §M13 | -| 현장 | M14 현장운영 | P2 | 혼잡·전력부하·주차·안전위반 플래그 | PLANNING §M14 | -| 경영 | M16 경영분석 BI | P1(승격) | 가동률·매출구성·P&L·RevPAD·리텐션/LTV·수요예측·KPI(운영사+참가사 관점 분리) | PLANNING §M16·§M16-1 | -| 경영 | M17 CMS | P1 | 콘텐츠·공지·마이크로사이트·다국어·게시 워크플로·사이니지 | PLANNING §M17 | -| 관리 | M18 관리자 백오피스 | P1 | RBAC·감사·마스터데이터·룰셋 버전관리 | PLANNING §M18 | -| 공통 | §5B 공통/시스템관리 레이어 | P1(선행기반) | UIWS 표준 이식: 시스템관리·공통 업무모듈·JWT+2FA/OTP | PLANNING §5B | -| 멀티테넌시 | §1A 다중 전시관 SaaS | P1 | tenant_id 격리·컨텍스트 해소·2계층 관리자·온보딩 | PLANNING §1A·§8-2 | - -### 2-2. 비기능 과업 범위 - -| 축 | 범위 | 출처 | -|---|---|---| -| 성능 | RenderJob 평균 40초·1024px 전처리, PostGIS GiST 실시간 응답, 읽기복제 BI 오프로드 | system.md §3-3, PLANNING §6-5 | -| 가용성/HA | 코어 경로 SLO 99.5%(성수기 99.9% 지향), 백엔드 최소 2인스턴스·롤링 무중단, PG 프라이머리-스탠바이+PITR, degraded 모드 | system.md §3-2 | -| 확장성 | 워커 독립 스케일(큐 깊이 기반), 공개영역 CDN, 역할별 프론트 번들 분리 | system.md §3-1 | -| 접근성 | WCAG 2.1 AA(핵심여정 AAA 지향), 공공 KWCAG 2.2 병행 | FEATURE_BACKLOG §6-N1 | -| 다국어 | 한(기본)+영/중/일, i18n 외부화·hreflang·로케일 포맷 | FEATURE_BACKLOG §6-N3 | -| 테마 | CSS 토큰 라이트/다크 2벌·시스템 연동 | FEATURE_BACKLOG §6-N4 | -| 공간데이터 | 부스 폴리곤·트렌치 포인트·배선 LineString 단일 PostGIS 원천 | data.md, PLANNING §7-3 | -| 보안 | §3 참조(REQ-S) | SECURITY.md | - -### 2-3. 범위 제외(Non-Goals) - -- 킨텍스 임대계약 법적 전자계약 체결(Phase 3 이후 검토) — 초기엔 견적·서류 준비까지. (PLANNING §1-4) -- 구조계산서의 구조 안전성 최종 판정(AI는 항목 체크·누락 검출까지, 판정은 구조기술사/킨텍스). (PLANNING §1-4·§M3) -- 소방 법규 최종 유권해석·승인(시스템은 사전 필터). (PLANNING §M2) -- 신규 전시관 고유 도메인 로직 자동생성(온보딩은 마스터데이터 입력 수준). (PLANNING §1-4·§1A-5) - ---- - -## 3. 요구사항 목록 - -> ID 체계: 기능 `REQ-F-###`, 비기능 `REQ-N-###`, 보안 `REQ-S-###`, AI `REQ-A-###`. -> 유형: **필수** / **선택**(P2·확장·조건부). 확인 필요 항목은 "⚠확인필요"로 표기(삭제 금지). - -### 3-1. 기능 요구사항 (REQ-F) - -#### M1 판매·홀배정·견적 -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-001 | 홀/반홀 가용성 실시간 캘린더 조회(행사일정 DB 연동) | 필수 | F001, §M1 | -| REQ-F-002 | 규칙엔진 자동 견적(2,250원/㎡×면적×일수×성수기/비수기/1전시장 계수+초과시간+예치금 15~20%) | 필수 | F002, §M1 | -| REQ-F-003 | 배정신청 웹폼 → 킨텍스 제출 서식(HWP) 자동 생성 | 필수 | F004, §M1 | -| REQ-F-004 | 부스 판매 인벤토리 관리(부스 단위 판매/홀드/예약 상태) | 필수 | F005 | -| REQ-F-005 | 온라인 부스 셀프 선택·판매(인터랙티브 맵) | 필수 | F006 | -| REQ-F-006 | 임대계약 전자서명 워크플로 | 선택 | F007(Phase3, Non-Goal 완화) | - -#### M2 플로어플랜 스튜디오 (P0) -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-007 | 홀 선택·조건 입력 후 부스 배치 정확히 3안 자동생성(제약 솔버, 상이한 최적화 목표) | 필수 | F008, §M2 | -| REQ-F-008 | 배치안 선택 또는 구역/블록 병합 편집(안별 레이어 토글·드래그) | 필수 | F009, §M2 | -| REQ-F-009 | 배치 규정 자동검증(피난통로·바닥하중 홀별·비상구·복층가능홀·소방 체크리스트), 병합 후 재검증 | 필수 | F010, §M2 | -| REQ-F-010 | 최종안 버전 기록(선택/병합 출처 안번호 추적)·부스별 좌표/번호 확정·참가업체 초대 링크 발급 | 필수 | §M2 | - -#### M3 부스 설계 스튜디오 (P0) -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-011 | 조립부스 옵션(간판·가구·조명) 웹 선택·3D 프리뷰 | 필수 | F012, §M3 | -| REQ-F-012 | 독립부스 레이아웃·파라메트릭 구조 3안 자동생성·선택/병합·버전 기록 | 필수 | F011, §M3 | -| REQ-F-013 | 설계 규정 사전검증(높이 5m·리깅 6.5~8.5m·복층 1/2·방염·이격 30/60cm·금지작업) 제출 전 플래깅 | 필수 | F013, §M3 | -| REQ-F-014 | 도면(PDF/이미지) 업로드 비전 추출·치수/구조 검증("참고용 검증" 라벨) | 필수 | F014, §M3 | - -#### M4 유틸리티 설계 (P0) -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-015 | 기기목록→kW 합산→분전반 용량/수량 자동 산출 | 필수 | F015, §M4a | -| REQ-F-016 | 최근접 트렌치→분전반 배선 경로 자동생성(통로 횡단 최소화, PostGIS 최단) | 필수 | F016, §M4 | -| REQ-F-017 | 유틸리티 위치표시도 자동생성(좌표 클릭, 수기 작도 폐지) | 필수 | F017, §M4b | -| REQ-F-018 | 네트워크·급배수·압축공기 신청·자동견적·마감(D-25) 역산 리마인더 | 필수 | F038, §M4b | -| REQ-F-019 | 조명 조도목표별 배치안 제안(전시품 강조/상담존) | 선택 | F018, §M4a | - -#### M6 서류·마일스톤 -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-020 | D-150/30/25/7 마일스톤 자동생성·역산 알림 | 필수 | F021, §M6 | -| REQ-F-021 | 신고서류 7종+ 웹폼→HWP/PDF 자동생성(킨텍스 제출 형식) | 필수 | F022, §M6 | -| REQ-F-022 | kxwp 제출 파일 릴레이·업로드 안내(초기 수동 릴레이, 목표 API) ⚠확인필요(폐쇄형 API 미공개) | 필수 | F027, §M6·R3 | -| REQ-F-023 | 서류 버전·다단계 전자결재 워크플로 | 필수 | F028, §5B | -| REQ-F-024 | OCR 계약·납품서 자동 등록 | 필수 | F026 | - -#### M15 공사/장치 옥션 (P1 핵심) -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-025 | 역경매 옥션 개설·라운드·마감(유형 설정형: 역경매/단일라운드 RFQ/고정가 비교) | 필수 | F029, §M15 | -| REQ-F-026 | AI 생성 자료(M2배치·M3설계·M4배선/BOQ·M5이미지) 열람 기반 견적서(Quotation) 제출·PDF·버전 | 필수 | F030, §M15 | -| REQ-F-027 | 실시간 순위·최저가·내 위치 노출(익명 옵션)·라운드 종료 자동마감 | 필수 | F031, §M15 | -| REQ-F-028 | 종합평가 낙찰 스코어(가격+평판+납기 가중)·항목별 비교표 | 필수 | F032, §M15 | -| REQ-F-029 | 등록업체 검증 게이트 — 미등록 업체 응찰 원천 차단 | 필수 | F033, §M15·§M7 | -| REQ-F-030 | 등록업체 AI 매칭 추천(규모·업종·지역) | 필수 | F034, §M7 | -| REQ-F-031 | 물량서(BOQ) 설계기반 자동산출 | 필수 | F035, §M4·§M15 | -| REQ-F-032 | 낙찰(Award)→계약/발주 문서 자동전환(M6·M9 연동) | 필수 | F036, §M15 | - -#### M8 물류 -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-033 | 반입/반출 하역장 슬롯 예약제 | 선택 | F039, §M8 | -| REQ-F-034 | 통행증 QR 발급·중량물 우선배치 | 선택 | F040, §M8 | -| REQ-F-035 | 지게차·부대장비 신청 연동, 부대물품 렌탈 주문 | 선택 | F041·F042, §M8 | -| REQ-F-036 | 철거 피크 대기열 시뮬레이션 | 선택 | F044, §M8 | - -#### M10 관람객 등록·배지·리드 -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-037 | 온라인 사전등록(유형별 폼·중복 검증) | 필수 | F045, §M10 | -| REQ-F-038 | 모바일 배지/QR 발급 | 필수 | F047, §M10 | -| REQ-F-039 | 현장 QR 체크인·즉석 배지 인쇄·실시간 입장 집계 | 필수 | F048, §M10 | -| REQ-F-040 | 오프라인 체크인 폴백(현장 네트워크 장애 대비 후동기화) | 필수 | F049, §M10, network.md | -| REQ-F-041 | 참가업체 리드캡처(배지 QR 스캔·관심도·메모) | 필수 | F050, §M10 | -| REQ-F-042 | AI 리드 스코어링·자동 분류 | 선택 | F051 | -| REQ-F-043 | 리드 팔로업 세그먼트 EDM 자동 | 필수 | F052, §M10·§M12 | -| REQ-F-044 | 티켓 발권·유료 등록 결제, AI 폼빌더, 스마트배지 | 선택 | F053·F046·F054 | - -#### M11 비즈매칭·이벤트앱 -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-045 | AI 비즈매칭 추천(프로필·intent) | 선택 | F055, §M11 | -| REQ-F-046 | 미팅 슬롯 예약·일정관리·성과 리포트 | 선택 | F056·F062, §M11 | -| REQ-F-047 | 이벤트 모바일앱(일정·부스·프로필), 세션·아젠다, 인앱 채팅, 실시간 설문 | 선택 | F057~F061 | - -#### M12 마케팅·공개 홍보사이트 -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-048 | 공개 홍보 사이트(SEO·다국어 한/영/중/일·SSR/정적) | 필수 | F063, §M12 | -| REQ-F-049 | 공개 인터랙티브 플로어플랜(M2 데이터 부산물) | 필수 | F064, §M12 | -| REQ-F-050 | 세그먼트 EDM·캠페인 자동화·리마인더 | 필수 | F065, §M12 | -| REQ-F-051 | 스폰서십 패키지·판매·이행 관리 + 스폰서 대시보드 | 필수 | F070·F071 | - -#### M13·M14 현장/wayfinding -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-052 | 실내 wayfinding(블루닷/존레벨 폴백)·POI 검색·경로안내 | 선택 | F072·F073, §M13 | -| REQ-F-053 | 실시간 입장·혼잡 모니터·오버플로 예측 | 선택 | F074, §M14 | -| REQ-F-054 | 홀 전력부하 집계·에너지 모니터, 주차 점유 연동 | 선택 | F075·F077, §M14 | -| REQ-F-055 | 안전 위반·소음·금지작업 자동 플래그·신고, 현장 이상탐지 알림 | 선택 | F076·F079, §M14 | - -#### M9 정산·결제 -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-056 | 납부 스케줄(계약금/중도금/잔금/예치금) 자동생성·알림 | 필수 | F080, §M9 | -| REQ-F-057 | 유틸리티·부대 PG 결제 | 필수 | F081, §M9 | -| REQ-F-058 | 예치금 대비 실사용(검침) 정산 투명화 | 필수 | F082, §M9 | -| REQ-F-059 | 세금계산서·정산 리포트 자동, 옥션 수수료 정산 연동 | 필수 | F083·F084, §M9 | -| REQ-F-060 | 환불·취소 규정 자동 적용 | 선택 | F085 | - -#### M16 경영분석 BI -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-061 | 홀·기간별 가동률(occupancy) 대시보드(반홀 분할·성수기 구분) | 필수 | F086, §M16-1① | -| REQ-F-062 | 매출 구성(임대·유틸리티·부대·옥션 수수료)·행사별 P&L | 필수 | F087, §M16-1②③ | -| REQ-F-063 | 전시장 ROI·RevPAD·㎡당 수익(㎡ 정규화) | 필수 | F088, §M16-1④ | -| REQ-F-064 | 참가사 리텐션·LTV 코호트 | 필수 | F089, §M16-1⑤ | -| REQ-F-065 | 참가업체 관점 ROI(리드 기반) — 운영사 관점과 대시보드/권한 격리 | 필수 | F091, §M16-1 | -| REQ-F-066 | 경영진 KPI 대시보드(목표 대비 실적·드릴다운) | 필수 | F093, §M16-1⑦ | - -#### M17 CMS -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-067 | 전시 콘텐츠·공지 게시 워크플로(초안→검수→게시)·버전관리 | 필수 | F068, §M17 | -| REQ-F-068 | 참가업체 마이크로사이트(부스·제품·나노바나나 예상샷) | 필수 | F067, §M17 | -| REQ-F-069 | 다국어(한/영/중/일) 콘텐츠 관리·hreflang | 필수 | F069, §M17 | -| REQ-F-070 | 디지털 사이니지 콘텐츠 중앙 배포 ⚠확인필요(사이니지 HW 연동 킨텍스 협의) | 선택 | F078, §M17 | - -#### M18·§5B·§1A 관리·공통·멀티테넌시 -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-F-071 | RBAC·감사로그·시스템설정 백오피스(admin. 웹 전용) | 필수 | F094, §M18·§5B-1 | -| REQ-F-072 | 룰셋·마스터데이터(홀·요율·규정·부스표준) 버전관리·무중단 개정 | 필수 | F095, §M18 | -| REQ-F-073 | 공통코드·메뉴 관리(도메인 코드: 홀/부스유형/공종14/유틸요금) | 필수 | §5B-1 | -| REQ-F-074 | 공통 업무모듈: 업무일지·일정·쪽지·공지·의견·통합검색 | 필수 | F098, §5B-2 | -| REQ-F-075 | 회의록(녹음→STT→회의록 PDF), 일/주/월/분기/연 업무보고·통계 PDF | 필수 | §5B-2 | -| REQ-F-076 | 통합 알림센터(마감 D-데이·낙찰·승인·결제, WebSocket) | 필수 | §5B-2 | -| REQ-F-077 | 멀티테넌트 격리·전시관 온보딩(코드 배포 없이 데이터 온보딩 6단계) | 필수 | F096, §1A·§8-2 | -| REQ-F-078 | 테넌트 컨텍스트 해소(서브도메인 우선+사용자소속 보조, 불일치 거부) | 필수 | §1A-3 | -| REQ-F-079 | 2계층 관리자(플랫폼 슈퍼관리자 테넌트스위처 / 테넌트 관리자 단일스코프) | 필수 | §1A-4 | -| REQ-F-080 | API·웹훅·CRM/ERP 연동 게이트웨이 | 선택 | F099 | -| REQ-F-081 | 역할별 프론트 포털 분리(organizer·exhibitor·contractor·ops·admin·public+visitor) 공유 디자인시스템 상속 | 필수 | §2-1, §8-1 | - -### 3-2. AI 요구사항 (REQ-A) - -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-A-001 | 나노바나나 시공 예상 사진 표준 샷세트(S1 정면·S2 야간·S3 통로·S4 내부·S5 Before/After·S7 홀전경) 생성 | 필수 | F019, §6-3 | -| REQ-A-002 | image-to-image 구조보존(참조이미지 inlineData+지시문) — 골격/치수/통로/카메라 앵글 잠금, 스타일만 사실화 | 필수 | §6-4 | -| REQ-A-003 | 구조화 프롬프트 사전(BOOTH_TYPES·BOOTH_STYLES·FIXTURE_LAYERS) 조립·보존/교체 명시 분리 | 필수 | §6-4 | -| REQ-A-004 | S6 배선 오버레이 백엔드 래스터 합성 우선(전기 적·네트워크 청·급배수 녹, 좌표 정확) | 필수 | §6-3 | -| REQ-A-005 | Before/After 드래그 슬라이더(빈 부스↔시공 후, ARIA·키보드) | 필수 | F020, §6-5 | -| REQ-A-006 | RenderJob 방어(다층 크기가드·mimeType 자동감지·SAFETY 분기·친화적 한글 에러·성공 시에만 쿼터 차감) | 필수 | §6-5 | -| REQ-A-007 | 단계별 로딩 UX(골격인식→집기→배선→렌더) aria-live 순환·비동기 큐·WebSocket 완료 푸시 | 필수 | §6-5, §8 | -| REQ-A-008 | 간판 텍스트 비전 오탈자 검수 + 한글 렌더 실패 시 간판영역 후처리 합성 | 필수 | §6-4, R5 | -| REQ-A-009 | 부스 배치 3안 AI 자동생성(제약 솔버 + LLM 조건해석, 결정적 알고리즘 중심) | 필수 | F008, §M2 | -| REQ-A-010 | AI 규정 자동검증(배치·설계 규정 위반 플래깅) | 필수 | F010·F013 | -| REQ-A-011 | AI 서류 검수(누락·배치도-계획서 불일치 검출) | 필수 | F023, §M6 | -| REQ-A-012 | 규정 자연어 챗봇(근거·인용 기반) | 선택 | F025 | -| REQ-A-013 | AI 수요예측·수율/동적가격 시뮬레이션 | 필수 | F090·F003, §M16-1⑥ | -| REQ-A-014 | AI 경영진 KPI 요약·인사이트 브리핑 자동, 자연어 조회(Text-to-SQL) | 필수/선택 | F093/F092 | -| REQ-A-015 | AI 카피·이미지 초안 생성, 다국어 콘텐츠 자동번역+검수 | 필수 | F066·F069, §M12·§M17 | -| REQ-A-016 | AI 플랫폼 라우터(Claude 기본→Ollama 폴백)·중앙경유·LLM 직접호출 신설 금지 | 필수 | F100, §6, FEATURE_BACKLOG §0 | -| REQ-A-017 | AI 답변 근거(RAG)·인용·환각 차단(abstain) | 필수 | FEATURE_BACKLOG §0 | -| REQ-A-018 | AI 리드 스코어링, 비즈매칭 추천, 세션/부스 추천 | 선택 | F051·F055·F059 | -| REQ-A-019 | 현장 이상탐지·혼잡/철거 대기열 예측 | 선택 | F074·F044·F079 | -| REQ-A-020 | 도면 비전 추출(치수·구조), OCR 문서 자동등록 | 필수 | F014·F026 | - -### 3-3. 비기능 요구사항 (REQ-N) - -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-N-001 | 코어 인증·설계·조회 경로 가용성 SLO 99.5%(성수기 99.9% 지향) | 필수 | system.md §3-2 | -| REQ-N-002 | 백엔드 최소 2 인스턴스+헬스체크 LB·롤링 무중단 배포 | 필수 | system.md §3-2 | -| REQ-N-003 | PostgreSQL 프라이머리-스탠바이 스트리밍 복제+자동 failover+PITR 백업 | 필수 | system.md §3-2 | -| REQ-N-004 | Fail-Safe 배포(백업→배포→헬스체크 200→실패 시 롤백) | 필수 | system.md §3-2 | -| REQ-N-005 | degraded 모드(G1 미승인/Gemini 장애 시 워커 목 응답·구조·워터마크 유지, Claude 실패 시 Ollama 폴백) | 필수 | system.md §3-2 | -| REQ-N-006 | 나노바나나 생성 성능: 평균 40초 목표·1024px 다운스케일+JPEG 0.85 전처리 | 필수 | system.md §3-3, §6-5 | -| REQ-N-007 | 대량 동시 생성 제어: 자동생성 S1·S7 한정·동일 스키마해시 캐시·행사별 RenderJob 쿼터 | 필수 | system.md §3-3, R6 | -| REQ-N-008 | 공간쿼리 성능: 전 geom 컬럼 GiST 인덱스(트렌치 KNN·통로버퍼·비상구 교차 실시간) | 필수 | data.md §3-4 | -| REQ-N-009 | 워커 독립 스케일(큐 깊이 기반 증감), 백엔드 응답성과 분리 | 필수 | system.md §3-1 | -| REQ-N-010 | BI·공개조회·플로어플랜 열람 읽기복제 오프로드(운영 프라이머리 보호) | 필수 | system.md §3-1, §8-1 | -| REQ-N-011 | 워커 장애 시 가시성 타임아웃 후 재큐(멱등 Job) | 필수 | system.md §3-2 | -| REQ-N-012 | 웹접근성 WCAG 2.1 AA(핵심여정 AAA 지향)·공공 KWCAG 2.2 병행 | 필수 | FEATURE_BACKLOG §6-N1 | -| REQ-N-013 | 캔버스/맵 비시각 대안(데이터 테이블·검색·키보드 선택) 필수 제공 | 필수 | FEATURE_BACKLOG §6-N1 | -| REQ-N-014 | 다국어 i18n 외부화(한 기본+영/중/일)·로케일 전환·hreflang·로케일 포맷 | 필수 | FEATURE_BACKLOG §6-N3 | -| REQ-N-015 | 라이트/다크 테마 CSS 토큰 2벌·prefers-color-scheme 연동·선택 영속 | 필수 | FEATURE_BACKLOG §6-N4 | -| REQ-N-016 | 반응형(데스크톱=설계/에디터, 모바일=현장/조회/승인) | 필수 | PLANNING §8, §2-1 | -| REQ-N-017 | 단일 공간 원천(M2폴리곤·M4배선 매퍼 권위, 소비 모듈 조회만·중복 저장 금지) | 필수 | app.md, data.md | -| REQ-N-018 | 관측성(로그·메트릭·트레이스) 관리존 수집, 로그에 자격증명·PII·스택트레이스 미기록 | 필수 | network.md, tech.md | -| REQ-N-019 | 공개영역 CDN·요청률 상한·slowloris 타임아웃(가용성 계층 방어) | 필수 | network.md §7 | -| REQ-N-020 | tenant_id 선두 복합 인덱스 표준화(격리 쿼리 성능) | 필수 | §8-2 | -| REQ-N-021 | BI 데이터마트(스타 스키마 Fact/Dim) 야간 배치 ETL 또는 읽기복제 적재 | 필수 | data.md, §M16-1 | - -### 3-4. 보안 요구사항 (REQ-S) - -| REQ-ID | 요구사항 | 유형 | 출처 | -|---|---|---|---| -| REQ-S-001 | 외부 API 정책: 온프레미스 Ollama 기본, Claude(api.anthropic.com)·Gemini(나노바나나)만 승인 예외, 그 외 금지 | 필수 | SECURITY.md §1 | -| REQ-S-002 | GEMINI_API_KEY 나노바나나 Python 워커 env only(백엔드 미취급), Spring은 큐/콜백만 | 필수 | SECURITY.md §1 | -| REQ-S-003 | 자격증명·키·내부IP·SSH·워커토큰 응답/로그/에러/커밋 완전 제외·마스킹 | 필수 | SECURITY.md §2 | -| REQ-S-004 | admin 비밀번호 env `ADMIN_PASSWORD_ENC`(AES-256-GCM)+별도 키파일→기동 시 BCrypt 재시드, 하드코딩 시드 금지 | 필수 | SECURITY.md §3 | -| REQ-S-005 | JWT(HS256)+2차 인증 OTP/EMAIL(TOTP RFC6238)+로그인 실패 잠금(관리자 해제) | 필수 | SECURITY.md §4, §5B-3 | -| REQ-S-006 | 행사 단위 RBAC(열람=멤버/홀매니저, 편집=역할별 게이트) — 테넌트 격리 ⊃ 행사 RBAC 3중 | 필수 | SECURITY.md §4, §2 | -| REQ-S-007 | 미등록 장치업체 초대·응찰 차단(NOT_REGISTERED_COMPANY 403) | 필수 | SECURITY.md §4, §M15 | -| REQ-S-008 | `/api/admin/**`=ADMIN 게이트, `/api/internal/**`=워커 공유 시크릿(X-Worker-Token)만 | 필수 | SECURITY.md §4 | -| REQ-S-009 | M5 나노바나나 이미지 응답 항상 watermarkRequired:true+watermarkText+notice(계약·심사 서류 금지) | 필수 | SECURITY.md §5, §6-6 | -| REQ-S-010 | 오류 응답 스택트레이스·relation/컬럼명·내부경로 미노출(ApiResponse.error 요약만) | 필수 | SECURITY.md §6 | -| REQ-S-011 | DataAccessException @RestControllerAdvice로 INTERNAL 요약 매핑(테이블/컬럼 누출 차단) | 필수 | SECURITY.md §6, system.md | -| REQ-S-012 | 민감 액션(승인·낙찰·설계변경·룰셋개정·리드접근·로그인/권한변경) TB_AUDIT_LOG 전수(tenant_id 포함) | 필수 | SECURITY.md §7, §6-N2 | -| REQ-S-013 | PII(관람객·리드) 동의·보존정책·최소수집·마스킹·응답 완전 제외, `*_enc` AES-256-GCM | 필수 | data.md §5, R10 | -| REQ-S-014 | 입력 검증(Bean Validation 화이트리스트·파일 MIME/크기 가드) | 필수 | FEATURE_BACKLOG §6-N2 | -| REQ-S-015 | XSS 방지(React 이스케이프·dangerouslySetInnerHTML 금지·CMS sanitize·CSP) | 필수 | FEATURE_BACKLOG §6-N2 | -| REQ-S-016 | 인젝션 차단(MyBatis `#{}` 강제·`${}` 금지, PostGIS/명령/LDAP) | 필수 | FEATURE_BACKLOG §6-N2 | -| REQ-S-017 | IDOR 방지(리소스 소유·tenant_id 검증), CSRF/SameSite·JWT 만료·회전 | 필수 | FEATURE_BACKLOG §6-N2 | -| REQ-S-018 | fail-closed 다층 테넌트 격리(컨텍스트 필터→서비스 가드→MyBatis 공통 인터셉터→PostGIS) | 필수 | §8-2 | -| REQ-S-019 | 의존성 취약점 SCA(OWASP Dependency-Check) CI 게이트·정기 패치 | 필수 | FEATURE_BACKLOG §6-N2 | -| REQ-S-020 | 보안영역 분리(공개 DMZ 쓰기권한 없음·데이터존 직결 불가, DMZ↔내부 HTTPS만·DB 프로토콜 횡단 금지) | 필수 | network.md | - ---- - -## 4. 평가 관점 (공공 SI 표준 배점 — 가정) - -> 실제 RFP·평가표가 없어 공공 정보화 SI 통례를 가정한다. **[가정]** — 실제 공고 확인 시 교체. - -| 구분 | 평가부문 | 배점(가정) | win theme 우선순위 근거 | -|---|---|---|---| -| 기술 | 사업이해·추진전략 | 10 | 전시 도메인·As-Is 병목 이해(§3) | -| 기술 | **AI 시각화·설계 자동화(핵심 차별화)** | **20** | M2~M5 P0 코어 + 나노바나나 시공 예측(경쟁 전시테크 미보유 폐루프) | -| 기술 | 기능 구현(M1~M18 전 모듈) | 15 | 100대 기능(이미 63·부분 18·신규 19) | -| 기술 | 아키텍처·기술스택 | 10 | 확정 스택 정합·PostGIS 공간데이터·Python 워커 사이드카 | -| 기술 | 보안·개인정보(SECURITY 불변) | 10 | REQ-S 20항목·워터마크·PII·테넌트 격리 | -| 기술 | 비기능(성능·가용성·접근성·다국어) | 8 | SLO 99.5%·WCAG AA·KWCAG·i18n | -| 기술 | 멀티테넌시·확장성(COEX 온보딩) | 5 | §1A SaaS 확장 | -| 기술 | 사업관리·품질·일정 | 2 | Phase 1~3·QA 게이트 | -| **기술 소계** | | **80** | | -| 가격 | 입찰가격 | 20 | | -| **총계** | | **100** | | - -**win theme 우선순위(배점 가중)**: ① 나노바나나 시공 예측 시각화(최고 차별화, 20점) → ② 부스 배치·설계·배선 AI 자동화 폐루프(M2~M5) → ③ 공사 옥션(M15) 발주 종결 → ④ 보안·개인정보·워터마크(공공 신뢰) → ⑤ 멀티테넌트 SaaS 확장. - ---- - -## 5. 제출 규격 (가정) - -> 실제 RFP 규격이 없어 공공 SI 제안 통례를 가정한다. **[가정]** — 실제 공고 확인 시 교체. - -| 항목 | 가정값 | 비고 | -|---|---|---| -| 제안서 형식 | PowerPoint(.pptx) 기술제안서 | proposal-builder 파이프라인 정합 | -| 분량 | 자율(권장 40~80p) | 실제 공고 확인 필요 | -| 목차 | 사업이해→추진전략→기술제안(AI 시각화·설계·옥션·관람·BI)→아키텍처→보안→비기능→멀티테넌시→사업관리→기대효과 | §4 배점 정합 | -| 서식 | 발주처 지정 시 준수, 미지정 시 표준 제안 템플릿 | ⚠확인필요 | -| 필수 도식 | 모듈맵(§4)·아키텍처(§8)·나노바나나 파이프라인(§6)·옥션 플로우(§M15)·추진체계·일정(Phase 1~3) | PLANNING 근거 | -| 제출 부수·마감 | ⚠확인필요(실제 공고) | — | -| 자격요건 | ⚠확인필요(실적·인증 — CSAP/ISMS 등 공공 요건 가능성) | — | - ---- - -## 6. 확인 필요·상충 항목 (삭제 금지) - -| # | 항목 | 사유 | -|---|---|---| -| C1 | 예산·계약방식·사업기간·평가배점·제출규격 | 실제 RFP 부재 — 전부 가정, 공고 확인 시 교체 | -| C2 | kxwp/kxfp API 연동 | 폐쇄형·API 미공개, 킨텍스 IT 협의 필수(R3) — 초기 수동 릴레이 | -| C3 | 트렌치·CAD 실측 데이터 | 미확보, 공개 스펙 기반 가정 그리드+'가정' 라벨(R4) | -| C4 | 인터넷 요금 정합성(150,000원 vs KT 80,000원 병존) | 킨텍스 확인 필요(R8) | -| C5 | 나노바나나 Gemini 외부 API 승인(G1) | 미승인 시 온프레미스 폴백 검토(R12) | -| C6 | 코엑스 등 신규 전시관 홀·요율·규정 실측 | 미확보, 온보딩 시 입력·근거 없는 추정 금지(§1A-5) | -| C7 | 사이니지 HW·비콘(wayfinding)·IoT 센서 연동 | 킨텍스 시설 인프라 협의 전제(M13·M14·M17) | -| C8 | 자격요건(공공 인증 CSAP/ISMS 등) | 실제 공고 확인 필요 | - ---- - -## 부록. REQ 총계 - -| 유형 | 개수 | -|---|---| -| REQ-F (기능) | 81 | -| REQ-A (AI) | 20 | -| REQ-N (비기능) | 21 | -| REQ-S (보안) | 20 | -| **총계** | **142** | - diff --git a/docs/deliverables/제안서/_gen/rtm.md b/docs/deliverables/제안서/_gen/rtm.md deleted file mode 100644 index b48f731..0000000 --- a/docs/deliverables/제안서/_gen/rtm.md +++ /dev/null @@ -1,196 +0,0 @@ -# 요구사항 추적표 (RTM) — 킨텍스 자동전시시스템 제안서 - -> 작성: rfp-analyst · 작성일: 2026-07-11 · 버전 v1.0 -> 짝 문서: `rfp_analysis.md`(요구사항 정의). 본 RTM은 **REQ-ID ↔ 제안서 목차 섹션 1:1 매핑 준비 + 평가부문 매핑 + 반영 상태**의 단일 진실원천이다. -> 사용법: writer/deck-designer가 각 REQ를 제안서 본문/슬라이드에 반영하며 "제안서 섹션" 및 "반영상태(☐미반영/◑작성중/☑완료)"를 갱신한다. QA는 전 REQ 반영 여부를 이 표로 교차 점검한다. -> 제안서 목차 코드: **P1**사업이해 · **P2**추진전략 · **P3**기술제안(AI 시각화·설계·옥션·관람·BI) · **P4**아키텍처 · **P5**보안·개인정보 · **P6**비기능 · **P7**멀티테넌시·확장 · **P8**사업관리·일정·조직 · **P9**기대효과. (평가부문은 rfp_analysis §4 배점 기준) - ---- - -## 1. 기능 요구사항 (REQ-F) - -| REQ-ID | 요구사항 요약 | 유형 | 모듈 | 제안서 섹션(준비) | 평가부문 | 반영상태 | -|---|---|---|---|---|---|---| -| REQ-F-001 | 홀/반홀 가용성 실시간 캘린더 | 필수 | M1 | P3 판매·견적 | 기능 구현 | ☐ | -| REQ-F-002 | 규칙엔진 자동 견적 | 필수 | M1 | P3 판매·견적 | 기능 구현 | ☐ | -| REQ-F-003 | 배정신청 웹폼→HWP 자동생성 | 필수 | M1 | P3 판매·견적 | 기능 구현 | ☐ | -| REQ-F-004 | 부스 판매 인벤토리 관리 | 필수 | M1/M2 | P3 판매·견적 | 기능 구현 | ☐ | -| REQ-F-005 | 온라인 부스 셀프 선택·판매 | 필수 | M2 | P3 판매·견적 | 기능 구현 | ☐ | -| REQ-F-006 | 임대계약 전자서명 | 선택 | M6/M9 | P3 판매·견적 | 기능 구현 | ☐ | -| REQ-F-007 | 부스 배치 3안 자동생성 | 필수 | M2 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-008 | 배치안 선택/병합 편집 | 필수 | M2 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-009 | 배치 규정 자동검증·병합 후 재검증 | 필수 | M2 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-010 | 최종안 버전기록·부스좌표 확정·초대링크 | 필수 | M2 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-011 | 조립부스 옵션·3D 프리뷰 | 필수 | M3 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-012 | 독립부스 설계 3안·선택/병합·버전 | 필수 | M3 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-013 | 설계 규정 사전검증(높이·리깅·방염·이격) | 필수 | M3 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-014 | 도면 업로드 비전 추출·검증 | 필수 | M3 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-015 | 전기 용량·분전반 자동산출 | 필수 | M4a | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-016 | 배선 경로 자동생성(PostGIS 최단) | 필수 | M4 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-017 | 위치표시도 자동생성 | 필수 | M4b | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-F-018 | 네트워크·급배수·압축공기 신청·마감 리마인더 | 필수 | M4b | P3 AI 시각화·설계 | 기능 구현 | ☐ | -| REQ-F-019 | 조명 조도 배치안 제안 | 선택 | M4a | P3 AI 시각화·설계 | AI 시각화·설계 | ☐ | -| REQ-F-020 | 마일스톤 자동생성·역산 알림 | 필수 | M6 | P3 서류·워크플로 | 기능 구현 | ☐ | -| REQ-F-021 | 신고서류 웹폼→HWP/PDF 자동생성 | 필수 | M6 | P3 서류·워크플로 | 기능 구현 | ☐ | -| REQ-F-022 | kxwp 제출 파일 릴레이 ⚠확인필요 | 필수 | M6 | P3 서류·워크플로 | 기능 구현 | ☐ | -| REQ-F-023 | 서류 버전·전자결재 워크플로 | 필수 | M6/§5B | P3 서류·워크플로 | 기능 구현 | ☐ | -| REQ-F-024 | OCR 계약·납품서 자동등록 | 필수 | AI | P3 서류·워크플로 | 기능 구현 | ☐ | -| REQ-F-025 | 역경매 옥션 개설·라운드·마감 | 필수 | M15 | P3 공사 옥션 | AI 시각화·설계(폐루프) | ☐ | -| REQ-F-026 | AI 자료 기반 견적서 응찰·PDF·버전 | 필수 | M15 | P3 공사 옥션 | AI 시각화·설계(폐루프) | ☐ | -| REQ-F-027 | 실시간 순위·자동마감 | 필수 | M15 | P3 공사 옥션 | 기능 구현 | ☐ | -| REQ-F-028 | 종합평가 낙찰 스코어·비교표 | 필수 | M15 | P3 공사 옥션 | 기능 구현 | ☐ | -| REQ-F-029 | 등록업체 검증 게이트(응찰 차단) | 필수 | M15/M7 | P3 공사 옥션 / P5 보안 | 보안·개인정보 | ☐ | -| REQ-F-030 | 등록업체 AI 매칭 추천 | 필수 | M7 | P3 공사 옥션 | 기능 구현 | ☐ | -| REQ-F-031 | 물량서(BOQ) 자동산출 | 필수 | M4/M15 | P3 공사 옥션 | AI 시각화·설계 | ☐ | -| REQ-F-032 | 낙찰→계약·발주 자동전환 | 필수 | M15 | P3 공사 옥션 | 기능 구현 | ☐ | -| REQ-F-033 | 반입/반출 슬롯 예약 | 선택 | M8 | P3 참가·물류 | 기능 구현 | ☐ | -| REQ-F-034 | 통행증 QR·중량물 우선 | 선택 | M8 | P3 참가·물류 | 기능 구현 | ☐ | -| REQ-F-035 | 지게차·부대장비·렌탈 주문 | 선택 | M8 | P3 참가·물류 | 기능 구현 | ☐ | -| REQ-F-036 | 철거 대기열 시뮬레이션 | 선택 | M8 | P3 참가·물류 | AI 시각화·설계 | ☐ | -| REQ-F-037 | 온라인 사전등록(유형별·중복검증) | 필수 | M10 | P3 관람 | 기능 구현 | ☐ | -| REQ-F-038 | 모바일 배지/QR 발급 | 필수 | M10 | P3 관람 | 기능 구현 | ☐ | -| REQ-F-039 | 현장 QR 체크인·즉석 인쇄·집계 | 필수 | M10 | P3 관람 | 기능 구현 | ☐ | -| REQ-F-040 | 오프라인 체크인 폴백 | 필수 | M10 | P3 관람 / P6 비기능 | 비기능 | ☐ | -| REQ-F-041 | 참가업체 리드캡처 | 필수 | M10 | P3 관람 | 기능 구현 | ☐ | -| REQ-F-042 | AI 리드 스코어링·분류 | 선택 | AI | P3 관람 | AI 시각화·설계 | ☐ | -| REQ-F-043 | 리드 팔로업 세그먼트 EDM | 필수 | M10/M12 | P3 관람 | 기능 구현 | ☐ | -| REQ-F-044 | 티켓발권·AI폼빌더·스마트배지 | 선택 | M10 | P3 관람 | 기능 구현 | ☐ | -| REQ-F-045 | AI 비즈매칭 추천 | 선택 | M11 | P3 관람 | AI 시각화·설계 | ☐ | -| REQ-F-046 | 미팅 슬롯·성과 리포트 | 선택 | M11 | P3 관람 | 기능 구현 | ☐ | -| REQ-F-047 | 이벤트 모바일앱·세션·채팅·설문 | 선택 | M11 | P3 관람 | 기능 구현 | ☐ | -| REQ-F-048 | 공개 홍보 사이트(SEO·다국어) | 필수 | M12 | P3 마케팅·공개 | 기능 구현 | ☐ | -| REQ-F-049 | 공개 인터랙티브 플로어플랜 | 필수 | M12 | P3 마케팅·공개 | 기능 구현 | ☐ | -| REQ-F-050 | 세그먼트 EDM·캠페인 자동화 | 필수 | M12 | P3 마케팅·공개 | 기능 구현 | ☐ | -| REQ-F-051 | 스폰서십 패키지·대시보드 | 필수 | M12/M16 | P3 마케팅·공개 | 기능 구현 | ☐ | -| REQ-F-052 | 실내 wayfinding·POI·경로 | 선택 | M13 | P3 현장운영 | 기능 구현 | ☐ | -| REQ-F-053 | 실시간 혼잡 모니터·예측 | 선택 | M14 | P3 현장운영 | AI 시각화·설계 | ☐ | -| REQ-F-054 | 홀 전력부하·주차 점유 | 선택 | M14 | P3 현장운영 | 기능 구현 | ☐ | -| REQ-F-055 | 안전 위반·이상탐지 알림 | 선택 | M14 | P3 현장운영 | AI 시각화·설계 | ☐ | -| REQ-F-056 | 납부 스케줄 자동생성·알림 | 필수 | M9 | P3 정산 | 기능 구현 | ☐ | -| REQ-F-057 | 유틸리티·부대 PG 결제 | 필수 | M9 | P3 정산 | 기능 구현 | ☐ | -| REQ-F-058 | 예치금 실사용 정산 투명화 | 필수 | M9 | P3 정산 | 기능 구현 | ☐ | -| REQ-F-059 | 세금계산서·옥션 수수료 정산 | 필수 | M9 | P3 정산 | 기능 구현 | ☐ | -| REQ-F-060 | 환불·취소 규정 자동 | 선택 | M9 | P3 정산 | 기능 구현 | ☐ | -| REQ-F-061 | 홀·기간별 가동률 대시보드 | 필수 | M16 | P3 경영 BI | 기능 구현 | ☐ | -| REQ-F-062 | 매출 구성·행사별 P&L | 필수 | M16 | P3 경영 BI | 기능 구현 | ☐ | -| REQ-F-063 | 전시장 ROI·RevPAD·㎡당 수익 | 필수 | M16 | P3 경영 BI | 기능 구현 | ☐ | -| REQ-F-064 | 참가사 리텐션·LTV 코호트 | 필수 | M16 | P3 경영 BI | 기능 구현 | ☐ | -| REQ-F-065 | 참가업체 관점 ROI(권한 격리) | 필수 | M16 | P3 경영 BI / P5 보안 | 기능 구현 | ☐ | -| REQ-F-066 | 경영진 KPI 대시보드·드릴다운 | 필수 | M16 | P3 경영 BI | 기능 구현 | ☐ | -| REQ-F-067 | CMS 게시 워크플로·버전 | 필수 | M17 | P3 마케팅·공개 | 기능 구현 | ☐ | -| REQ-F-068 | 참가업체 마이크로사이트 | 필수 | M17 | P3 마케팅·공개 | 기능 구현 | ☐ | -| REQ-F-069 | 다국어 콘텐츠·hreflang | 필수 | M17 | P3 마케팅·공개 / P6 | 비기능 | ☐ | -| REQ-F-070 | 사이니지 콘텐츠 배포 ⚠확인필요 | 선택 | M17 | P3 현장운영 | 기능 구현 | ☐ | -| REQ-F-071 | RBAC·감사·시스템설정 백오피스 | 필수 | M18/§5B | P5 보안 / P8 관리 | 보안·개인정보 | ☐ | -| REQ-F-072 | 룰셋·마스터데이터 버전관리 | 필수 | M18 | P4 아키텍처 / P8 | 아키텍처 | ☐ | -| REQ-F-073 | 공통코드·메뉴 관리 | 필수 | §5B | P8 관리 | 기능 구현 | ☐ | -| REQ-F-074 | 공통 업무모듈(일지·일정·쪽지·공지·검색) | 필수 | §5B | P8 관리 | 기능 구현 | ☐ | -| REQ-F-075 | 회의록(STT)·업무보고 통계 PDF | 필수 | §5B | P8 관리 | 기능 구현 | ☐ | -| REQ-F-076 | 통합 알림센터(WebSocket) | 필수 | §5B | P8 관리 | 기능 구현 | ☐ | -| REQ-F-077 | 멀티테넌트 격리·전시관 온보딩 | 필수 | §1A | P7 멀티테넌시 | 멀티테넌시·확장 | ☐ | -| REQ-F-078 | 테넌트 컨텍스트 해소 | 필수 | §1A | P7 멀티테넌시 | 멀티테넌시·확장 | ☐ | -| REQ-F-079 | 2계층 관리자(플랫폼/테넌트) | 필수 | §1A | P7 멀티테넌시 / P5 | 멀티테넌시·확장 | ☐ | -| REQ-F-080 | API·웹훅·CRM/ERP 연동 게이트웨이 | 선택 | 연동 | P4 아키텍처 | 아키텍처 | ☐ | -| REQ-F-081 | 역할별 프론트 포털 분리 | 필수 | §2-1 | P4 아키텍처 | 아키텍처 | ☐ | - -## 2. AI 요구사항 (REQ-A) - -| REQ-ID | 요구사항 요약 | 유형 | 모듈 | 제안서 섹션(준비) | 평가부문 | 반영상태 | -|---|---|---|---|---|---|---| -| REQ-A-001 | 나노바나나 시공 예상사진 샷세트 S1~S7 | 필수 | M5 | P3 AI 시각화(핵심) | AI 시각화·설계(핵심) | ☐ | -| REQ-A-002 | image-to-image 구조보존(골격/앵글 잠금) | 필수 | M5 | P3 AI 시각화(핵심) | AI 시각화·설계(핵심) | ☐ | -| REQ-A-003 | 구조화 프롬프트 사전·보존/교체 분리 | 필수 | M5 | P3 AI 시각화(핵심) | AI 시각화·설계(핵심) | ☐ | -| REQ-A-004 | S6 배선 오버레이 백엔드 래스터 합성 | 필수 | M5 | P3 AI 시각화(핵심) | AI 시각화·설계(핵심) | ☐ | -| REQ-A-005 | Before/After 드래그 슬라이더 | 필수 | M5 | P3 AI 시각화(핵심) | AI 시각화·설계(핵심) | ☐ | -| REQ-A-006 | RenderJob 방어(크기가드·SAFETY·쿼터) | 필수 | M5 | P3 AI 시각화 / P5 보안 | 보안·개인정보 | ☐ | -| REQ-A-007 | 단계별 로딩·비동기 큐·WebSocket 푸시 | 필수 | M5 | P4 아키텍처 | 아키텍처 | ☐ | -| REQ-A-008 | 간판 텍스트 비전 검수·후처리 합성 | 필수 | M5 | P3 AI 시각화 | AI 시각화·설계 | ☐ | -| REQ-A-009 | 부스 배치 3안 AI(제약 솔버+LLM) | 필수 | M2 | P3 AI 시각화·설계 | AI 시각화·설계(핵심) | ☐ | -| REQ-A-010 | AI 규정 자동검증(배치·설계) | 필수 | M2/M3 | P3 AI 시각화·설계 | AI 시각화·설계 | ☐ | -| REQ-A-011 | AI 서류 검수(누락·불일치) | 필수 | M6 | P3 서류·워크플로 | AI 시각화·설계 | ☐ | -| REQ-A-012 | 규정 자연어 챗봇(근거·인용) | 선택 | AI | P3 서류·워크플로 | AI 시각화·설계 | ☐ | -| REQ-A-013 | AI 수요예측·수율/동적가격 | 필수 | M16 | P3 경영 BI | AI 시각화·설계 | ☐ | -| REQ-A-014 | AI KPI 브리핑·자연어 조회(Text-to-SQL) | 필수/선택 | M16 | P3 경영 BI | AI 시각화·설계 | ☐ | -| REQ-A-015 | AI 카피·이미지 초안·다국어 번역 | 필수 | M12/M17 | P3 마케팅·공개 | AI 시각화·설계 | ☐ | -| REQ-A-016 | AI 플랫폼 라우터(Claude→Ollama 폴백) | 필수 | §6 | P4 아키텍처 / P5 보안 | 아키텍처 | ☐ | -| REQ-A-017 | AI 근거(RAG)·인용·환각 차단 | 필수 | §6 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-A-018 | AI 리드 스코어·매칭·추천 | 선택 | M10/M11 | P3 관람 | AI 시각화·설계 | ☐ | -| REQ-A-019 | 현장 이상탐지·혼잡/철거 예측 | 선택 | M14/M8 | P3 현장운영 | AI 시각화·설계 | ☐ | -| REQ-A-020 | 도면 비전 추출·OCR 문서 등록 | 필수 | M3/AI | P3 AI 시각화·설계 | AI 시각화·설계 | ☐ | - -## 3. 비기능 요구사항 (REQ-N) - -| REQ-ID | 요구사항 요약 | 유형 | 제안서 섹션(준비) | 평가부문 | 반영상태 | -|---|---|---|---|---|---| -| REQ-N-001 | 코어 가용성 SLO 99.5%(성수기 99.9%) | 필수 | P6 비기능 | 비기능 | ☐ | -| REQ-N-002 | 백엔드 2인스턴스·롤링 무중단 | 필수 | P4 아키텍처 / P6 | 비기능 | ☐ | -| REQ-N-003 | PG 스탠바이 복제·failover·PITR | 필수 | P4 아키텍처 / P6 | 비기능 | ☐ | -| REQ-N-004 | Fail-Safe 배포(헬스체크·롤백) | 필수 | P8 사업관리 | 사업관리·품질 | ☐ | -| REQ-N-005 | degraded 모드(목 응답·폴백) | 필수 | P4 아키텍처 / P6 | 비기능 | ☐ | -| REQ-N-006 | 나노바나나 40초·1024px 전처리 | 필수 | P6 비기능 | 비기능 | ☐ | -| REQ-N-007 | 대량 생성 제어(S1·S7·캐시·쿼터) | 필수 | P6 비기능 | 비기능 | ☐ | -| REQ-N-008 | 공간쿼리 GiST 실시간 | 필수 | P4 아키텍처 | 아키텍처 | ☐ | -| REQ-N-009 | 워커 독립 스케일 | 필수 | P4 아키텍처 | 아키텍처 | ☐ | -| REQ-N-010 | BI/공개 읽기복제 오프로드 | 필수 | P4 아키텍처 | 아키텍처 | ☐ | -| REQ-N-011 | 워커 장애 재큐(멱등 Job) | 필수 | P4 아키텍처 / P6 | 비기능 | ☐ | -| REQ-N-012 | WCAG 2.1 AA·KWCAG 2.2 | 필수 | P6 비기능 | 비기능 | ☐ | -| REQ-N-013 | 캔버스/맵 비시각 대안 | 필수 | P6 비기능 | 비기능 | ☐ | -| REQ-N-014 | 다국어 i18n·hreflang·로케일 포맷 | 필수 | P6 비기능 | 비기능 | ☐ | -| REQ-N-015 | 라이트/다크 테마 CSS 토큰 | 필수 | P6 비기능 | 비기능 | ☐ | -| REQ-N-016 | 반응형(데스크톱/모바일) | 필수 | P6 비기능 | 비기능 | ☐ | -| REQ-N-017 | 단일 공간 원천(중복 저장 금지) | 필수 | P4 아키텍처 | 아키텍처 | ☐ | -| REQ-N-018 | 관측성(로그·PII/스택트레이스 미기록) | 필수 | P5 보안 / P6 | 비기능 | ☐ | -| REQ-N-019 | 공개영역 CDN·요청률 상한 | 필수 | P4 아키텍처 / P6 | 비기능 | ☐ | -| REQ-N-020 | tenant_id 선두 복합 인덱스 | 필수 | P7 멀티테넌시 / P4 | 멀티테넌시·확장 | ☐ | -| REQ-N-021 | BI 데이터마트 배치 ETL/읽기복제 | 필수 | P4 아키텍처 | 아키텍처 | ☐ | - -## 4. 보안 요구사항 (REQ-S) - -| REQ-ID | 요구사항 요약 | 유형 | 제안서 섹션(준비) | 평가부문 | 반영상태 | -|---|---|---|---|---|---| -| REQ-S-001 | 외부 API 정책(Ollama 기본·Claude/Gemini 예외) | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-002 | GEMINI_API_KEY 워커 env only | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-003 | 자격증명·키 응답/로그/커밋 완전 제외 | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-004 | admin 비번 env 재시드(AES-256-GCM) | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-005 | JWT+2FA OTP(TOTP)·실패 잠금 | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-006 | 행사 RBAC(테넌트 격리 ⊃ 행사 3중) | 필수 | P5 보안 / P7 | 보안·개인정보 | ☐ | -| REQ-S-007 | 미등록 업체 차단(403) | 필수 | P5 보안 / P3 옥션 | 보안·개인정보 | ☐ | -| REQ-S-008 | admin/internal 엔드포인트 게이트 | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-009 | M5 이미지 워터마크 항상 포함 | 필수 | P5 보안 / P3 AI | 보안·개인정보 | ☐ | -| REQ-S-010 | 오류 응답 스택트레이스 미노출 | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-011 | DataAccessException 요약 매핑 | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-012 | 민감 액션 감사로그 전수(tenant_id) | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-013 | PII 동의·보존·마스킹·AES-256-GCM | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-014 | 입력 검증(Bean Validation·파일 가드) | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-015 | XSS 방지·CSP·CMS sanitize | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-016 | 인젝션 차단(MyBatis #{} 강제) | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-017 | IDOR 방지·CSRF·JWT 회전 | 필수 | P5 보안 | 보안·개인정보 | ☐ | -| REQ-S-018 | fail-closed 다층 테넌트 격리 | 필수 | P7 멀티테넌시 / P5 | 멀티테넌시·확장 | ☐ | -| REQ-S-019 | 의존성 SCA CI 게이트 | 필수 | P5 보안 / P8 | 사업관리·품질 | ☐ | -| REQ-S-020 | 보안영역 분리(DMZ/내부망) | 필수 | P4 아키텍처 / P5 | 보안·개인정보 | ☐ | - ---- - -## 5. 커버리지 요약 - -| 유형 | REQ 수 | 필수 | 선택 | -|---|---|---|---| -| REQ-F (기능) | 81 | 62 | 19 | -| REQ-A (AI) | 20 | 15 | 5 | -| REQ-N (비기능) | 21 | 21 | 0 | -| REQ-S (보안) | 20 | 20 | 0 | -| **총계** | **142** | **118** | **24** | - -**제안서 섹션 커버리지 목표**: P3(기술제안)에 최다 REQ 집중(AI 시각화·설계 win theme), P5(보안)에 REQ-S 20 + 관련 REQ-A/F, P4(아키텍처)에 REQ-N/A 인프라 항목, P7(멀티테넌시)에 §1A 계열. QA는 전 142개 REQ의 반영상태가 ☑ 완료되었는지 본 표로 최종 검증한다. - -## 6. 확인 필요 REQ (제안 전 발주처 협의) - -| REQ-ID | 확인 사항 | rfp_analysis 참조 | -|---|---|---| -| REQ-F-022 | kxwp API 연동(폐쇄형·미공개) | C2 | -| REQ-F-070 | 사이니지 HW 연동 | C7 | -| REQ-F-052/053/054/055 | wayfinding 비콘·IoT 센서 인프라 | C7 | -| REQ-A-001~008 | 나노바나나 Gemini 승인(G1) — 미승인 시 온프레미스 폴백 | C5 | -| REQ-F-002 | 인터넷 요금 정합성·요율 공식본 | C4 | -| REQ-F-077 | 코엑스 등 신규 전시관 실측 데이터 | C6 | - diff --git a/docs/deliverables/제안서/_gen/tech_advisory.md b/docs/deliverables/제안서/_gen/tech_advisory.md deleted file mode 100644 index 2cf7e97..0000000 --- a/docs/deliverables/제안서/_gen/tech_advisory.md +++ /dev/null @@ -1,374 +0,0 @@ -# 킨텍스 자동전시시스템 구축 — 기술·아키텍처 자문서 (Tech Advisory) - -> 작성: proposal-tech-advisor(아키텍처 허브) · 작성일: 2026-07-11 · 버전 v1.0 -> 입력: `rfp_analysis.md`(REQ 142)·`rtm.md`·`proposal_strategy.md`+`proposal_outline.md`(Win Theme 5·필수 도식 12종)·**`docs/architecture/{app,system,tech,data,network}.md`**(AA/SA/TA/DA/NA 확정 아키텍처 5종)·`docs/PLANNING.md` v3.0·`docs/SECURITY.md`·`docs/WORK_STATUS.md`(라이브 실적)·kintex `CLAUDE.md`(스택 확정). -> **성격**: 아키텍처 전문가 풀(AA·SA·TA·DA·NA)이 이미 확정한 5종 아키텍처를 **재발명하지 않고**, 제안서(writer·visual-designer·deck-designer)용으로 **종합·상충 조정·재구성**한 기술 자문. 전문가 풀 산출물이 이미 충분해 추가 소집(kintex-sa/ta/da) 불요 — 공백(SM 운영·SLA·WBS/공수·도식 사양)만 본 문서가 저술. -> **원칙(불변)**: ①과장 금지 — WORK_STATUS 라이브 확인분만 "구축 완료/검증됨", 나머지는 "구현 계획". ②시크릿(키·비번·내부IP) 미기재. ③실현 가능성 우선 — 확정 스택·기존 스캐폴드·이식 자산으로 뒷받침. - ---- - -## 0. 기술 신뢰성 기반 — 라이브 실적 vs 구현 계획 (과장 방지 핵심) - -> 평가위원 최대 관심 = "제안이 말뿐인가, 실제로 되는가". 본 절이 그 답이다. WORK_STATUS(2026-07-11) 라이브 확인분과 구현 계획을 **명확히 구분**한다. 제안서 P8(사업관리)·P4(아키텍처) 신뢰 근거로 인용. - -### 0-1. 구축 완료·라이브 (검증됨 — "이미 돈다") - -| 영역 | 라이브 실적 | 검증 근거 | -|---|---|---| -| **개발/배포 인프라** | `kintex.zioinfo.co.kr` TLS + 백엔드 8021 + PostGIS + Redis + 나노바나나 워커 systemd 상주 | WORK_STATUS §4 인프라(라이브) | -| **CI/CD 자동배포** | Gitea webhook → `deploy_kintex.sh`(bootJar+vite build→배포→health→롤백). push 시 자동배포 | WORK_STATUS §2·§7 | -| **나노바나나 이미지 생성** | Gemini 라이브(G1 소유자 승인 완료), `gemini-3.1-flash-image-preview` 워커 | WORK_STATUS §2·§4 | -| **인증(JWT/RBAC/2FA)** | JWT+RBAC 6역할 + TOTP OTP + 로그인 실패 잠금 + admin env 시더(admin123 금지) | WORK_STATUS §4, Flyway V7/V8 | -| **P0 코어 매퍼(PostGIS)** | M2~M5 매퍼 배선(501 해소)·룰엔진·공간 SQL | WORK_STATUS §4 백엔드(라이브) | -| **공통 레이어(WISE 이식)** | 시스템관리·공통 업무기능·공개 인증(register/forgot/reset) | Flyway V1~V9(테이블 39) | -| **프론트(Stitch 반영)** | 로그인(히어로+CI+회원가입/비번찾기/아이디기억) + 부스 설계 스튜디오·대시보드·갤러리·신규 8+5화면 | WORK_STATUS §4·§6 | -| **평면도/CAD 자산** | 홀별 평면도 JPG 15 + CAD(제1전시장 "평면,트렌치.dwg") 확보 → 트렌치 실측 원천 | WORK_STATUS §4, CLAUDE.md 확보자산 | - -→ **메시지**: "제안 아키텍처는 도상이 아니라 **이미 기동 중인 시스템**이다. 스캐폴드·인증·PostGIS 코어·나노바나나·자동배포 파이프라인이 라이브로 검증됐다." (전시테크/범용 SI가 제시하지 못하는 실증 우위) - -### 0-2. 구현 계획 (설계 확정·미완 — "표준대로 채운다") - -| 영역 | 상태 | 근거 | -|---|---|---| -| 도메인 모듈 M15 옥션·M10 관람·M12/17 CMS·M16 BI·M18 관리자 | 설계 확정, 구현 대기(Phase D) | IMPLEMENTATION_BACKLOG Phase D | -| 멀티테넌시(tenant_id 전파·컨텍스트 해소) | 데이터모델 확정, 스키마·컨텍스트 구현 대기 | WORK_STATUS §6, data.md §1A | -| 실데이터 집계 전환(신규 5화면 샘플→API) | 화면 이식 완료, 집계 API 미구축 | WORK_STATUS §6, UNDEVELOPED_BACKLOG §1 | -| 접근성/i18n/테마 전면 적용 | NFR 표준 확정, 전면 스윕 대기 | WORK_STATUS §6 | -| 역할별 프론트 번들 분리 | 방식 확정 대기(모노레포 vs 서브패스) | tech.md §9-5 | -| AiTextRouter/AiConfig(텍스트 AI) | 설계 확정(UIWS 패턴), 착수 대기 | tech.md §6-1·R-T7 | - -→ **원칙**: 위 항목은 제안서에서 **"구현 계획"**으로만 서술. "구현 완료"로 오기 금지(감점/신뢰 훼손 리스크). - ---- - -## 1. 목표 아키텍처 종합 (5종 아키텍처 → 제안서 재구성) - -### 1-1. 목표 아키텍처 4-Tier + 사이드카 (한 문장) - -**React 18/19(Vite·TS) 역할별 분리 프론트 → SSO(JWT+2FA)·이중 RBAC → 공유 Spring Boot 3.2.5(Java 17)+MyBatis 모듈러 모놀리스 → PostgreSQL+PostGIS 단일 공간 원천(+읽기복제) / Redis 비동기 큐 / 오브젝트 스토리지**, 그 위에 **나노바나나 Python 워커 사이드카**(이미지 생성)와 **Claude AI(AiTextRouter·Ollama 폴백)**(텍스트 지능)를 **얇은 큐/HTTP 계약으로 결합**한다. (app.md §1·§10, system.md §1~§2, tech.md §1) - -### 1-2. 계층별 종합 (전문가 풀 산출 통합) - -| 계층 | 구성(확정) | 권위 문서 | 핵심 근거 | -|---|---|---|---| -| **표현(App)** | 역할별 6프론트(organizer·exhibitor·contractor·ops·admin·public/visitor) 번들 분리 + 공유 디자인시스템/컴포넌트/api-client 상속. 데스크톱=설계·에디터, 모바일=현장·조회·승인 | app.md §10, system.md §2-1 | 최소권한·공격면 축소, 중복구현 금지 | -| **인증·인가** | 단일 JWT SSO(HS256, jjwt 0.12.5) + TOTP 2FA(RFC6238) + 로그인 실패 잠금 + **이중 RBAC**(플랫폼 `plat`/행사 `roles·hm`) | app.md §6, tech.md §7 | WISE/UIWS 이식(재설계 금지) | -| **애플리케이션(백엔드)** | 공유 Spring Boot **모듈러 모놀리스**(`com.zioinfo.kintex.module.mN`, 4계층 Controller/Service/Mapper/DTO) + 룰엔진 + 배치/배선 엔진(PostGIS) + 옥션·BI·CMS 서비스 | app.md §1~§5·§10-2 | 단일 공유 백엔드·경로/RBAC로 역할 표면 분리 | -| **비동기** | Redis 큐(RenderJob·서류·EDM·알림) + 옥션 실시간 순위/타이머 + 캐시/쿼터/WS 세션 상관. STOMP over WebSocket 실시간 푸시 | app.md §8·§9, system.md §6-3 | 백엔드 무상태화 → 수평 확장 | -| **AI(이미지)** | 나노바나나 Python 워커(google-genai, `gemini-3.1-flash-image-preview`) — image-to-image 구조보존 + 워터마크 후처리 + S6 로컬 PIL 래스터 | tech.md §1-3, system.md ADR-1 | google-genai=Python SDK, Java 재구현 회피 | -| **AI(텍스트)** | `AiTextRouter`: Claude 기본(`api.anthropic.com` 승인 예외) → **Ollama 폴백**(폐쇄망/장애). 부스 배치는 **제약 솔버 주도**, LLM은 조건해석만 | tech.md §6 | 결정론 우선·환각 차단·데이터 주권 | -| **데이터** | PostgreSQL+PostGIS 단일 공간 원천(부스 Polygon·트렌치 Point·배선 LineString, SRID 0) + 프라이머리-스탠바이 복제 + 읽기복제(BI·공개 오프로드) + BI 데이터마트(스타 스키마) | data.md §2~§6 | 단일 공간 원천 불변·운영/분석 분리 | -| **저장(바이너리)** | 오브젝트 스토리지(도면·생성이미지·서식·PDF·콘텐츠) — 경로만 DB, CDN 프론팅 | data.md §0-1, system.md §2-1 | 대용량 독립 확장 | -| **네트워크·보안영역** | 4존+관리존(엣지·DMZ·내부망·AI워커·데이터·관리) 계층 방어, DMZ→데이터존 직결 금지, egress 화이트리스트(Claude·Gemini·PG·SMTP) | network.md §2~§5 | 공개/내부 물리·논리 분리·폐쇄망 지향 | - -### 1-3. 멀티테넌시 격리 구조 (다중 전시관 SaaS) - -- **격리 축**: `tenant_id`(전시관: KINTEX #1·COEX #2…) ⊃ `event_id`(행사) ⊃ 행사 RBAC(역할). 3중 격리. -- **fail-closed 다층 방어**(REQ-S-018): ①테넌트 컨텍스트 필터(서브도메인 우선+사용자소속 보조, 불일치 거부) → ②서비스 가드 → ③MyBatis 공통 인터셉터(tenant_id 자동 주입) → ④PostGIS/쿼리(선두 복합 인덱스). 어느 한 층이라도 불일치면 차단. -- **온보딩**: 코드 배포 없이 **데이터 온보딩**(홀·요율·규정 룰셋·마스터데이터 입력). 기능 로직 불변. (app.md §6, data.md §4-4, system.md §3-1 멀티테넌시) -- **주의(현실)**: 멀티테넌시는 **구현 계획**(WORK_STATUS §6). 제안 시 "데이터모델 확정→스키마 tenant_id→컨텍스트 배선" 3단계 로드맵으로 서술, 백필 전 단일테넌트 무중단 동작→검증 후 필터 활성화(회귀 방지). - -### 1-4. 필수 도식 12종 — 작도 사양 (deck-designer 직접 작도용) - -> 각 도식의 **박스·연결·라벨 텍스트**를 명세. visual-designer(선 SVG 아이콘)·deck-designer(python-pptx)가 재해석 없이 작도. 모든 라벨은 한국어, 아이콘은 선(stroke) 스타일. - -#### ① 3중 해자 벤다이어그램 (S04·S12) -- **형태**: 3원 교집합 벤다이어그램. 3개 동일 크기 원 60% 겹침. -- **원 라벨**: 원A "나노바나나 시공 예측 이미지"(부제: image-to-image 구조보존·40초·S1~S7), 원B "PostGIS 실측 공간데이터"(부제: 부스 폴리곤·트렌치 포인트·배선 LineString 단일 원천), 원C "공사 옥션 폐루프"(부제: AI 자료→응찰→낙찰→발주 자동전환). -- **중앙 교집합(3원 겹침)**: 강조 배지 "재현 난이도 높은 결합 = 우리만의 해자(Moat)". 2원 교집합에는 회색 "단일 요소는 모방 가능". -- **하단 캡션**: "셋의 결합은 경쟁 전시테크·범용 SI가 재현하기 어렵다." - -#### ② 모듈맵 — 생애주기 × 우선순위 매트릭스 (S16) -- **형태**: 가로축=전시 생애주기 6단계(판매·기획 → 설계·시각화 → 발주·계약 → 참가·관람 → 현장운영 → 사후·경영), 세로축=우선순위(P0 코어 / P1 / P2). -- **박스(모듈 칩)**: P0행에 **M2 플로어플랜·M3 부스설계·M4 유틸리티·M5 나노바나나**(강조색·굵은 테두리). P1행 M1·M6·M7·M9·M15(옥션 강조)·M10·M12·M16·M17·M18·§5B 공통·§1A 멀티테넌시. P2행 M8·M11·M13·M14. -- **하단 레이어 띠**: "공통/시스템관리 레이어(§5B) — 전 모듈 선행 기반" + "멀티테넌시(§1A) — tenant_id 격리". -- **캡션**: "18개 모듈 + 공통 레이어를 한 장으로 조망. P0 4개가 심장." - -#### ③ 나노바나나 파이프라인 플로우 (S20) -- **형태**: 좌→우 수평 플로우, 8박스. -- **박스·라벨**: `[사용자: 렌더 요청]` → `[Spring 백엔드: RenderJob 생성·쿼터 확인]` → `[Redis 큐 kintex:renderjob:queue(leftPush)]` → `[나노바나나 Python 워커(BLPOP 소비)]` → `[Gemini image-to-image(G1 승인·라이브)]` → `[워터마크 후처리·S6 로컬 PIL 래스터]` → `[오브젝트 스토리지 적재]` → `[pub/sub → 백엔드 relay → WebSocket/STOMP /topic/render/{jobId}]` → `[프론트: 완료 푸시·갤러리 갱신]`. -- **분기 주석**: 워커 하단에 점선 "degraded: G1 미승인/Gemini 장애 시 목(mock) 응답·구조·워터마크 유지". "성공 시에만 쿼터 차감". -- **보안 배지**: `GEMINI_API_KEY`는 워커 env only(백엔드 미취급). -- **캡션**: "이미지 생성 전면 비동기 — 사용자 동기 대기 없음, 평균 40초." - -#### ④ Before/After 슬라이더 (S21) -- **형태**: 좌우 분할 이미지 카드 + 중앙 세로 드래그 핸들. -- **라벨**: 좌 "Before: 빈 부스(도면/실측)", 우 "After: 시공 후 예상(AI 생성·워터마크)". 핸들 "드래그로 비교". -- **접근성 배지**: "ARIA slider·키보드 좌우 조작 지원(WCAG AA)". -- **하단**: 워터마크 텍스트 예시 배지 "AI 생성 예상 이미지 — 실제 시공과 다를 수 있음. 계약·심사 서류 사용 금지." - -#### ⑤ 3안 비교 화면 (S24) -- **형태**: 3열 카드(안1·안2·안3) + 상단 공통 홀 정보 바 + 하단 액션 바. -- **카드 라벨**: 각 카드 상단 "안 N — 최적화 목표"(예: 안1 "통로 효율 최대", 안2 "프리미엄 부스 최대", 안3 "균형"). 카드 본문 미니 배치도 썸네일 + 지표(판매면적 ㎡·부스 수·규정 위반 0/N). -- **액션 바**: `[안 선택]` `[구역/블록 병합 편집]` `[병합 후 재검증]`. -- **캡션**: "제약 솔버가 상이한 최적화 목표로 정확히 3안 생성. 사람이 선택·병합·최종 확정." - -#### ⑥ PostGIS 배선 오버레이 (S27) -- **형태**: 홀 평면도 위 배선 경로 오버레이. 부스 폴리곤(연회색) + 트렌치 포인트(◆) + 분전반(▣) + 배선 LineString(색상별). -- **색상 범례**: 전기=적색, 네트워크=청색, 급배수=녹색(S6 규약 정합). -- **주석 배지**: "최근접 트렌치 KNN(`geom <-> point`)", "최단 경로(`ST_Length`)·통로 횡단 최소화", 가정 트렌치엔 "가정 좌표(실측 대기)" 배지. -- **캡션**: "수기 위치표시도 폐지 — 좌표 클릭 자동 작도. SRID 0 홀 로컬 미터, GiST 인덱스 실시간." - -#### ⑦ 옥션 폐루프 흐름도 (S29) -- **형태**: 순환/좌→우 흐름, 좌측에 AI 자료 패키지 스택. -- **박스**: `[M2 배치 + M3 설계 + M4 배선/BOQ + M5 시공예측 이미지]`(AI 생성 자료 패키지) → `[옥션 개설(역경매/RFQ/고정가)]` → `[등록업체 검증 게이트: 미등록 응찰 차단 403]` → `[견적서(Quotation) 제출·PDF·버전]` → `[실시간 순위·라운드 마감(Redis 정렬셋·WS 델타)]` → `[종합평가 낙찰(가격+평판+납기)]` → `[Award → 계약/발주 자동전환(M6·M9 연동)]`. -- **캡션**: "AI 설계자료가 곧 발주 근거. 사진 수준 자료로 응찰→발주까지 한 흐름." - -#### ⑧ 종합평가 스코어 비교표 (S31) -- **형태**: 업체 행 × 평가항목 열 표 + 가중 합계 열 + 낙찰 하이라이트 행. -- **열**: 업체명 | 견적금액(점수) | 평판(점수) | 납기(점수) | **가중 종합점수** | 순위. -- **가중치 주석 바**: "가중치(예): 가격 0.6 / 평판 0.25 / 납기 0.15 — 옥션별 설정형(`weight` jsonb)". -- **하이라이트**: 1위 행 강조 + "낙찰(Award)" 배지 + "선정 사유·항목별 비교" 캡션. - -#### ⑨ 시스템 아키텍처 다이어그램 — 전 계층 (S41) -- **형태**: 상하 6밴드 계층도(system.md §2 논리 아키텍처 재구성). -- **밴드1 엣지/DMZ**: `[CDN·정적캐시]` `[WAF/Reverse Proxy(nginx)·TLS 종단·레이트리밋]`. -- **밴드2 역할별 프론트**: organizer·exhibitor·contractor·ops·admin(내부 배지)·public/visitor(공개 SSR/SEO/다국어 배지). -- **밴드3 SSO·RBAC**: `[JWT SSO + TOTP 2FA · 이중 RBAC(플랫폼/행사)]`. -- **밴드4 공유 백엔드**: `[REST + WebSocket/STOMP]` 아래 `[룰엔진]` `[배치·배선 엔진·PostGIS]` `[옥션 M15]` `[BI M16·KpiSnapshot]` `[CMS M17]` `[공개 API read-only]`. -- **밴드5 비동기**: `[Redis 큐·순위·타이머·캐시·쿼터]` → `[나노바나나 워커]` `[서류·EDM 워커]`. -- **밴드6 데이터**: `[PostgreSQL+PostGIS]` `[읽기복제(BI·공개)]` `[오브젝트 스토리지]`. -- **우측 외부 게이트 컬럼**: `[Gemini(G1·라이브)]` `[Claude(AiTextRouter)]` `[PG 결제]` `[kxwp 릴레이]` `[등록업체 DB 739]`. -- **연결 주석**: 워커→Gemini "egress only", 워커→백엔드 "WebSocket 완료 푸시", 백엔드→읽기복제 "BI/공개 오프로드". - -#### ⑩ 보안영역 분리도 (망 구성) (S47) -- **형태**: 좌→우 신뢰도 상승 존 다이어그램(network.md §2). -- **존 박스**: `[인터넷]` → `[엣지: CDN·WAF·DDoS]` → `[DMZ(공개): 공개LB·public SSR·visitor GW·공개 API GW·PG콜백 — 쓰기권한 없음]` → `[내부망(인증): 내부LB·SSO/2FA·내부 API GW·공유 백엔드]` → `[AI 워커존(egress 제한): Redis·나노바나나·Ollama]` → `[데이터존(최내곽): PostgreSQL+PostGIS·오브젝트 스토리지·읽기복제 — 아웃바운드 전면 차단]`. 상단 `[관리존: 배스천·관측성]`. -- **철칙 배지(굵게)**: "DMZ → 데이터존 직접 접근 절대 금지", "데이터존 아웃바운드 0(유출 경로 제거)", "admin. = VPN/허용IP+2FA, 인터넷 미도달". -- **egress 컬럼**: 승인 4목적지만(Claude·Gemini·PG·SMTP), 그 외 DROP. - -#### ⑪ 멀티테넌시 격리 아키텍처도 (S56) -- **형태**: 수직 4층 방어 스택 + 좌측 테넌트 컨텍스트 진입. -- **진입**: `[요청: 서브도메인(kintex./coex.) + JWT 사용자 소속]` → `[테넌트 컨텍스트 해소: 불일치 시 거부]`. -- **4층 박스(위→아래)**: `[①컨텍스트 필터]` → `[②서비스 가드]` → `[③MyBatis 공통 인터셉터(tenant_id 자동 주입)]` → `[④PostGIS/쿼리: tenant_id 선두 복합 인덱스]`. -- **우측 데이터**: `[TB_* … WHERE tenant_id = ctx]` 격리 표현. 하단 배지 "fail-closed — 한 층이라도 불일치면 차단". -- **캡션**: "KINTEX #1 검증 → COEX #2 코드 배포 없이 데이터 온보딩. 기능 불변, 데이터·권한만 분리." - -#### ⑫ 일정 간트 (Phase × 모듈) (S58) -- **형태**: 가로 타임라인 3구간(Phase 1 ~4M / Phase 2 ~4M / Phase 3 ~4M+), 세로 워크스트림 막대. -- **막대(Phase 1)**: 아키텍처(A)·공통레이어(B, 2FA/시스템관리)·P0 코어(C: M2·M3·M4·M5). -- **막대(Phase 2)**: 옥션 M15·관람 M10·BI M16·관리자 M18·판매/서류/정산 M1/6/7/9·CMS M12/17. -- **막대(Phase 3)**: 멀티테넌시·M11·M13·M14·배포 안정화·SM 이관. -- **마일스톤 다이아몬드**: G1(나노바나나 승인·완료)·G2(배포서버·완료)·Phase 게이트·QA 게이트·오픈. -- **캡션**: "선행 게이트 통제 + Phase 게이트 QA 통과 후 진행." - -> **추가 표준 도식**(공공 제안 필수, outline 명시): 추진체계도(S15)=거버넌스(PM/PMO/dev-PM)+아키텍트(AA·SA·TA·DA·NA)+코어/도메인/AI/QA/DevOps 팀 조직도 / 리스크 매트릭스(S59)=발생×영향 2×2에 R-1~R-8·C1~C8 배치 / 기대효과 인포그래픽(S61)=견적·배치·검수·위치표시도·시공예측 5지표 Before/After. 사양은 §2·§5·strategy §4 참조. - ---- - -## 2. Win Theme 기술 근거 (5선 — 실현 가능성 입증) - -### WT-1. 나노바나나 시공 예측 (배점 20 · 최우선) -- **기술 골자**: RenderJob 큐(Redis `kintex:renderjob:queue`) → Python 워커(BLPOP) → Gemini **image-to-image 구조보존** → 워터마크 후처리 → 오브젝트 스토리지 → WebSocket `/topic/render/{jobId}` 완료 푸시. -- **구조보존 메커니즘**(REQ-A-002·003): 참조 이미지 `inlineData` + 구조화 프롬프트 사전(BOOTH_TYPES·BOOTH_STYLES·FIXTURE_LAYERS)에서 **보존(골격·치수·통로·카메라 앵글) / 교체(스타일·재질만 사실화) 명시 분리**. ReRoomAI 검증 패턴 이식(reroomai-source.md). -- **방어**(REQ-A-006): 다층 크기가드·mimeType 자동감지·SAFETY 분기·친화적 한글 에러·**성공 시에만 쿼터 차감**. 간판 한글 렌더 실패 시 간판영역 후처리 합성(REQ-A-008). -- **S6 배선 오버레이**(REQ-A-004): Gemini 미경유 — **백엔드/워커 로컬 PIL 결정적 래스터 합성**(전기 적·네트 청·급배수 녹). 좌표 정확성 확보·비용/지연 제외. -- **실현 근거**: **라이브 검증**(G1 승인·워커 Gemini 실동작, WORK_STATUS §4). "말이 아니라 이미 돈다." -- **도식**: ③파이프라인·④Before/After. - -### WT-2. 배치·설계·배선 AI 3안 + 규정 자동검증 (배점 20+15 교차) -- **기술 골자**: 부스 배치 3안은 **결정적 제약 솔버**(비-LLM)가 상이한 최적화 목표로 생성, LLM(Claude)은 조건 해석·설명만(REQ-A-009). → 공공 "AI 신뢰성" 프레이밍의 핵심(결정론 주도). -- **규정 자동검증**(REQ-F-009·013, REQ-A-010): 룰셋(`compliance-v*.json` 버전 데이터)을 룰엔진이 평가 + PostGIS 공간 판정(통로폭 `ST_Distance/ST_Buffer`·비상구 `ST_Intersects`·홀 이탈 `ST_Contains`·바닥하중 홀별). 병합 후 재검증. 위반 시 `COMPLIANCE_BLOCKED`(422). -- **배선 자동 작도**(REQ-F-015~017): 기기목록→kW 합산→분전반 산출, 최근접 트렌치 KNN(`geom <-> point`)→최단 배선 LineString(`ST_Length`, 통로 횡단 최소 휴리스틱), 위치표시도 좌표 클릭 자동 작도(수기 폐지). -- **AI 신뢰 3층**: 결정적 알고리즘(제약 솔버·PostGIS) 중심 + LLM 보조 + **사람 최종 확정**. 규정검증=플래깅까지, 구조안전 판정은 구조기술사/킨텍스(Non-Goal). RAG 근거·인용·환각 차단(abstain, REQ-A-017). -- **실현 근거**: M2~M5 매퍼(PostGIS 공간 SQL)·룰엔진 **라이브**(WORK_STATUS §4). 3안 생성·병합·집계 API는 구현 계획. -- **도식**: ⑤3안 비교·⑥PostGIS 배선. - -### WT-3. 공사 옥션 폐루프 (배점 15 · 폐루프 핵심) -- **기술 골자**: `TB_AUCTION 1─N TB_QUOTATION ─ TB_AWARD`. 옥션 유형 설정형(REVERSE/RFQ/FIXED). AI 자료 패키지(M2~M5 참조 `attached_refs`) 열람 기반 견적서 제출·PDF·버전. 실시간 순위=Redis 정렬셋 + WebSocket `/topic/auction/{auctionId}` 델타 푸시, 라운드 마감=Redis 타이머. -- **등록 게이트**(REQ-S-007): 미등록 업체 응찰 원천 차단 `NOT_REGISTERED_COMPANY`(403). M7이 `TB_COMPANY.kintex_registered` 검증 권위. → 킨텍스 "등록업체 시공 필수" 규정을 시스템이 강제. -- **종합평가·발주 전환**(REQ-F-028·032): 가중 스코어(가격+평판+납기, `weight` jsonb) → Award → 계약/발주 자동전환(M6·M9 연동, 이벤트 디커플·순환 금지). -- **실현 근거**: 옥션은 **구현 계획**(Phase D-M15). 스캐폴드 4계층·큐·WS 패턴·데이터모델 확정으로 실현성 입증. -- **도식**: ⑦옥션 폐루프·⑧종합평가 스코어. - -### WT-4. 3중 보안 — 워터마크·PII·테넌트 (배점 10) -- **워터마크**(REQ-S-009): M5 이미지 응답 **항상** `watermarkRequired:true`+`watermarkText`+`notice`(계약·심사 서류 금지). 제거 불가(계약 강제, app.md §5-6). 워커 목/live 공통. -- **PII 격리**(REQ-S-013): 관람객/리드 `*_enc` AES-256-GCM 컬럼 암호화, 응답 완전 제외/마스킹, 동의 분리(privacy 필수/marketing 선택/share), 접근 감사 전수. BI 마트는 개인 식별자 비반입(집계만). -- **테넌트 3중**(REQ-S-018): §1-3 fail-closed 4층(컨텍스트→서비스→MyBatis→PostGIS). -- **외부 API 격리**(REQ-S-001·002): Ollama 기본 + Claude/Gemini만 승인 예외. `GEMINI_API_KEY` 워커 env only(백엔드 미취급), egress 워커존 단일 경로. -- **실현 근거**: 인증(JWT/RBAC/2FA/admin env)·워터마크 **라이브**. PII 암호화·테넌트 필터는 구현 계획. -- **도식**: ⑩보안영역 분리·⑪멀티테넌시 격리. - -### WT-5. 멀티테넌트 SaaS 확장 (배점 5 · 미래가치) -- **기술 골자**: §1-3. tenant_id 전파 + 2계층 관리자(플랫폼 슈퍼관리자 테넌트스위처 / 테넌트 관리자 단일스코프) + 컨텍스트 해소. 온보딩 6단계=마스터데이터 입력 수준. -- **개발 부담 최소화**(strategy R-8): 멀티테넌시=격리 레이어만 추가, 기능 로직 불변. 백필 전 단일테넌트 무중단→검증 후 필터 활성화(회귀 방지). -- **실현 근거**: 구현 계획(WORK_STATUS §6). GUARDiA 표준 프레임워크 멀티테넌트 패턴 재사용. -- **도식**: ⑪멀티테넌시 격리. - ---- - -## 3. 비기능(NFR) 충족안 — REQ-N 21건 전부 대응 (P6·P4) - -> 근거: system.md §3(확장성·HA·성능·용량)·tech.md §5(관측성·성능)·data.md §3·§6·network.md §7. **SLO 99.5%(성수기 99.9% 지향)**. - -| REQ-ID | 요구 | 충족 방안(기술) | 측정/게이트 | 근거 | -|---|---|---|---|---| -| REQ-N-001 | 코어 SLO 99.5% | 무상태 백엔드 + Redis 외부화 + HA(§아래) | 월 가용성 ≥99.5%, `/actuator/health` | system.md §3-2 | -| REQ-N-002 | 백엔드 2인스턴스·롤링 무중단 | 최소 2 인스턴스 + 헬스체크 LB + 롤링 배포 | 무중단 배포 검증(스테이징→prod) | system.md §3-2 | -| REQ-N-003 | PG 스탠바이·failover·PITR | 프라이머리-스탠바이 스트리밍 복제 + Patroni/관리형 자동 failover + PITR | RPO 최소·RTO 분 단위 | system.md §3-2 | -| REQ-N-004 | Fail-Safe 배포 | 백업→배포→헬스체크 200→실패 시 롤백(이전 jar 유지) | **라이브**(deploy_kintex.sh) | WORK_STATUS §2 | -| REQ-N-005 | degraded 모드 | G1 미승인/Gemini 장애=워커 목 응답(구조·워터마크 유지), Claude 실패=Ollama 폴백, PG 복제지연=프라이머리 폴백 | 무네트워크 스모크 통과 | system.md §3-2 | -| REQ-N-006 | 나노바나나 40초·1024px | 전면 비동기(WS 완료 푸시) + 클라 Canvas 1024px 다운스케일+JPEG 0.85 전처리 | 단건 평균 ≤40s | system.md §3-3 | -| REQ-N-007 | 대량 생성 제어 | 자동생성 **S1·S7 한정** + 동일 스키마해시 캐시 + 행사별 RenderJob 쿼터(`RENDER_EVENT_QUOTA` 기본 500·성공 시만 차감) | 쿼터/캐시 히트율 | system.md §3-3, R6 | -| REQ-N-008 | 공간쿼리 GiST 실시간 | 전 `geom` 컬럼 GiST 인덱스(트렌치 KNN·통로버퍼·비상구 교차) | 배치·배선 P95 <2s | data.md §3-4 | -| REQ-N-009 | 워커 독립 스케일 | 나노바나나·서류·EDM 워커 큐 깊이 기반 증감, 백엔드 응답성과 분리 | 큐 적체 모니터 | system.md §3-1 | -| REQ-N-010 | BI/공개 읽기복제 오프로드 | BI 집계·공개 조회·플로어플랜 열람 읽기복제 라우팅, 운영 프라이머리 보호 | 프라이머리 부하 격리 | system.md §3-1 | -| REQ-N-011 | 워커 장애 재큐(멱등) | 무상태 소비자 N대 + 가시성 타임아웃 후 재큐(멱등 Job) + AOF 지속화 + ACK | 진행 Job 유실 0 | system.md §3-2 | -| REQ-N-012 | WCAG 2.1 AA·KWCAG 2.2 | 시맨틱 마크업·ARIA·키보드·대비, 공공 KWCAG 2.2 병행 점검 | 접근성 자동+수동 감사 | rfp §6-N1 | -| REQ-N-013 | 캔버스/맵 비시각 대안 | 플로어플랜/배선 캔버스에 데이터 테이블·검색·키보드 부스 선택 대안 필수 제공 | 비시각 경로 검증 | rfp §6-N1 | -| REQ-N-014 | 다국어 i18n·hreflang | i18n 외부화(한 기본+영/중/일)·로케일 전환·hreflang·로케일 포맷 | 4로케일 전환 | rfp §6-N3 | -| REQ-N-015 | 라이트/다크 테마 | CSS 토큰 2벌·prefers-color-scheme 연동·선택 영속 | 테마 왕복 | rfp §6-N4 | -| REQ-N-016 | 반응형 | 데스크톱=설계/에디터, 모바일=현장/조회/승인 | 브레이크포인트 검증 | PLANNING §8 | -| REQ-N-017 | 단일 공간 원천 | M2 폴리곤·M4 배선 매퍼 권위, 소비 모듈 조회만·중복 저장 금지 | ArchUnit·계약 검수 | app.md §3-2, data.md §0-2 | -| REQ-N-018 | 관측성·미기록 | 로그·메트릭·트레이스 관리존 수집, 자격증명·PII·스택트레이스 미기록(`include-stacktrace: never`) | 로그 감사 | tech.md §5, network.md §8 | -| REQ-N-019 | 공개영역 CDN·요청률 상한 | 공개 SSG+CDN 캐시·요청률 상한·slowloris 타임아웃·동시연결 캡 | 스파이크 CDN 흡수 | network.md §7 | -| REQ-N-020 | tenant_id 선두 복합 인덱스 | 격리 쿼리 표준 인덱스(tenant_id 선두) | 격리 쿼리 성능 | data.md, §1-3 | -| REQ-N-021 | BI 데이터마트 배치 ETL | 스타 스키마(FACT/DIM) 야간 배치 ETL 또는 읽기복제 적재, KpiSnapshot | 원천-집계 정합(오차 0) | data.md §6 | - -**HA 구성 요약(P6 SLA 표 입력)**: 프론트/공개=CDN 다중엣지+nginx 다중화 / 백엔드=2+ 인스턴스 헬스체크 LB·롤링 / Redis=Sentinel(자동 failover)+AOF / PostgreSQL=프라이머리-스탠바이+자동 failover+PITR / 워커=무상태 N대·재큐 / 배포=Fail-Safe(롤백). **SLO: 코어 99.5%(성수기 99.9% 지향), 나노바나나 생성=best-effort 비동기(큐 소진 목표).** - -**용량 산정(P4·P6 근거)**: 단일 대형 행사 3,000~5,000부스(수만 지오메트리·GiST 필수) / RenderJob 부스 3,000×자동 2샷(S1·S7)+온디맨드=수천~1만 이미지(수 GB~수십 GB, OBJ+CDN·캐시 억제) / 리드·체크인 수만~수십만(파티셔닝 후보) / 동시 사용자 인증 수백+공개 수천~수만(CDN 흡수) / **DB 연결풀 Hikari max 3(`DB_POOL_MAX`)+PgBouncer 권고**(공유 PG 포화 방지). (system.md §3-4) - ---- - -## 4. 보안 충족안 — REQ-S 20건 전부 대응 (P5 · 시크릿 값 미기재) - -> 근거: SECURITY.md·app.md §5-6·§6·network.md·data.md §5·tech.md §7. **불변(위반=QA 반려)**. - -| REQ-ID | 요구 | 충족 방안(기술) | 근거 | -|---|---|---|---| -| REQ-S-001 | 외부 API 정책 | Ollama 기본 + Claude(`api.anthropic.com`)·Gemini만 승인 예외, egress 화이트리스트 그 외 DROP | network.md §5 | -| REQ-S-002 | GEMINI 키 워커 env only | 나노바나나 워커 env only, Spring은 큐/콜백만, 워커존 단일 egress | tech.md §3-5 | -| REQ-S-003 | 자격증명·키 완전 제외 | 응답/로그/에러/커밋 마스킹, `*_enc`·해시·내부IP·워커토큰 미노출 | tech.md §7 | -| REQ-S-004 | admin 비번 env 재시드 | `ADMIN_PASSWORD_ENC`(AES-256-GCM)+별도 키파일→기동 시 BCrypt 재시드, admin123 금지 | **라이브**(env 시더) | -| REQ-S-005 | JWT+2FA+실패 잠금 | JWT(HS256)+TOTP(RFC6238)+로그인 실패 잠금(관리자 해제) | **라이브**(V7/V8) | -| REQ-S-006 | 행사 RBAC 3중 | 테넌트 격리 ⊃ 행사 RBAC(열람=멤버/홀매니저, 편집=역할별 게이트) | app.md §6 | -| REQ-S-007 | 미등록 업체 차단 | M15 응찰·초대 `NOT_REGISTERED_COMPANY`(403), M7 검증 권위 | app.md §6-2 | -| REQ-S-008 | admin/internal 게이트 | `/api/admin/**`=hasRole(ADMIN), `/api/internal/**`=X-Worker-Token | app.md §5-5 | -| REQ-S-009 | 이미지 워터마크 항상 | M5 응답 항상 watermarkRequired:true+text+notice, 제거 불가 | **라이브**·app.md §5-6 | -| REQ-S-010 | 오류 스택트레이스 미노출 | `server.error.include-*: never` + ApiResponse.error 요약만 | tech.md §5-1 | -| REQ-S-011 | DataAccessException 요약 매핑 | `@RestControllerAdvice GlobalExceptionHandler` INTERNAL 요약(테이블/컬럼 누출 차단) | app.md §7-2 | -| REQ-S-012 | 민감 액션 감사 전수 | 승인·낙찰·설계변경·룰셋개정·리드접근·로그인/권한 `TB_AUDIT_LOG`(tenant_id 포함, `@Audited` AOP) | app.md §7-3 | -| REQ-S-013 | PII 동의·마스킹·암호화 | 동의 분리(privacy/marketing/share)·최소수집·`*_enc` AES-256-GCM·응답 제외·보존/파기 | data.md §5 | -| REQ-S-014 | 입력 검증 | Bean Validation 화이트리스트·파일 MIME/크기 가드(WAF+앱 이중) | tech.md §7, network.md §4-2 | -| REQ-S-015 | XSS 방지·CSP | React 이스케이프·dangerouslySetInnerHTML 금지·CMS sanitize·CSP | rfp §6-N2 | -| REQ-S-016 | 인젝션 차단 | MyBatis `#{}` 강제·`${}` 금지, PostGIS/명령/LDAP 파라미터 바인딩 | rfp §6-N2 | -| REQ-S-017 | IDOR·CSRF·JWT 회전 | 리소스 소유·tenant_id 검증, CSRF/SameSite, JWT 만료·회전 | rfp §6-N2 | -| REQ-S-018 | fail-closed 테넌트 격리 | 4층(컨텍스트 필터→서비스 가드→MyBatis 인터셉터→PostGIS) | §1-3 | -| REQ-S-019 | 의존성 SCA CI 게이트 | OWASP Dependency-Check CI 게이트·정기 패치 | rfp §6-N2 | -| REQ-S-020 | 보안영역 분리 | 공개 DMZ 쓰기 없음·데이터존 직결 불가·DMZ↔내부 HTTPS(mTLS 권장)만·DB 프로토콜 횡단 금지 | network.md §2·§4 | - -**보안 성숙도 강조점(P5)**: ①이미 라이브인 인증·워터마크·admin env(4·5·9번 실증) → "설계가 아니라 동작하는 통제". ②데이터존 아웃바운드 0으로 유출 경로 자체 제거(network.md 철칙). ③외부 AI 키 격리=네트워크 격리 정합(Gemini 워커존만, Claude 백엔드만, 데이터존 무egress). - ---- - -## 5. WBS·공수(M/M) 산정 (P8) - -> 근거: IMPLEMENTATION_BACKLOG Phase A~E·PLANNING §9 로드맵(Phase 1~3 각 ~4개월)·142 REQ·18 모듈. 산정 기반=유사 공공 AI SI 생산성 + 기능 규모(100대 기능: 기구현 63·부분 18·신규 19) + 라이브 스캐폴드로 인한 **착수 리스크·초기 구축 공수 절감** 반영. - -### 5-1. WBS 구조 (Phase × 산출물) - -| Phase | 기간(가정) | 주요 작업(WBS) | 완료 게이트 | -|---|---|---|---| -| **A 아키텍처·거버넌스** | 착수~1M | AA/SA/TA/DA/NA 5종 아키텍처(**완료**)·정합 검증 | 아키텍처 확정·불일치 0 | -| **B 공통/시스템관리 레이어** | ~Phase1 내 | 인증(JWT/2FA)·시스템관리·공통 업무기능·공통 컴포넌트(WISE 이식) | 2FA/RBAC/감사 왕복(**대부분 라이브**) | -| **C P0 부스 코어** | Phase1(~4M) | M2 플로어플랜(3안·규정검증)·M3 부스설계·M4 배선·M5 나노바나나 | 3안·병합·재검증·시각화 | -| **D P1 도메인** | Phase2(~4M) | M15 옥션·M10 관람·M12/17 CMS/공개·M16 BI·M18 관리자·M1/6/7/9 | 각 모듈 왕복·QA 통과 | -| **E P2·배포·확장** | Phase3(~4M+) | M11·M13·M14·**멀티테넌시**·역할별 번들 분리·배포 안정화·SM 이관 | (G2 후)배포·이관 | - -### 5-2. 직군별 공수 산정 (M/M) - -> 12개월(Phase 1~3) 기준. 라이브 스캐폴드/인프라/인증 실적으로 초기 구축 공수를 이미 일부 소화 → 산정에 반영(과다 산정 금지). Phase A는 대부분 완료(감독 공수만 잔여). - -| 직군 | Phase 1 | Phase 2 | Phase 3 | 합계(M/M) | 산정 근거 | -|---|---|---|---|---|---| -| 총괄 PM | 4 | 4 | 4 | 12 | 상주 1.0 FTE | -| 아키텍트(AA·SA·TA·DA·NA) | 8 | 4 | 2 | 14 | Phase A 집중(완료)→거버넌스 감독 | -| 백엔드(공통·코어·도메인) | 12 | 16 | 12 | 40 | 18모듈 코어, 최대 투입 직군 | -| 프론트엔드(역할별 6포털) | 8 | 12 | 8 | 28 | Stitch 이식+역할별 번들 | -| DB 엔지니어(PostGIS) | 4 | 4 | 2 | 10 | 공간 스키마·매퍼·BI 마트·멀티테넌시 | -| AI 개발(Claude·제약 솔버) | 4 | 4 | 2 | 10 | 배치 솔버·규정보조·예측·AiTextRouter | -| 시각화(나노바나나 워커) | 4 | 2 | 2 | 8 | 파이프라인(라이브)·샷세트·품질 | -| QA | 4 | 6 | 6 | 16 | 점진 경계면·보안 불변·회귀 | -| DevOps/인프라 | 2 | 2 | 4 | 8 | CI/CD(라이브)·배포·이관 | -| **소계** | **50** | **54** | **42** | **146** | | - -- **총 공수: 약 146 M/M**(핵심 개발) + PMO/형상관리·품질보증 오버헤드(약 4~8 M/M) → **총 ~150 M/M 규모**. -- **가격평가·투입계획 정합**: 위 산정은 가격제안 투입인력표(직군·기간·M/M)와 1:1 매핑. 라이브 실적(스캐폴드·인증·인프라·나노바나나)이 Phase A/B 공수를 실측 절감 → 동일 규모 대비 **착수 리스크·초기 구축비 우위**. -- **주의**: 예산·사업기간은 실제 RFP 부재로 [가정](공공 SI 통례). 실제 공고 확인 시 M/M·기간 즉시 재정렬. - ---- - -## 6. SM 운영 체계 (SLA·장애대응·정기점검·CI/CD 운영) - -> SI 구축 후 유지보수/운영 제안. **이미 라이브인 자동배포 파이프라인을 실적으로 인용**(과장 아님 — WORK_STATUS §2·§7 검증분). - -### 6-1. 운영 체계·조직(R&R) -- **서비스 데스크·ITSM 프로세스**: 요청(SR)·장애(Incident)·변경(Change)·배포(Release)·문제(Problem·RCA). 공통 업무모듈(통합검색·알림·회의록)·감사로그 기반 티켓 이력. -- **조직**: 운영 총괄 1 + 백엔드/프론트 유지보수 + DB/인프라(DevOps) + AI 워커 담당 + QA. 근무체계=평일 상주 + 행사 성수기(3~5·9~11월) 온콜. - -### 6-2. SLA 지표 -| 지표 | 목표 | 측정 | -|---|---|---| -| 코어 서비스 가용성 | **99.5%**(성수기 99.9% 지향) | 월 가용성·`/actuator/health` | -| 장애 응답시간 | 심각도별(P1 즉시·P2 30분·P3 4시간) | 티켓 타임스탬프 | -| 장애 복구시간(MTTR) | P1 ≤2h·P2 ≤1영업일 | 복구 완료 기록 | -| 배포 성공률 | ≥99%(Fail-Safe 롤백 자동) | 배포 로그 health 게이트 | -| 나노바나나 큐 소진 | best-effort(피크 워커 스케일아웃) | 큐 적체·처리량 | - -측정·보고: 월간 SLA 리포트(가용성·MTTR·티켓·배포). 페널티 체계는 계약 조건에 따름(RFP 확인 필요). - -### 6-3. 유지보수 활동 -- **예방정비·정기점검**: DB 인덱스/통계·복제 지연·백업(PITR) 정상성, Redis AOF·큐 백로그, 워커 하트비트, 디스크/스토리지 용량, 인증서(TLS) 만료 점검. -- **패치/보안 업데이트**: OWASP Dependency-Check SCA 정기 스캔(REQ-S-019)·의존성 패치, OS/미들웨어 보안 패치. 룰셋(요율·규정) **무중단 개정**(버전 데이터, M18 백오피스·감사 기록). -- **성능 튜닝**: PostGIS 공간 쿼리(GiST·EXPLAIN ANALYZE)·Hikari 풀·읽기복제 라우팅·BI 배치 ETL 시간 최적화. -- **형상관리**: Gitea `zio/kintex`·Conventional Commits·main 보호·환경 분리(dev/staging/prod). - -### 6-4. 장애 대응 -- **등급분류·에스컬레이션**: P1(서비스 중단)~P4(경미). 자동 알림(WebSocket/통합 알림센터)·온콜 에스컬레이션. -- **RCA·재발방지**: 장애 후 근본원인 분석(RCA) 리포트·재발방지 조치 티켓화. 감사로그·관측성(로그/메트릭/트레이스, PII·스택트레이스 미기록)로 추적. -- **degraded/BCP·DR**: G1/Gemini 장애=워커 목 응답(구조·워터마크 유지), Claude 장애=Ollama 폴백, PG 장애=자동 failover+PITR, 배포 실패=자동 롤백. 데이터 백업·복구 절차(RPO 최소·RTO 분 단위). - -### 6-5. CI/CD 운영 (라이브 실적 인용) -- **자동배포 파이프라인(라이브·검증됨)**: `workspace/kintex` → Gitea webhook → `deploy_kintex.sh`(bootJar + vite build → 배포 → **health 게이트 → 실패 시 롤백**). push 시 자동배포. (WORK_STATUS §2·§7) -- **운영 함정 숙지(실측 교훈)**: 서버 `/opt/zioinfo/deploy_server.py`(kintex 블록)·`/opt/kintex/` 수정 시 **반영+재시작 필수**(로컬만 고치면 웹훅 1ms no-op), 백엔드도 반드시 빌드, `sh ./gradlew`(실행권한), 로컬 vite rollup win32 크래시=**서버 빌드 신뢰**(tsc 통과 검증). - -### 6-6. 이행/이관 -- 인수인계(WORK_STATUS.md 인수인계 문서 체계 활용)·안정화·지식이전(운영자/개발자 지침서 PPT·프로그램 사양서)·종료 시 이관 계획. 운영 도메인 `kintex.wise.ai.kr` 전환(후속). - ---- - -## 7. RTM 기술 파트 갱신 입력 (REQ ↔ tech_advisory 절 매핑) - -> rfp-analyst RTM 반영상태 갱신용. 각 REQ 계열이 본 자문서 어느 절에서 기술적으로 대응되는지 매핑. QA는 이 표로 기술 파트 커버리지 교차 점검. - -| REQ 계열 | 범위 | tech_advisory 대응 절 | 제안서 목차 | -|---|---|---|---| -| REQ-F-007~019(배치·설계·배선 코어) | M2~M4 | §2 WT-2, §1-2, 도식 ⑤⑥ | P3 | -| REQ-F-025~032(옥션) | M15 | §2 WT-3, 도식 ⑦⑧ | P3 | -| REQ-F-037~055(관람·현장·물류) | M8·M10~M14 | §1-2 App/데이터, §3(오프라인 폴백 N-폴백) | P3 | -| REQ-F-061~066(BI) | M16 | §1-2 데이터, §3 REQ-N-021, data.md 마트 | P3 | -| REQ-F-071~081(관리·공통·멀티테넌시·포털) | M18·§5B·§1A | §1-2·§1-3, 도식 ⑪ | P4·P5·P7·P8 | -| REQ-F-001~006·020~024·033~036·048~051·056~060·067~070 | 판매·서류·정산·CMS·공개 | §1-2 계층·§2 WT, §6(룰셋 운영) | P3 | -| **REQ-A-001~008**(나노바나나) | M5 | §2 WT-1, 도식 ③④ | P3(핵심) | -| REQ-A-009·010·020(배치·규정·비전) | M2/M3 | §2 WT-2 | P3 | -| REQ-A-013·014(예측·KPI·Text-to-SQL) | M16 | §1-2 AI(텍스트), §3 N-021 | P3 | -| REQ-A-016·017(라우터·RAG·환각) | §6 | §1-2 AI(텍스트), §4(abstain) | P4·P5 | -| REQ-A-011·012·015·018·019(검수·챗봇·카피·리드·이상탐지) | 다수 | §1-2 AI(텍스트) | P3 | -| **REQ-N-001~021** | 비기능 21 | **§3 전체 표(21행 1:1)** | P6·P4 | -| **REQ-S-001~020** | 보안 20 | **§4 전체 표(20행 1:1)** | P5 | -| (인프라·HA·배포·관측성) | N-002·003·004·018 | §1-2·§3·§6 SM·§0 실적 | P4·P6·P8 | - ---- - -## 요약 보고 (오케스트레이터·writer·deck-designer용) - -**1. 도식 12종 사양 완비 여부**: ✅ **12/12 완비**(§1-4). ①3중 해자 벤 ②모듈맵 ③나노바나나 파이프라인 ④Before/After ⑤3안 비교 ⑥PostGIS 배선 ⑦옥션 폐루프 ⑧종합평가 스코어 ⑨시스템 아키텍처 ⑩보안영역 분리 ⑪멀티테넌시 격리 ⑫일정 간트 — 각 박스·연결·라벨 텍스트 작도 사양 기술. 추가 표준 도식 3종(추진체계도·리스크 매트릭스·기대효과)은 사양 위치 지정(§1-4 말미). - -**2. REQ 대응 커버리지**: **미대응 0**. REQ-N 21건 전부(§3 표 21행 1:1), REQ-S 20건 전부(§4 표 20행 1:1), REQ-F/REQ-A는 §1-2·§2 Win Theme·§7 매핑표로 전 계열 대응. 총 142 REQ 기술 파트 커버(§7 RTM 갱신 입력). - -**3. 공수 총 M/M**: **약 146 M/M**(핵심 개발, 직군 9종×Phase 3구간) + PMO/QA 오버헤드 4~8 → **총 ~150 M/M 규모**(12개월·Phase 1~3). 라이브 스캐폴드·인증·인프라·나노바나나 실적으로 Phase A/B 공수 실측 절감 반영 = 동일 규모 대비 착수 우위. - -**4. 과장 방지 준수**: 라이브 확인분(인프라·CI/CD·나노바나나·인증·워터마크·PostGIS 코어·평면도 자산)만 "구축 완료/검증됨", 도메인 모듈·멀티테넌시·집계 API·i18n/접근성 전면 적용은 "구현 계획"으로 명확 구분(§0). 시크릿(키·비번·내부IP) 전면 미기재. - -**5. 파일**: `C:\GUARDiA\workspace\kintex\_workspace\proposal\tech_advisory.md` diff --git a/docs/deliverables/제안서/_gen/visual_concepts.md b/docs/deliverables/제안서/_gen/visual_concepts.md deleted file mode 100644 index 62bbf17..0000000 --- a/docs/deliverables/제안서/_gen/visual_concepts.md +++ /dev/null @@ -1,274 +0,0 @@ -# 킨텍스 자동전시시스템 구축 — 도식 시각 컨셉 (Visual Concepts) - -> 작성: proposal-visual-designer · 작성일: 2026-07-11 · 버전 v1.0 -> 짝 문서: `design_system.md`(팔레트·타이포·레이아웃) · `assets/icons/*.svg`(선 아이콘 20종) · `tech_advisory.md` §1-4(도식 12종 박스·연결·라벨 사양) · `proposal_outline.md`(슬라이드 배치) -> 목적: 필수 도식 12종 + 표준 3종(추진체계·리스크·기대효과) + 표지·간지·3중 해자의 **시각 컨셉을 좌표·색·라벨 수준으로 구체화**. deck-designer가 python-pptx로 재해석 없이 작도. -> 좌표 규약: 단위 **inch**, 16:9 캔버스 13.333×7.5, 콘텐츠 폭 12.23(x 0.55~12.78), 본문 콘텐츠 y 1.45~6.9. 색 토큰=`design_system.md` §1. 폰트 맑은 고딕. -> 공통: 노드=ROUNDED_RECTANGLE, 카드 그림자 alpha 18%, 연결 화살표="→"(15pt)/"›"(16pt) SLATE, 하단 캡션 MUTED 9pt 1줄. **본문 10pt 하한 준수**. - ---- - -## A. 표지 (S01) — "도면이 아니라 사진으로 컨펌한다" - -**컨셉**: 좌측은 절제된 텍스트 존, 우측은 나노바나나 시공 예측 이미지가 대각으로 침투 — "왼쪽은 제안(텍스트), 오른쪽은 결과(사진)"의 물리적 은유. - -- **배경**: `BLUE_DK` 풀블리드(-0.1,-0.1,13.53,7.7). -- **우측 대각 이미지 존**: 평행사변형 2매 겹침 — ①`BLUE` (6.4,-1.5,9,11, PARALLELOGRAM) ②`PURPLE` (9.2,-2.0,7,12, PARALLELOGRAM). **그 위에 나노바나나 생성 부스 시공 사진(워터마크 포함)을 반투명(alpha ~35%) 합성** — 이미지 없으면 blue→purple 그라디언트 도형으로 대체(폴백). 이미지 우하단에 소형 워터마크 배지 "AI 생성 예상 이미지". -- **좌상 브랜드**: WHITE OVAL(0.85,0.8,0.42) + PURPLE 코어 OVAL(0.97,0.92,0.18) + "KINTEX · WISE AI" 15pt WHITE Bold (1.42,0.78). -- **문서 배지**: (0.85,2.35) "기술제안서" PURPLE 12pt / (2.72,2.35) "Technical Proposal" 딥블루 10.5pt. -- **대제목** (0.85,2.9,11,1.9): 2줄 46pt WHITE Bold — "킨텍스 자동전시시스템" / "구축 기술제안서". line 1.02. -- **슬로건 배너**(핵심): (0.85,4.75,11.4,0.7) 반투명 화이트 라운드 rect(alpha 12%) 위 PURPLE 좌측 바 + "신청서를 내는 순간, 시공 후 사진을 먼저 본다." 18pt WHITE Bold. -- **서브 카피** (0.88,5.6): "설계 → 시각화 → 발주 → 시공 → 운영 → 분석. 킨텍스에서 한 흐름으로 끝난다." 12.5pt `#B9C7EA`. -- **하단 메타바** (0.85,6.15 라인): 사업명·기술스택 2줄 + 우측 "작성일 2026-07-11". 개발계획서 표지 하단과 동일 구조. -- **아이콘 앵커**: 슬로건 좌측에 `camera-render` 근사 도형(WHITE stroke). - ---- - -## B. 간지 (Section Divider) — P1~P9 공통 템플릿 - -**컨셉**: 개발계획서 간지 계승 + **배점 배지 추가**(제안서 배점 규율 시각화). - -- 배경 `BLUE_DK` + 우측 평행사변형 2매(BLUE 8.6,-1.5,8,11 / PURPLE 10.9,-2,6,12). -- 좌측 대형 넘버 150pt `#2C4E82` (1.0,2.15). PURPLE 언더바(1.15,3.9,0.9,0.09). 국문 제목 34pt WHITE(1.15,4.05). 영문 13pt `#9CB4D8`(1.18,4.85). -- 우측 포인트 3개: PURPLE OVAL 불릿(8.05,y) + WHITE 12.5pt(8.35,y), y=2.35+i*0.62. -- **★배점 배지 (우상단, 신규)**: (10.3,0.7,2.2,0.62) 라운드 rect, fill=섹션색(P3=PURPLE, 그 외 BLUE), WHITE Bold — 예 "기술제안 · 35점". P3만 PURPLE로 최다 배점 강조. -- 섹션별 넘버/제목/영문/포인트/배점: - | 섹션 | No | 제목 | EN | 배점배지 | 포인트 3 | - |---|---|---|---|---|---| - | P1 | 01 | 사업이해 | BUSINESS UNDERSTANDING | 사업이해·전략 10점(BLUE) | As-Is 병목 / 6역할 페르소나 / To-Be 정량목표 | - | P2 | 02 | 추진전략 | STRATEGY | Win Theme 5선(BLUE) | 3중 해자 / 생애주기 폐루프 / 추진체계 | - | P3 | 03 | 기술제안 | TECHNICAL SOLUTION | 기술제안 · 35점(PURPLE) | 나노바나나 시공예측 / 배치·설계·배선 AI / 옥션 폐루프 | - | P4 | 04 | 아키텍처·기술스택 | ARCHITECTURE | 아키텍처 10점(BLUE) | 확정 스택 4-Tier / 단일 공간원천 / AI 라우팅 | - | P5 | 05 | 보안·개인정보 | SECURITY & PRIVACY | 보안 10점(BLUE) | 워터마크 / PII 3중격리 / 망분리 | - | P6 | 06 | 비기능(NFR) | NON-FUNCTIONAL | 비기능 8점(BLUE) | SLO 99.5% / 렌더 40초 / 접근성·다국어 | - | P7 | 07 | 멀티테넌시·확장 | MULTI-TENANCY | 확장 5점(BLUE) | 격리 아키텍처 / 온보딩 6단계 / COEX 확장 | - | P8 | 08 | 사업관리·일정·조직 | PROJECT MANAGEMENT | 사업관리 2점(BLUE) | Phase 간트 / 선행게이트 / 품질관리 | - | P9 | 09 | 기대효과 | EXPECTED BENEFITS | 가점(PURPLE) | 정량 ROI / 정성효과 / 확장비전 | - ---- - -## C. 필수 도식 12종 - -### ① 3중 해자 벤다이어그램 (S04 요약판 · S12 상세판) ★최중요 - -**컨셉**: 3원 60% 교집합. 중앙 교집합만 컬러 채도 최대(해자), 2원 교집합은 회색(모방 가능). "결합의 희소성"을 색 채도로 표현. - -- **3원 배치**(정삼각 구도, 반경 r=1.9): 원A 중심(5.4,3.6) `PURPLE` stroke 2.5pt + PURPLE_LT 반투명 fill / 원B 중심(7.9,3.6) `BLUE` / 원C 중심(6.65,5.5) `TEAL`. OVAL 도형, fill은 반투명(alpha 22%)로 겹침 표현. -- **원 라벨**(각 원 바깥 가까이): - - 원A(좌상) `ai-sparkle` 아이콘 + "나노바나나 시공 예측 이미지" 13pt PURPLE Bold / 부제 "image-to-image 구조보존 · 40초 · S1~S7" 8.5pt MUTED. - - 원B(우상) `postgis-spatial` + "PostGIS 실측 공간데이터" 13pt BLUE Bold / "부스 폴리곤·트렌치·배선 LineString 단일 원천". - - 원C(하) `closed-loop`+`auction-gavel` + "공사 옥션 폐루프" 13pt TEAL Bold / "AI 자료→응찰→낙찰→발주 자동전환". -- **중앙 3원 교집합**(6.65,4.2 부근): 강조 배지 라운드 rect(INK fill) + WHITE Bold "우리만의 해자(Moat)" 11pt / 소자 "재현 난이도 높은 결합". -- **2원 교집합 3곳**: 소형 회색 라벨 "단일 요소는 모방 가능" 8pt SLATE. -- **하단 캡션**(중앙 정렬): "셋의 결합은 경쟁 전시테크·범용 SI가 재현하기 어렵다." 9.5pt MUTED. -- S04(요약판)=벤+한줄, S12(상세판)=벤 좌측 + 우측에 3결합 재현난이도 근거 3불릿 카드(strategy §3-3). - -### ② 모듈맵 — 생애주기 × 우선순위 매트릭스 (S16) - -- **매트릭스 프레임**: x=생애주기 6단계 열, y=우선순위 3행(P0/P1/P2). 콘텐츠 영역(0.55,1.7)~(12.78,6.2). -- **열 헤더**(y=1.7, 각 폭 ~1.95): "판매·기획" / "설계·시각화" / "발주·계약" / "참가·관람" / "현장운영" / "사후·경영". BLUE 밴드 WHITE 10pt Bold. -- **행 라벨**(좌측 x=0.55, 폭 0.9): P0(PURPLE)/P1(BLUE)/P2(SLATE) 세로 배지. -- **모듈 칩** 배치: - - **P0행**(굵은 테두리 2pt·PURPLE·강조): M2 플로어플랜(설계열) · M3 부스설계 · M4 유틸리티 · M5 나노바나나. 각 칩에 대응 아이콘(`booth-layout`/`camera-render`/`wiring-power`/`ai-sparkle`). - - **P1행**(BLUE 1pt): M1(판매)·M16(사후, 2회 span)·M6/M7(발주)·M15 옥션(발주·강조 테두리)·M10(참가)·M12·M17(참가/사후)·M18(전열 하단)·§5B·§1A. - - **P2행**(SLATE): M8·M11·M13·M14(현장운영열 집중). -- **하단 레이어 띠 2줄**(y~5.8): ①BLUE_LT 풀폭 "공통/시스템관리 레이어(§5B) — 전 모듈 선행 기반" ②PURPLE_LT "멀티테넌시(§1A) — tenant_id 격리". -- **캡션**: "18개 모듈 + 공통 레이어를 한 장으로 조망. P0 4개가 심장." 9.5pt. - -### ③ 나노바나나 파이프라인 플로우 (S20) ★WT-1 - -**컨셉**: 좌→우 8박스 수평 플로우. 전면 비동기를 "동기 대기 없음" 배지로 각인. AI 구간(워커·Gemini)만 PURPLE, 나머지 BLUE. - -- **박스 8개**(y=2.6, 높이 0.95, 폭 ~1.35, gap 0.06): - 1. `[사용자: 렌더 요청]` BLUE_LT (아이콘 `mobile`) - 2. `[Spring 백엔드: RenderJob 생성·쿼터 확인]` WHITE/BLUE - 3. `[Redis 큐 leftPush]` WHITE/BLUE (아이콘 `pipeline-flow`) - 4. `[나노바나나 Python 워커 (BLPOP)]` PURPLE_LT/PURPLE (아이콘 `ai-sparkle`) - 5. `[Gemini image-to-image (G1 승인·라이브)]` PURPLE_LT/PURPLE - 6. `[워터마크 후처리 · S6 로컬 PIL 래스터]` PURPLE_LT/PURPLE (아이콘 `watermark`) - 7. `[오브젝트 스토리지 적재]` WHITE/BLUE - 8. `[WebSocket/STOMP → 프론트 완료 푸시·갤러리 갱신]` BLUE_LT -- 박스 사이 "→" 15pt SLATE. -- **분기 주석**(워커 하단 점선 콜아웃, AMBER 테두리): "degraded: G1 미승인/Gemini 장애 시 목(mock) 응답 — 구조·워터마크 유지". 옆에 "성공 시에만 쿼터 차감" GREEN 소배지. -- **보안 배지**(우상단): `shield-security` + "GEMINI_API_KEY = 워커 env only (백엔드 미취급)" 9pt RED 테두리. -- **라이브 배지**: 박스4·5에 GREEN "라이브" 소배지("이미 돈다"). -- **캡션**: "이미지 생성 전면 비동기 — 사용자 동기 대기 없음, 평균 40초." 9.5pt. - -### ④ Before/After 슬라이더 (S21) - -- **좌우 분할 이미지 카드**(2.0,2.0,9.3,3.3): 좌반부=빈 부스(HALL_BG + 도면 라인 오버레이), 우반부=시공 후(BLUE→PURPLE 그라디언트 + 나노바나나 이미지 자리). -- **중앙 세로 드래그 핸들**(x≈6.65): 세로 WHITE 라인 3pt + 중앙 원형 핸들(WHITE OVAL + `‹›` 양방향 화살표). -- **라벨**: 좌상단 "Before: 빈 부스 (도면/실측)" 칩 SLATE / 우상단 "After: 시공 후 예상 (AI 생성)" 칩 PURPLE. -- **접근성 배지**(하단): `check-verify` + "ARIA slider · 키보드 좌우 조작 지원 (WCAG AA)" 9pt. -- **워터마크 고지 배지**(맨 하단, RED 테두리): "AI 생성 예상 이미지 — 실제 시공과 다를 수 있음. 계약·심사 서류 사용 금지." 8.5pt. -- **우측 배선 색상 범례 미니**(S21이 배선 오버레이 겸함): 전기=RED / 네트워크=CYAN / 급배수=TEAL 3줄(색 사각+텍스트+선스타일 실선/파선/점선). - -### ⑤ 3안 비교 화면 목업 (S24) - -- **상단 홀 정보 바**(0.55,1.6,12.23,0.5) BLUE_LT: "제1전시장 5홀 · 판매가능 10,080㎡ · 통로규정 v3" 좌 + `booth-layout` 아이콘. -- **3열 카드**(각 3.9 폭, gap 0.28, y 2.25, 높이 3.3): - - 카드 상단 헤더바: "안 1 — 통로 효율 최대"(BLUE) / "안 2 — 프리미엄 부스 최대"(PURPLE) / "안 3 — 균형"(TEAL). - - 본문: 미니 배치도 썸네일(HALL_BG + 부스 폴리곤 격자 도형) + 지표 3행 "판매면적 ㎡ / 부스 수 / 규정위반 0건"(위반0=GREEN 체크 `check-verify`). - - 안2는 프리미엄 강조(굵은 부스 몇 개), 안3은 균등 격자. -- **하단 액션 바**(0.55,5.75,12.23,0.6): 버튼칩 3개 "[안 선택]" BLUE / "[구역·블록 병합 편집]" WHITE 테두리 / "[병합 후 재검증]" PURPLE. -- **캡션**: "제약 솔버가 상이한 최적화 목표로 정확히 3안 생성. 사람이 선택·병합·최종 확정." (AI 신뢰 3층 프레이밍). - -### ⑥ PostGIS 배선 오버레이 (S27) ★WT-2 - -**컨셉**: 홀 평면도 위 실측 배선. 3색 규약 고정 + 색맹 안전(선 스타일 병기). - -- **평면도 캔버스**(0.55,1.7,8.6,4.6) HALL_BG 라운드: - - **부스 폴리곤** 다수: 연회색(`#D8DEE8`) 사각 격자 도형. - - **트렌치 포인트** ◆: TEAL 다이아 소도형, 라인 따라 배치. - - **분전반** ▣: INK 사각 + 라벨. - - **배선 LineString 3색**(부스→최근접 트렌치→분전반): - - 전기 = `RED` 실선 2pt - - 네트워크 = `CYAN` 파선(dash 6 3) - - 급배수 = `TEAL` 점선(dot 2 3) - - **가정 트렌치**엔 AMBER "가정 좌표(실측 대기)" 소배지. -- **우측 범례·주석 패널**(9.35,1.7,3.4,4.6) WHITE 카드: - - 제목 "배선 색상 범례" + 3색 행(색+선스타일+라벨). - - 주석 배지 3개: "최근접 트렌치 KNN (geom <-> point)" / "최단 경로 ST_Length · 통로 횡단 최소" / "SRID 0 홀 로컬 미터 · GiST 실시간". - - 아이콘 `wiring-power`·`postgis-spatial`. -- **캡션**: "수기 위치표시도 폐지 — 좌표 클릭 자동 작도." - -### ⑦ 옥션 폐루프 흐름도 (S29) ★WT-3 - -**컨셉**: 좌측 AI 자료 패키지 스택 → 우측 순환 흐름. "AI 자료가 곧 발주 근거"를 좌→우 물리 연결로. - -- **좌측 AI 자료 패키지 스택**(0.55,2.0,2.6,3.0) PURPLE_LT 카드, 4겹 쌓임 효과: - - "M2 배치 + M3 설계 + M4 배선/BOQ + M5 시공예측 이미지" 각 줄, 아이콘 `booth-layout`/`camera-render`. 상단 `ai-sparkle`. -- **우측 흐름 7박스**(순환 배열 또는 좌→우 2행 지그재그, y 2.0~5.5): - 1. `[옥션 개설 (역경매/RFQ/고정가)]` BLUE (`auction-gavel`) - 2. `[등록업체 검증 게이트: 미등록 응찰 차단 403]` RED 테두리 강조 (`shield-security`) - 3. `[견적서 제출 · PDF · 버전]` WHITE/BLUE (`document-rtm`) - 4. `[실시간 순위 · 라운드 마감 (Redis 정렬셋·WS 델타)]` BLUE (`dashboard-chart`) - 5. `[종합평가 낙찰 (가격+평판+납기)]` PURPLE (`check-verify`) - 6. `[Award → 계약/발주 자동전환 (M6·M9)]` TEAL - - 6→다시 자료로 순환 화살표(`closed-loop` 곡선 화살표). -- **게이트2 강조**: RED "미등록 = 응찰 원천 차단" 배지 — 킨텍스 규정 시스템 강제 포인트. -- **캡션**: "AI 설계자료가 곧 발주 근거. 사진 수준 자료로 응찰→발주까지 한 흐름." - -### ⑧ 종합평가 스코어 비교표 (S31) - -- **표**(0.55,2.0,12.23): 헤더 BLUE 풀폭 — 열: "업체명 | 견적금액(점수) | 평판(점수) | 납기(점수) | **가중 종합점수** | 순위". -- **행 4~5개**(zebra): 예 A사/B사/C사/D사. 각 셀 숫자 + 괄호 환산점수. -- **가중치 주석 바**(표 상단 또는 하단): "가중치(예): 가격 0.6 / 평판 0.25 / 납기 0.15 — 옥션별 설정형(weight jsonb)" 9pt MUTED. -- **1위 행 강조**: BLUE_LT 배경 + Bold + 우측 "낙찰(Award)" GREEN 배지 + `check-verify`. -- **우측 또는 하단 캡션 카드**: "선정 사유·항목별 비교 근거 자동 산출". 종합점수 열은 막대 미니바(가중합 시각화, BLUE 그라데이션). -- **캡션**: "가격만이 아니라 평판·납기 가중 — 투명·최적 발주." - -### ⑨ 시스템 아키텍처 다이어그램 — 전 계층 (S41) - -**컨셉**: 개발계획서 아키텍처 밴드도 계승·확장. 상하 6밴드 + 우측 외부 게이트 컬럼. - -- **밴드**(좌측 라벨칩 1.55폭 + 본문, 폭 12.23): - 1. **엣지/DMZ**(y~1.5): `[CDN·정적캐시]` `[WAF/Reverse Proxy(nginx)·TLS·레이트리밋]` BLUE_LT. - 2. **역할별 프론트**(y~2.15): organizer·exhibitor·contractor·ops·admin(내부 배지 회색)·public/visitor(공개 SSR/SEO/다국어 배지). 6노드. - 3. **SSO·RBAC**(y~2.8): `[JWT SSO + TOTP 2FA · 이중 RBAC(플랫폼/행사)]` 풀폭 (`shield-security`). - 4. **공유 백엔드**(y~3.45, 높이 늘림): `[REST + WebSocket/STOMP]` 상단 + 하위 `[룰엔진]` `[배치·배선 엔진·PostGIS]` `[옥션 M15]` `[BI M16]` `[CMS M17]` `[공개 API read-only]`. - 5. **비동기**(y~4.5): `[Redis 큐·순위·타이머·캐시·쿼터]` → `[나노바나나 워커]`(PURPLE) `[서류·EDM 워커]`. - 6. **데이터**(y~5.15): `[PostgreSQL+PostGIS]` `[읽기복제(BI·공개)]` `[오브젝트 스토리지]`. -- **우측 외부 게이트 컬럼**(x~11.4 세로): `[Gemini(G1·라이브)]` `[Claude(AiTextRouter)]` `[PG 결제]` `[kxwp 릴레이]` `[등록업체 DB 739]`. 라이브=GREEN 배지. -- **연결 주석**(얇은 선+라벨): 워커→Gemini "egress only" / 워커→백엔드 "WebSocket 완료 푸시" / 백엔드→읽기복제 "BI/공개 오프로드". -- 계층별 색: 표현=BLUE_LT, 백엔드=WHITE/BLUE, AI밴드=PURPLE_LT, 데이터=HALL_BG. - -### ⑩ 보안영역 분리도 (망 구성) (S47) ★WT-4 - -**컨셉**: 좌→우 신뢰도 상승 존. 철칙 배지를 굵게. `network-zone` 메타포. - -- **존 박스 6개**(좌→우, 각 폭 ~2.0, y 2.2 높이 3.0, 우로 갈수록 테두리 진하게): - 1. `[인터넷]` SLATE - 2. `[엣지: CDN·WAF·DDoS]` BLUE_LT - 3. `[DMZ(공개): 공개LB·public SSR·visitor GW·공개 API·PG콜백 — 쓰기권한 없음]` BLUE - 4. `[내부망(인증): 내부LB·SSO/2FA·내부 API·공유 백엔드]` BLUE_DK - 5. `[AI 워커존(egress 제한): Redis·나노바나나·Ollama]` PURPLE - 6. `[데이터존(최내곽): PostgreSQL+PostGIS·오브젝트·읽기복제 — 아웃바운드 전면 차단]` INK -- 존 사이 화살표 "HTTPS만" 라벨. -- **상단 관리존**(가로 띠): `[관리존: 배스천·관측성 — VPN/허용IP+2FA, 인터넷 미도달]`. -- **철칙 배지 3개**(하단, RED 굵은 테두리): "DMZ → 데이터존 직접 접근 절대 금지" / "데이터존 아웃바운드 0 (유출경로 제거)" / "admin = VPN/허용IP+2FA". -- **egress 컬럼**(우측): "승인 4목적지만 — Claude·Gemini·PG·SMTP / 그 외 DROP" (`shield-security`). -- **캡션**: "공개/내부 물리·논리 분리 · 데이터존 무egress로 유출경로 자체 제거." - -### ⑪ 멀티테넌시 격리 아키텍처도 (S56) - -**컨셉**: 수직 4층 방어 스택. fail-closed를 "한 층이라도 불일치 = 차단" 배지로. - -- **좌측 진입**(1.0,2.0,3.2,1.4): `[요청: 서브도메인(kintex./coex.) + JWT 사용자 소속]` → `[테넌트 컨텍스트 해소: 불일치 시 거부]` (`tenant-building`). -- **중앙 4층 방어 스택**(위→아래, 각 폭 5.0, 높이 0.85, y 2.0~5.5): - 1. `[①컨텍스트 필터]` BLUE_LT - 2. `[②서비스 가드]` BLUE - 3. `[③MyBatis 공통 인터셉터 (tenant_id 자동 주입)]` BLUE_DK - 4. `[④PostGIS/쿼리: tenant_id 선두 복합 인덱스]` PURPLE - - 층 사이 아래 화살표. -- **우측 데이터 표현**(9.3,2.0,3.4,3.5) WHITE 카드: `[TB_* … WHERE tenant_id = ctx]` 격리 도형 — KINTEX(#1)·COEX(#2) 두 색 파티션 시각화. -- **하단 배지**(RED): "fail-closed — 한 층이라도 불일치면 차단". -- **캡션**: "KINTEX #1 검증 → COEX #2 코드 배포 없이 데이터 온보딩. 기능 불변, 데이터·권한만 분리." - -### ⑫ 일정 간트 (Phase × 모듈) (S58) - -**컨셉**: 개발계획서 간트 계승. 3구간 + 라이브 실적(GREEN 완료)으로 "이미 돈다" 실증. - -- **타임라인**(gx=3.55, gw=9.15, 3구간 Phase1/2/3 각 ~4M 눈금). -- **상태 범례**(우상단): 완료 GREEN / 진행 AMBER / 예정 SLATE. -- **워크스트림 막대 행**(좌측 Phase+워크스트림 라벨, 우측 막대): - - Phase1: 아키텍처(A·완료 GREEN) / 공통레이어 B 2FA·시스템관리(완료 GREEN) / P0 코어 M2·M3·M4·M5(진행 AMBER, 하위 "스캐폴드·인증·매퍼·룰엔진·나노바나나=완료 GREEN" / "3안·병합·실데이터=진행 AMBER"). - - Phase2: 옥션 M15 / 관람 M10 / BI M16 / 관리자 M18 / 판매·서류·정산 M1·M6·M7·M9 / CMS M12·M17 (예정 SLATE). - - Phase3: 멀티테넌시 / M11·M13·M14 / 배포 안정화·SM 이관 (예정 SLATE, 배포 일부 진행 AMBER). -- **마일스톤 다이아몬드**(◆): G1(나노바나나 승인·완료 GREEN) · G2(배포서버·완료 GREEN) · Phase 게이트 · QA 게이트 · 오픈. -- **현재 시점 마커**: PURPLE 세로선 + "현재". -- **캡션**: "선행 게이트 통제 + Phase 게이트 QA 통과 후 진행. G1·G2 완료 — 실증 기반." (`schedule-gantt`). - ---- - -## D. 표준 필수 도식 3종 - -### ⑬ 추진체계도 (조직도) (S15) - -- **최상위**(중앙 4.9,1.5,3.55,0.72) BLUE: "프로젝트 총괄 (PM)" / "범위·일정·리스크·게이트(G1·G2)" (`org-people`). -- **2단**(연결선 아래): "개발 PM"(PURPLE) / "PMO"(PURPLE). -- **3단 아키텍트 그룹**(BLUE_LT 띠): AA·SA·TA·DA·NA 5노드(WHITE/BLUE) — "앱/시스템·NFR/기술표준/데이터·ERD/네트워크·보안". -- **4단 구현 그룹**(PURPLE_LT 띠): 공통/시스템·백엔드·프론트·DB·도메인(bidding·visitor·cms·bi·admin)·AI·시각화 6노드(WHITE/PURPLE). -- **하단**: 품질(kintex-qa · GREEN) / 배포(devops-dev · 딥블루). -- 개발계획서 조직도와 동일 구조 재사용 — 제안서용으로 "발주처 보고 라인" 소표기 추가 가능. - -### ⑭ 리스크 매트릭스 (S59) - -**컨셉**: 발생가능성 × 영향도 2축 4사분면 버블 + 게이트. - -- **매트릭스**(2.5,1.8,6.5,4.5): x=발생가능성(낮→높), y=영향도(낮→높). 4사분면 배경 명도(우상=RED_LT 위험, 좌하=GREEN_LT 안전). -- **버블**(R-1~R-8 배치, tech_advisory/strategy §4): - - R-1(나노바나나 미승인) 고영향·중발생 → 우상 근처, AMBER. - - R-2(CAD·트렌치 미확보) 고영향·중발생 AMBER. - - R-3(kxwp 폐쇄 API) 중·중 SLATE. - - R-4(배점 가정) 중·중, R-5(간판 품질) 중·중, R-6(AI 환각) 고·저, R-7(HW 연동) 중·저, R-8(확장 부담) 저·저 GREEN. - - 버블 크기=영향도, 색=위험등급, 라벨 "R-n" + 툴팁 텍스트 소자. -- **우측 게이트·대응 패널**(9.3,1.8,3.4,4.5) WHITE 카드: "선행 게이트 G1(완료)·G2(완료)" + "각 리스크 방어 논리 1줄" 스크롤 리스트 + `check-verify`/`risk-warning`. -- **캡션**: "약한 고리를 선제 노출·방어 — 숨기지 않아 신뢰." - -### ⑮ 기대효과 정량 인포그래픽 (S61) - -- **5지표 Before/After 카드**(5열, 각 2.4 폭, y 1.85, 높이 1.85) — 개발계획서 기대효과 카드 계승: - | 지표 | As-Is(SLATE) | ↓(PURPLE) | To-Be(BLUE) | - |---|---|---|---| - | 홀 배정 견적 | 수일 | | 즉시 | - | 배치도 초안 | 수일~수주 | | 수 분 | - | 규정 검수 | 육안 검토 | | 자동 플래깅 | - | 유틸리티 신청 | 수기 작도 | | 클릭+자동견적 | - | 시공 결과 예측 | 불가(외주) | | 표준 샷 자동생성 | -- 각 카드: 지표명 Bold + As-Is(회색) + ↓ 화살표 PURPLE + To-Be(BLUE Bold) + 대응 아이콘(`document-rtm`/`booth-layout`/`check-verify`/`wiring-power`/`camera-render`). -- **하단 임팩트 지표 스트립**(대형 수치): "40초 렌더 · 99.5% SLO · 142 REQ 커버 · 18 모듈" BLUE/PURPLE 교차 KPI 카드. -- **캡션**: "리드타임 수일→즉시·수분, 검수 육안→자동. 정량 개선을 숫자로 각인." - ---- - -## E. deck-designer 작도 체크리스트 - -1. **색 규율**: 퍼플=AI/나노바나나 전용. 배선 3색 고정(전기 RED 실선 / 네트워크 CYAN 파선 / 급배수 TEAL 점선) — 색+선스타일 이중 인코딩(색맹 안전). -2. **라이브 배지(GREEN "이미 돈다")**: 나노바나나 파이프라인(③)·간트(⑫)·아키텍처(⑨) 외부게이트에 반드시 표기 — tech_advisory §0 실증 우위. -3. **워터마크 고지**: Before/After(④)·AI 이미지 보안 슬라이드에 RED 테두리 고지 배지 필수(REQ-S-009). -4. **캡션 1줄**: 12종 각 도식 하단에 tech_advisory 지정 캡션 그대로. MUTED 9~9.5pt. -5. **아이콘**: `assets/icons/*.svg` 20종 — 헤더 앵커는 도형 근사, 콘텐츠 아이콘은 PNG 변환 후 stroke=맥락색. 이모지 금지. -6. **가독성**: 본문 10pt 하한, 한 슬라이드 1 도식/1 메시지, 여백 확보. 도식 박스 라벨 8~10pt. -7. **배점 규율 시각화**: 간지 배점 배지(P3=PURPLE 35점 최강조), 모듈맵 P0 굵은 테두리. -8. **보안**: 시크릿·내부IP·키값 도식·라벨 미기재. 외부 API는 "승인 예외/env only"로만. diff --git a/docs/deliverables/지침서/_gen/gen_guides_pptx.py b/docs/deliverables/지침서/_gen/gen_guides_pptx.py deleted file mode 100644 index f5fecfa..0000000 --- a/docs/deliverables/지침서/_gen/gen_guides_pptx.py +++ /dev/null @@ -1,1025 +0,0 @@ -# -*- coding: utf-8 -*- -""" -킨텍스 자동전시시스템 — 지침서 3종 PPTX 일괄 생성기 - · 사용자지침서.pptx · 운영자지침서.pptx · 개발자지침서.pptx - -- 디자인: docs/deliverables/_gen/gen_dev_plan_pptx.py 스타일 헬퍼 그대로 차용 - (KINTEX Blue #0066B3 / AI Purple #6D4AFF / 맑은 고딕 / 16:9, - 표지·간지·본문 마스터, 도식은 python-pptx 도형 직접 작도) -- 내용 소스(사실 기반): docs/PLANNING.md v3.0, docs/design.md v2.0(§2.8 80화면 인벤토리), - docs/OPS_RUNBOOK.md, docs/ENV_SETUP.md, docs/BUILD_DEPLOY.md, - docs/DEVELOPMENT_GUIDE.md, docs/SECURITY.md, mobile/README.md -- 보안 불변: 비밀번호/API키/서버IP/SSH 계정 미기재(env 키 '이름'만 언급, 값 금지) -재실행: python gen_guides_pptx.py -""" -import os -from pptx import Presentation -from pptx.util import Pt -from pptx.dml.color import RGBColor -from pptx.enum.text import PP_ALIGN, MSO_ANCHOR -from pptx.enum.shapes import MSO_SHAPE -from pptx.oxml.ns import qn - -# ---------------- 팔레트 (개발계획서 동일) ---------------- -BLUE = RGBColor(0x00, 0x66, 0xB3) -BLUE_DK = RGBColor(0x00, 0x44, 0x7A) -BLUE_LT = RGBColor(0xE1, 0xEF, 0xF9) -PURPLE = RGBColor(0x6D, 0x4A, 0xFF) -PURPLE_LT = RGBColor(0xEC, 0xE8, 0xFF) -INK = RGBColor(0x10, 0x18, 0x28) -MUTED = RGBColor(0x66, 0x70, 0x85) -LINE = RGBColor(0xE4, 0xE7, 0xEC) -BG = RGBColor(0xF9, 0xFA, 0xFB) -WHITE = RGBColor(0xFF, 0xFF, 0xFF) -GREEN = RGBColor(0x12, 0x9E, 0x63) -AMBER = RGBColor(0xE0, 0x8A, 0x00) -RED = RGBColor(0xD0, 0x3A, 0x3A) -SLATE = RGBColor(0x98, 0xA2, 0xB3) -CARD = RGBColor(0xFF, 0xFF, 0xFF) -HALL_BG = RGBColor(0xF2, 0xF4, 0xF7) - -FONT = "맑은 고딕" -EMU_W, EMU_H = 12192000, 6858000 -IN = 914400 -def inch(v): return int(v * IN) - -# 문서 컨텍스트 (build 시 세팅) -PRS = None -BLANK = None -DOC_FOOTER = "" -DOC_DATE = "2026-07-11" - -# ---------------- 헬퍼 ---------------- -def slide(): - return PRS.slides.add_slide(BLANK) - -def _set_fill(shape, color): - if color is None: - shape.fill.background() - else: - shape.fill.solid() - shape.fill.fore_color.rgb = color - -def _no_line(shape): - shape.line.fill.background() - -def rect(s, x, y, w, h, fill=None, line=None, line_w=1.0, shape=MSO_SHAPE.RECTANGLE, shadow=False): - sp = s.shapes.add_shape(shape, inch(x), inch(y), inch(w), inch(h)) - _set_fill(sp, fill) - if line is None: - _no_line(sp) - else: - sp.line.color.rgb = line - sp.line.width = Pt(line_w) - sp.shadow.inherit = False - if shadow: - el = sp._element.spPr - ef = el.find(qn('a:effectLst')) - if ef is None: - ef = el.makeelement(qn('a:effectLst'), {}) - el.append(ef) - sh = ef.makeelement(qn('a:outerShdw'), - {'blurRad':'90000','dist':'38100','dir':'5400000','rotWithShape':'0'}) - clr = sh.makeelement(qn('a:srgbClr'), {'val':'101828'}) - alp = clr.makeelement(qn('a:alpha'), {'val':'18000'}) - clr.append(alp); sh.append(clr); ef.append(sh) - return sp - -def text(s, x, y, w, h, content, size=14, color=INK, bold=False, align=PP_ALIGN.LEFT, - anchor=MSO_ANCHOR.TOP, line_spacing=1.0, space_after=2, wrap=True): - tb = s.shapes.add_textbox(inch(x), inch(y), inch(w), inch(h)) - tf = tb.text_frame - tf.word_wrap = wrap - tf.vertical_anchor = anchor - tf.margin_left = 0; tf.margin_right = 0; tf.margin_top = 0; tf.margin_bottom = 0 - if isinstance(content, str): - content = [content] - for i, para in enumerate(content): - p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() - p.alignment = align - p.line_spacing = line_spacing - p.space_after = Pt(space_after) - p.space_before = Pt(0) - if isinstance(para, str): - segs = [{"t": para}] - elif isinstance(para, dict): - segs = [para] - else: - segs = para - for seg in segs: - r = p.add_run() - r.text = seg.get("t","") - r.font.name = FONT - r.font.size = Pt(seg.get("size", size)) - r.font.bold = seg.get("bold", bold) - r.font.color.rgb = seg.get("color", color) - rPr = r._r.get_or_add_rPr() - ea = rPr.makeelement(qn('a:ea'), {'typeface':FONT}) - rPr.append(ea) - return tb - -def page_bg(s, color=BG): - rect(s, -0.1, -0.1, 13.53, 7.7, fill=color) - -def chip(s, x, y, w, h, label, fill, txtcolor=WHITE, size=9.5, bold=True): - rect(s, x, y, w, h, fill=fill, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, w, h, label, size=size, color=txtcolor, bold=bold, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -def card(s, x, y, w, h, fill=CARD, line=LINE): - return rect(s, x, y, w, h, fill=fill, line=line, line_w=1.0, - shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - -def content_header(s, kicker, title, idx): - page_bg(s, BG) - rect(s, 0, 0, 13.333, 0.12, fill=BLUE) - rect(s, 0.55, 0.5, 0.62, 0.62, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.55, 0.5, 0.62, 0.62, f"{idx:02d}", size=20, color=WHITE, bold=True, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, 1.32, 0.5, 10.5, 0.28, kicker, size=11, color=PURPLE, bold=True) - text(s, 1.32, 0.74, 11.4, 0.44, title, size=22, color=INK, bold=True) - rect(s, 0.55, 1.28, 12.23, 0.02, fill=LINE) - text(s, 0.55, 7.06, 9, 0.3, DOC_FOOTER, size=8.5, color=MUTED) - text(s, 11.3, 7.06, 1.48, 0.3, DOC_DATE, size=8.5, color=MUTED, align=PP_ALIGN.RIGHT) - -def bullets(s, x, y, w, items, dot=BLUE, size=10.5, gap=0.46, color=INK, ls=1.0, dotsz=0.1): - """items: str 또는 [head, desc] 튜플""" - for i, it in enumerate(items): - yy = y + i*gap - rect(s, x, yy+0.07, dotsz, dotsz, fill=dot, shape=MSO_SHAPE.OVAL) - if isinstance(it, (tuple, list)): - text(s, x+0.24, yy, w-0.24, gap, [[{"t":it[0]+" — ","size":size,"color":INK,"bold":True}, - {"t":it[1],"size":size-0.6,"color":MUTED}]], line_spacing=ls) - else: - text(s, x+0.24, yy, w-0.24, gap, it, size=size, color=color, line_spacing=ls) - -def titled_card(s, x, y, w, h, title, tcolor, items, item_size=9.6, gap=0.4): - card(s, x, y, w, h) - rect(s, x, y, w, 0.5, fill=tcolor, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, w, 0.5, title, size=12.5, color=WHITE, bold=True, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - for i, it in enumerate(items): - yy = y+0.66+i*gap - rect(s, x+0.2, yy+0.07, 0.09, 0.09, fill=tcolor, shape=MSO_SHAPE.OVAL) - if isinstance(it, (tuple, list)): - text(s, x+0.4, yy, w-0.55, gap, [[{"t":it[0]+" ","size":item_size,"color":INK,"bold":True}, - {"t":it[1],"size":item_size-0.8,"color":MUTED}]], line_spacing=0.95) - else: - text(s, x+0.4, yy, w-0.55, gap, it, size=item_size, color=INK, line_spacing=0.95) - -def steps(s, x, y, w, items, band=BLUE, sep="›"): - """가로 단계 파이프라인. items: [(title, sub, color)]""" - n = len(items); g = 0.14 - bw = (w - (n-1)*g) / n - for i, (t, sub, cl) in enumerate(items): - bx = x + i*(bw+g) - rect(s, bx, y, bw, 0.78, fill=WHITE, line=cl, line_w=1.3, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, bx, y+0.1, bw, 0.3, t, size=11, color=cl, bold=True, align=PP_ALIGN.CENTER) - if sub: - text(s, bx, y+0.42, bw, 0.3, sub, size=7.6, color=MUTED, align=PP_ALIGN.CENTER) - if i < n-1: - text(s, bx+bw-0.03, y, 0.22, 0.78, sep, size=16, color=SLATE, bold=True, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -# ---------------- 공통 슬라이드: 표지 ---------------- -def cover(badge_kr, badge_en, title_lines, subtitle, closing, meta_pairs): - s = slide() - rect(s, -0.1, -0.1, 13.53, 7.7, fill=BLUE_DK) - rect(s, 6.4, -1.5, 9, 11, fill=BLUE, shape=MSO_SHAPE.PARALLELOGRAM) - rect(s, 9.2, -2.0, 7, 12, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) - rect(s, 0.85, 0.8, 0.42, 0.42, fill=WHITE, shape=MSO_SHAPE.OVAL) - rect(s, 0.97, 0.92, 0.18, 0.18, fill=PURPLE, shape=MSO_SHAPE.OVAL) - text(s, 1.42, 0.78, 6, 0.46, "KINTEX · WISE AI", size=15, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - chip(s, 0.85, 2.35, 1.95, 0.42, badge_kr, PURPLE, WHITE, size=12) - chip(s, 2.92, 2.35, 2.35, 0.42, badge_en, RGBColor(0x2B,0x4C,0x7E), WHITE, size=10.5) - text(s, 0.85, 2.95, 11, 1.9, [[{"t":ln, "size":46, "color":WHITE, "bold":True}] for ln in title_lines], - line_spacing=1.02) - text(s, 0.88, 2.95 + 0.92*len(title_lines) + 0.08, 11, 0.5, subtitle, - size=16, color=RGBColor(0xC7,0xDD,0xF2)) - text(s, 0.88, 5.35, 11.4, 0.4, closing, size=12.5, color=RGBColor(0xB9,0xC7,0xEA)) - rect(s, 0.85, 6.15, 11.63, 0.02, fill=RGBColor(0x3A,0x5A,0x8C)) - ly = 6.35 - for label, val in meta_pairs[:2]: - text(s, 0.85, ly, 9, 0.4, [[{"t":label+" ","size":11,"color":RGBColor(0x90,0xA6,0xC9)}, - {"t":val,"size":11,"color":WHITE,"bold":True}]]) - ly += 0.37 - text(s, 9.7, 6.35, 2.78, 0.4, "작성일 "+DOC_DATE, size=11, color=WHITE, bold=True, align=PP_ALIGN.RIGHT) - text(s, 9.7, 6.72, 2.78, 0.4, "KINTEX 자동전시시스템", size=9.5, color=RGBColor(0x90,0xA6,0xC9), align=PP_ALIGN.RIGHT) - -# ---------------- 공통 슬라이드: 목차 ---------------- -def toc(items): - s = slide() - content_header(s, "CONTENTS", "목차", 0) - rect(s, 0.55, 0.5, 0.62, 0.62, fill=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.55, 0.5, 0.62, 0.62, "C", size=22, color=WHITE, bold=True, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - n = len(items) - per_col = (n + 1) // 2 if n > 5 else n - col_w = 5.85 - for i, (no, t, d) in enumerate(items): - col = i // per_col - row = i % per_col - x = 0.7 + col * (col_w + 0.35) - y = 1.62 + row * 1.02 - rect(s, x, y, col_w, 0.88, fill=CARD, line=LINE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - rect(s, x, y, 0.09, 0.88, fill=BLUE if col==0 else PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.28, y, 0.9, 0.88, no, size=24, color=(BLUE if col==0 else PURPLE), bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+1.15, y+0.15, col_w-1.3, 0.35, t, size=14.5, color=INK, bold=True) - text(s, x+1.15, y+0.5, col_w-1.3, 0.32, d, size=9.5, color=MUTED) - -# ---------------- 공통 슬라이드: 간지 ---------------- -def divider(num, kr, en, points): - s = slide() - rect(s, -0.1, -0.1, 13.53, 7.7, fill=BLUE_DK) - rect(s, 8.6, -1.5, 8, 11, fill=BLUE, shape=MSO_SHAPE.PARALLELOGRAM) - rect(s, 10.9, -2, 6, 12, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) - text(s, 1.0, 2.15, 5, 2.2, num, size=150, color=RGBColor(0x2C,0x4E,0x82), bold=True) - rect(s, 1.15, 3.9, 0.9, 0.09, fill=PURPLE) - text(s, 1.15, 4.05, 8, 0.8, kr, size=34, color=WHITE, bold=True) - text(s, 1.18, 4.85, 8, 0.4, en, size=13, color=RGBColor(0x9C,0xB4,0xD8), bold=True) - for i, p in enumerate(points): - yy = 2.35 + i*0.62 - rect(s, 8.05, yy+0.06, 0.16, 0.16, fill=PURPLE, shape=MSO_SHAPE.OVAL) - text(s, 8.35, yy, 4.4, 0.5, p, size=12.5, color=WHITE, anchor=MSO_ANCHOR.MIDDLE) - return s - -# 안내(고지) 배너 -def banner(s, y, msg, fill=BLUE, h=0.62, size=13, x=0.55, w=12.23): - rect(s, x, y, w, h, fill=fill, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.3, y, w-0.6, h, msg, size=size, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - -# ===================================================================== -# 1) 사용자지침서 -# ===================================================================== -def build_user(): - global PRS, BLANK, DOC_FOOTER - PRS = Presentation(); PRS.slide_width = EMU_W; PRS.slide_height = EMU_H - BLANK = PRS.slide_layouts[6] - DOC_FOOTER = "킨텍스 자동전시시스템 · 사용자지침서" - - cover("사용자지침서", "User Guide", - ["킨텍스 자동전시시스템", "사용자 지침서"], - "역할별 사용자를 위한 화면·업무 흐름 안내서", - "로그인 → 부스 설계·시각화 → 옥션 → 관람객 등록 → 승인까지 한 흐름", - [("대상", "주최자 · 참가업체 · 장치/공사업체 · 홀매니저 · 관람객"), - ("구성", "접속 · 대시보드 · 핵심업무 · 모바일 · FAQ")]) - - toc([ - ("01", "시스템 소개 · 접속", "개요 / 로그인·2FA / 아이디기억·비번찾기"), - ("02", "역할별 홈 · 대시보드", "6역할 포털 / MDI 탭 / 마일스톤 대시보드"), - ("03", "핵심 업무 흐름", "부스 설계·병합 / 배선·신청 / 시각화 / 옥션 / 등록·승인"), - ("04", "모바일 앱 사용법", "현장 체크리스트 / 검수 / 시각화 갤러리"), - ("05", "FAQ · 문의", "자주 묻는 질문 / 지원 채널"), - ]) - - # ---- 01 소개·접속 ---- - divider("01", "시스템 소개 · 접속", "INTRODUCTION & SIGN-IN", - ["전시 생애주기 폐루프", "역할별 포털", "로그인 · 2차 인증"]) - - s = slide(); content_header(s, "01 · 시스템 소개", "무엇을 할 수 있나요", 1) - text(s, 0.55, 1.45, 12.2, 0.5, - "킨텍스 자동전시시스템은 홀 배정·부스 설계·유틸리티 배선·규정 검증·시공 발주(옥션)·관람객 운영·경영분석을 하나의 계정으로 잇는 전시 운영 플랫폼입니다.", - size=11.5, color=MUTED) - banner(s, 2.05, "핵심 “신청서를 내는 순간, 시공 후 사진을 먼저 봅니다.” — 나노바나나(AI) 시공 예상 이미지", - fill=BLUE, size=15) - loop = [("판매·기획", BLUE), ("AI 설계·시각화", PURPLE), ("공사 옥션", BLUE), - ("시공", BLUE), ("운영·관람", BLUE), ("경영분석", PURPLE)] - bw = 1.83; gap = 0.19; x0 = 0.55; yb = 3.05 - for i, (lb, cl) in enumerate(loop): - x = x0 + i*(bw+gap) - rect(s, x, yb, bw, 0.7, fill=(PURPLE_LT if cl==PURPLE else BLUE_LT), line=cl, line_w=1.2, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, yb, bw, 0.7, lb, size=11.5, color=(PURPLE if cl==PURPLE else BLUE_DK), bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - if i < len(loop)-1: - text(s, x+bw-0.03, yb, 0.28, 0.7, "→", size=15, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - titled_card(s, 0.55, 4.1, 3.95, 2.55, "내가 얻는 것", BLUE, [ - ("주최자", "자동 견적·배치·마일스톤 트래킹"), - ("참가업체", "클릭 신청 + 시공 전 부스 사진"), - ("장치·공사업체", "제출 전 규정검증·옥션 응찰"), - ("홀매니저", "위반 자동 플래깅·부하 집계"), - ("관람객", "사전등록·모바일 배지·길찾기"), - ], item_size=9.8, gap=0.36) - titled_card(s, 4.65, 4.1, 3.95, 2.55, "AI가 돕는 일", PURPLE, [ - ("배치 3안", "조건 입력 → 3안 자동생성"), - ("규정 검증", "높이·통로·바닥하중 자동 점검"), - ("배선 설계", "트렌치 최단 경로·자동 견적"), - ("시각화", "시공 후 예상 사진(S1~S7)"), - ("검수 보조", "AI 사전심사·위반 플래그"), - ], item_size=9.8, gap=0.36) - titled_card(s, 8.75, 4.1, 4.03, 2.55, "꼭 알아둘 점", BLUE_DK, [ - ("AI 이미지", "‘예상’ 이미지 — 실물과 다를 수 있음"), - ("워터마크", "계약·인허가 서류로 사용 불가"), - ("등록업체", "미등록 업체는 초대·응찰 불가"), - ("최종 판정", "소방·구조는 관할기관·기술사 몫"), - ("2차 인증", "로그인 시 OTP/이메일 코드 필요"), - ], item_size=9.8, gap=0.36) - - s = slide(); content_header(s, "01 · 접속 · 로그인", "로그인 · 2차 인증 · 계정 관리", 1) - steps(s, 0.55, 1.5, 12.23, [ - ("① 이메일·비밀번호", "또는 킨텍스 SSO", BLUE), - ("② 2차 인증", "OTP 또는 이메일 코드", PURPLE), - ("③ 행사 선택", "워크스페이스 카드", BLUE), - ("④ 역할별 홈", "대시보드 진입", GREEN), - ]) - titled_card(s, 0.55, 2.55, 6.05, 2.15, "로그인 화면(SCR-01)", BLUE, [ - "이메일/비밀번호 또는 ‘킨텍스 내부 계정(SSO)’ 로그인", - "‘아이디 기억’ 체크 — 이메일만 저장(비밀번호는 저장하지 않음)", - "1차 로그인 후 2차 인증코드 입력(OTP/이메일)", - "로그인 후 참여 행사 카드에서 워크스페이스 선택", - "초대 코드로 참여 — 하단 ‘초대 코드로 참여하기’", - ], item_size=9.8, gap=0.36) - titled_card(s, 6.75, 2.55, 6.03, 2.15, "2차 인증·계정(SCR-49~51)", PURPLE, [ - ("회원가입", "SCR-49 웹 회원가입 폼"), - ("비밀번호 찾기", "SCR-50 이메일 인증 후 재설정"), - ("OTP 설정", "SCR-51 최초 QR 등록·재설정"), - ("잠금 해제", "실패 반복 잠금 시 관리자 초기화"), - ("마이페이지", "SCR-48 OTP 재설정·개인화"), - ], item_size=9.8, gap=0.36) - banner(s, 4.95, "안내 미등록 장치업체가 초대를 수락하면 ‘킨텍스 등록업체만 참여할 수 있습니다’ 안내와 함께 차단됩니다.", - fill=BLUE_DK, size=11.5, h=0.5) - text(s, 0.55, 5.6, 12.2, 0.9, [ - [{"t":"비밀번호를 잊었을 때 ","size":10.5,"color":INK,"bold":True}, - {"t":"로그인 화면 → ‘비밀번호 찾기’ → 이메일 인증코드 → 새 비밀번호 설정. ","size":10,"color":MUTED}], - [{"t":"OTP 기기를 바꿨을 때 ","size":10.5,"color":INK,"bold":True}, - {"t":"마이페이지에서 재설정하거나, 접근 불가 시 관리자에게 OTP 초기화를 요청하세요.","size":10,"color":MUTED}], - ], line_spacing=1.15, space_after=6) - - # ---- 02 역할별 홈·대시보드 ---- - divider("02", "역할별 홈 · 대시보드", "ROLE HOMES & DASHBOARD", - ["6역할 포털 분리", "MDI 탭 작업환경", "마일스톤 대시보드"]) - - s = slide(); content_header(s, "02 · 역할별 포털", "내 역할에 맞는 포털로 들어갑니다", 2) - text(s, 0.55, 1.4, 12.2, 0.35, - "역할마다 목적이 다르므로 포털이 분리되어 있습니다. 데스크톱은 설계·대시보드, 모바일은 현장·조회·승인에 최적화됩니다.", - size=10.5, color=MUTED) - rect(s, 0.55, 1.9, 12.23, 0.44, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - for x,w,t in [(0.55,2.0,"역할"),(2.55,4.6,"웹 포털(데스크톱)"),(7.15,3.1,"모바일"),(10.25,2.53,"주 사용")]: - text(s, x+0.15, 1.9, w-0.2, 0.44, t, size=10.5, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - portals = [ - ("주최자", "주최자 콘솔 — 홀 배정·배치·옥션 발주·BI", "조회·승인·상황판", "M1·M2·M15·M16"), - ("참가업체", "참가업체 포털 — 부스 신청·설계·유틸리티", "체크인·리드캡처", "M3·M4·M5·M10"), - ("장치·공사업체", "업체 포털 — 도면 제출·설계 열람·옥션 응찰", "시공·반입·안전", "M3·M15·M8"), - ("홀매니저·운영", "운영 대시보드 — 검수·규정 플래그·부하", "현장 검수·혼잡", "M2·M6·M14"), - ("관리자", "백오피스 — 사용자·RBAC·마스터데이터·룰셋", "(웹 전용)", "M18"), - ("관람객·대중", "공개 홍보 사이트 — 일정·사전등록", "배지·길찾기·매칭", "M12·M10·M13"), - ] - ry = 2.42; rh = 0.62 - for i,(r,web,mob,mod) in enumerate(portals): - y = ry + i*rh - if i%2==0: rect(s, 0.55, y, 12.23, rh, fill=RGBColor(0xFC,0xFC,0xFD)) - rect(s, 0.55, y, 12.23, 0.01, fill=LINE) - text(s, 0.67, y, 1.9, rh, r, size=9.8, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 2.7, y, 4.45, rh, web, size=9.2, color=INK, anchor=MSO_ANCHOR.MIDDLE) - text(s, 7.3, y, 2.95, rh, mob, size=9.2, color=MUTED, anchor=MSO_ANCHOR.MIDDLE) - text(s, 10.4, y, 2.4, rh, mod, size=8.8, color=PURPLE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 0.55, 6.35, 12.2, 0.4, - "접속 서브도메인으로 전시관(테넌트)이 고정됩니다 — 자신이 소속되지 않은 전시관의 데이터는 조회·검색되지 않습니다.", - size=9.5, color=MUTED) - - s = slide(); content_header(s, "02 · 대시보드 · 작업환경", "마일스톤 대시보드와 MDI 탭", 2) - titled_card(s, 0.55, 1.5, 6.05, 3.3, "주최자 대시보드(SCR-02)", BLUE, [ - ("마일스톤 타임라인", "D-150 배정 → D-30 협의 → D-25 유틸리티 → D-7 서류 → D-0 개장"), - ("완료·현재·임박", "녹색 체크 / 파란 진행 / 주황 펄스로 표시"), - ("KPI 카드", "참가업체 신청·배치 확정률·유틸리티 완료·위반 미해결"), - ("참가업체 현황표", "부스번호·설계 상태·유틸리티·예상 사진 썸네일"), - ("알림 피드", "승인·반려·마감 이벤트 실시간"), - ("홀 전경 조감", "S7 미니 이미지 카드(AI 생성)"), - ], item_size=9.6, gap=0.44) - titled_card(s, 6.75, 1.5, 6.03, 3.3, "MDI 탭 작업환경(§2.7)", PURPLE, [ - ("여러 문서 동시", "부스 에디터·승인 큐·설계 스튜디오를 탭으로 병행"), - ("상태 보존", "탭 전환해도 미저장 편집·스크롤·줌 유지"), - ("미저장 표시", "탭에 ● 표시, 닫을 때 저장 확인"), - ("탭 열기", "메뉴·링크 클릭 → 새 탭(같은 문서는 1개)"), - ("단축키", "Ctrl+Tab 전환 · Ctrl+W 닫기 · Ctrl+1~9 이동"), - ("모바일", "MDI 미적용 — 한 화면 집중(오조작 방지)"), - ], item_size=9.6, gap=0.44) - banner(s, 5.0, "팁 좌측 사이드바에서 행사(이벤트)를 바꾸면 그 행사의 탭 세트로 전환됩니다. 미저장 탭이 있으면 먼저 확인합니다.", - fill=BLUE_DK, size=11, h=0.5) - text(s, 0.55, 5.7, 12.2, 0.9, [ - [{"t":"화면 딥링크 ","size":10.5,"color":INK,"bold":True}, - {"t":"알림·메일 링크를 클릭하면 해당 문서 탭이 바로 열립니다(예: 검수 상세·배치 에디터). ","size":10,"color":MUTED}], - [{"t":"세션 복원 ","size":10.5,"color":INK,"bold":True}, - {"t":"새로고침·재접속 시 열려 있던 탭 목록이 복원됩니다(서버 미저장 편집 내용 자체는 복원되지 않음).","size":10,"color":MUTED}], - ], line_spacing=1.15, space_after=6) - - # ---- 03 핵심 업무 흐름 ---- - divider("03", "핵심 업무 흐름", "CORE WORKFLOWS", - ["부스 설계 3안·병합", "배선·신청·규정검증", "시각화 · 옥션 · 등록·승인"]) - - s = slide(); content_header(s, "03 · 부스 설계 스튜디오", "3안 생성 · 선택 · 병합 (M2·M3)", 3) - steps(s, 0.55, 1.5, 12.23, [ - ("① 조건 입력", "홀·목표부스·비율", BLUE), - ("② AI 3안 생성", "1·2·3안 자동", PURPLE), - ("③ 선택 / 병합", "구역·블록 조합", BLUE), - ("④ 규정 재검증", "병합 후 필수", AMBER), - ("⑤ 확정·버전", "부스번호 발급", GREEN), - ]) - titled_card(s, 0.55, 2.6, 6.05, 2.7, "배치·설계(SCR-03·04·06)", BLUE, [ - ("AI 자동배치", "보라 버튼 → 제약조건 풀이로 3안 생성"), - ("배치안 비교", "S7 조감 이미지·지표(부스수·통로폭·위반) 비교"), - ("선택 또는 병합", "1안 통로 + 2안 프리미엄존 + 3안 무대 조합"), - ("위반 오버레이", "통로 미달 빨강 밴드·바닥하중 초과 주황 빗금"), - ("부스 설계", "조립부스 옵션 선택 / 독립부스 3안 레이아웃"), - ], item_size=9.6, gap=0.44) - titled_card(s, 6.75, 2.6, 6.03, 2.7, "규정 검증·검수(SCR-09~11)", PURPLE, [ - ("제출 전 자동검증", "높이 5m·리깅 6.5~8.5m·복층·방염 체크리스트"), - ("규정 리포트", "차단/경고 요약 + 규정 근거·위치 보기"), - ("승인 큐", "홀매니저 AI 사전심사 플래그로 검토"), - ("검수 상세", "도면 위 위반 오버레이 확인 후 승인/반려"), - ("최종 판정", "소방·구조 유권해석은 관할기관·구조기술사"), - ], item_size=9.6, gap=0.44) - banner(s, 5.5, "중요 ‘병합’으로 제약이 깨질 수 있으므로, 병합 결과는 반드시 규정 검증을 다시 실행한 뒤 확정하세요.", - fill=AMBER, size=11.5, h=0.5) - - s = slide(); content_header(s, "03 · 유틸리티 · 시각화", "배선 신청과 시각화 갤러리 (M4·M5)", 3) - titled_card(s, 0.55, 1.5, 6.05, 3.15, "유틸리티 배선·신청(SCR-07·08)", BLUE, [ - ("기기 목록 입력", "전시장비·조명·PC → kW 합산·분전반 자동 산출"), - ("단말 위치 클릭", "부스 도면 위 클릭 → 트렌치 최단 배선 자동"), - ("위치표시도 자동", "수기 작도 폐지 — 자동 생성"), - ("자동 견적", "전기·급배수·압축공기·인터넷 요금 자동"), - ("마감 리마인더", "D-25 역산 알림 — 인터넷은 현장 추가 불가"), - ("배선 오버레이", "M5 이미지로 배선 확인"), - ], item_size=9.5, gap=0.42) - titled_card(s, 6.75, 1.5, 6.03, 3.15, "시각화 갤러리(SCR-12)", PURPLE, [ - ("표준 샷 세트", "부스 정면·야간 조명·통로 뷰·홀 전경(S1~S7)"), - ("Before/After", "설계 데이터 → 시공 후 예상 사진"), - ("워터마크 상시", "모든 이미지에 ‘AI 생성 예상 이미지’ 고지"), - ("컨펌 자료", "참가업체·장치업체 합의용으로 공유"), - ("생성 대기", "완료 시 실시간 푸시로 알림"), - ], item_size=9.5, gap=0.42) - banner(s, 4.9, "AI 워터마크 고지 나노바나나 생성 이미지는 참고용 예상 이미지입니다 — 계약·심사·인허가 서류로 사용할 수 없습니다.", - fill=RED, size=11.5, h=0.55) - text(s, 0.55, 5.7, 12.2, 0.9, [ - [{"t":"유틸리티 신청 팁 ","size":10.5,"color":INK,"bold":True}, - {"t":"인터넷·전화·급배수·압축공기는 현장 추가신청이 어렵습니다. D-25 마감 알림을 놓치지 마세요. ","size":10,"color":MUTED}], - [{"t":"전기 용량 ","size":10.5,"color":INK,"bold":True}, - {"t":"과소 신청 시 현장 증설이 불가할 수 있어, 기기 목록 기반 자동 산출값을 확인하고 신청하세요.","size":10,"color":MUTED}], - ], line_spacing=1.15, space_after=6) - - s = slide(); content_header(s, "03 · 공사 옥션 · 관람객", "옥션(견적·낙찰)과 관람객 등록 (M15·M10)", 3) - steps(s, 0.55, 1.45, 12.23, [ - ("① 옥션 개설", "설계자료 첨부", BLUE), - ("② 응찰(견적서)", "업체 제출", PURPLE), - ("③ 실시간 순위", "역경매", BLUE), - ("④ 낙찰 비교", "동일사양 비교", AMBER), - ("⑤ 선정·발주", "정산 연계", GREEN), - ]) - titled_card(s, 0.55, 2.5, 6.05, 2.75, "공사/장치 옥션(SCR-26~29)", BLUE, [ - ("옥션 목록·개설", "AI 설계·물량·시공 예상 이미지를 응찰 근거로"), - ("응찰·순위(업체)", "견적서 작성·제출 → 실시간 순위 확인"), - ("낙찰 비교(발주자)", "동일 사양 위 공정 비교로 선정"), - ("등록업체 게이트", "킨텍스 등록업체만 초대·응찰 가능"), - ("발주 종결", "낙찰 → M9 정산·결제로 연계"), - ], item_size=9.6, gap=0.44) - titled_card(s, 6.75, 2.5, 6.03, 2.75, "관람객 등록·배지·리드(SCR-30~32)", PURPLE, [ - ("사전등록", "공개 사이트 폼(SCR-P4)에서 관람 신청"), - ("모바일 배지/QR", "관람객 앱에서 배지 발급·체크인"), - ("현장 체크인", "데스크/키오스크(SCR-31)로 입장"), - ("리드캡처", "참가업체가 배지 스캔으로 리드 수집(M4)"), - ("리드 스코어링", "AI가 리드 우선순위 제안(SCR-32)"), - ], item_size=9.6, gap=0.44) - banner(s, 5.45, "승인 워크플로 제출물은 홀매니저 승인 큐 → 검수 상세 → 승인/반려를 거칩니다. 반려 시 사유와 위반 위치가 함께 전달됩니다.", - fill=BLUE_DK, size=11, h=0.5) - - # ---- 04 모바일 ---- - divider("04", "모바일 앱 사용법", "MOBILE APP", - ["현장 체크리스트·검수", "시각화 갤러리", "오프라인·워터마크"]) - - s = slide(); content_header(s, "04 · 모바일 앱", "현장·조회·승인을 위한 모바일 앱", 4) - text(s, 0.55, 1.4, 12.2, 0.35, - "모바일 앱(Expo)은 조회·승인·현장 시나리오 전용입니다. 설계·배치 편집은 데스크톱 웹에서 수행합니다.", - size=10.5, color=MUTED) - titled_card(s, 0.55, 1.95, 3.95, 3.6, "현장(장치·홀매니저)", BLUE, [ - ("로그인", "2FA/OTP·아이디 기억·비번찾기"), - ("현장 체크리스트", "48px 큰 터치·오프라인·사진 첨부(SCR-M1)"), - ("현장 검수", "승인/반려·AI 플래그 확인(SCR-M2)"), - ("반입 통행증", "QR 통행증·안전 체크(SCR-M10)"), - ("서류·승인 조회", "이동 중 확인(SCR-M12)"), - ], item_size=9.6, gap=0.42) - titled_card(s, 4.65, 1.95, 3.95, 3.6, "참가·관람", PURPLE, [ - ("리드캡처", "배지 스캔으로 방문객 리드 수집(SCR-M4)"), - ("관람객 배지/QR", "모바일 배지·체크인(SCR-M5)"), - ("길찾기", "실내 지도·wayfinding(SCR-M6)"), - ("부스 검색", "플로어플랜·부스 찾기(SCR-M7)"), - ("비즈매칭", "미팅 요청·일정(SCR-M8·M9)"), - ], item_size=9.6, gap=0.42) - titled_card(s, 8.75, 1.95, 4.03, 3.6, "공통 · 유의", BLUE_DK, [ - ("홈/대시보드", "역할별 진입(SCR-M3)"), - ("시각화 갤러리", "워터마크·고지 항상 표시"), - ("알림센터", "마감·승인·반려 푸시(SCR-M11)"), - ("오프라인", "현장 통신 불안정 시 임시 저장"), - ("보안", "비밀번호 저장 안 함 — 이메일만 기억"), - ], item_size=9.6, gap=0.42) - banner(s, 5.85, "설치 앱은 QR 코드 배포로 설치합니다(스토어 불필요). AI 생성 이미지는 앱에서도 워터마크·고지가 항상 표시됩니다.", - fill=BLUE, size=11, h=0.5) - - # ---- 05 FAQ·문의 ---- - divider("05", "FAQ · 문의", "FAQ & SUPPORT", - ["자주 묻는 질문", "오류 대처", "지원 채널"]) - - s = slide(); content_header(s, "05 · FAQ", "자주 묻는 질문", 5) - faqs = [ - ("Q. 로그인 후 2차 인증코드를 못 받았어요.", "이메일 인증 방식은 스팸함을 확인하세요. OTP 방식은 인증 앱의 6자리 코드를 입력합니다. 기기 변경 시 마이페이지에서 재설정하거나 관리자에게 OTP 초기화를 요청하세요."), - ("Q. AI가 만든 예상 사진을 계약서에 넣어도 되나요?", "안 됩니다. 모든 AI 이미지는 ‘예상 이미지’로 워터마크·고지가 붙으며 계약·인허가·심사 서류로 사용할 수 없습니다."), - ("Q. 3안을 합쳐서 쓰고 싶어요.", "배치안 비교에서 여러 안의 구역/블록을 골라 병합할 수 있습니다. 병합 후에는 반드시 규정 검증을 다시 실행한 뒤 확정하세요."), - ("Q. 유틸리티 신청 마감을 놓치면?", "인터넷·급배수 등은 현장 추가신청이 어렵습니다. D-25 역산 알림을 활용하고, 부득이한 경우 홀매니저/사무국에 문의하세요."), - ("Q. 우리 장치업체를 옥션에 초대할 수 없어요.", "킨텍스 등록업체만 초대·응찰할 수 있습니다. 미등록 업체는 등록 절차 후 이용 가능합니다."), - ("Q. 탭을 닫았는데 편집 내용이 사라졌어요.", "탭 세션은 복원되지만 서버에 저장하지 않은 편집 내용 자체는 복원되지 않습니다. 중요한 작업은 저장 후 닫으세요."), - ] - for i,(q,a) in enumerate(faqs): - x = 0.55 + (i%2)*6.2 - y = 1.55 + (i//2)*1.62 - card(s, x, y, 6.05, 1.5) - rect(s, x, y, 0.08, 1.5, fill=(BLUE if i%2==0 else PURPLE), shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.24, y+0.14, 5.65, 0.5, q, size=10.5, color=INK, bold=True, line_spacing=0.95) - text(s, x+0.24, y+0.66, 5.65, 0.78, a, size=9, color=MUTED, line_spacing=0.98) - - s = slide(); content_header(s, "05 · 문의 · 지원", "지원 채널 및 도움말", 5) - titled_card(s, 0.55, 1.6, 6.05, 2.7, "이런 순서로 확인하세요", BLUE, [ - "① 화면 안내(툴팁)·본 사용자지침서 해당 절 확인", - "② 오류 배너의 요약 메시지·재시도 버튼 사용", - "③ 마감·승인 관련은 알림센터에서 이력 확인", - "④ 권한·계정 문제는 관리자(테넌트 관리자)에게", - "⑤ 규정·현장 문제는 홀매니저·킨텍스 운영팀에", - ], item_size=10, gap=0.44) - titled_card(s, 6.75, 1.6, 6.03, 2.7, "역할별 지원 접점", PURPLE, [ - ("참가업체", "설계·유틸리티 → 주최자 사무국"), - ("장치·공사업체", "등록·옥션 → 킨텍스 등록업체 담당"), - ("주최자", "배정·정산 → 킨텍스 행사지원팀"), - ("관람객", "등록·배지 → 공개 사이트 문의(SCR-P6)"), - ("공통", "계정·권한 → 시스템 관리자"), - ], item_size=10, gap=0.44) - banner(s, 4.55, "오류 메시지는 원인 요약만 표시됩니다(내부 정보 비노출). 반복되면 발생 시각·화면명·행사명을 함께 전달하면 빠르게 처리됩니다.", - fill=BLUE_DK, size=11, h=0.6) - text(s, 0.55, 5.5, 12.2, 0.8, - "본 지침서의 화면 번호(SCR-*)는 설계 문서 docs/design.md의 화면 인벤토리(총 80화면)를 기준으로 합니다. 화면 구성은 업데이트로 달라질 수 있습니다.", - size=9.5, color=MUTED, line_spacing=1.1) - - return PRS - -# ===================================================================== -# 2) 운영자지침서 -# ===================================================================== -def build_ops(): - global PRS, BLANK, DOC_FOOTER - PRS = Presentation(); PRS.slide_width = EMU_W; PRS.slide_height = EMU_H - BLANK = PRS.slide_layouts[6] - DOC_FOOTER = "킨텍스 자동전시시스템 · 운영자지침서" - - cover("운영자지침서", "Operations Guide", - ["킨텍스 자동전시시스템", "운영자 지침서"], - "킨텍스 운영·관리자를 위한 운영·백오피스·장애 대응 안내서", - "아키텍처 → 백오피스 → 배포 → 모니터링 → 백업·보안 → 정기 점검", - [("대상", "테넌트(전시관) 관리자 · 플랫폼 슈퍼관리자 · 운영 담당"), - ("근거", "OPS_RUNBOOK · SECURITY · BUILD_DEPLOY 준용")]) - - toc([ - ("01", "운영 아키텍처 개요", "서비스 토폴로지 / 의존 관계"), - ("02", "관리자 백오피스", "사용자·RBAC·감사·설정 / 마스터·룰셋·테넌트·AiConfig"), - ("03", "배포 · CI/CD 운영", "push→자동배포 / health 게이트 / 롤백"), - ("04", "모니터링 · 장애 대응", "헬스체크·로그·재기동 / 자주 겪는 이슈"), - ("05", "백업 · 보안 점검", "보안 불변 / 워터마크 강제 / 자격증명 보호"), - ("06", "정기 점검 체크리스트", "일 · 주 · 월 점검표"), - ]) - - # ---- 01 아키텍처 ---- - divider("01", "운영 아키텍처 개요", "OPERATIONS ARCHITECTURE", - ["백엔드·워커·프론트", "PostgreSQL·Redis", "webhook 자동배포"]) - - s = slide(); content_header(s, "01 · 운영 아키텍처", "서비스 토폴로지 (개발 kintex.zioinfo.co.kr)", 1) - def node(x,y,w,h,title,sub,fill,tcol,line=None): - rect(s, x, y, w, h, fill=fill, line=(line or LINE), line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y+0.1, w, 0.3, title, size=11, color=tcol, bold=True, align=PP_ALIGN.CENTER) - if sub: text(s, x, y+0.44, w, 0.28, sub, size=8.4, color=MUTED, align=PP_ALIGN.CENTER) - # 프론트/nginx - rect(s, 0.55, 1.55, 12.23, 0.44, fill=BLUE_LT, line=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.7, 1.55, 6, 0.44, "접속 계층 — nginx (kintex.service 도메인 :80/443)", size=10.5, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - node(0.55, 2.15, 3.9, 0.8, "프론트(웹 정적)", "역할별 번들 · SPA 폴백", WHITE, INK, BLUE) - node(4.65, 2.15, 3.9, 0.8, "백엔드 API", "kintex.service · REST + WebSocket", WHITE, INK, BLUE) - node(8.75, 2.15, 4.03, 0.8, "나노바나나 워커", "kintex-nanobanana.service · 큐 소비", PURPLE_LT, PURPLE, PURPLE) - # 데이터 - rect(s, 0.55, 3.2, 12.23, 0.44, fill=HALL_BG, line=BLUE_DK, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.7, 3.2, 6, 0.44, "데이터·큐 계층", size=10.5, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - node(0.55, 3.8, 3.9, 0.8, "PostgreSQL + PostGIS", "kintex_db · 공간 데이터", WHITE, INK, BLUE_DK) - node(4.65, 3.8, 3.9, 0.8, "Redis", "작업 큐·세션·캐시", WHITE, INK, BLUE_DK) - node(8.75, 3.8, 4.03, 0.8, "오브젝트 스토리지", "렌더 이미지·문서", WHITE, INK, BLUE_DK) - # 배포 - rect(s, 0.55, 4.85, 12.23, 0.44, fill=PURPLE_LT, line=PURPLE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.7, 4.85, 6, 0.44, "배포·연동", size=10.5, color=PURPLE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - node(0.55, 5.45, 3.9, 0.8, "deploy_server (webhook)", "Gitea(zio/kintex) push 수신", WHITE, INK, PURPLE) - node(4.65, 5.45, 3.9, 0.8, "Gemini (G1)", "워커 env only · 미승인시 degraded", WHITE, INK, PURPLE) - node(8.75, 5.45, 4.03, 0.8, "헬스 게이트", "GET /health → data.status=UP", WHITE, INK, PURPLE) - text(s, 0.55, 6.45, 12.2, 0.4, - "의존 백엔드 → PostgreSQL·Redis · 워커 → Redis·Gemini(G1) · 프론트 → nginx → 백엔드. 헬스 판정 = /health UP + 두 서비스 active + Redis PONG + DB 응답.", - size=9.3, color=MUTED) - - # ---- 02 백오피스 ---- - divider("02", "관리자 백오피스", "ADMIN BACK-OFFICE", - ["사용자·RBAC·감사·설정", "마스터데이터·룰셋", "테넌트 온보딩·AiConfig"]) - - s = slide(); content_header(s, "02 · 관리자 백오피스", "시스템관리 영역 (M18 · §5B)", 2) - text(s, 0.55, 1.4, 12.2, 0.35, - "백오피스(admin.)는 웹 전용입니다. 테넌트 관리자는 자기 전시관에 고정되고, 플랫폼 슈퍼관리자는 테넌트 스위처로 전 전시관을 통제합니다.", - size=10.5, color=MUTED) - titled_card(s, 0.55, 1.95, 3.95, 3.9, "시스템관리 (SCR-A1~A6)", BLUE, [ - ("사용자 관리", "6역할 + 등록업체·관람객 계정(SCR-A1)"), - ("역할·권한 RBAC", "플랫폼·행사 이중 평가(SCR-A2)"), - ("공통코드", "홀·부스유형·공종14분류·요금(SCR-A3)"), - ("메뉴 관리", "역할별 포털 IA 구동(SCR-A4)"), - ("감사로그", "승인·낙찰·룰셋개정·리드접근(SCR-A5)"), - ("시스템설정", "요율/규정 룰셋 버전(SCR-A6)"), - ("잠금·OTP 초기화", "실패 잠금 해제·OTP 재설정"), - ], item_size=9.4, gap=0.42) - titled_card(s, 4.65, 1.95, 3.95, 3.9, "마스터·룰셋 (SCR-A7·A8)", PURPLE, [ - ("마스터데이터", "홀·요율·요금·부스표준·등록업체(SCR-A7)"), - ("규정 룰셋 버전", "높이·통로·바닥하중 규칙 개정(SCR-A8)"), - ("룰셋은 데이터", "코드가 아닌 버전 파일로 관리"), - ("개정 추적", "리포트에 rulesetVersion·고지 기록"), - ("전시관 소유", "홀·요율·규정은 각 전시관이 소유"), - ("근거 없는 추정 금지", "미확보 실측값은 온보딩 시 입력"), - ], item_size=9.4, gap=0.42) - titled_card(s, 8.75, 1.95, 4.03, 3.9, "테넌트·AI (SCR-A9·A10)", BLUE_DK, [ - ("테넌트 관리", "전시관 마스터·온보딩(SCR-A9)"), - ("테넌트 격리", "tenant_id로 전 데이터 스코프"), - ("서브도메인", "kintex/coex 등 컨텍스트 해소"), - ("KINTEX = 테넌트#1", "기존 데이터 백필 기준"), - ("AiConfig", "AI 플랫폼·모델 설정(SCR-A10)"), - ("Claude 기본", "실패 시 Ollama 자동 폴백"), - ("크로스 집계", "전 테넌트 BI는 슈퍼관리자만"), - ], item_size=9.4, gap=0.42) - - # ---- 03 배포 ---- - divider("03", "배포 · CI/CD 운영", "DEPLOY & CI/CD", - ["push → 자동배포", "health 게이트", "자동·수동 롤백"]) - - s = slide(); content_header(s, "03 · 배포 · CI/CD", "push → 자동배포 → 헬스 게이트", 3) - steps(s, 0.55, 1.5, 12.23, [ - ("① push", "scripts/push_kintex.py", BLUE), - ("② webhook", "Gitea #47", PURPLE), - ("③ 빌드", "bootJar · vite build", BLUE), - ("④ 원자 교체", "deploy_kintex.sh", AMBER), - ("⑤ health 게이트", "GET /health", GREEN), - ]) - titled_card(s, 0.55, 2.6, 6.05, 2.7, "정상 배포 경로(자동)", BLUE, [ - ("git pull", "webhook 수신 후 소스 갱신"), - ("Flyway 마이그", "스키마 전진 적용(전진만 권장)"), - ("빌드", "프론트 vite build · 백엔드 gradlew bootJar"), - ("jar 검증", "KintexApplication.class 존재 확인"), - ("원자 교체", "무중단 교체 후 헬스체크"), - ("검증", "kintex_smoke_test.py로 라우터 등록 확인"), - ], item_size=9.6, gap=0.44) - titled_card(s, 6.75, 2.6, 6.03, 2.7, "금지·주의", RED, [ - ("동시 빌드 금지", "수동 Jenkins 추가 트리거 금지(jar 교체 깨짐)"), - ("서버빌드 신뢰", "로컬 빌드 이슈보다 서버 빌드 결과 신뢰"), - ("deploy_server 반영", "kintex 블록 서버 반영·zioinfo-deploy 재시작"), - ("‘완료’ 오인", "로그 완료여도 1ms no-op면 블록 부재 의심"), - ("빌드 OOM", "공유 8GB — 타 솔루션과 직렬 빌드"), - ("운영 배포", "소유자 승인 필수"), - ], item_size=9.6, gap=0.44) - banner(s, 5.5, "롤백 deploy_kintex.sh는 헬스 실패 시 이전 jar를 자동 유지·복원합니다. 자동 실패 시 백업 jar 복원 또는 직전 정상 커밋으로 재배포합니다.", - fill=BLUE_DK, size=11, h=0.5) - - # ---- 04 모니터링·장애 ---- - divider("04", "모니터링 · 장애 대응", "MONITORING & INCIDENT", - ["헬스체크 우선", "로그·재기동", "자주 겪는 이슈"]) - - s = slide(); content_header(s, "04 · 모니터링 · 장애", "헬스체크 · 로그 · 재기동 (런북 요약)", 4) - titled_card(s, 0.55, 1.5, 6.05, 2.55, "점검 순서", BLUE, [ - ("① 헬스체크", "kintex_smoke_test.py(자동) / /health(수동)"), - ("② 서비스 상태", "kintex.service·kintex-nanobanana.service active"), - ("③ 의존", "Redis PONG · DB SELECT 1"), - ("④ 로그", "journalctl -u kintex.service -n 200"), - ("⑤ 재기동", "systemctl restart → /health 재확인"), - ], item_size=9.8, gap=0.46) - titled_card(s, 6.75, 1.5, 6.03, 2.55, "재기동 원칙", PURPLE, [ - ("백엔드", "restart 후 5초 뒤 /health 확인"), - ("워커", "이미지 멈춤·큐 적체 시 워커만 재기동"), - ("nginx", "정적 이상 시 nginx -t 후 reload"), - ("DB/Redis", "공유 인스턴스 — 타 솔루션 영향 확인 후"), - ("자동기동", "두 유닛 enable 상태 유지"), - ], item_size=9.8, gap=0.46) - # 이슈 표 - text(s, 0.55, 4.25, 6, 0.3, "자주 겪는 이슈", size=12, color=INK, bold=True) - rect(s, 0.55, 4.62, 12.23, 0.4, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - for x,w,t in [(0.55,4.2,"증상"),(4.75,3.6,"원인 후보"),(8.35,4.43,"조치")]: - text(s, x+0.12, 4.62, w-0.15, 0.4, t, size=10, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - issues = [ - ("/health 200인데 특정 API 404", "라우터 미배포(구 jar)", "스모크 러너로 라우터 확인 → 재배포"), - ("500 relation ... does not exist", "마이그 미적용", "flyway info 확인, 보정 마이그(다음 번호) 추가"), - ("이미지 생성 안 됨 / degraded", "G1 미승인·워커 다운", "워커 로그·env 확인, 워커 재기동(키는 워커 env only)"), - ("배포 ‘완료’인데 반영 안 됨", "deploy_server 블록 부재", "서버 kintex 블록 반영·zioinfo-deploy 재시작"), - ("push 했는데 자동배포 안 됨", "webhook 미설정/시크릿 불일치", "Gitea webhook #47 활성·secret·URL 확인"), - ] - ry=5.02; rh=0.4 - for i,(sym,cause,act) in enumerate(issues): - y=ry+i*rh - if i%2==0: rect(s, 0.55, y, 12.23, rh, fill=RGBColor(0xFC,0xFC,0xFD)) - rect(s, 0.55, y, 12.23, 0.008, fill=LINE) - text(s, 0.67, y, 4.1, rh, sym, size=8.6, color=INK, anchor=MSO_ANCHOR.MIDDLE) - text(s, 4.87, y, 3.5, rh, cause, size=8.6, color=MUTED, anchor=MSO_ANCHOR.MIDDLE) - text(s, 8.35, y, 4.4, rh, act, size=8.6, color=BLUE_DK, anchor=MSO_ANCHOR.MIDDLE) - - # ---- 05 백업·보안 ---- - divider("05", "백업 · 보안 점검", "BACKUP & SECURITY", - ["보안 불변 원칙", "AI 워터마크 강제", "자격증명 보호"]) - - s = slide(); content_header(s, "05 · 백업 · 보안 점검", "보안 불변 및 배포·리뷰 전 점검", 5) - titled_card(s, 0.55, 1.5, 6.05, 3.35, "보안 불변(위반 불가)", BLUE_DK, [ - ("외부 API 통제", "온프레미스 우선 · Claude·Gemini만 예외"), - ("키는 env only", "코드·DB·커밋·로그·응답 기록 금지"), - ("Gemini는 워커만", "백엔드는 키 미취급(큐 발행·콜백만)"), - ("자격증명 보호", "IP·SSH·비번·해시 응답/로그 미노출"), - ("암호화 저장", "admin 비번 env 주입(AES-256-GCM)·하드코딩 금지"), - ("스택트레이스 차단", "오류는 요약 메시지만 — 테이블/컬럼명 미노출"), - ("AI 워터마크", "M5 이미지 응답 watermarkRequired 항상 포함"), - ], item_size=9.4, gap=0.4) - titled_card(s, 6.75, 1.5, 6.03, 3.35, "배포·리뷰 전 체크리스트", PURPLE, [ - "응답/로그/커밋에 IP·SSH·비번·해시·API키 노출 0(grep)", - "Gemini/Claude 키 env only — 코드·DB·커밋 미포함", - "admin 비번 하드코딩 시드 없음(env 재시드)", - "M5 이미지 응답 watermarkRequired:true 포함", - "DB/서버 오류 → 요약 메시지(스택·relation 미노출)", - "운영 스크립트 시크릿 하드코딩 0(마스킹)", - "/api/admin/** ADMIN 게이트·미등록업체 차단 동작", - ], item_size=9.4, gap=0.4) - banner(s, 5.05, "백업·스키마 Flyway는 전진(forward)만 — 잘못된 마이그는 되돌리지 말고 보정 마이그를 추가합니다. 운영 DB 임의 롤백 금지.", - fill=RED, size=11, h=0.55) - text(s, 0.55, 5.8, 12.2, 0.8, [ - [{"t":"로그에 자격증명·API키가 보이면 ","size":10.5,"color":INK,"bold":True}, - {"t":"즉시 보안 이슈로 에스컬레이션합니다(SECURITY §2). 감사로그에도 비밀값을 기재하지 않습니다.","size":10,"color":MUTED}], - ], line_spacing=1.1, space_after=4) - - # ---- 06 정기 점검 ---- - divider("06", "정기 점검 체크리스트", "ROUTINE CHECKLIST", - ["일간 점검", "주간 점검", "월간 점검"]) - - s = slide(); content_header(s, "06 · 정기 점검", "일 · 주 · 월 점검 체크리스트", 6) - titled_card(s, 0.55, 1.5, 3.95, 4.6, "일간 (Daily)", BLUE, [ - "스모크 러너 실행·전체 통과 확인", - "/health UP · 두 서비스 active", - "Redis PONG · DB SELECT 1", - "워커 큐 소비·이미지 생성 정상", - "에러 로그 급증·5xx 여부 확인", - "마감 임박 알림(D-25·D-7) 발송 상태", - "감사로그 이상 접근 스캔", - ], item_size=9.6, gap=0.44) - titled_card(s, 4.65, 1.5, 3.95, 4.6, "주간 (Weekly)", PURPLE, [ - "배포 이력·롤백 발생 여부 검토", - "Flyway 마이그 상태(info) 확인", - "디스크·렌더 스토리지 용량 점검", - "백업 정상 수행·복구 가능성 확인", - "OTP·로그인 실패 잠금 문의 처리", - "룰셋/요율 개정 요청 반영 검토", - "webhook·시크릿 정합 점검", - ], item_size=9.6, gap=0.44) - titled_card(s, 8.75, 1.5, 4.03, 4.6, "월간 (Monthly)", BLUE_DK, [ - "보안 불변 체크리스트 전수 점검(grep)", - "권한(RBAC)·계정 접근 재검토", - "감사로그 보존·이상 패턴 분석", - "테넌트 격리·크로스 접근 점검", - "AiConfig·모델 폴백 동작 확인", - "의존 서비스(공유 DB/Redis) 용량 추이", - "장애·변경 이력 회고 및 런북 갱신", - ], item_size=9.6, gap=0.44) - text(s, 0.55, 6.25, 12.2, 0.4, - "점검 결과는 운영 기록으로 남기고, 이상 발견 시 OPS_RUNBOOK 절차(헬스→로그→재기동→롤백)로 대응합니다. 운영 배포·재구성은 소유자 승인이 필요합니다.", - size=9.3, color=MUTED) - - return PRS - -# ===================================================================== -# 3) 개발자지침서 -# ===================================================================== -def build_dev(): - global PRS, BLANK, DOC_FOOTER - PRS = Presentation(); PRS.slide_width = EMU_W; PRS.slide_height = EMU_H - BLANK = PRS.slide_layouts[6] - DOC_FOOTER = "킨텍스 자동전시시스템 · 개발자지침서" - - cover("개발자지침서", "Developer Guide", - ["킨텍스 자동전시시스템", "개발자 지침서"], - "개발 참여자를 위한 환경·표준·빌드·보안 가이드", - "환경 구축 → 스택·구조 → 개발 표준 → 빌드·배포 → 하네스 → 보안 코딩", - [("대상", "백엔드 · 프론트 · 모바일 · 워커 개발 참여자"), - ("스택", "React(Vite)+TS · Spring Boot 3.x(Java17)+MyBatis · PostgreSQL(PostGIS) · Redis")]) - - toc([ - ("01", "개발환경 구축", "clone · env(키 이름) · 로컬 실행"), - ("02", "기술 스택 · 구조", "확정 스택 / backend·frontend·mobile·nanobanana"), - ("03", "개발 표준", "레이어링·MyBatis·PostGIS / API 봉투·오류코드·토큰"), - ("04", "빌드 · 테스트 · 배포", "게이트 / 테스트 / CI/CD 함정"), - ("05", "하네스 · 에이전트 워크플로", "planner→designer→dev→qa→devops"), - ("06", "보안 코딩 규칙", "보안 불변 · 워터마크 · 자격증명 보호"), - ]) - - # ---- 01 환경 ---- - divider("01", "개발환경 구축", "DEV ENVIRONMENT", - ["도구·clone", "DB·Redis·워커", "env 키 이름만"]) - - s = slide(); content_header(s, "01 · 개발환경 구축", "사전 도구 · clone · 로컬 실행", 1) - titled_card(s, 0.55, 1.5, 6.05, 3.0, "사전 요구 도구", BLUE, [ - ("JDK 17 (LTS)", "Spring Boot 3.x 빌드/실행(gradlew)"), - ("Node.js 18+", "Vite 빌드(Node 16 불가 — WISE 함정)"), - ("PostgreSQL 15+/16", "kintex_db"), - ("PostGIS 3.x", "부스 polygon·트렌치 point·배선 LineString"), - ("Redis 6+/7", "작업 큐(RenderJob·서류·알림)"), - ("Python 3.11+", "나노바나나 워커 사이드카"), - ], item_size=9.6, gap=0.42) - titled_card(s, 6.75, 1.5, 6.03, 3.0, "초기 세팅 흐름", PURPLE, [ - ("clone", "git clone /zio/kintex.git"), - ("백엔드", "src/backend → ./gradlew build"), - ("프론트", "src/frontend → npm install"), - ("워커", "tools/nanobanana → pip install google-genai Pillow"), - ("DB 준비", "kintex_db + CREATE EXTENSION postgis"), - ("스키마", "Flyway 순번 마이그레이션으로 적용"), - ], item_size=9.6, gap=0.42) - banner(s, 4.7, "환경변수는 이름(키)만 규약입니다 — 실제 비밀값·포트는 저장소에 두지 않고 사내 비밀관리·env에서 주입합니다.", - fill=BLUE_DK, size=11, h=0.5) - text(s, 0.55, 5.35, 12.2, 1.05, [ - [{"t":"백엔드 env 키(이름만) ","size":10,"color":INK,"bold":True}, - {"t":"KINTEX_DB_PASSWORD · KINTEX_JWT_SECRET · REDIS_HOST/REDIS_PORT · RENDER_WORKER_TOKEN · ANTHROPIC_API_KEY · ADMIN_PASSWORD_ENC/ADMIN_KEY_FILE · SMTP_*","size":9.3,"color":MUTED}], - [{"t":"워커 env 키(이름만) ","size":10,"color":INK,"bold":True}, - {"t":"GEMINI_API_KEY(워커 전용·G1 승인 대상) · NANOBANANA_MODEL(기본 gemini-3.1-flash-image-preview). 값은 코드·커밋·로그에 기록 금지.","size":9.3,"color":MUTED}], - [{"t":"원격 DB ","size":10,"color":INK,"bold":True}, - {"t":"5432 외부 차단 시 SSH 로컬 포워딩으로 jdbc:postgresql://localhost:5432/kintex_db 접속.","size":9.3,"color":MUTED}], - ], line_spacing=1.12, space_after=5) - - # ---- 02 스택·구조 ---- - divider("02", "기술 스택 · 구조", "STACK & STRUCTURE", - ["확정 스택(변경 금지)", "패키지 레이어", "프론트·모바일·워커"]) - - s = slide(); content_header(s, "02 · 기술 스택", "확정 스택 (변경 금지)", 2) - rect(s, 0.55, 1.55, 12.23, 0.44, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - for x,w,t in [(0.55,3.0,"레이어"),(3.55,9.23,"표준")]: - text(s, x+0.15, 1.55, w-0.2, 0.44, t, size=10.5, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - stack = [ - ("프론트(웹)", "React 18/19 + Vite + TypeScript — 역할별 번들 분리(§2-1)"), - ("백엔드", "Spring Boot 3.x(Java 17) + MyBatis — REST + WebSocket(STOMP)"), - ("DB", "PostgreSQL + PostGIS(부스 polygon·트렌치 point·배선 LineString)"), - ("비동기", "Redis 작업 큐(RenderJob·서류·알림)"), - ("이미지 생성", "나노바나나 Python 워커 사이드카(tools/nanobanana, google-genai)"), - ("AI(텍스트)", "Claude 기본 + 설정형 전환(AiTextRouter/AiConfig) — 실패 시 Ollama 폴백"), - ("인증", "행사 단위 RBAC(JWT HS256) + 2차 인증(OTP/TOTP)"), - ] - ry=2.05; rh=0.52 - for i,(k,v) in enumerate(stack): - y=ry+i*rh - if i%2==0: rect(s, 0.55, y, 12.23, rh, fill=RGBColor(0xFC,0xFC,0xFD)) - rect(s, 0.55, y, 12.23, 0.008, fill=LINE) - text(s, 0.7, y, 2.9, rh, k, size=10, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 3.6, y, 9.1, rh, v, size=9.6, color=INK, anchor=MSO_ANCHOR.MIDDLE) - banner(s, 5.8, "패키지 루트 com.zioinfo.kintex · DB kintex_db · 나노바나나 호출은 tools/nanobanana 워커만(백엔드는 GEMINI_API_KEY 미취급).", - fill=BLUE_DK, size=10.5, h=0.5) - - s = slide(); content_header(s, "02 · 프로젝트 구조", "백엔드 레이어 · 프론트 · 모바일 · 워커", 2) - titled_card(s, 0.55, 1.5, 6.05, 4.0, "백엔드 (com.zioinfo.kintex)", BLUE, [ - ("config", "Security·Jwt·WebSocket·Redis·@MapperScan"), - ("security", "JwtTokenProvider·Filter·KintexPrincipal"), - ("common", "response(ApiResponse·PageResponse)·exception·audit"), - ("auth", "AuthService·TotpService·mapper·dto"), - ("module/m2~m5", "플로어플랜·부스설계·배선·나노바나나"), - ("module/m10·m15·m16·m18", "도메인(Phase D)"), - ("system", "사용자·권한·공통코드·메뉴·감사·설정"), - ("work", "worklog·schedule·message·stats·notice…"), - ], item_size=9.2, gap=0.4) - titled_card(s, 6.75, 1.5, 6.03, 4.0, "프론트 · 모바일 · 워커", PURPLE, [ - ("src/frontend", "pages·components·api·store·hooks·routes"), - ("역할별 번들", "organizer·exhibitor·contractor·ops·admin·public"), - ("공유 상속", "디자인 시스템·공통 컴포넌트·API 계약 상속"), - ("axios", "baseURL=/api · 응답 타입 = 계약 shape 1:1"), - ("mobile/", "Expo SDK 51·expo-router·RN 0.74·TS"), - ("모바일 저장", "JWT는 expo-secure-store(웹 AsyncStorage)"), - ("tools/nanobanana", "큐 소비 데몬 → 콜백(RENDER_WORKER_TOKEN)"), - ("룰셋은 데이터", "rulesets/*.json 버전 파일(코드 아님)"), - ], item_size=9.2, gap=0.4) - - # ---- 03 개발 표준 ---- - divider("03", "개발 표준", "DEVELOPMENT STANDARDS", - ["레이어링·MyBatis·PostGIS", "API 봉투·오류코드", "디자인 토큰·네이밍"]) - - s = slide(); content_header(s, "03 · 개발 표준", "레이어링 · API 계약 · 코딩 규약", 3) - titled_card(s, 0.55, 1.5, 6.05, 3.4, "백엔드 레이어링·MyBatis", BLUE, [ - ("계층 규칙", "controller(검증·RBAC) → service(트랜잭션·룰) → mapper"), - ("컨트롤러", "도메인 로직 금지"), - ("매퍼", "비즈니스 판단 금지 · @Mapper + XML"), - ("PostGIS", "ST_MakePolygon·ST_Area·ST_Distance·<-> KNN은 XML"), - ("룰셋", "규정·요율은 버전 파일 · rulesetVersion·disclaimer 기록"), - ("네이밍", "Java camel/Pascal · DB SNAKE_CASE · DTO camelCase"), - ("마이그레이션", "Flyway 순번 · 번호 충돌 금지(최대+1)"), - ], item_size=9.3, gap=0.4) - titled_card(s, 6.75, 1.5, 6.03, 3.4, "API·응답 규약", PURPLE, [ - ("응답 봉투", "ApiResponse = { success, data, error }"), - ("목록", "PageResponse = { items, page, size, total }"), - ("오류코드", "VALIDATION 400·FORBIDDEN 403·COMPLIANCE_BLOCKED 422"), - ("미구현", "NOT_IMPLEMENTED 501"), - ("스코프", "도메인 경로는 {eventId} + 행사 RBAC 가드"), - ("프론트 타입", "TS strict · 응답 타입 = 계약 shape 1:1 · any 지양"), - ("코드그룹", "boothType·myRole·severity는 COMMON_CODES 단일 출처"), - ], item_size=9.3, gap=0.4) - banner(s, 5.1, "디자인 토큰 색·타이포·간격은 design.md §1 토큰만 사용(신규 토큰 추가 금지). 데이터 시각화는 CSS 변수 주입(하드코딩 hex 금지).", - fill=BLUE_DK, size=11, h=0.5) - text(s, 0.55, 5.8, 12.2, 0.7, - "인증 1차 로그인(ID/PW) → verifyToken → 2차 검증(EMAIL/OTP TOTP RFC6238) → access·refresh. 로그인 실패 잠금 + 관리자 해제. /api/system·/api/admin = hasRole(ADMIN).", - size=9.5, color=MUTED, line_spacing=1.1) - - # ---- 04 빌드·테스트·배포 ---- - divider("04", "빌드 · 테스트 · 배포", "BUILD · TEST · DEPLOY", - ["push 전 게이트", "단위·경계면·보안 회귀", "CI/CD 함정"]) - - s = slide(); content_header(s, "04 · 빌드 · 테스트 · 배포", "게이트 · 테스트 · 파이프라인", 4) - steps(s, 0.55, 1.5, 12.23, [ - ("① compile/tsc", "변경분 통과", BLUE), - ("② 시크릿 차단", ".env·*.key 금지", PURPLE), - ("③ 마이그 번호", "충돌 검사", BLUE), - ("④ push", "= 배포 트리거", AMBER), - ("⑤ health 게이트", "실패 시 롤백", GREEN), - ]) - titled_card(s, 0.55, 2.6, 6.05, 2.75, "테스트", BLUE, [ - ("백엔드 단위", "룰 엔진(규정 평가·요율 산식·배선 최단경로)"), - ("공간 쿼리", "PostGIS 통합(testcontainers/로컬 PostGIS)"), - ("경계면(계약)", "API 응답 shape ↔ 프론트 훅/컴포넌트 교차 대조"), - ("보안 회귀", "자격증명·PII·스택트레이스·워터마크·응찰 가드"), - ("워커", "키/네트워크 없이도 import·구조 성립(목/degraded)"), - ], item_size=9.5, gap=0.44) - titled_card(s, 6.75, 2.6, 6.03, 2.75, "빌드·배포 함정", RED, [ - ("Node 16 금지", "vite build 불가 — Node 18+ 사용"), - ("서버빌드 신뢰", "로컬 rollup/win32 크래시는 서버 빌드 신뢰"), - ("동시 빌드 금지", "target/*.jar 교체 깨짐 — 직렬 빌드"), - ("자기방어 스크립트", "jar 검증 → 원자 교체 → 헬스 → 실패 시 롤백"), - ("커밋 시점", "지시가 있을 때만 · 운영 배포 소유자 승인"), - ], item_size=9.5, gap=0.44) - banner(s, 5.6, "Flyway는 전진만 — 잘못된 마이그는 되돌리지 말고 보정 마이그(다음 번호)를 추가합니다. baseline-on-migrate=true.", - fill=AMBER, size=11, h=0.5) - - # ---- 05 하네스 ---- - divider("05", "하네스 · 에이전트 워크플로", "HARNESS & AGENTS", - ["기획→디자인→구현", "점진 QA", "배포 오케스트레이션"]) - - s = slide(); content_header(s, "05 · 하네스 · 에이전트", "에이전트 파이프라인 개요", 5) - steps(s, 0.55, 1.55, 12.23, [ - ("기획", "planner", PURPLE), - ("설계", "AA/SA/TA/DA/NA", BLUE), - ("디자인", "designer", PURPLE), - ("구현", "backend·front·db·domain", BLUE), - ("검증", "kintex-qa", GREEN), - ("배포", "devops-dev", BLUE_DK), - ]) - titled_card(s, 0.55, 2.65, 6.05, 2.7, "문서 소유권·경유", BLUE, [ - ("PLANNING.md", "범위 권위 — planner만 수정"), - ("design.md", "화면 스펙 — designer 경유"), - ("src/", "PLANNING·design 근거로 developer 구현"), - ("나노바나나", "tools/nanobanana만 경유(visualizer)"), - ("오케스트레이터", "kintex-impl / kintex-mobile 스킬로 조율"), - ], item_size=9.6, gap=0.44) - titled_card(s, 6.75, 2.65, 6.03, 2.7, "품질 게이트(점진 QA)", PURPLE, [ - ("모듈 완성 직후", "경계면·공간 로직·보안 불변·빌드 회귀"), - ("경계면", "백엔드 shape ↔ 프론트 호출 교차 비교"), - ("보안 불변", "자격증명·PII·스택트레이스 미노출 강제"), - ("AI 워터마크", "이미지 워터마크·응찰 게이트 검증"), - ("반려 기준", "compileJava·tsc 통과까지 반려"), - ], item_size=9.6, gap=0.44) - banner(s, 5.6, "브랜치 main 보호 · Conventional Commits(feat/fix/docs…) · 코드 식별자·커밋 제목은 영어, 설명은 한국어 허용.", - fill=BLUE_DK, size=11, h=0.5) - - # ---- 06 보안 코딩 ---- - divider("06", "보안 코딩 규칙", "SECURE CODING", - ["보안 불변(위반 시 반려)", "AI 워터마크", "자격증명·오류 응답"]) - - s = slide(); content_header(s, "06 · 보안 코딩 규칙", "보안 불변 (위반 시 QA 반려)", 6) - titled_card(s, 0.55, 1.5, 6.05, 3.9, "지켜야 할 규칙", BLUE_DK, [ - ("자격증명 미노출", "IP·SSH·비번·해시·API키·OTP 시크릿 미노출"), - ("민감 필드 제외", "사용자/업체 응답은 비민감 필드만"), - ("스택트레이스 차단", "error.message는 요약만 · 상세는 서버 로그"), - ("AI 이미지 워터마크", "watermarkRequired+watermarkText+notice 항상"), - ("등록업체 응찰", "미등록 CONTRACTOR NOT_REGISTERED_COMPANY 403"), - ("외부 API 금지", "예외 = Claude(env·Ollama 폴백)·Gemini(워커·G1)"), - ("암호화 저장", "비밀 AES-256-GCM · 비밀번호 BCrypt 해시"), - ], item_size=9.3, gap=0.4) - titled_card(s, 6.75, 1.5, 6.03, 3.9, "코드에서 이렇게", PURPLE, [ - ("키 로드", "env에서만 — 코드·DB·커밋·로그 기록 금지"), - ("Gemini", "워커에서만 로드 · 백엔드는 큐 발행/콜백만"), - ("admin 비번", "ADMIN_PASSWORD_ENC 복호 → 기동 시 BCrypt 재시드"), - ("오류 핸들러", "GlobalExceptionHandler·DataAccessException로 누출 차단"), - ("gitignore", ".env·*.key·*-adminsdk-*.json 커밋 차단"), - ("워커 실패", "errorMessage는 요약만(스택 유입 차단)"), - ("감사로그", "민감 액션 기록 · 비밀값 미기재"), - ], item_size=9.3, gap=0.4) - banner(s, 5.6, "체크(grep) — 응답·로그·커밋에 IP·SSH·비번·해시·API키·워커토큰 노출 0. 위반은 QA 반려 사유입니다.", - fill=RED, size=11, h=0.5) - - return PRS - -# ===================================================================== -# 저장 -# ===================================================================== -if __name__ == "__main__": - out_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # ...\지침서 - targets = [ - ("사용자지침서.pptx", build_user), - ("운영자지침서.pptx", build_ops), - ("개발자지침서.pptx", build_dev), - ] - for fname, fn in targets: - prs = fn() - path = os.path.join(out_dir, fname) - prs.save(path) - print(f"SAVED: {path} (slides={len(prs.slides._sldIdLst)})") diff --git a/docs/deliverables/지침서/개발자지침서.pptx b/docs/deliverables/지침서/개발자지침서.pptx deleted file mode 100644 index 4a4135a..0000000 Binary files a/docs/deliverables/지침서/개발자지침서.pptx and /dev/null differ diff --git a/docs/deliverables/지침서/사용자지침서.pptx b/docs/deliverables/지침서/사용자지침서.pptx deleted file mode 100644 index ed2d04c..0000000 Binary files a/docs/deliverables/지침서/사용자지침서.pptx and /dev/null differ diff --git a/docs/deliverables/지침서/운영자지침서.pptx b/docs/deliverables/지침서/운영자지침서.pptx deleted file mode 100644 index 099623e..0000000 Binary files a/docs/deliverables/지침서/운영자지침서.pptx and /dev/null differ diff --git a/docs/deliverables/킨텍스AI_사업수행계획서.pptx b/docs/deliverables/킨텍스AI_사업수행계획서.pptx deleted file mode 100644 index a8e4f7c..0000000 Binary files a/docs/deliverables/킨텍스AI_사업수행계획서.pptx and /dev/null differ diff --git a/docs/deliverables/킨텍스AI_완료보고서.pptx b/docs/deliverables/킨텍스AI_완료보고서.pptx deleted file mode 100644 index 88372fe..0000000 Binary files a/docs/deliverables/킨텍스AI_완료보고서.pptx and /dev/null differ diff --git a/docs/deliverables/킨텍스AI_중간보고서.pptx b/docs/deliverables/킨텍스AI_중간보고서.pptx deleted file mode 100644 index 2b2e58d..0000000 Binary files a/docs/deliverables/킨텍스AI_중간보고서.pptx and /dev/null differ diff --git a/docs/deliverables/킨텍스AI_착수보고서.pptx b/docs/deliverables/킨텍스AI_착수보고서.pptx deleted file mode 100644 index b896518..0000000 Binary files a/docs/deliverables/킨텍스AI_착수보고서.pptx and /dev/null differ diff --git a/docs/deliverables/프로그램사양서/_gen/gen_program_spec_pptx.py b/docs/deliverables/프로그램사양서/_gen/gen_program_spec_pptx.py deleted file mode 100644 index 8eb02b9..0000000 --- a/docs/deliverables/프로그램사양서/_gen/gen_program_spec_pptx.py +++ /dev/null @@ -1,1115 +0,0 @@ -# -*- coding: utf-8 -*- -""" -킨텍스 자동전시시스템 구축 — 프로그램사양서 PPTX 생성기 -- 소스: docs/PLANNING.md(v3.0 M1~M18 + §5B 공통레이어), docs/design.md(§2.8 화면 80종 SCR), - docs/architecture/app.md(응답 봉투·에러·감사로그), IMPLEMENTATION_BACKLOG.md -- 디자인: 개발계획서(gen_dev_plan_pptx.py) 스타일 재사용 — KINTEX Blue #0066B3 / AI Purple #6D4AFF / 맑은 고딕 / 16:9 -- 순서도는 python-pptx 도형(단자·처리·판단·데이터·커넥터)으로 직접 작도(외부 이미지 금지) -- 보안 불변: 비밀번호/API키/서버IP/SSH 계정 미기재 -재실행: python gen_program_spec_pptx.py -""" -import os -from pptx import Presentation -from pptx.util import Pt -from pptx.dml.color import RGBColor -from pptx.enum.text import PP_ALIGN, MSO_ANCHOR -from pptx.enum.shapes import MSO_SHAPE, MSO_CONNECTOR -from pptx.oxml.ns import qn - -# ---------------- 팔레트 (개발계획서와 동일) ---------------- -BLUE = RGBColor(0x00, 0x66, 0xB3) -BLUE_DK = RGBColor(0x00, 0x44, 0x7A) -BLUE_LT = RGBColor(0xE1, 0xEF, 0xF9) -PURPLE = RGBColor(0x6D, 0x4A, 0xFF) -PURPLE_LT = RGBColor(0xEC, 0xE8, 0xFF) -INK = RGBColor(0x10, 0x18, 0x28) -MUTED = RGBColor(0x66, 0x70, 0x85) -LINE = RGBColor(0xE4, 0xE7, 0xEC) -BG = RGBColor(0xF9, 0xFA, 0xFB) -WHITE = RGBColor(0xFF, 0xFF, 0xFF) -GREEN = RGBColor(0x12, 0x9E, 0x63) -AMBER = RGBColor(0xE0, 0x8A, 0x00) -SLATE = RGBColor(0x98, 0xA2, 0xB3) -CARD = RGBColor(0xFF, 0xFF, 0xFF) -HALL_BG = RGBColor(0xF2, 0xF4, 0xF7) -ROW_ALT = RGBColor(0xFC, 0xFC, 0xFD) - -FONT = "맑은 고딕" -EMU_W = 12192000 -EMU_H = 6858000 - -prs = Presentation() -prs.slide_width = EMU_W -prs.slide_height = EMU_H -BLANK = prs.slide_layouts[6] -IN = 914400 -def inch(v): return int(v * IN) - -# ---------------- 기본 헬퍼 ---------------- -def slide(): - return prs.slides.add_slide(BLANK) - -def _set_fill(shape, color): - if color is None: - shape.fill.background() - else: - shape.fill.solid(); shape.fill.fore_color.rgb = color - -def _no_line(shape): - shape.line.fill.background() - -def rect(s, x, y, w, h, fill=None, line=None, line_w=1.0, shape=MSO_SHAPE.RECTANGLE, shadow=False): - sp = s.shapes.add_shape(shape, inch(x), inch(y), inch(w), inch(h)) - _set_fill(sp, fill) - if line is None: - _no_line(sp) - else: - sp.line.color.rgb = line; sp.line.width = Pt(line_w) - sp.shadow.inherit = False - if shadow: - el = sp._element.spPr - ef = el.find(qn('a:effectLst')) - if ef is None: - ef = el.makeelement(qn('a:effectLst'), {}); el.append(ef) - sh = ef.makeelement(qn('a:outerShdw'), - {'blurRad':'90000','dist':'38100','dir':'5400000','rotWithShape':'0'}) - clr = sh.makeelement(qn('a:srgbClr'), {'val':'101828'}) - alp = clr.makeelement(qn('a:alpha'), {'val':'16000'}) - clr.append(alp); sh.append(clr); ef.append(sh) - # 텍스트 없는 도형은 자동맞춤 방지 - sp.text_frame.word_wrap = True - return sp - -def text(s, x, y, w, h, content, size=14, color=INK, bold=False, align=PP_ALIGN.LEFT, - anchor=MSO_ANCHOR.TOP, line_spacing=1.0, space_after=2, wrap=True): - tb = s.shapes.add_textbox(inch(x), inch(y), inch(w), inch(h)) - tf = tb.text_frame - tf.word_wrap = wrap - tf.vertical_anchor = anchor - tf.margin_left = 0; tf.margin_right = 0; tf.margin_top = 0; tf.margin_bottom = 0 - if isinstance(content, str): - content = [content] - for i, para in enumerate(content): - p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() - p.alignment = align; p.line_spacing = line_spacing - p.space_after = Pt(space_after); p.space_before = Pt(0) - if isinstance(para, str): - segs = [{"t": para}] - elif isinstance(para, dict): - segs = [para] - else: - segs = para - for seg in segs: - r = p.add_run() - r.text = seg.get("t","") - r.font.name = FONT - r.font.size = Pt(seg.get("size", size)) - r.font.bold = seg.get("bold", bold) - r.font.color.rgb = seg.get("color", color) - rPr = r._r.get_or_add_rPr() - ea = rPr.makeelement(qn('a:ea'), {'typeface':FONT}); rPr.append(ea) - return tb - -def page_bg(s, color=BG): - rect(s, -0.1, -0.1, 13.53, 7.7, fill=color) - -def chip(s, x, y, w, h, label, fill, txtcolor=WHITE, size=9.5, bold=True): - rect(s, x, y, w, h, fill=fill, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, y, w, h, label, size=size, color=txtcolor, bold=bold, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -def card(s, x, y, w, h, fill=CARD, line=LINE): - return rect(s, x, y, w, h, fill=fill, line=line, line_w=1.0, - shape=MSO_SHAPE.ROUNDED_RECTANGLE, shadow=True) - -FOOT = "킨텍스 자동전시시스템 구축 · 프로그램사양서" -def content_header(s, kicker, title, idx, chip_txt=None, chip_col=BLUE): - page_bg(s, BG) - rect(s, 0, 0, 13.333, 0.12, fill=BLUE) - rect(s, 0.55, 0.5, 0.62, 0.62, fill=chip_col, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.55, 0.5, 0.62, 0.62, (chip_txt if chip_txt else f"{idx:02d}"), - size=(20 if not chip_txt else 22), color=WHITE, bold=True, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, 1.32, 0.5, 10.5, 0.28, kicker, size=11, color=PURPLE, bold=True) - text(s, 1.32, 0.74, 11.4, 0.44, title, size=21, color=INK, bold=True) - rect(s, 0.55, 1.28, 12.23, 0.02, fill=LINE) - text(s, 0.55, 7.06, 8, 0.3, FOOT, size=8.5, color=MUTED) - text(s, 11.3, 7.06, 1.48, 0.3, "2026-07-11", size=8.5, color=MUTED, align=PP_ALIGN.RIGHT) - -def divider(num, kr, en, points): - s = slide() - rect(s, -0.1, -0.1, 13.53, 7.7, fill=BLUE_DK) - rect(s, 8.6, -1.5, 8, 11, fill=BLUE, shape=MSO_SHAPE.PARALLELOGRAM) - rect(s, 10.9, -2, 6, 12, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) - text(s, 1.0, 2.15, 5, 2.2, num, size=150, color=RGBColor(0x2C,0x4E,0x82), bold=True) - rect(s, 1.15, 3.9, 0.9, 0.09, fill=PURPLE) - text(s, 1.15, 4.05, 8, 0.8, kr, size=34, color=WHITE, bold=True) - text(s, 1.18, 4.85, 8, 0.4, en, size=13, color=RGBColor(0x9C,0xB4,0xD8), bold=True) - for i, p in enumerate(points): - yy = 2.35 + i*0.62 - rect(s, 8.05, yy+0.06, 0.16, 0.16, fill=PURPLE, shape=MSO_SHAPE.OVAL) - text(s, 8.35, yy, 4.4, 0.5, p, size=12.5, color=WHITE, anchor=MSO_ANCHOR.MIDDLE) - return s - -# ---------------- 순서도(플로우차트) 도형 헬퍼 ---------------- -def fnode(s, cx, cy, w, h, txt, kind="proc"): - """중심좌표 기준 순서도 노드. return dict(cx,cy,l,r,t,b).""" - x = cx - w/2; y = cy - h/2 - if kind == "term": # 시작/끝 단자 - sh, fill, line, tcol = MSO_SHAPE.FLOWCHART_TERMINATOR, BLUE, BLUE, WHITE - elif kind == "proc": # 처리 - sh, fill, line, tcol = MSO_SHAPE.FLOWCHART_PROCESS, WHITE, BLUE, INK - elif kind == "dec": # 판단 - sh, fill, line, tcol = MSO_SHAPE.FLOWCHART_DECISION, PURPLE_LT, PURPLE, PURPLE - elif kind == "io": # 입출력(데이터) - sh, fill, line, tcol = MSO_SHAPE.FLOWCHART_DATA, BLUE_LT, BLUE, BLUE_DK - elif kind == "db": # 데이터베이스 - sh, fill, line, tcol = MSO_SHAPE.FLOWCHART_MAGNETIC_DISK, HALL_BG, SLATE, INK - elif kind == "wkr": # 사전정의 처리(워커/외부) - sh, fill, line, tcol = MSO_SHAPE.FLOWCHART_PREDEFINED_PROCESS, PURPLE_LT, PURPLE, PURPLE - elif kind == "proc_ok": # 성공 처리(강조) - sh, fill, line, tcol = MSO_SHAPE.FLOWCHART_PROCESS, RGBColor(0xEC,0xF7,0xF0), GREEN, RGBColor(0x0C,0x6B,0x43) - elif kind == "proc_bad": # 반려/차단 처리 - sh, fill, line, tcol = MSO_SHAPE.FLOWCHART_PROCESS, RGBColor(0xFD,0xF1,0xE3), AMBER, RGBColor(0x8A,0x54,0x00) - else: - sh, fill, line, tcol = MSO_SHAPE.FLOWCHART_PROCESS, WHITE, BLUE, INK - sp = rect(s, x, y, w, h, fill=fill, line=line, line_w=1.3, shape=sh) - sz = 8.4 if kind != "dec" else 8.0 - text(s, x, y, w, h, txt, size=sz, color=tcol, bold=(kind in ("term","dec")), - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.95) - return {"cx":cx,"cy":cy,"l":x,"r":x+w,"t":y,"b":y+h} - -def _arrowline(cxn, color, w): - cxn.line.color.rgb = color; cxn.line.width = Pt(w) - ln = cxn.line._get_or_add_ln() - tail = ln.makeelement(qn('a:tailEnd'), {'type':'triangle','w':'med','len':'med'}) - ln.append(tail) - -def arrow(s, x1, y1, x2, y2, color=SLATE, w=1.6, label=None, lcol=MUTED): - cxn = s.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, inch(x1), inch(y1), inch(x2), inch(y2)) - _arrowline(cxn, color, w) - if label: - mx, my = (x1+x2)/2, (y1+y2)/2 - text(s, mx-0.35, my-0.16, 0.9, 0.24, label, size=8, color=lcol, bold=True, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - -def arrow_elbow(s, x1, y1, x2, y2, first='v', color=SLATE, w=1.6, label=None, lcol=MUTED): - """L자 커넥터. first='v'면 수직→수평, 'h'면 수평→수직. 마지막 구간에 화살촉.""" - if first == 'v': - seg1 = s.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, inch(x1), inch(y1), inch(x1), inch(y2)) - seg1.line.color.rgb = color; seg1.line.width = Pt(w) - cxn = s.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, inch(x1), inch(y2), inch(x2), inch(y2)) - else: - seg1 = s.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, inch(x1), inch(y1), inch(x2), inch(y1)) - seg1.line.color.rgb = color; seg1.line.width = Pt(w) - cxn = s.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, inch(x2), inch(y1), inch(x2), inch(y2)) - _arrowline(cxn, color, w) - if label: - text(s, x1-0.05, (y1+y2)/2-0.12, 1.1, 0.24, label, size=8, color=lcol, bold=True, - anchor=MSO_ANCHOR.MIDDLE) - -def vchain(s, cx, y0, items, w=3.7, h=0.6, gap=0.34): - """세로 스택 + 순차 연결. items=[(txt,kind),...] return nodes.""" - nodes = []; y = y0 - for txt, kind in items: - n = fnode(s, cx, y + h/2, w, h, txt, kind); nodes.append(n); y += h + gap - for a, b in zip(nodes, nodes[1:]): - arrow(s, a["cx"], a["b"], b["cx"], b["t"]) - return nodes - -def fc_legend(s): - items = [("시작/끝",BLUE,"term"),("처리",WHITE,"proc"),("판단",PURPLE_LT,"dec"),("입출력",BLUE_LT,"io")] - x0 = 8.0 - for i,(lb,cl,kd) in enumerate(items): - x = x0 + i*1.2 - sh = {"term":MSO_SHAPE.FLOWCHART_TERMINATOR,"proc":MSO_SHAPE.FLOWCHART_PROCESS, - "dec":MSO_SHAPE.FLOWCHART_DECISION,"io":MSO_SHAPE.FLOWCHART_DATA}[kd] - ln = {"term":BLUE,"proc":BLUE,"dec":PURPLE,"io":BLUE}[kd] - rect(s, x, 1.34, 0.26, 0.2, fill=cl, line=ln, line_w=1.0, shape=sh) - text(s, x+0.31, 1.31, 0.85, 0.26, lb, size=8, color=MUTED, anchor=MSO_ANCHOR.MIDDLE) - -# ================================================================ -# 1. 표지 -# ================================================================ -s = slide() -rect(s, -0.1, -0.1, 13.53, 7.7, fill=BLUE_DK) -rect(s, 6.4, -1.5, 9, 11, fill=BLUE, shape=MSO_SHAPE.PARALLELOGRAM) -rect(s, 9.2, -2.0, 7, 12, fill=PURPLE, shape=MSO_SHAPE.PARALLELOGRAM) -rect(s, 0.85, 0.8, 0.42, 0.42, fill=WHITE, shape=MSO_SHAPE.OVAL) -rect(s, 0.97, 0.92, 0.18, 0.18, fill=PURPLE, shape=MSO_SHAPE.OVAL) -text(s, 1.42, 0.78, 6, 0.46, "KINTEX · WISE AI", size=15, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) -chip(s, 0.85, 2.35, 1.95, 0.42, "프로그램사양서", PURPLE, WHITE, size=12) -chip(s, 2.92, 2.35, 2.35, 0.42, "Program Specification", RGBColor(0x2B,0x4C,0x7E), WHITE, size=10.5) -text(s, 0.85, 2.95, 11, 1.9, [ - [{"t":"킨텍스 자동전시시스템", "size":46, "color":WHITE, "bold":True}], - [{"t":"프로그램사양서", "size":46, "color":WHITE, "bold":True}], - ], line_spacing=1.02) -text(s, 0.88, 4.78, 11, 0.5, - "Exhibition Automation Platform — 기능별 상세 사양 + 업무 순서도", - size=16, color=RGBColor(0xC7,0xDD,0xF2)) -text(s, 0.88, 5.35, 11.4, 0.4, - "프로그램 식별체계 PGM-ID = M{모듈}-{유형}-{일련} (예: M02-BATCH-01)", - size=12.5, color=RGBColor(0xB9,0xC7,0xEA)) -rect(s, 0.85, 6.15, 11.63, 0.02, fill=RGBColor(0x3A,0x5A,0x8C)) -text(s, 0.85, 6.35, 9, 0.4, [ - [{"t":"대상 ","size":11,"color":RGBColor(0x90,0xA6,0xC9)}, - {"t":"모듈 M1~M18 + 공통·시스템관리 레이어(§5B)","size":11.5,"color":WHITE,"bold":True}]]) -text(s, 0.85, 6.72, 9, 0.4, [ - [{"t":"기술스택 ","size":11,"color":RGBColor(0x90,0xA6,0xC9)}, - {"t":"React(Vite)+TS · Spring Boot 3.x(Java17)+MyBatis · PostgreSQL(PostGIS) · Redis · 나노바나나 워커","size":10.5,"color":WHITE}]]) -text(s, 9.7, 6.35, 2.78, 0.4, "작성일 2026-07-11", size=11, color=WHITE, bold=True, align=PP_ALIGN.RIGHT) -text(s, 9.7, 6.72, 2.78, 0.4, "planner · dev team", size=9.5, color=RGBColor(0x90,0xA6,0xC9), align=PP_ALIGN.RIGHT) - -# ================================================================ -# 2. 목차 -# ================================================================ -s = slide() -content_header(s, "CONTENTS", "목차", 0, chip_txt="C", chip_col=PURPLE) -toc = [ - ("01","개요 · 식별체계","문서 목적 / PGM-ID 부여 체계 / 프로그램 유형"), - ("02","프로그램 목록 총괄","모듈(M1~M18+공통)별 프로그램 ID·명·유형·화면"), - ("03","핵심 프로그램 상세 사양","20개 대표 프로그램 — 입출력·처리로직·테이블·예외"), - ("04","핵심 업무 순서도","8종 — 배치/검증/배선/렌더/옥션/등록/승인/배포"), - ("05","공통 규약","API 응답 봉투 · 에러 처리 · 감사 로그"), -] -for i,(no,t,d) in enumerate(toc): - y = 1.75 + i*1.0 - rect(s, 0.7, y, 11.9, 0.86, fill=CARD, line=LINE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - rect(s, 0.7, y, 0.09, 0.86, fill=(BLUE if i%2==0 else PURPLE), shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 1.0, y, 0.95, 0.86, no, size=26, color=(BLUE if i%2==0 else PURPLE), bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 2.1, y+0.15, 9.8, 0.34, t, size=15, color=INK, bold=True) - text(s, 2.1, y+0.52, 9.8, 0.3, d, size=10, color=MUTED) - -# ================================================================ -# 3. 개요 · 식별체계 -# ================================================================ -divider("01", "개요 · 식별체계", "OVERVIEW & ID SCHEME", - ["문서 목적 · 범위", "PGM-ID 부여 체계", "프로그램 유형 정의"]) - -s = slide() -content_header(s, "01 · 개요 · 식별체계", "프로그램 식별 체계 및 유형 정의", 1) -# 목적 -card(s, 0.55, 1.5, 12.23, 1.02) -text(s, 0.8, 1.66, 11.7, 0.3, "문서 목적", size=12, color=BLUE, bold=True) -text(s, 0.8, 1.98, 11.75, 0.5, - "본 사양서는 킨텍스 자동전시시스템(M1~M18 + 공통·시스템관리 §5B)의 프로그램 단위를 식별·정의하고, 핵심 프로그램의 상세 처리 사양과 주요 업무 순서도를 규정한다. 화면(SCR)·모듈 정의의 권위 출처는 PLANNING v3.0·design.md v2.0이며, 본 문서는 이를 프로그램 단위로 분해한다.", - size=10, color=INK, line_spacing=1.02) -# ID 체계 -text(s, 0.55, 2.78, 8, 0.3, "PGM-ID 부여 체계", size=12.5, color=PURPLE, bold=True) -seg = [("M02","모듈 코드 (M1~M18 / SYS·CMN=공통)",BLUE), - ("BATCH","유형·업무 약어",PURPLE), - ("01","모듈 내 일련번호",BLUE_DK)] -x = 0.55 -for i,(v,d,cl) in enumerate(seg): - w = 2.0 if i!=1 else 3.5 - rect(s, x, 3.14, w, 0.72, fill=WHITE, line=cl, line_w=1.4, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 3.22, w, 0.34, v, size=15, color=cl, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 3.56, w, 0.26, d, size=8, color=MUTED, align=PP_ALIGN.CENTER) - if i<2: - text(s, x+w-0.02, 3.14, 0.34, 0.72, "-", size=20, color=SLATE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - x += w + 0.34 -text(s, 8.5, 3.28, 4.3, 0.5, [[{"t":"예) ","size":11,"color":MUTED}, - {"t":"M02-BATCH-01","size":13,"color":INK,"bold":True}], - [{"t":"= 플로어플랜 배치 3안 생성 엔진","size":9,"color":MUTED}]], line_spacing=1.1) -# 유형 정의 -text(s, 0.55, 4.1, 8, 0.3, "프로그램 유형", size=12.5, color=BLUE, bold=True) -types = [ - ("온라인","ONL","사용자 화면 트랜잭션(React↔REST), 조회·입력·처리",BLUE), - ("API","API","엔진·서비스 API(룰·배치·배선·집계), 화면 비종속",BLUE_DK), - ("배치","BAT","스케줄러/야간 적재·역산 알림·주기 수집",PURPLE), - ("워커","WKR","Redis 큐 소비 비동기 워커(나노바나나 렌더 등)",PURPLE), -] -tw = 2.98 -for i,(kr,ab,d,cl) in enumerate(types): - x = 0.55 + i*(tw+0.11) - card(s, x, 4.46, tw, 1.72) - rect(s, x, 4.46, tw, 0.5, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 4.46, tw, 0.5, f"{kr} ({ab})", size=12, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+0.2, 5.1, tw-0.4, 1.0, d, size=9.5, color=INK, line_spacing=1.05) -text(s, 0.55, 6.4, 12.2, 0.3, - "표기 원칙 온라인 프로그램은 관련 화면(SCR ID)을 명시. 하나의 화면이 복수 프로그램을, 하나의 엔진 API가 복수 화면을 지원할 수 있다(N:M).", - size=9, color=MUTED) - -# ================================================================ -# 4. 프로그램 목록 총괄 -# ================================================================ -divider("02", "프로그램 목록 총괄", "PROGRAM CATALOG", - ["모듈별 PGM-ID·명·유형", "관련 화면(SCR) 매핑", "약 60개 프로그램"]) - -# 총괄 통계 -s = slide() -content_header(s, "02 · 프로그램 목록 총괄", "프로그램 총괄 개요", 2) -stats = [("18","도메인 모듈 (M1~M18)"),("60+","프로그램 (온라인·API·배치·워커)"), - ("80","화면 (SCR·웹/모바일/공개/관리자)"),("4","프로그램 유형")] -mw = 2.94 -for i,(v,l) in enumerate(stats): - x = 0.55 + i*(mw+0.1) - rect(s, x, 1.55, mw, 1.15, fill=(BLUE if i%2==0 else PURPLE), shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x, 1.72, mw, 0.6, v, size=34, color=WHITE, bold=True, align=PP_ALIGN.CENTER) - text(s, x, 2.36, mw, 0.28, l, size=9, color=WHITE, align=PP_ALIGN.CENTER) -# 모듈군 구성 요약 -groups = [ - ("공통 · 시스템관리 (§5B / SYS·CMN)", BLUE_DK, - "인증(JWT+OTP)·사용자·RBAC·공통코드·메뉴·감사로그·설정 + 업무일지·일정·쪽지·공지·검색·회의록·보고·알림"), - ("설계·시각화 코어 (M2~M5 · P0)", PURPLE, - "배치 3안 생성·규정검증·부스설계·배선 최단경로·나노바나나 RenderJob 워커"), - ("판매·발주·정산 (M1·M6~M9·M15)", BLUE, - "자동 견적·서류/마일스톤·등록업체 매칭·물류 슬롯·정산결제·공사 옥션(역경매·낙찰)"), - ("관람·참가·마케팅 (M10~M14)", BLUE, - "관람객 등록·배지·체크인·리드캡처·비즈매칭·EDM/공개사이트·wayfinding·현장운영"), - ("경영·콘텐츠·관리 (M16~M18)", PURPLE, - "BI 데이터마트/대시보드·CMS/마이크로사이트·마스터데이터·룰셋·테넌트 관리"), -] -y = 2.92 -for i,(t,cl,d) in enumerate(groups): - rect(s, 0.55, y, 12.23, 0.72, fill=CARD, line=LINE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - rect(s, 0.55, y, 0.1, 0.72, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.82, y+0.1, 5.0, 0.5, t, size=11, color=cl, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 5.9, y+0.1, 6.75, 0.52, d, size=9, color=MUTED, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.95) - y += 0.8 - -# --- 프로그램 목록 표 헬퍼 --- -LIST_COLS = [(0.6,1.55,"PGM-ID"),(2.2,3.35,"프로그램명"),(5.62,1.05,"유형"), - (6.75,1.45,"화면(SCR)"),(8.25,4.5,"개요")] -def list_slide(title, rows, note=None): - s = slide() - content_header(s, "02 · 프로그램 목록 총괄", title, 2) - # 헤더 - rect(s, 0.55, 1.5, 12.23, 0.42, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - for x,w,h in LIST_COLS: - text(s, x+0.1, 1.5, w-0.12, 0.42, h, size=9.5, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE) - y = 1.96 - rh = 0.445 if len(rows) > 10 else 0.49 - for i,(pid,name,typ,scr,desc) in enumerate(rows): - if i % 2 == 0: - rect(s, 0.55, y, 12.23, rh, fill=ROW_ALT) - rect(s, 0.55, y+rh, 12.23, 0.008, fill=LINE) - text(s, 0.7, y, 1.5, rh, pid, size=8.6, color=BLUE_DK, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 2.3, y, 3.25, rh, name, size=8.8, color=INK, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.9) - tcol = {"온라인":BLUE,"API":BLUE_DK,"배치":PURPLE,"워커":PURPLE,"온라인/WS":BLUE}.get(typ,MUTED) - text(s, 5.62, y, 1.05, rh, typ, size=8.2, color=tcol, bold=True, anchor=MSO_ANCHOR.MIDDLE) - text(s, 6.85, y, 1.35, rh, scr, size=8.0, color=MUTED, anchor=MSO_ANCHOR.MIDDLE) - text(s, 8.35, y, 4.35, rh, desc, size=8.0, color=MUTED, anchor=MSO_ANCHOR.MIDDLE, line_spacing=0.9) - y += rh - if note: - text(s, 0.55, 6.72, 12.2, 0.3, note, size=8.2, color=MUTED) - return s - -# 목록 1 — 공통 · 시스템관리 (SYS/CMN) -list_slide("프로그램 목록 (1/6) — 공통 · 시스템관리 (§5B)", [ - ("SYS-AUTH-01","로그인·JWT 발급","온라인","SCR-01","ID/PW 인증→JWT 발급, 행사 워크스페이스 선택"), - ("SYS-AUTH-02","2차 인증 OTP(TOTP)","API","SCR-51","TOTP RFC6238 검증·QR 등록·재설정"), - ("SYS-AUTH-03","로그인 실패 잠금·해제","API","SCR-01","실패 횟수 잠금 + 관리자 해제"), - ("SYS-USER-01","사용자 관리","온라인","SCR-A1","6역할+등록업체·관람객 계정 CRUD·상태"), - ("SYS-RBAC-01","역할·권한(RBAC) 관리","온라인","SCR-A2","플랫폼·행사 이중 평가 권한 매핑"), - ("SYS-CODE-01","공통코드 관리","온라인","SCR-A3","홀·부스유형·공종14분류·요금코드"), - ("SYS-MENU-01","메뉴 관리","온라인","SCR-A4","역할별 포털 IA 메뉴 트리·권한"), - ("SYS-AUDIT-01","감사로그 조회","온라인","SCR-A5","승인·낙찰·룰셋개정·리드접근 전수 조회"), - ("SYS-CONF-01","시스템설정","온라인","SCR-A6","요율/규정 룰셋 버전·마감정책·AI 게이트"), -], note="§5B = UIWS/WISE 표준 이식. 시스템관리는 M18 백오피스와 통합(중복 재구현 없음).") - -list_slide("프로그램 목록 (2/6) — 공통 업무 기능 (§5B)", [ - ("CMN-WORK-01","업무일지(worklog)","온라인","SCR-39","홀매니저 현장일지·업체 시공일지·댓글"), - ("CMN-SCHD-01","통합 일정·캘린더","온라인","SCR-40","마일스톤·옥션마감·반입슬롯 통합 뷰"), - ("CMN-MSG-01","쪽지(message)","온라인","SCR-41","행사 내 주최자↔참가↔업체↔홀매니저"), - ("CMN-NOTI-01","공지(notice)","온라인","SCR-42","킨텍스/행사 공지·팝업 (M17 CMS와 정합)"), - ("CMN-SRCH-01","통합검색(search)","API","SCR-44","행사·부스·업체·문서·콘텐츠 크로스 검색"), - ("CMN-MEET-01","회의록(STT→PDF)","배치","SCR-45","사전협의(D-30) 녹음→STT→Jasper PDF"), - ("CMN-RPT-01","업무보고·통계","배치","SCR-46","일/주/월/분기/연 보고·통계 Jasper PDF"), - ("CMN-ALRM-01","알림센터(notification)","온라인/WS","SCR-47","D-데이·낙찰·승인·결제 알림 WebSocket"), - ("CMN-PREF-01","마이페이지·개인화","온라인","SCR-48","즐겨찾기·최근방문·환경설정"), -]) - -# 목록 3 — M1~M5 코어 -list_slide("프로그램 목록 (3/6) — M1 배정견적 · M2~M5 코어 (P0)", [ - ("M01-QUOTE-01","자동 견적 계산 엔진","API","SCR-18","2,250원/㎡×면적×일수×성수기·전시장 계수"), - ("M01-ONL-01","홀 배정 신청","온라인","SCR-18","가용성 캘린더→홀/반홀 선택→신청서 생성"), - ("M01-ONL-02","부스 판매 인벤토리","온라인","SCR-19","부스 재고·상태·셀프선택 판매 관리"), - ("M02-BATCH-01","부스 배치 3안 생성 엔진","배치","SCR-03","제약 솔버+휴리스틱, 목표 다양화 3안"), - ("M02-RULE-01","배치 규정 자동검증","API","SCR-03","통로·비상구·바닥하중·복층 위반 플래깅"), - ("M02-ONL-01","부스 배치 에디터","온라인","SCR-03","PostGIS 폴리곤 캔버스 편집·확정"), - ("M02-ONL-02","배치안 비교·병합","온라인","SCR-04","레이어 토글·병합→규정 재검증(S7 조감)"), - ("M03-DSN-01","부스 설계 3안 생성","API","SCR-06","레이아웃·파라메트릭 구조 3안(용도 다양화)"), - ("M03-ONL-01","부스 설계 스튜디오","온라인","SCR-06","조립/독립 설계·3D 프리뷰·선택/병합"), - ("M03-RULE-01","설계 규정 사전검증","API","SCR-09","높이5m·리깅·복층·방염·금지작업 플래깅"), - ("M04-WIRE-01","배선 최단경로 산출 엔진","API","SCR-07","트렌치→분전반 통로횡단 최소 배선(PostGIS)"), - ("M04-ONL-01","유틸리티 배선 뷰","온라인","SCR-07","전기/네트워크/급배수 배선·조명 배치"), - ("M04-ONL-02","신청 요약·위치표시도","온라인","SCR-08","단말 클릭→위치표시도 자동생성·견적"), - ("M05-API-01","RenderJob 큐 발행","API","SCR-12","씬 컴파일→스키마해시→Redis 큐 발행"), - ("M05-WKR-01","나노바나나 렌더 워커","워커","SCR-12","Gemini image-to-image·방어·워터마크"), - ("M05-ONL-01","시각화 갤러리","온라인","SCR-12","표준 샷 S1~S7·Before/After 슬라이더"), -], note="M2~M5 = 핵심 차별화 P0. 배치·배선은 결정적 알고리즘(솔버) 중심, LLM은 조건해석 보조.") - -# 목록 4 — M6~M9, M15 -list_slide("프로그램 목록 (4/6) — M6~M9 판매운영 · M15 옥션", [ - ("M06-ONL-01","서류·마일스톤 워크플로","온라인","SCR-22","D-150/30/25/7 마일스톤·서류 상태"), - ("M06-DOC-01","신고서류 웹폼→HWP/PDF","API","SCR-23","7종 서식 자동생성·LLM 누락/불일치 검수"), - ("M06-BAT-01","마일스톤 D-데이 알림","배치","SCR-40","역산 리마인더 스케줄 발행→알림센터"), - ("M07-ONL-01","등록업체 매칭·RFQ","온라인","SCR-24","규모·업종·예산·지역 추천→RFQ 발송"), - ("M07-BAT-01","등록업체 DB 주기 수집","배치","SCR-A7","14분류×739개 공개데이터 수집·정규화"), - ("M08-ONL-01","반입/반출 슬롯 예약","온라인","SCR-25","하역장 슬롯·중량물 우선·통행증 QR"), - ("M09-ONL-01","정산·결제 대시보드","온라인","SCR-21","납부 스케줄·PG 결제·예치금 정산 내역"), - ("M09-BAT-01","납부 스케줄 자동 생성","배치","SCR-21","계약금20/중도금30+20/잔금30+예치금"), - ("M15-ONL-01","옥션 목록·개설","온라인","SCR-26","유형·라운드·마감·낙찰기준·가중치 설정"), - ("M15-ONL-02","옥션 상세·실시간 순위","온라인/WS","SCR-27","자료열람·응찰·실시간 순위 WebSocket"), - ("M15-DOC-01","견적서 작성·PDF 산출","API","SCR-28","라인아이템·총액·부가세·버전·PDF"), - ("M15-AWD-01","낙찰 비교·선정(Award)","API","SCR-29","최저가/종합점수(가격+평판+납기) 비교"), -], note="M15 = 폐루프 연결고리. M2~M5 산출물(물량서·시공 예상 이미지)이 응찰 근거 자료.") - -# 목록 5 — M10~M14 관람·현장 -list_slide("프로그램 목록 (5/6) — M10~M14 관람 · 참가 · 현장", [ - ("M10-ONL-01","관람객 등록 관리","온라인","SCR-30","유형별 사전등록 폼·현황 대시보드"), - ("M10-BADGE-01","배지/QR 발급","API","SCR-P4","모바일 배지/QR 발급·배지 템플릿(M17)"), - ("M10-ONL-02","현장 체크인","온라인","SCR-31","QR 체크인·즉석 배지·오프라인 폴백"), - ("M10-LEAD-01","리드캡처·AI 스코어링","API","SCR-32","배지 QR 스캔→연락처·관심도·팔로업"), - ("M11-MATCH-01","AI 미팅 추천","API","SCR-M8","프로필·intent 매칭→미팅 슬롯 예약"), - ("M12-EDM-01","EDM·캠페인 관리","배치","SCR-33","세그먼트 캠페인·리마인더·수신동의"), - ("M12-PUB-01","공개 홍보 사이트","온라인","SCR-P1","행사소개·일정·교통, SEO·다국어(한영중일)"), - ("M12-ONL-01","스폰서십 패키지 판매","온라인","SCR-34","스폰서 등급·재고·판매 관리"), - ("M13-NAV-01","실내 내비 경로 산출","API","SCR-M6","블루닷/존레벨 경로(부스·시설·비상구)"), - ("M14-ONL-01","홀 현장운영 대시보드","온라인","SCR-14","입장·혼잡·전력부하·주차·안전 플래그"), -]) - -# 목록 6 — M16~M18 경영·콘텐츠·관리 -list_slide("프로그램 목록 (6/6) — M16 BI · M17 CMS · M18 관리", [ - ("M16-ETL-01","BI 데이터마트 적재","배치","SCR-13","스타스키마 Fact/Dim 야간 적재(KpiSnapshot)"), - ("M16-ONL-01","경영분석 대시보드","온라인","SCR-13","가동률·매출구성·P&L·리텐션·수요예측"), - ("M17-ONL-01","CMS 콘텐츠·게시 워크플로","온라인","SCR-35","초안→검수→게시·버전·다국어"), - ("M17-ONL-02","마이크로사이트 편집기","온라인","SCR-36","참가업체 부스소개·제품·예상샷 게시"), - ("M17-ONL-03","다국어 콘텐츠 관리","온라인","SCR-37","한/영/중/일 콘텐츠·번역 검수"), - ("M18-ONL-01","마스터데이터 관리","온라인","SCR-A7","홀·요율·유틸요금·부스표준·등록업체"), - ("M18-ONL-02","규정 룰셋 버전 관리","온라인","SCR-A8","연 단위 요율·규정 개정 버전·이력"), - ("M18-ONL-03","테넌트 관리·온보딩","온라인","SCR-A9","전시관(테넌트) 마스터·온보딩(플랫폼관)"), - ("M18-ONL-04","AI 플랫폼 설정(AiConfig)","온라인","SCR-A10","Claude 기본·설정형 전환·폴백"), -], note="M18 = §5B-1 시스템관리 + 킨텍스 마스터데이터 확장. 테넌트/AiConfig는 v3.0·F100 반영.") - -# ================================================================ -# 5. 핵심 프로그램 상세 사양 -# ================================================================ -divider("03", "핵심 프로그램 상세 사양", "PROGRAM DETAIL SPEC", - ["모듈 대표 20개 프로그램", "입력·출력·처리로직", "관련 테이블·예외 처리"]) - -def detail_slide(idx_total, d): - s = slide() - page_bg(s, BG) - rect(s, 0, 0, 13.333, 0.12, fill=BLUE) - cl = d.get("color", BLUE) - # PGM-ID chip - rect(s, 0.55, 0.42, 2.35, 0.62, fill=cl, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.55, 0.42, 2.35, 0.62, d["id"], size=15, color=WHITE, bold=True, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, 3.02, 0.4, 8.5, 0.34, d["name"], size=18, color=INK, bold=True) - text(s, 3.02, 0.78, 9.7, 0.26, d["module"], size=9.5, color=MUTED) - # 메타 칩 (유형·화면) - chip(s, 10.6, 0.42, 1.05, 0.3, d["type"], cl, WHITE, size=9) - chip(s, 11.72, 0.42, 1.06, 0.3, d["scr"], RGBColor(0x2B,0x4C,0x7E), WHITE, size=9) - text(s, 10.6, 0.78, 2.2, 0.24, f"상세사양 {idx_total}/20", size=8, color=MUTED, align=PP_ALIGN.RIGHT) - rect(s, 0.55, 1.18, 12.23, 0.02, fill=LINE) - text(s, 0.55, 7.06, 8, 0.3, FOOT, size=8.5, color=MUTED) - text(s, 11.3, 7.06, 1.48, 0.3, d["id"], size=8.5, color=MUTED, align=PP_ALIGN.RIGHT) - - def block(x, y, w, h, title, tcol, items, numbered=False): - card(s, x, y, w, h) - rect(s, x, y+0.12, 0.09, h-0.24, fill=tcol, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.26, y+0.13, w-0.4, 0.3, title, size=10.5, color=tcol, bold=True) - yy = y + 0.5 - for i,it in enumerate(items): - if numbered: - rect(s, x+0.26, yy+0.015, 0.26, 0.22, fill=tcol, shape=MSO_SHAPE.OVAL) - text(s, x+0.26, yy, 0.26, 0.22, str(i+1), size=8, color=WHITE, bold=True, - align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) - text(s, x+0.62, yy-0.02, w-0.78, 0.44, it, size=8.6, color=INK, line_spacing=0.92) - else: - rect(s, x+0.28, yy+0.07, 0.08, 0.08, fill=tcol, shape=MSO_SHAPE.OVAL) - text(s, x+0.5, yy-0.01, w-0.66, 0.44, it, size=8.7, color=INK, line_spacing=0.92) - yy += d.get("lh", 0.315) - return - - # 좌: 기능설명 / 입력 / 출력 - block(0.55, 1.32, 6.0, 1.42, "기능 설명", cl, d["desc"]) - block(0.55, 2.86, 2.9, 3.05, "입력 (Input)", BLUE_DK, d["input"]) - block(3.65, 2.86, 2.9, 3.05, "출력 (Output)", BLUE_DK, d["output"]) - # 우: 처리 로직 / 테이블 / 예외 - block(6.72, 1.32, 6.06, 3.05, "처리 로직", PURPLE, d["logic"], numbered=True) - block(6.72, 4.49, 6.06, 1.42, "예외 처리", AMBER, d["exc"]) - # 하단: 관련 테이블 스트립 - rect(s, 0.55, 6.05, 6.0, 0.86, fill=HALL_BG, line=LINE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 0.75, 6.14, 5.7, 0.26, "관련 테이블 / 데이터", size=9.5, color=BLUE_DK, bold=True) - text(s, 0.75, 6.42, 5.7, 0.44, " · ".join(d["tables"]), size=8.6, color=INK, line_spacing=0.95) - # 하단 우: 연동 - rect(s, 6.72, 6.05, 6.06, 0.86, fill=BLUE_LT, line=BLUE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, 6.92, 6.14, 5.8, 0.26, "연동 / 비고", size=9.5, color=BLUE_DK, bold=True) - text(s, 6.92, 6.42, 5.8, 0.44, d["link"], size=8.6, color=INK, line_spacing=0.95) - -DETAILS = [ - {"id":"M01-QUOTE-01","name":"자동 견적 계산 엔진","type":"API","scr":"SCR-18","color":BLUE, - "module":"M1 행사·홀 배정 및 자동 견적","lh":0.30, - "desc":["규칙 엔진이 홀/반홀·기간·성수기 조건으로 임대 견적을 즉시 산정", - "HWP 신청→협의→견적(수일)을 즉시 자동 회신으로 전환"], - "input":["행사 기간(장치·전시·철거일)","홀/반홀 선택·면적(㎡)","부대(로비·옥외·회의실)","요율 룰셋 버전(테넌트)"], - "output":["임대료·관리비 예치금 소계","성수기/전시장 계수 반영 총액","견적 명세(항목별) JSON","킨텍스 제출 신청서식"], - "logic":["요율 마스터·성수기 규칙 로딩(tenant_id 스코프)", - "기본액 = 2,250원/㎡ × 면적 × 일수", - "계수 적용: 성수기 +10% / 비수기 -10% / 1전시장 +10%", - "초과시간(1일 임대료 1/10)·용도외 30% 할증 가산", - "예치금 15~20% 산출 → 견적 명세 조립·반환"], - "tables":["hall_master","rate_master","event","quotation_estimate"], - "exc":["요율 룰셋 미확정 → RULESET_NOT_FOUND 반환","면적/기간 유효성 위반 → VALIDATION 오류"], - "link":"연동 M9 정산·결제 · 행사일정 DB. 최종 배정 확정은 킨텍스 내부 의사결정(가견적까지)."}, - - {"id":"M02-BATCH-01","name":"부스 배치 3안 생성 엔진","type":"배치","scr":"SCR-03","color":PURPLE, - "module":"M2 플로어플랜 스튜디오 (P0)","lh":0.30, - "desc":["제약충족 솔버+휴리스틱으로 서로 다른 최적화 목표의 배치 정확히 3안 생성", - "생성형 LLM이 아닌 결정적 알고리즘 중심(LLM은 조건 해석 보조)"], - "input":["홀 선택·실측 지오메트리","목표 부스 수·프리미엄 비율","주출입구·무대·라운지 조건","통로폭·비상구·트렌치 제약"], - "output":["배치 3안(부스 폴리곤 집합)","안별 최적화 지표(부스수·동선·안전)","안별 규정검증 결과","S7 조감 시각화 입력"], - "logic":["홀 좌표계·제약(바닥하중·비상구·트렌치) 로딩", - "목표별 파라미터 분기(1안 부스수 / 2안 동선 / 3안 안전)", - "솔버로 부스 폴리곤 배치·통로 확보", - "각 안 M02-RULE-01 규정검증 호출", - "3안 + 지표·위반 요약 저장·반환"], - "tables":["hall_master","booth(PostGIS polygon)","design_plan(버전·출처)","trench_grid"], - "exc":["제약 충족 해 없음 → 부분안+완화 제안","트렌치 좌표 미확보 → 근사도면 경고 라벨"], - "link":"연동 M02-RULE-01·M03·M5·M6(제출 서류). 소방 최종 유권해석은 관할기관."}, - - {"id":"M02-RULE-01","name":"배치 규정 자동검증","type":"API","scr":"SCR-03·09","color":PURPLE, - "module":"M2 플로어플랜 스튜디오 (P0)","lh":0.30, - "desc":["배치안을 수치 규정에 대조해 위반을 차단(hard)·경고(soft)로 플래깅", - "병합/편집 결과는 재검증 필수(제약 파손 가능)"], - "input":["대상 배치안(부스 폴리곤)","홀 규정 룰셋(버전)","바닥하중·비상구·통로 기준","복층 가능 홀 매트릭스"], - "output":["위반 목록(항목·좌표·등급)","차단/경고 분류","검증 통과 여부(boolean)","검수 리포트(SCR-09)"], - "logic":["룰셋 로딩(tenant_id·버전 고정)", - "피난 통로폭·비상구 접근 거리 판정", - "홀별 바닥하중(2~5t/㎡)·복층 1/2 판정", - "이격(벽30cm·천장60cm)·소방 체크리스트 대조", - "위반 등급화 → hard=확정 차단, soft=경고"], - "tables":["ruleset_version","booth","hall_master","compliance_result"], - "exc":["룰셋 버전 불일치 → 최신 버전 강제·경고","좌표 결손 폴리곤 → 검증 제외·플래그"], - "link":"연동 M2·M3(SCR-09)·M10 승인. 최종 판정은 사람(홀매니저·구조기술사)."}, - - {"id":"M03-DSN-01","name":"부스 설계 3안 생성","type":"API","scr":"SCR-06","color":PURPLE, - "module":"M3 부스 설계 스튜디오 (P0)","lh":0.30, - "desc":["부스 크기·업종·예산 입력으로 레이아웃·파라메트릭 구조 초안 3안 생성", - "조립부스는 옵션 선택형, 독립부스는 AI 초안+장치업체 편집"], - "input":["부스 좌표·크기(M2)","업종·전시품·예산","용도존(시연/상담/창고)","조립/독립 유형"], - "output":["설계 3안(벽체·트러스·사인)","존 배치·집기 블록","M5 예상 사진 프롬프트 입력","규정 사전검증 플래그"], - "logic":["부스 골격·표준 자재 카탈로그 로딩", - "목표별 3안 분기(시연/상담/예산절감)", - "파라메트릭 구조·존 레이아웃 생성", - "M03-RULE-01 사전검증 호출(높이·방염)", - "3안 저장 → 선택/병합 대기"], - "tables":["booth","design_plan","material_catalog","design_zone"], - "exc":["예산/크기 비현실 → 경고·근사안","업로드 도면 인식 저정확 → 참고용 라벨"], - "link":"연동 M7 매칭·M5 시각화·kxwp 제출. 시공 상세도·구조판정은 장치업체 책임."}, - - {"id":"M04-WIRE-01","name":"배선 최단경로 산출 엔진","type":"API","scr":"SCR-07","color":PURPLE, - "module":"M4 유틸리티 설계 (P0)","lh":0.30, - "desc":["단말 위치에서 최근접 트렌치까지 통로 횡단 최소 배선 경로를 PostGIS로 산출", - "위치표시도 수기 작도를 자동 생성으로 대체"], - "input":["부스 내 단말 좌표(전기·LAN·급배수)","홀 트렌치 그리드 좌표","기기 목록·kW/회선","유틸리티 요금 마스터"], - "output":["배선 LineString(전기적/망청/급배수녹)","분전반 용량·수량","위치표시도·자동 견적","S6 오버레이 입력"], - "logic":["트렌치 그리드·공급 매트릭스 로딩", - "단말→최근접 트렌치 매칭(ST_ClosestPoint)", - "통로 횡단 최소 경로 산출(ST_ShortestLine)", - "kW 합산→분전반 용량·수량 산정", - "요금 견적·위치표시도 조립·반환"], - "tables":["trench_grid","utility_order(LineString)","rate_utility","booth"], - "exc":["트렌치 좌표 미확보 → 산출 보류·근사 경고","인터넷 마감(D-25) 초과 → 신청 차단 안내"], - "link":"연동 M9 결제·M5(S6 오버레이는 백엔드 래스터 우선)·kxwp/사무국 릴레이."}, - - {"id":"M05-WKR-01","name":"나노바나나 렌더 워커","type":"워커","scr":"SCR-12","color":PURPLE, - "module":"M5 나노바나나 시각화 (P0·핵심 차별화)","lh":0.30, - "desc":["Redis 큐의 RenderJob을 소비해 Gemini image-to-image로 시공 후 예상 사진 생성", - "ReRoomAI 방어 로직 이식 + 워터마크·고지 강제"], - "input":["RenderJob(씬 스키마·샷 프리셋)","참조 이미지(간이 렌더·홀 사진)","프롬프트 조각(유형·스타일·레이어)","행사별 렌더 쿼터"], - "output":["생성 이미지(오브젝트 스토리지)","워터마크·메타데이터 임베드","WebSocket 완료 푸시","스키마 해시 캐시"], - "logic":["큐 소비 → 크기 가드(8MB·base64 재검증)", - "참조 이미지 1024px 정규화·mimeType 감지", - "Gemini generateContent 호출(inlineData+text)", - "SAFETY/quota 에러 분기→친화 메시지", - "성공 시에만 쿼터 차감·워터마크→푸시"], - "tables":["render_job(상태·이미지)","render_quota","object_storage(메타)"], - "exc":["finishReason=SAFETY → 차단 에러","RESOURCE_EXHAUSTED/429 → 재시도·상한","한글 간판 렌더 불안정 → 후처리 합성"], - "link":"연동 M2~M4(자료)·WebSocket 푸시·M15 응찰 근거. API키는 서버 env(GEMINI_API_KEY)."}, - - {"id":"M06-DOC-01","name":"신고서류 웹폼→HWP/PDF","type":"API","scr":"SCR-23","color":BLUE, - "module":"M6 서류·마일스톤 워크플로","lh":0.30, - "desc":["7종 신고서류를 웹폼 입력으로 킨텍스 제출 형식(HWP/PDF) 자동 생성", - "LLM이 누락 항목·배치도-운영계획서 불일치 검수"], - "input":["서식 유형·행사·부스 데이터","배치도(M2)·정산(M9) 참조","작성자 입력 필드","서식 템플릿(마스터)"], - "output":["HWP/PDF 제출 파일","검수 요약 리포트(누락·불일치)","마일스톤 제출 상태","업로드 안내 릴레이"], - "logic":["서식 템플릿·행사 데이터 병합", - "필수 항목 누락 검출", - "배치도-운영계획서 부스수 등 교차 검증", - "HWP/PDF 렌더링·서명 필드 생성", - "검수 리포트→홀매니저 전달"], - "tables":["document","doc_template","milestone","event"], - "exc":["필수 필드 누락 → 생성 차단·목록 반환","AI 생성 이미지 포함 시 → 서류에서 자동 배제"], - "link":"연동 kxwp(제출 릴레이·API 미공개)·M10 승인. 계약·심사 서류엔 도면만 유효."}, - - {"id":"M07-ONL-01","name":"등록업체 매칭·RFQ","type":"온라인","scr":"SCR-24","color":BLUE, - "module":"M7 등록업체 매칭·견적","lh":0.30, - "desc":["14분류×739개 등록업체를 조건 기반 추천, 설계안 첨부 RFQ 발송·비교", - "미등록 업체 시공 엄금 규정을 시스템이 강제"], - "input":["부스 규모·업종·예산·지역","M3 설계안 첨부","공종 분류 필터","등록업체 DB(검증)"], - "output":["추천 업체 목록·점수","RFQ 발송·수신 현황","견적 비교표","옥션 초대 자격(적격)"], - "logic":["등록업체 DB 조회·자격 검증", - "조건 매칭 점수화·정렬", - "RFQ에 설계안·물량 첨부 발송", - "회신 견적 취합·비교표 구성", - "적격 업체만 M15 옥션 초대 허용"], - "tables":["company(등록업체)","rfq","rfq_quote","booth"], - "exc":["미등록 업체 초대 시도 → 원천 차단","응답 없음 → 리마인더·대체 추천"], - "link":"연동 M3(설계)·M15(옥션 자격 게이트)·CCPY 공개 DB 수집(M07-BAT-01)."}, - - {"id":"M09-BAT-01","name":"납부 스케줄 자동 생성","type":"배치","scr":"SCR-21","color":BLUE, - "module":"M9 정산·결제","lh":0.30, - "desc":["계약 확정 시 단계별 납부 스케줄을 자동 생성하고 역산 알림을 발행", - "예치금 대비 실사용 정산 내역 투명화"], - "input":["계약 총액·확정일","납부 규칙(20/30+20/30)","예치금 비율(15~20%)","행사 일정"], - "output":["납부 스케줄(회차·금액·기일)","알림센터 리마인더 이벤트","예치금 정산 기준","결제 대기 항목"], - "logic":["계약 총액·규칙 로딩", - "계약금20→중도금30+20→잔금30 분할", - "예치금 15~20% 별도 스케줄", - "기일 역산 알림 이벤트 발행", - "PG 결제 대기 항목 생성"], - "tables":["payment","payment_schedule","event","rate_master"], - "exc":["계약 미확정 → 스케줄 생성 보류","PG 실패 → 재시도·수동 처리 플래그"], - "link":"연동 M1 견적·PG 결제·CMN-ALRM 알림. 세금계산서 등 재무연동 협의."}, - - {"id":"M15-ONL-02","name":"옥션 상세·실시간 순위·응찰","type":"온라인/WS","scr":"SCR-27","color":BLUE, - "module":"M15 공사/장치 옥션 (핵심 플로우)","lh":0.30, - "desc":["적격 등록업체가 AI 자료 열람 후 견적서로 응찰, 실시간 순위를 WebSocket으로 노출", - "역경매(라운드 내 인하)·마감 자동 처리"], - "input":["옥션(유형·라운드·마감·기준)","M2~M5 자료·물량서","응찰 견적서(총액·납기)","업체 자격(M7 검증)"], - "output":["실시간 순위(현재/최저/내 위치)","응찰 이력(버전)","라운드 마감 상태","비교표 입력(Award)"], - "logic":["응찰 자격 검증(등록업체만)", - "AI 자료 열람 권한 부여", - "견적서 제출→종합점수 산정", - "실시간 순위 WebSocket 브로드캐스트", - "마감 도달→자동 마감·집계"], - "tables":["auction","quotation(=bid·버전)","company","render_job"], - "exc":["미등록 응찰 → 차단","마감 후 제출 → 거부","동점 → 납기·평판 tie-break"], - "link":"연동 M2~M5·M7·M15-AWD(낙찰)·M6/M9. 익명 순위 옵션 지원."}, - - {"id":"M15-AWD-01","name":"낙찰 비교·선정(Award)","type":"API","scr":"SCR-29","color":BLUE, - "module":"M15 공사/장치 옥션 (핵심 플로우)","lh":0.30, - "desc":["최저가 또는 종합평가(가격+평판+납기) 가중 스코어로 견적서를 비교·낙찰", - "낙찰 견적서를 계약/발주 문서로 전환"], - "input":["옥션 응찰 견적서 집합","낙찰기준·가중치","평판 데이터(규정준수·평점)","납기 조건"], - "output":["항목별 비교표·순위","낙찰(Award) 결과·사유","계약/발주 링크(M6/M9)","시공 일정 연계(M8)"], - "logic":["응찰 견적서 정규화(사양 정렬)", - "가격/평판/납기 점수화·가중합", - "최저가 or 종합점수 순위 산정", - "전시업체 낙찰 선정(사유 기록)", - "낙찰서→계약/발주 전환·감사기록"], - "tables":["auction","quotation","award","company_reputation"], - "exc":["유효 응찰 0건 → 유찰·재개설","가중치 합≠100% → 검증 오류"], - "link":"연동 M6 계약·M9 발주정산·M8 시공일정·SYS-AUDIT(낙찰 전수기록)."}, - - {"id":"M10-BADGE-01","name":"배지/QR 발급","type":"API","scr":"SCR-P4","color":BLUE, - "module":"M10 관람객 등록·배지·체크인","lh":0.30, - "desc":["사전등록 관람객에게 모바일 배지/QR을 발급, 현장 즉석 배지 인쇄 대비", - "배지 템플릿은 M17 CMS 관리"], - "input":["관람객 등록 정보(유형)","배지 템플릿(M17)","행사·부스 매핑","개인정보 동의 상태"], - "output":["모바일 배지/QR(서명)","배지 인쇄 데이터","체크인 검증 토큰","리드 스캔 대상 매핑"], - "logic":["등록·동의 상태 검증", - "배지 템플릿 병합·QR 토큰 생성", - "서명·만료 정책 적용", - "모바일 발급·인쇄 데이터 산출", - "체크인/리드 스캔 매핑 등록"], - "tables":["visitor","registration","badge","badge_template"], - "exc":["개인정보 미동의 → 발급 보류","중복 등록 → 기존 배지 재사용"], - "link":"연동 M10 체크인·M10-LEAD·M13 wayfinding. PII 동의·보존정책 필수(R10)."}, - - {"id":"M10-LEAD-01","name":"리드캡처·AI 스코어링","type":"API","scr":"SCR-32","color":BLUE, - "module":"M10 관람객 (참가업체 리드)","lh":0.30, - "desc":["참가업체가 배지 QR을 스캔해 연락처·관심도·메모를 저장하고 AI로 스코어링", - "팔로업 EDM(M12)·ROI 측정(M16)으로 연계"], - "input":["스캔 배지 QR 토큰","관심도 평점·메모","부스·참가업체 컨텍스트","동의 범위"], - "output":["리드 레코드(연락처·점수)","AI 리드 스코어","팔로업 세그먼트","리드 접근 감사로그"], - "logic":["QR 토큰 검증·관람객 해소", - "동의 범위 내 연락처 취득", - "관심도·행동신호 AI 스코어링", - "리드 저장·세그먼트 분류", - "리드 접근 감사로그 기록"], - "tables":["lead","visitor","booth","audit_log"], - "exc":["동의 범위 밖 필드 → 마스킹","오프라인 스캔 → 큐 저장 후 동기화"], - "link":"연동 M12 EDM·M16 BI. 리드 접근은 개인정보 전수 감사(§5B-1)."}, - - {"id":"M11-MATCH-01","name":"AI 미팅 추천","type":"API","scr":"SCR-M8","color":BLUE, - "module":"M11 비즈니스 매칭","lh":0.30, - "desc":["프로필·관심업종·의향(intent) 기반으로 미팅을 추천하고 슬롯 예약", - "부스 위치(M2)·wayfinding(M13) 길안내 연계"], - "input":["관람/참가 프로필","관심 업종·intent 신호","부스 업종 태그(M3)","가용 미팅 슬롯"], - "output":["매칭 추천 목록·점수","미팅 슬롯 예약","길안내 경로(M13)","매칭 성과(M16)"], - "logic":["프로필·행동신호 수집", - "관심·의향 임베딩 유사도 산정", - "상호 의향 매칭 랭킹", - "미팅 슬롯 예약·충돌 해소", - "부스 위치·경로 연계 반환"], - "tables":["visitor","match_recommendation","meeting_slot","booth"], - "exc":["프로필 결손 → 저신뢰 라벨","외부 모델 게이트 위반 → 승인 모델만"], - "link":"연동 M10·M13·M16. AI 추천은 온프레미스/승인 모델 범위(R12)."}, - - {"id":"M16-ETL-01","name":"BI 데이터마트 적재","type":"배치","scr":"SCR-13","color":PURPLE, - "module":"M16 경영분석 BI (P1 승격)","lh":0.30, - "desc":["운영 DB를 스타 스키마 데이터마트로 야간 집계해 운영 부하 없이 지표 제공", - "운영사 관점·참가사 관점 지표 분리"], - "input":["M1 배정·M9 정산 이력","M10 관람·M15 옥션","행사일정·비용 마스터","전 적재 스냅샷"], - "output":["Fact/Dim 테이블 적재","KpiSnapshot(가동률·매출·P&L)","수요예측·수율 입력","이상탐지 대상"], - "logic":["증분 소스 추출(읽기 복제)", - "DimHall/Event/Date/Exhibitor 갱신", - "FactBooking/Settlement/Utility/Auction/Visitor 적재", - "KPI 집계·스냅샷 생성", - "품질 검증→커밋(멱등)"], - "tables":["fact_booking","fact_settlement","dim_hall","kpi_snapshot"], - "exc":["소스 정합 결손 → 부분적재·경고","적재 실패 → 재시도·이전 스냅샷 유지"], - "link":"연동 전 모듈(소비)·M18 권한. 크로스테넌트 집계는 플랫폼 슈퍼관리자 전용."}, - - {"id":"M17-ONL-01","name":"CMS 콘텐츠·게시 워크플로","type":"온라인","scr":"SCR-35","color":PURPLE, - "module":"M17 CMS","lh":0.30, - "desc":["전시 콘텐츠·공지를 초안→검수→게시 워크플로와 버전관리로 발행", - "다국어(한/영/중/일)·사이니지 연계"], - "input":["콘텐츠 초안·미디어","게시 대상·채널","다국어 번역","승인자·게시 일정"], - "output":["게시 콘텐츠(버전)","예약 게시 큐","다국어 렌더","사이니지 배포 콘텐츠"], - "logic":["콘텐츠 작성·미디어 첨부", - "검수 요청→승인/반려", - "다국어 번역 검수", - "게시/예약 게시 처리", - "사이니지·공개사이트 배포"], - "tables":["content(버전)","content_publish","content_i18n","microsite"], - "exc":["승인 없이 게시 → 차단","번역 누락 → 기본 로케일 폴백"], - "link":"연동 M12 마케팅·M13 wayfinding·M10 배지. 공지 채널은 CMN-NOTI와 정합."}, - - {"id":"M18-ONL-02","name":"규정 룰셋 버전 관리","type":"온라인","scr":"SCR-A8","color":PURPLE, - "module":"M18 관리자 백오피스","lh":0.30, - "desc":["요율·규정 룰셋을 버전으로 관리해 연 단위 개정을 안전하게 반영", - "룰 엔진(M1·M2 검증)이 버전 고정으로 참조"], - "input":["룰셋 초안(요율·규정 값)","적용 시작일·테넌트","개정 사유·근거","승인자"], - "output":["룰셋 버전(불변 스냅샷)","활성 버전 포인터","개정 이력·감사로그","엔진 참조 캐시 무효화"], - "logic":["룰셋 초안 작성·검증", - "버전 스냅샷 생성(불변)", - "승인→활성 버전 전환(시작일)", - "엔진 캐시 무효화·재로딩", - "개정 감사로그 기록"], - "tables":["ruleset_version","rate_master","audit_log","tenant"], - "exc":["활성 버전 삭제 시도 → 차단","시작일 소급 → 경고·승인 필요"], - "link":"연동 M1 견적·M2 검증·전 모듈. 테넌트 스코프 격리(§1A)."}, - - {"id":"SYS-AUTH-02","name":"2차 인증 OTP(TOTP) 검증","type":"API","scr":"SCR-51","color":BLUE_DK, - "module":"§5B-3 인증 (UIWS 표준 이식)","lh":0.30, - "desc":["TOTP(RFC6238)로 로그인 2단계 인증을 수행하고 QR 등록·재설정을 제공", - "관리자·홀매니저·주최자·업체 필수, 관람객 선택"], - "input":["1차 인증 통과 컨텍스트","OTP 6자리 코드","OTP 시크릿(등록 시)","서버 시간"], - "output":["OTP 검증 결과","QR 등록 데이터(최초)","실패 카운트·잠금 상태","인증 완료 토큰"], - "logic":["사용자 OTP 시크릿 조회", - "SHA1·30s·±1 윈도우 코드 산정", - "입력 코드 대조", - "실패 시 카운트 증가·잠금 판정", - "성공→인증 완료·감사기록"], - "tables":["user(otp_secret)","login_attempt","audit_log"], - "exc":["코드 불일치 → 실패 카운트·잠금","관리자 초기화 → otp_secret=NULL 재등록"], - "link":"연동 SYS-AUTH-01·SYS-AUTH-03. 시크릿·비번은 응답/로그 미노출."}, - - {"id":"SYS-RBAC-01","name":"역할·권한(RBAC) 관리","type":"온라인","scr":"SCR-A2","color":BLUE_DK, - "module":"§5B-1 시스템관리 / M18","lh":0.30, - "desc":["플랫폼·행사 이중 평가 권한 모델을 관리(테넌트 격리 ⊃ 행사 RBAC)", - "역할별 포털 IA·메뉴 접근을 구동"], - "input":["역할 정의·계층","권한(리소스·액션) 매핑","테넌트·행사 스코프","사용자-역할 배정"], - "output":["역할-권한 매트릭스","메뉴·API 게이트 규칙","이중 평가 정책","권한 변경 감사로그"], - "logic":["역할 계층 로딩(플랫폼/테넌트/행사)", - "권한 매핑 편집·검증", - "테넌트 격리 제약 적용", - "메뉴·API 게이트 규칙 생성", - "변경 감사로그 기록"], - "tables":["role","permission","user_role","menu"], - "exc":["테넌트 경계 위반 배정 → 차단","슈퍼관리자 외 크로스테넌트 → 거부"], - "link":"연동 SYS-MENU·전 포털. 행사 RBAC는 테넌트 내부로 스코프(§1A-4)."}, - - {"id":"CMN-ALRM-01","name":"알림센터(notification)","type":"온라인/WS","scr":"SCR-47","color":BLUE, - "module":"§5B-2 공통 업무 기능","lh":0.30, - "desc":["마감(D-데이)·낙찰·승인·결제 등 이벤트를 단일 채널로 수집·푸시", - "M10·M12·M15가 이벤트를 발행, 발송 채널은 공통 단일화"], - "input":["이벤트(유형·대상·페이로드)","수신자·역할","알림 설정·구독","WebSocket 세션"], - "output":["알림 레코드(읽음 상태)","실시간 WebSocket 푸시","배지 카운트","이메일/모바일 팬아웃"], - "logic":["이벤트 수신·정규화", - "수신자 해소(역할·구독)", - "알림 저장·중복 억제", - "WebSocket 실시간 푸시", - "미접속 시 이메일/푸시 팬아웃"], - "tables":["notification","notification_pref","event_outbox"], - "exc":["WebSocket 미연결 → 오프라인 큐","발송 실패 → 재시도·데드레터"], - "link":"연동 M6 마일스톤·M15 낙찰·M9 결제. 마케팅 발송은 M12(수신동의)."}, -] -for i, d in enumerate(DETAILS): - detail_slide(i+1, d) - -# ================================================================ -# 6. 핵심 업무 순서도 (도형 작도 8종) -# ================================================================ -divider("04", "핵심 업무 순서도", "KEY WORKFLOW FLOWCHARTS", - ["8종 핵심 업무 흐름", "python-pptx 도형 직접 작도", "단자·처리·판단·커넥터"]) - -# --- 순서도 배치 헬퍼 --- -def after(nodes, h, gap=0.28, cx=None): - n = nodes[-1] - return (cx if cx is not None else n["cx"], n["b"] + gap + h/2) - -# --- FC1: 부스 배치 3안 생성 → 선택/병합 --- -s = slide() -content_header(s, "04 · 핵심 업무 순서도 (1/8)", "부스 배치 3안 AI 생성 → 선택 / 병합", 4, chip_txt="F1", chip_col=PURPLE) -fc_legend(s) -sp = vchain(s, 2.7, 1.55, [ - (["설계 조건 입력", "(홀·부스수·프리미엄)"], "term"), - (["제약 로딩(통로·비상구", "·트렌치·바닥하중)"], "proc"), - (["솔버+휴리스틱 3안 생성", "(목표 다양화)"], "proc"), - (["3안 규정검증·시각화(S7)"], "io"), -], w=3.4, h=0.56, gap=0.22) -dcx, dcy = after(sp, 0.86, gap=0.26) -dec = fnode(s, dcx, dcy, 2.2, 0.86, ["선택 or", "병합?"], "dec") -arrow(s, sp[-1]["cx"], sp[-1]["b"], dec["cx"], dec["t"]) -sel = fnode(s, 6.6, dcy-1.45, 3.0, 0.62, ["단일 안 선택"], "proc") -mrg = fnode(s, 6.6, dcy, 3.0, 0.62, ["레이어 병합 편집", "(안별 블록 조합)"], "proc") -rechk = fnode(s, 10.4, dcy-0.72, 2.8, 0.66, ["병합/선택안", "규정 재검증"], "proc") -fin = fnode(s, 7.6, 6.4, 7.4, 0.5, ["최종안 확정·버전기록 → 참가업체 초대링크(M3/M4)"], "proc_ok") -arrow(s, dec["r"], dec["cy"], sel["l"], sel["cy"], label="선택") -arrow(s, dec["r"], dec["cy"], mrg["l"], mrg["cy"], label="병합") -arrow(s, sel["r"], sel["cy"], rechk["l"], rechk["cy"]) -arrow(s, mrg["r"], mrg["cy"], rechk["l"], rechk["cy"]) -arrow(s, rechk["cx"], rechk["b"], rechk["cx"], fin["t"]) -text(s, 0.55, 6.66, 12, 0.3, "메모 규정 재검증에서 위반 발견 시 병합 편집으로 되돌려 재수정 후 재검증(hard 위반은 확정 차단).", size=9, color=MUTED) - -# --- FC2: 규정 자동검증 --- -s = slide() -content_header(s, "04 · 핵심 업무 순서도 (2/8)", "규정 자동검증 (위반 차단 / 경고)", 4, chip_txt="F2", chip_col=PURPLE) -fc_legend(s) -sp = vchain(s, 2.9, 1.6, [ - (["배치/설계안 제출"], "term"), - (["룰셋 버전 로딩", "(tenant·버전 고정)"], "proc"), - (["규정 항목 순차 판정", "(통로·하중·높이·방염·이격)"], "proc"), -], w=3.5, h=0.6, gap=0.3) -dcx, dcy = after(sp, 0.94, gap=0.28) -dec = fnode(s, 2.9, dcy, 2.4, 0.94, ["위반", "등급?"], "dec") -arrow(s, sp[-1]["cx"], sp[-1]["b"], dec["cx"], dec["t"]) -hard = fnode(s, 6.95, dcy-1.3, 3.1, 0.66, ["HARD 위반", "→ 제출 차단"], "proc_bad") -soft = fnode(s, 6.95, dcy, 3.1, 0.66, ["SOFT 위반", "→ 경고(진행 가능)"], "proc") -none = fnode(s, 6.95, dcy+1.3, 3.1, 0.6, ["위반 없음 → 통과·확정"], "proc_ok") -rep = fnode(s, 11.45, dcy, 1.5, 2.0, ["검증", "리포트", "(SCR-09)", "위반좌표", "오버레이"], "io") -arrow(s, dec["r"], dec["cy"], hard["l"], hard["cy"], label="차단") -arrow(s, dec["r"], dec["cy"], soft["l"], soft["cy"], label="경고") -arrow(s, dec["r"], dec["cy"], none["l"], none["cy"], label="정상") -arrow(s, hard["r"], hard["cy"], rep["l"], rep["cy"]-0.6) -arrow(s, soft["r"], soft["cy"], rep["l"], rep["cy"]) -arrow(s, none["r"], none["cy"], rep["l"], rep["cy"]+0.6) -text(s, 0.55, 6.66, 12, 0.3, "원칙 hard=확정 차단, soft=경고 후 진행 가능. 최종 판정은 홀매니저·구조기술사(사람).", size=9, color=MUTED) - -# --- FC3: 배선 최단경로 산출 (단일 수직 흐름) --- -s = slide() -content_header(s, "04 · 핵심 업무 순서도 (3/8)", "유틸리티 배선 최단경로 산출 (PostGIS)", 4, chip_txt="F3", chip_col=PURPLE) -fc_legend(s) -vchain(s, 5.6, 1.66, [ - (["단말 위치 클릭(전기·LAN·급배수)"], "term"), - (["기기 목록·kW/회선 입력"], "io"), - (["트렌치 그리드·공급 매트릭스 로딩"], "proc"), - (["최근접 트렌치 매칭(ST_ClosestPoint)"], "proc"), - (["통로 횡단 최소 경로(ST_ShortestLine)"], "proc"), - (["kW 합산 → 분전반 용량·수량 + 요금 견적"], "proc"), - (["배선 LineString 저장·위치표시도 자동생성"], "proc_ok"), - (["S6 오버레이(백엔드 래스터 합성)"], "term"), -], w=5.4, h=0.48, gap=0.14) -text(s, 0.6, 6.72, 12, 0.3, "예외 트렌치 좌표 미확보 → 산출 보류·근사도면 경고. 인터넷 마감(D-25) 초과 → 신청 차단. S6는 생성모델 아닌 결정적 래스터 합성 우선.", size=8.6, color=MUTED) - -# --- FC4: 나노바나나 RenderJob --- -s = slide() -content_header(s, "04 · 핵심 업무 순서도 (4/8)", "나노바나나 RenderJob (큐→워커→워터마크→푸시)", 4, chip_txt="F4", chip_col=PURPLE) -fc_legend(s) -sp = vchain(s, 3.0, 1.6, [ - (["시각화 요청(M2~M4)"], "term"), - (["씬 컴파일·스키마 해시"], "proc"), - (["Redis 큐 발행(RenderJob)"], "io"), -], w=3.4, h=0.58, gap=0.26) -dcx, dcy = after(sp, 0.86, gap=0.28) -dec = fnode(s, 3.0, dcy, 2.3, 0.86, ["캐시", "HIT?"], "dec") -arrow(s, sp[-1]["cx"], sp[-1]["b"], dec["cx"], dec["t"]) -gem = fnode(s, 8.0, 1.95, 3.3, 0.64, ["워커 소비·Gemini 호출", "(inlineData + text)"], "wkr") -derr = fnode(s, 8.0, 3.25, 3.3, 0.86, ["응답", "상태?"], "dec") -bad = fnode(s, 11.6, 3.25, 1.4, 0.86, ["SAFETY/429", "친화 에러", "쿼터 미차감"], "proc_bad") -o1 = fnode(s, 8.0, 4.55, 3.3, 0.56, ["워터마크·메타 임베드"], "proc") -o2 = fnode(s, 8.0, 5.55, 3.3, 0.56, ["스토리지 저장 + 성공 시 쿼터 차감"], "proc") -o3 = fnode(s, 8.0, 6.55, 3.3, 0.56, ["WebSocket 완료 푸시(S1~S7)"], "proc_ok") -arrow(s, dec["r"], dec["cy"], gem["l"], gem["cy"], label="MISS") -arrow(s, gem["cx"], gem["b"], derr["cx"], derr["t"]) -arrow(s, derr["r"], derr["cy"], bad["l"], bad["cy"], label="실패") -arrow(s, derr["cx"], derr["b"], o1["cx"], o1["t"], label="성공") -arrow(s, o1["cx"], o1["b"], o2["cx"], o2["t"]) -arrow(s, o2["cx"], o2["b"], o3["cx"], o3["t"]) -arrow_elbow(s, dec["cx"], dec["b"], o3["l"], o3["cy"], first='v', label="HIT") - -# --- FC5: 공사 옥션 --- -s = slide() -content_header(s, "04 · 핵심 업무 순서도 (5/8)", "공사 옥션 (견적 → 역경매 → 낙찰)", 4, chip_txt="F5", chip_col=BLUE) -fc_legend(s) -sp = vchain(s, 3.0, 1.5, [ - (["옥션 개설(유형·라운드·마감)"], "term"), - (["M2~M5 자료·물량서 첨부"], "io"), - (["응찰 자격 검증(M7) 등록업체만"], "proc"), - (["적격 업체 자료 열람"], "proc"), - (["견적서 제출(응찰)"], "proc"), -], w=3.7, h=0.52, gap=0.18) -dcx, dcy = after(sp, 0.82, gap=0.24) -dec = fnode(s, 3.0, dcy, 2.2, 0.82, ["마감/라운드", "종료?"], "dec") -arrow(s, sp[-1]["cx"], sp[-1]["b"], dec["cx"], dec["t"]) -rebid = fnode(s, 6.7, dcy-1.25, 3.0, 0.62, ["재응찰", "(라운드 내 인하)"], "proc") -arrow(s, dec["r"], dec["cy"], rebid["l"], rebid["cy"], label="No") -arrow_elbow(s, rebid["cx"], rebid["b"], dec["cx"]+1.1, dec["t"]+0.15, first='v') -yes = vchain(s, 9.4, 1.6, [ - (["종합점수 산정(가격+평판+납기)"], "proc"), - (["견적서 비교표·순위"], "io"), - (["전시업체 낙찰(Award)"], "proc"), - (["계약/발주 전환(M6·M9)·감사기록"], "proc_ok"), -], w=3.5, h=0.58, gap=0.24) -arrow(s, dec["r"], dec["cy"]-0.1, yes[0]["l"], yes[0]["cy"], label="Yes") - -# --- FC6: 관람객 등록 → 배지 → 체크인 --- -s = slide() -content_header(s, "04 · 핵심 업무 순서도 (6/8)", "관람객 등록 → 배지 → 체크인", 4, chip_txt="F6", chip_col=BLUE) -fc_legend(s) -sp = vchain(s, 3.0, 1.55, [ - (["온라인 사전등록(유형별 폼)"], "term"), - (["개인정보 동의 확인"], "proc"), - (["배지/QR 발급(서명·만료)"], "proc"), - (["모바일 배지 수신"], "io"), -], w=3.4, h=0.54, gap=0.2) -dcx, dcy = after(sp, 0.86, gap=0.26) -dec = fnode(s, 3.0, dcy, 2.3, 0.86, ["현장", "네트워크?"], "dec") -arrow(s, sp[-1]["cx"], sp[-1]["b"], dec["cx"], dec["t"]) -onl = fnode(s, 6.9, dcy, 3.0, 0.62, ["QR 온라인 체크인"], "proc") -off = fnode(s, 6.9, dcy-1.35, 3.0, 0.62, ["오프라인 폴백 큐"], "proc_bad") -arrow(s, dec["r"], dec["cy"], onl["l"], onl["cy"], label="정상") -arrow(s, dec["r"], dec["cy"], off["l"], off["cy"], label="불안정") -arrow(s, off["cx"], off["b"], onl["cx"], onl["t"], label="복구 동기화") -tail = vchain(s, 10.7, dcy-1.35, [ - (["즉석 배지 인쇄·입장 집계"], "proc"), - (["리드캡처 매핑(M10-LEAD)"], "proc"), - (["체크인 데이터 → M16 BI"], "proc_ok"), -], w=3.1, h=0.6, gap=0.24) -arrow(s, onl["r"], onl["cy"], tail[0]["l"], tail[0]["cy"]) - -# --- FC7: 승인 워크플로 (2FA 포함) --- -s = slide() -content_header(s, "04 · 핵심 업무 순서도 (7/8)", "승인 워크플로 (2FA 포함)", 4, chip_txt="F7", chip_col=BLUE_DK) -fc_legend(s) -sp = vchain(s, 3.0, 1.55, [ - (["로그인(ID/PW)"], "term"), - (["JWT 발급"], "proc"), -], w=3.2, h=0.56, gap=0.22) -dcx, dcy = after(sp, 0.86, gap=0.24) -dec = fnode(s, 3.0, dcy, 2.3, 0.86, ["2FA 대상", "역할?"], "dec") -arrow(s, sp[-1]["cx"], sp[-1]["b"], dec["cx"], dec["t"]) -otp = fnode(s, 3.0, dcy+1.35, 3.1, 0.6, ["OTP(TOTP) 검증·실패 잠금"], "proc") -arrow(s, dec["cx"], dec["b"], otp["cx"], otp["t"], label="필수") -que = fnode(s, 8.3, 2.2, 3.2, 0.62, ["승인 큐 진입(AI 사전심사 플래그)"], "io") -rev = fnode(s, 8.3, 3.4, 3.2, 0.6, ["검수 상세(도면+위반 오버레이)"], "proc") -dap = fnode(s, 8.3, 4.65, 2.4, 0.86, ["승인", "판정?"], "dec") -rej = fnode(s, 11.6, 4.65, 1.4, 0.86, ["반려", "사유 통지", "재제출"], "proc_bad") -ap1 = fnode(s, 8.3, 5.85, 3.2, 0.56, ["승인 확정·상태 전이"], "proc") -ap2 = fnode(s, 8.3, 6.6, 3.2, 0.5, ["감사로그 기록 + 알림센터 통지(CMN-ALRM)"], "proc_ok") -arrow(s, dec["r"], dec["cy"], que["l"], que["cy"], label="선택/면제") -arrow(s, otp["r"], otp["cy"], que["l"], que["cy"]+0.1) -arrow(s, que["cx"], que["b"], rev["cx"], rev["t"]) -arrow(s, rev["cx"], rev["b"], dap["cx"], dap["t"]) -arrow(s, dap["r"], dap["cy"], rej["l"], rej["cy"], label="반려") -arrow(s, dap["cx"], dap["b"], ap1["cx"], ap1["t"], label="승인") -arrow(s, ap1["cx"], ap1["b"], ap2["cx"], ap2["t"]) - -# --- FC8: CI/CD 배포 --- -s = slide() -content_header(s, "04 · 핵심 업무 순서도 (8/8)", "CI/CD 배포 (빌드 → health → 롤백)", 4, chip_txt="F8", chip_col=BLUE_DK) -fc_legend(s) -sp = vchain(s, 3.0, 1.55, [ - (["Gitea push(main)"], "term"), - (["webhook 수신(deploy 서버)"], "proc"), - (["백엔드 bootJar + 프론트 Vite 빌드"], "proc"), - (["나노바나나 워커 패키징"], "proc"), - (["배포·서비스 재기동"], "proc"), -], w=3.7, h=0.56, gap=0.22) -dcx, dcy = after(sp, 0.9, gap=0.24) -dec = fnode(s, 3.0, dcy, 2.3, 0.9, ["health", "check", "OK?"], "dec") -arrow(s, sp[-1]["cx"], sp[-1]["b"], dec["cx"], dec["t"]) -okb = fnode(s, 7.3, dcy, 3.4, 0.62, ["트래픽 전환·배포 완료"], "proc_ok") -rb = fnode(s, 7.3, dcy-1.3, 3.4, 0.62, ["직전 버전 롤백"], "proc_bad") -arrow(s, dec["r"], dec["cy"], okb["l"], okb["cy"], label="UP") -arrow(s, dec["r"], dec["cy"], rb["l"], rb["cy"], label="FAIL") -arrow(s, rb["cx"], rb["b"], okb["cx"], okb["t"], label="복구") -fin8 = fnode(s, 7.3, dcy+1.1, 3.4, 0.56, ["배포 결과 알림 + 감사로그·배포 이력 기록"], "term") -arrow(s, okb["cx"], okb["b"], fin8["cx"], fin8["t"]) -text(s, 0.55, 6.74, 12, 0.3, "보안 불변 API키·비밀번호·서버IP·SSH 계정은 코드·커밋·로그·응답에 미기재(서버 env 로드).", size=9, color=MUTED) - -# ================================================================ -# 7. 공통 규약 -# ================================================================ -divider("05", "공통 규약", "COMMON CONVENTIONS", - ["API 응답 봉투(ApiResponse)", "에러 코드·처리", "감사 로그(@Audited)"]) - -s = slide() -content_header(s, "05 · 공통 규약", "API 응답 봉투 · 에러 처리 · 감사 로그", 5) -# 응답 봉투 -card(s, 0.55, 1.5, 6.05, 2.55) -rect(s, 0.55, 1.5, 6.05, 0.46, fill=BLUE, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.55, 1.5, 6.05, 0.46, "API 응답 봉투 (ApiResponse)", size=12, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -text(s, 0.78, 2.05, 5.6, 0.28, "모든 REST 응답은 공통 봉투 사용(예외 없음)", size=9, color=MUTED) -rect(s, 0.78, 2.38, 5.6, 0.62, fill=RGBColor(0x0E,0x14,0x22), shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.95, 2.44, 5.3, 0.52, [ - [{"t":'성공 ',"size":8.5,"color":GREEN,"bold":True},{"t":'{ "success": true, "data": {…}, "error": null }',"size":8.5,"color":RGBColor(0xD6,0xE2,0xF0)}], - [{"t":'실패 ',"size":8.5,"color":AMBER,"bold":True},{"t":'{ "success": false, "data": null, "error": {code,message} }',"size":8.5,"color":RGBColor(0xD6,0xE2,0xF0)}], -], line_spacing=1.1) -for i,t in enumerate([ - "성공: 컨트롤러 ApiResponse.ok(dto)", - "실패: ApiException throw → GlobalExceptionHandler 봉투 변환", - "페이징: 공통 PageResponse", -]): - y=3.12+i*0.3 - rect(s, 0.82, y+0.06, 0.09, 0.09, fill=BLUE, shape=MSO_SHAPE.OVAL) - text(s, 1.02, y, 5.4, 0.28, t, size=9, color=INK) -# 에러 처리 -card(s, 6.75, 1.5, 6.03, 2.55) -rect(s, 6.75, 1.5, 6.03, 0.46, fill=AMBER, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 6.75, 1.5, 6.03, 0.46, "에러 처리 (ErrorCode·ApiException)", size=12, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -for i,(h,d) in enumerate([ - ("단일 매핑","ErrorCode(enum)가 코드↔HTTP 단일 매핑, 신규는 여기에만 추가"), - ("3자 세트","ErrorCode → ApiException(코드+요약) → GlobalExceptionHandler"), - ("스택 미노출","error.message는 요약만, 상세는 서버 로그(include-* : never)"), - ("남용 금지","RuntimeException 남발 금지 — 최종 방어선만 INTERNAL"), - ("보안","IP·SSH·비번·PII를 응답·에러에 미노출"), -]): - y=2.08+i*0.38 - rect(s, 7.0, y+0.04, 0.11, 0.11, fill=AMBER, shape=MSO_SHAPE.OVAL) - text(s, 7.22, y, 5.4, 0.36, [[{"t":h+" — ","size":9.4,"color":INK,"bold":True},{"t":d,"size":8.8,"color":MUTED}]], line_spacing=0.92) -# 감사 로그 -card(s, 0.55, 4.28, 12.23, 2.15) -rect(s, 0.55, 4.28, 12.23, 0.46, fill=BLUE_DK, shape=MSO_SHAPE.ROUNDED_RECTANGLE) -text(s, 0.55, 4.28, 12.23, 0.46, "감사 로그 (@Audited AOP → TB_AUDIT_LOG)", size=12, color=WHITE, bold=True, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) -audit = [ - ("기록 대상","상태 변경 API — 승인·낙찰(M15)·설계 변경·룰셋 개정·리드 접근(PII)"), - ("기록 항목","액터 · 행사 · 대상 · before/after 요약 · 룰셋 버전 · 시각"), - ("미기록","민감정보 · 비밀번호 · 스택트레이스 (보안 불변)"), - ("스키마","WISE TB_AUDIT_LOG 이식 · 멱등 DDL(sql.init mode=always + continue-on-error)"), -] -for i,(h,d) in enumerate(audit): - x=0.75+(i%2)*6.1 - y=4.9+(i//2)*0.72 - rect(s, x, y, 5.85, 0.62, fill=HALL_BG, line=LINE, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE) - text(s, x+0.18, y+0.1, 5.55, 0.26, h, size=10, color=BLUE_DK, bold=True) - text(s, x+0.18, y+0.36, 5.55, 0.24, d, size=8.6, color=MUTED) -text(s, 0.55, 6.62, 12.2, 0.3, - "적용 원칙 ApiResponse·ErrorCode·감사로그는 전 프로그램 공통(architecture/app.md §5-2·5-3·7-1 정본). 신규 프로그램도 예외 없이 준수.", - size=9, color=MUTED) - -# ================================================================ -# 저장 -# ================================================================ -OUT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "프로그램사양서.pptx") -prs.save(OUT) -print("SAVED:", OUT) -print("SLIDES:", len(prs.slides._sldIdLst)) diff --git a/docs/deliverables/프로그램사양서/프로그램사양서.pptx b/docs/deliverables/프로그램사양서/프로그램사양서.pptx deleted file mode 100644 index e4601a9..0000000 Binary files a/docs/deliverables/프로그램사양서/프로그램사양서.pptx and /dev/null differ diff --git a/docs/design.md b/docs/design.md index 12f08bb..720afa0 100644 --- a/docs/design.md +++ b/docs/design.md @@ -1,6 +1,6 @@ # 킨텍스 AI 전시관리 시스템 — UI 디자인 스펙 (design.md) -> 작성: 디자인 에이전트(designer) · 작성일: 2026-07-12 · 버전: **v2.3.2** +> 작성: 디자인 에이전트(designer) · 작성일: 2026-07-12 · 버전: **v2.4** > 근거 문서: `docs/PLANNING.md` **v3.0**(모듈 맵 M1~M18 + §5B 공통/시스템관리 레이어 + §1A 멀티테넌시, 6+2 역할, 나노바나나 표준 샷 세트 S1~S7) · `docs/FEATURE_BACKLOG_100.md`(100대 기능 F001~F100·NFR 4축) · Stitch 산출물 `stitch_kintex_ai_system_architect/`(18화면 + DESIGN.md 3변형) 학습·정합 > **단일 출처(Single Source of Truth)**: 프론트 구현 토큰·컴포넌트 계약은 본 문서가 권위다. Stitch 화면(code.html/screen.png)은 시각 참조로만 사용하며, 상충 시 본 문서를 따른다. > 용도: **각 화면의 "Stitch 프롬프트" 코드블록을 Google Stitch(stitch.withgoogle.com)에 그대로 붙여넣어 화면을 생성**한다. 문서는 한국어, Stitch 프롬프트만 영어. @@ -1468,6 +1468,109 @@ A public "check & cancel my ticket booking" page for an exhibition, Korean marke > > **디자인 토큰**: §1 라이트 테마·`primary-600 #0066B3`·Pretendard·8px 카드·WCAG AA 준수. **신규 토큰 0.** AI 생성 이미지(나노바나나)는 워터마크 "AI 생성 예상 이미지" 상시 노출 규칙 유지. 이모지 금지·선(stroke) SVG 아이콘. +### 3C-V. 공개 사이트 비주얼·모션 스펙 (전 SCR-T*·SCR-P* 공통 비주얼 레이어 · v2.4 신설) + +> **v2.4 신설.** 소유자 확정("코엑스처럼 세련되게 전면 비주얼 리뉴얼 — 히어로 배경·애니메이션·비주얼, 지금 착수")에 따라, 기존 §3C 3-트랙(SCR-T0~T3)·§3B 공개 홍보(SCR-P*)의 **구조는 그대로 두고 그 위에 비주얼·모션 레이어만 얹는다**(구조 재설계 아님, 시각 격상). 대상: **미인증 공개 트랙 전체**(SCR-T0 관문·SCR-T1 visitor 메인·SCR-T2 business·SCR-T3 agency·SCR-P1~P8). **인증 업무 셸(AppShell/MDI)·관리자(SCR-A*)·모바일 앱은 대상 아님**(밀도·성능 우선 원칙 유지). **신규 컬러 토큰 0** — §1 팔레트를 CSS 변수로 주입하고, 본 절은 **모션 토큰·이미지 소스·비주얼 폴리시**만 신설한다. business/agency는 코엑스식 B2B 실무 톤을 유지하되 히어로 배경·리빌·카드 hover 등 **공통 모션만 절제 적용**(마케팅 과장 금지) — visitor(SCR-T1)만 풀 시네마틱 격상. + +**V-1. 비주얼 폴리시(공통 원칙)** + +- **풀블리드·공백 없음**(전역 NFR [[ui-fullbleed-responsive]] 준수): 히어로·밴드는 뷰포트 전폭, 좌우 거터 여백 금지. 콘텐츠는 `max-width 1440px` 중앙 정렬 그리드 안에서 리듬을 준다(밴드 배경만 전폭). +- **섹션 리듬**: 밴드(전폭 배경/딤) ↔ 카드 그리드(라이트 서피스) 교대. 섹션 세로 패딩 데스크톱 `96px`(태블릿 64px·모바일 40px), 섹션 헤더는 아이라인(eyebrow, 12px 트래킹 라벨)+H2. 인접 섹션 배경 톤을 번갈아 대비. +- **여백·라운드·그림자 토큰**(§1-4 재사용): 카드 라운드 8px 상한, 칩 pill. 그림자는 무거운 박스섀도 대신 **소프트 앰비언트**(hover 시에만 Level 2로 상승, Y8 Blur24 12%). 히어로/밴드 위 텍스트는 딤 오버레이로 대비 확보. +- **타이포 위계 강화**: 히어로 헤드라인 Display(1순위 visitor는 `clamp(32px,4vw,56px)` 확대 허용), 서브카피 H3, 섹션 eyebrow+H2. 숫자(통계·D-day·기간)는 tabular numerals. +- **선(stroke) SVG 아이콘 전용·이모지 금지**(불변). AI 산출물은 §1-4 AiLabel(보라)+워터마크 규칙 유지. +- **다크/라이트 양 테마**(SCR-T1은 토글 제공 — §3C SCR-T1 예외 규정): 히어로는 시네마틱 딤 톤(다크 계열 오버레이)이 기본 인상, 본문 섹션은 라이트 기본. 색은 CSS 변수 주입(hex 하드코딩 금지)로 양 테마 대응, **양 테마 WCAG AA**. +- **포스터/이미지 카드 정책**: visitor 쇼케이스·행사 레일은 큰 세로 2:3 포스터 카드 그리드/캐러셀. `poster_url`/배경 이미지 부재 시 **브랜드 그라디언트 + 옅은 기하 패턴(그리드/도트) 폴백**(V-4). + +**V-2. 모션 토큰(신설 — duration/easing, CSS 커스텀 프로퍼티)** + +| 토큰 | 값 | 용도 | +|---|---|---| +| `--motion-fast` | `150ms` | hover 색/보더, 칩 상태 | +| `--motion-base` | `240ms` | 카드 리프트·이미지 줌·버튼 | +| `--motion-slow` | `400ms` | 스크롤 리빌 기본 | +| `--motion-hero` | `600ms` | 히어로 인트로·대형 리빌 | +| `--motion-countup` | `1200ms` | 숫자 카운트업 | +| `--ease-emphasized` | `cubic-bezier(0.2,0,0,1)` | 리빌·히어로(감속 강조) | +| `--ease-standard` | `cubic-bezier(0.4,0,0.2,1)` | 일반 hover/전환 | +| `--ease-decelerate` | `cubic-bezier(0,0,0.2,1)` | 진입(카운트업·슬라이드인) | + +**V-3. 섹션별 배경·애니메이션 트리거 매핑** + +| 섹션(적용 화면) | 배경 | 애니메이션 | 트리거·파라미터 | +|---|---|---|---| +| **풀블리드 히어로**(T0·T1·T2·T3·P1) | `/media/hero/*.jpg`(전시장 전경) + 딤 그라디언트 오버레이 + AI 워터마크. 없으면 **그라디언트+패턴 폴백** | ① 인트로 리빌(로드 시 헤드라인·CTA opacity+translateY(16px)→0, `--motion-hero`) ② **라이트 파랄랙스**(배경 레이어 scroll에 `translate3d(0,y*0.18,0)`, 전경/텍스트는 고정 또는 y*0.06) | 인트로=마운트 1회 · 파랄랙스=scroll(rAF 스로틀, 데스크톱만·모바일/reduced-motion 비활성) | +| **통계 밴드**(T1·P1·P2) | 라이트 서피스 또는 밴드 딤 | **숫자 카운트업**(0→목표, `--motion-countup`, ease-decelerate) | IntersectionObserver 최초 진입 1회(`.pub-countup[data-count-to]`) | +| **AI 기능 쇼케이스 타일**(T1) | 라이트, AI 카드 `ai-surface #F5F3FF` | **스태거 리빌**(카드 순차 fade/slide-up 60~80ms 간격) + hover 리프트 | 컨테이너 진입 시 스태거 1회 · hover=`--motion-base` | +| **전시 쇼케이스/행사 레일**(T0·T1·P1) | 라이트 | ① 카드 **hover 리프트**(translateY(-4px)+그림자) + **이미지 줌**(scale 1.04, overflow hidden 컨테이너) ② 레일 **오토스크롤**(가로) | hover=`--motion-base` · 오토스크롤=`data-interval`(기본 4500ms), hover/focus 시 일시정지, reduced-motion 시 미동작 | +| **AI 기획 브리핑(연/월)**(T1) | 라이트 | 타임라인 바 진입 시 좌→우 grow(scaleX) + AI 요약 태그 fade-in | 진입 1회 · 연/월 전환 시 콘텐츠 크로스페이드(`--motion-base`) | +| **방문 가이드/가치제안 카드**(T1·T2·T3) | 라이트 | 스태거 리빌 + hover 리프트 | 진입 스태거 1회 | +| **전시장 소개/CTA 밴드**(T1·T2·T3·P2) | `/media/bands/*.jpg`(전폭 밴드) + 딤. 없으면 그라디언트 폴백 | 밴드 진입 리빌(fade/slide-up) · 배경 미세 파랄랙스(선택, 약 y*0.08) | 진입 1회 | +| **공통 섹션 블록 전반** | — | **스크롤 리빌**(opacity 0→1 + translateY(20~24px)→0, `--motion-slow` ease-emphasized) | IntersectionObserver `threshold 0.15`·`once`(재역방향 재생 금지) | + +**V-4. 이미지 소스 매핑(`/media/` — build 내장, 폴백 규칙)** + +- **원칙**: 히어로/밴드 비주얼은 **프론트 build 내장 정적 에셋 `/media/`** 에서 로드(외부 실시간 호출 없음·온프레미스, CDN 캐시). 나노바나나 AI 생성 전경은 워터마크 "AI 생성 예상 이미지" 상시. 파일 부재 시 **그라디언트+패턴 폴백**으로 무공백 렌더(레이아웃 시프트 0). +- **디렉터리 계약(제안)**: + - `/media/hero/` — 풀블리드 히어로 배경. `hero-gateway.jpg`(T0)·`hero-visitor.jpg`(T1)·`hero-business.jpg`(T2)·`hero-agency.jpg`(T3) (+`@2x`·`.webp` 우선, `.jpg` 폴백) + - `/media/hero/fg/` — 라이트 파랄랙스용 전경 레이어(선택, 투명 PNG/webp) + - `/media/bands/` — 섹션 밴드 배경(`band-venue.jpg` 전시장 소개 등) + - `/media/poster/` — 행사 포스터 폴백 placeholder(그라디언트 생성) + - `/media/patterns/` — 폴백 패턴 SVG(`grid.svg`·`dots.svg`·`mesh.svg`, `fill:none;stroke:currentColor` 라인 패턴) +- **로딩 성능**: 히어로 이미지는 ``+`fetchpriority="high"`·`loading="eager"`, 폴드 하단 포스터/밴드는 `loading="lazy"`+`decoding="async"`. `srcset`/`sizes`로 DPR·뷰포트 대응, `.webp` 우선 ``. +- **폴백 그라디언트**: `linear-gradient(135deg, var(--primary-700), var(--primary-600))` + 패턴 SVG 오버레이(투명도 8~12%). 포스터 폴백은 그라디언트 위 행사명 텍스트 중앙(SCR-HOME 폴백 규칙과 동일). + +**V-5. 접근성(prefers-reduced-motion) — 필수** + +- `@media (prefers-reduced-motion: reduce)`: **파랄랙스 전면 비활성**(배경 고정)·**오토스크롤 정지**(수동 화살표만)·**카운트업은 최종값 즉시 표시**·스크롤 리빌은 **transform 제거·opacity 1 즉시**(콘텐츠 지연 노출 금지)·hover 대형 transform 억제(색/보더 전환만 허용). +- 애니메이션은 **전부 장식**이며 정보 전달·조작 가능성을 모션에 의존하지 않는다(모션 꺼도 전 콘텐츠·기능 동등 접근). 포커스 링(`primary-600` 2px)·`aria-live`(AI 답변) 규정은 §3C 원본 유지. +- 히어로 배경 위 텍스트는 딤 오버레이로 **양 테마 WCAG AA** 대비 확보. 색만으로 상태 전달 금지(배지 텍스트 병기). +- 모션 정지 시에도 레이아웃 시프트 0(리빌은 최종 위치 기준, 이미지 폭·높이 예약). + +**V-6. 성능 가이드(CSS 우선·60fps)** + +- **CSS 우선**: 스크롤 리빌·hover·오토스크롤은 **CSS `transition`/`@keyframes` + IntersectionObserver로 클래스 토글**(`.is-visible`)로 구현. 애니메이션 라이브러리 불필요(기본 리빌엔 JS 애니메이션 금지). 파랄랙스만 `requestAnimationFrame` 스로틀 스크롤 핸들러(passive listener). +- **컴포지터 친화 프로퍼티만 애니메이트**: `transform`·`opacity`만 사용. `width/height/top/left`·`box-shadow` 직접 애니메이트 금지(그림자는 pseudo-element opacity 트랜지션으로 대체). `will-change`는 애니메이션 직전 부여·종료 후 제거(상시 부여 금지). +- **60fps 목표**: 파랄랙스 계산 rAF 1프레임 1회·오프스크린 요소 무시. 오토스크롤은 `transform: translateX` + `ScrollTimeline` 또는 rAF, hidden 탭에서 정지(`visibilitychange`). +- **이미지**: 위 V-4 preload/lazy·webp·srcset. 폴드 상단 CLS 방지(에스펙트 비율 예약). 저사양/데이터세이버(`prefers-reduced-data`) 시 히어로 정적·패턴 폴백으로 강등. +- **60fps 미달 시 강등 순서**: 파랄랙스 → 오토스크롤 → 스태거(→ 최종 리빌만 유지). 리빌은 항상 유지 가능한 최소 비용. + +**V-7. 프론트 구현 계약(클래스/훅 ↔ 모션 매핑 — kintex-frontend-dev)** + +- **CSS 커스텀 프로퍼티**(공개 트랙 루트에 주입): `--motion-fast/base/slow/hero/countup`, `--ease-emphasized/standard/decelerate`(V-2 값). §1 컬러도 동일 방식 변수 주입(라이트/다크). +- **`.pub-reveal`** + IntersectionObserver로 `.is-visible` 토글 → opacity+translateY 리빌(공통 섹션 블록·카드). **`.pub-reveal--stagger > *`** = 자식 순차 지연(`--i` 인덱스 * 70ms). +- **`.pub-hero`** > **`.pub-hero__bg`**(파랄랙스 레이어, `data-parallax="0.18"`)·`.pub-hero__fg`(전경 y*0.06)·`.pub-hero__overlay`(딤). 인트로는 마운트 시 `.is-in`. +- **`.pub-card--lift`**(hover translateY(-4px)+그림자 pseudo) · **`.pub-card__media`**(overflow hidden) > `img`(hover `scale(1.04)`). +- **`.pub-countup`**(`data-count-to`·`data-suffix`) = IO 진입 1회 카운트업, reduced-motion 시 최종값. +- **`.pub-rail--auto`**(`data-interval`) = 오토스크롤 레일(hover/focus/visibility 정지, reduced-motion 미동작). +- **폴백 유틸**: `.pub-fallback-gradient`·`.pub-pattern-grid|dots`(V-4). 이미지 `onerror`→폴백 클래스 토글. +- **적용 범위 가드**: 위 클래스·훅은 **PublicShell 하위(공개 트랙)만** 스코프. AppShell/MDI·SCR-A*·모바일 앱에는 미주입(성능·밀도 보존). +- **src 무수정**: 본 절은 스펙만 — 실제 클래스/CSS 구현은 kintex-frontend-dev 후속. + +**V-8. Stitch 프롬프트 — 프리미엄 전시장 공개 홈페이지(비주얼·모션 격상판, SCR-T1 재생성용)** + +``` +A premium, visually striking public homepage for "KINTEX AI 전시·행사시스템" (an AI exhibition & event operations platform), the visitor track, for the general public — styled like a world-class convention-center website (COEX / Messe / global expo caliber). Korean UI text only, exactly as given; desktop 1440px, full-bleed responsive with NO empty side gutters; support BOTH light and dark theme (keep body sections light by default, but render the hero as a cinematic dark-tinted centerpiece). Primary blue #0066B3, deep #004C86, AI purple #6D4AFF; use line (stroke) SVG icons only, NO emoji. Product name must read exactly "KINTEX AI 전시·행사시스템". + +HERO (full-bleed, cinematic, the centerpiece): a large photorealistic exhibition-hall panorama photograph as the background, covered by a subtle dark-to-transparent gradient overlay for text contrast and a faint corner watermark "AI 생성 예상 이미지". Give it depth like a light parallax scene (a softly blurred far background plus a crisper foreground layer). Over it, a strong typographic hierarchy: a large display headline "무엇이든 물어보세요 — AI가 관람을 안내합니다", a subheading "전시 일정 · 오시는 길 · 입장권 · 부스 위치를 대화로 한 번에", and the visual focus — a wide conversational AI assistant search bar with a small purple "AI 도우미" tag and a send (paper-plane) icon, plus example question chips "이번 주말 로봇 전시 있어? · 킨텍스 주차 어디에 해요? · 가족과 볼만한 전시 추천". Under the hero: two CTAs "사전등록" and "입장권 예매", and a subtle animated scroll-down cue. + +Design should imply tasteful, restrained, performant motion: sections fade / slide up on scroll, KPI numbers count up, poster cards lift with a soft shadow and their images zoom on hover, and a poster rail auto-scrolls. + +Below the hero, with generous section rhythm (alternating full-bleed bands and light card grids): (1) a stats band "누적 전시 1,200+ · 참가업체 30,000+ · 연 관람객 500만+" in big tabular numerals (implying count-up). (2) an "AI가 도와드립니다" capability showcase of 4–6 cards, each with a purple "AI" tag, a stroke icon, a title and one line ("AI 관람 도우미", "AI 전시 추천", "AI 길찾기", "AI 일정 도우미", "AI 혼잡·대기 안내", "AI 시공 예상 이미지"). (3) an "AI 전시·행사 기획 브리핑" section with a "연간 / 월간" toggle and a 12-month timeline plus a purple "AI 요약" headline. (4) a rich "진행 중 · 예정 전시" showcase — a grid/carousel of large 2:3 exhibition poster cards (poster image, title, dates "2026.09.05–09.08", hall label, a status badge, a "D-25" day chip, "사전등록"), with tabs "진행 중 | 이번 달 | 다음 달" and a venue filter "전체 · 제1전시장 · 제2전시장". (5) a "방문 가이드" 2×3 grid of stroke-icon cards ("교통 · GTX-A 킨텍스역 도보 3분", "주차 · 실시간 안내", "실내 길찾기", "편의시설", "접근성 · 휠체어·수어통역", "고객문의"). (6) a full-width "전시장 소개" band with a background photograph and KINTEX scale figures. Footer with company info, "오시는 길 · GTX-A 킨텍스역", SNS, language. + +Where poster or hero/band images are missing, fall back to a branded blue gradient with a faint geometric grid/dot line-pattern overlay (no blank areas, no layout shift). Sophisticated, immersive, conversion-oriented global exhibition-center aesthetic; rounded 8px cards, soft ambient shadows, tabular numerals, strong type hierarchy, WCAG AA contrast in BOTH themes; all motion must respect prefers-reduced-motion (parallax and auto-scroll disabled, reveals instant when reduced). +``` + +**V-9. Stitch 프롬프트 — 3-트랙 관문 비주얼 격상판(SCR-T0 재생성용, 동일 톤)** + +``` +Re-generate the "track gateway" landing page for "KINTEX AI 전시·행사시스템" with a premium, cinematic visual upgrade in the same tone as the visitor homepage. Korean UI only, desktop 1440px, full-bleed (no side gutters), light theme body with a dark-tinted cinematic hero; primary blue #0066B3, AI purple #6D4AFF; line (stroke) SVG icons only, NO emoji. + +Full-bleed HERO: a photorealistic exhibition-hall panorama background with a dark-to-transparent overlay and a faint watermark "AI 생성 예상 이미지", light-parallax depth, a large display headline "KINTEX AI 전시·행사시스템", a subheading "전시 기획부터 시공·관람까지 전 과정을 AI로 자동화", and CTAs "관람 사전등록" and "참가 문의". Keep the top header with the wordmark, the 3-tab track switcher "관람 · 비즈니스 · 협력사" (none active), and utilities "언어 · 로그인". + +Directly below, three large branching track cards in one row that lift on hover with an image zoom — card 1 "관람객" (slightly larger, primary) "행사 일정 · 사전등록 · 오시는 길" button "관람 트랙 →"; card 2 "주최 · 참가 (비즈니스)" "대관 · 자동견적 · 부스 신청 · 시공 시안" button "비즈니스 트랙 →"; card 3 "공사 · 협력사 (에이전시)" "등록업체 · 공사 옥션 · 작업신고" button "협력사 트랙 →". Then a "진행 중 · 예정 전시" auto-scrolling rail of 2:3 poster cards (poster, title, dates, hall, "D-25" chip, "사전등록"). Sections fade/slide up on scroll. Footer with "오시는 길 · GTX-A 킨텍스역". Missing images fall back to a branded gradient with a faint grid/dot pattern. Rounded 8px cards, soft shadows, tabular numerals, WCAG AA; all motion respects prefers-reduced-motion. +``` + ### 3C-0. 공개 셸(PublicShell) + 3-트랙 스위처 (전 SCR-T*·SCR-P* 공통 크롬) - **재사용 전제**: SCR-P*가 쓰는 **PublicShell**(전통 페이지 헤더 GNB·푸터·SSR/CDN·MDI 비적용)을 그대로 재사용하되, **상단에 3-트랙 전환 탭(TrackSwitcher)을 추가**한다. 관문(SCR-T0)은 트랙 미선택 상태, 서브홈(T1~T3)은 해당 트랙 탭 활성. @@ -1805,6 +1908,7 @@ A mobile "my ticket wallet" screen for an exhibition visitor, 390px width, light | 버전 | 일자 | 작성자 | 내용 | |---|---|---|---| +| **v2.4** | 2026-07-12 | designer | **공개 사이트 비주얼·모션 전면 리뉴얼 스펙 신설(소유자 확정: "코엑스처럼 세련되게 — 히어로 배경·애니메이션·비주얼, 지금 착수").** §3C에 **3C-V 공개 사이트 비주얼·모션 스펙** 절 순증 — 기존 SCR-T0~T3·SCR-P* **구조는 불변**, 그 위에 비주얼/모션 레이어만 격상(구조 재설계 아님). ①**비주얼 폴리시(V-1)** 풀블리드·공백 없음·섹션 리듬(밴드↔카드 교대·96/64/40px)·여백·라운드 8px·소프트 앰비언트 그림자·타이포 위계 강화·선 SVG·이모지 금지·다크/라이트·2:3 포스터 카드. ②**모션 토큰(V-2)** duration(`--motion-fast 150`/`base 240`/`slow 400`/`hero 600`/`countup 1200`)·easing(`--ease-emphasized/standard/decelerate`) 신설(컬러 신규 토큰 0). ③**섹션별 배경·애니메이션 트리거 매핑(V-3)** 풀블리드 히어로(라이트 파랄랙스+인트로 리빌)·통계 카운트업·AI 타일 스태거·쇼케이스 hover 리프트/이미지 줌·행사 레일 오토스크롤·기획 브리핑 grow·공통 스크롤 리빌(IO threshold 0.15·once). ④**이미지 소스 매핑(V-4)** build 내장 `/media/`(hero·bands·poster·patterns 계약)·부재 시 그라디언트+패턴 폴백(레이아웃 시프트 0)·preload/lazy/webp/srcset. ⑤**접근성(V-5)** `prefers-reduced-motion` 필수 — 파랄랙스/오토스크롤 정지·카운트업 즉시·리빌 instant·모션은 전부 장식(정보 비의존)·양 테마 WCAG AA. ⑥**성능(V-6)** CSS 우선·`transform`/`opacity`만·`will-change` 절제·60fps·강등 순서. ⑦**프론트 계약(V-7)** `.pub-reveal`/`--stagger`·`.pub-hero__bg[data-parallax]`·`.pub-card--lift`·`.pub-card__media img` 줌·`.pub-countup`·`.pub-rail--auto`·폴백 유틸, **PublicShell 하위만 스코프**(AppShell/MDI·SCR-A*·모바일 미주입). ⑧**영어 Stitch 프롬프트 2종(V-8·V-9)** — 프리미엄 전시장 홈페이지(SCR-T1 시네마틱 히어로·스크롤 리빌·카운트업·hover 줌·오토스크롤·폴백·reduced-motion, 제품명 정확·NO emoji·선 SVG·라이트/다크) + 3-트랙 관문(SCR-T0 동일 톤 격상). **경계 준수**: visitor(T1)만 풀 시네마틱, business/agency는 B2B 톤 유지+공통 모션 절제 적용. 신규 컬러 토큰 0·PLANNING.md·src 무수정. | | **v2.3.2** | 2026-07-12 | designer | **SCR-T1 — AI 전시·행사 기획 브리핑(연간·월간 선제 제시) 추가(소유자 추가 지시 2026-07-12).** 단발 질의응답을 넘어 **AI가 개최 예정 전시·행사를 먼저 큐레이션**하는 섹션을 히어로 AI 능력 타일 직하(레이아웃 4번)에 신설 — **연/월 관점 토글**(① 연간=12개월 타임라인+분기 하이라이트+AiLabel "AI 요약" 헤드라인, ② 월간=선택 월 캘린더/리스트+AI 월간 요약), 연/월 전환 시 AI가 해당 기간 데이터 근거로 요약·추천 갱신. **중복 회피**: 하단 "진행 중/이번 달/다음 달" 쇼케이스(레이아웃 5)와 달리 본 섹션은 **연간 기획 관점**이 차별점, 월간은 동일 event 소스로 정합. 구성요소 `AiPlanningBriefing` 추가, 이하 레이아웃 5~11 재번호. **데이터 원천** — 신규 공개 엔드포인트 제안 `GET /api/public/events/planning?view=year|month&year&month`(크롤 적재 `exhibition`/`event`를 개최연·월 그룹 조회, AI 기간 요약은 AiTextRouter Claude→Ollama 폴백이 해당 기간 DB만 근거·근거 인용·캐시, DB 서빙·외부 실시간 호출 없음, 계약 제안). 상태(로딩·성공·**degraded(요약 미가용→데이터만 노출)**·빈·에러·기간 전환 부분 로딩) 정의. **Stitch 프롬프트 갱신** — 연간 타임라인+"AI 요약" 헤드라인("2026년은 상반기 스마트팩토리·로봇, 하반기 헬스케어·친환경…")+월간 요약("이번 달은 로봇·헬스케어 전시가 집중됩니다") 문단 추가, 쇼케이스와 구별 명시. 세련된 홈페이지 톤·풀블리드·반응형·라이트/다크·크롤 적재 DB 근거 원칙은 유지. 신규 토큰 0·PLANNING.md·src 무수정 | | **v2.3.1** | 2026-07-12 | designer | **SCR-T1 마무리 — AI 전면 노출 + AI 답변 근거=크롤 적재 DB 명시(소유자 추가 지시 2건 2026-07-12).** ①**AI 기능 쇼케이스 타일 신설**(히어로 직하 레이아웃 3번): "AI 시스템인데 AI가 어디 있는지 모르겠다" 지적 반영 — AI 관람 도우미·AI 전시 추천·AI 길찾기·AI 일정 도우미·AI 혼잡/대기 안내·AI 시공 예상 이미지 6능력을 AiLabel 카드로 전면 홍보(구성요소 AiCapabilityTiles·상태(정적 상시·미가용 "준비 중")·이하 레이아웃 4~10 재번호). ②**데이터 원천 = 사전 크롤 적재 온프레미스 DB 명시**: AI 관람 도우미(`POST /api/public/ai/visitor-assistant`)가 **실시간 외부 크롤이 아니라 미리 크롤해 DB에 적재한 공개 데이터만을 RAG 근거로 답하고 참조 레코드를 인용**(범위 밖=degraded)함을 명문화 — 근거 테이블 계약 예시(`exhibition`/`event`·`transport_info`·`parking_info`·`ticket_policy`·`amenity_info`·`visitor_guide`·`floorplan`), 각 섹션 출처를 크롤 적재 DB로 매핑, degraded 폴백을 "크롤 적재 데이터 검색"으로 갱신, 백엔드 계약을 "공개 조회 API가 크롤 적재 DB에서 서빙(외부 실시간 호출 없음·온프레미스·근거 인용)" 전제로 기술. ③**Stitch 프롬프트 갱신** — AI 능력 쇼케이스 카드 6종 문구 + "served from a pre-crawled on-premise database, AI answers ONLY from stored data and cites its source" 추가. 세련된 홈페이지 톤·풀블리드·반응형·라이트/다크는 v2.3 그대로 유지. 신규 토큰 0·PLANNING.md·src 무수정 | | **v2.3** | 2026-07-12 | designer | **SCR-T1 visitor 메인을 "세련된 공개 마케팅 홈페이지"로 격상 + 중심 요소를 대화형 AI 관람 도우미로 재구성(소유자 지시 2026-07-12).** ①**§3C SCR-T1 전면 재작성** — 기존 "visitor 서브홈(가이드 허브)"을 일반 대중 대상 마케팅 홈페이지(코엑스/글로벌 전시장 홈 수준·풀블리드·타이포 위계·공백 없는 밀도·전환 지향)로 격상. **화면 중심 = AI 관람 도우미**(히어로 정중앙 대형 자연어 질문 입력창 + 예시 질문칩 5종 + 대화형 답변 패널: 답변 텍스트·구조화 결과 카드(전시/교통/입장/wayfinding)·후속 질문칩·AI 고지문·근거 출처, AiLabel 보라 구분). 관람객 관심 우선순위 5(전시 추천·교통·입장·부스위치·편의)를 자연어 하나로 관통. 하위 섹션 순증: 전시 쇼케이스(2:3 포스터 그리드/캐러셀·탭·전시장 필터·`poster_url`)·관심사 맞춤 추천 레일(M11 AI)·방문가이드 허브(2×3, GTX-A·주차·M13 길찾기·편의·접근성·문의)·관람객 여정 3-스텝(사전등록→배지/QR→체크인)·공지·뉴스·전시장 소개·푸터(회사·SNS·문의). ②**데이터 원천** — **신규 공개 엔드포인트 제안 `POST /api/public/ai/visitor-assistant`**(비인증·CORS·레이트리밋·PII 미수집, 기존 AiTextRouter Claude→Ollama 폴백 + 공개 행사 데이터 근거·환각방지, **계약 제안·백엔드 후속**), 나머지 `/api/public/events`·notice·CMS 재사용. ③**상태** — AI 도우미 대기/생각중/성공/**degraded(AI 미가용→키워드 검색 폴백)**/에러/빈결과 정의. ④**테마** — 공개 마케팅 홈페이지에 한해 **라이트/다크 테마 토글 제공(§1-5 업무 셸 라이트 단일 원칙의 공개 트랙 예외)**, 색은 §1 토큰 CSS 변수 주입·라이트 기본. ⑤**접근성** — AI 입력 `role="search"`·답변 `aria-live="polite"`·색 외 라벨·양 테마 WCAG AA, i18n `track.visitor.*`. ⑥영어 **Stitch 프롬프트 전면 갱신**("premium exhibition-center marketing homepage"·1440 full-bleed·AI 관람 도우미 centerpiece·제품명 "KINTEX AI 전시·행사시스템" 정확 표기·NO emoji/line SVG·라이트·다크). ⑦**§2.8-2 인벤토리** SCR-T1 행 명칭/모듈 갱신. **경계 준수**: business(SCR-T2)·agency(SCR-T3) 메인은 B2B 업무 톤 유지(격상 대상 아님) 명시, 라우트 `/visitor`·PublicShell·TrackSwitcher·세션키·기존 SCR 불변, **신규 토큰 0**, PLANNING.md·src 무수정 | diff --git a/mobile/README.md b/mobile/README.md index 87b0eca..64fbe71 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -7,6 +7,19 @@ React Native + Expo(expo-router, TypeScript) 모바일 앱. 조회·승인·현 - Expo SDK 51 · expo-router ~3.5 · React Native 0.74 · TypeScript - JWT는 `expo-secure-store`(네이티브 keychain/keystore)에 저장, 웹은 AsyncStorage 폴백 - 디자인 토큰: `theme/`(docs/design.md §1 팔레트·타이포). 신규 토큰 없음. +- 다국어: `react-i18next` + `expo-localization`(SDK51 호환). 지원 ko(기본/폴백)·en·zh·ja. + +## 다국어(i18n) +- 초기화 `lib/i18n/`(동기 init — 디바이스 로케일 감지, 저장 선택은 부팅 시 반영). 리소스 `lib/i18n/locales/{ko,en,zh,ja}.ts`. +- **ko가 키 형상의 단일 출처** — en/zh/ja는 `Translations`(DeepString) 타입 구현이라 키 누락 시 tsc가 잡음(병렬 편집 함정 회피: 직렬·키 대조). +- 언어 선택 지속: AsyncStorage 키 `kintex_lang`. 전환 UI `components/LanguageSelector.tsx`(더보기·내정보에 배치), 상태 `context/LanguageContext.tsx`. +- 커버 화면(우선순위): 로그인/2FA·회원가입·비번찾기·홈·현장·갤러리·더보기·내정보·티켓 지갑·탭/스택 타이틀·역할 라벨. 미커버(폴백=현행 한국어): `checklist`·`inspection`·`tickets/select`·티켓 카드/필터탭 컴포넌트(`components/tickets/*`). + +## 역할별 랜딩 +- `lib/roleTrack.ts` — 웹 `src/frontend/src/lib/roleTrack.ts` 패리티(우선순위 admin>ops>business>agency>visitor, 미판정=visitor 기본). +- 로그인 응답 user에 roleCode가 없어 `hallManager`를 ops 신호로 사용(roleCode 아는 호출부는 옵션 전달). +- 모바일 랜딩 매핑: admin/ops/business→`/(tabs)` 홈 · agency(장치·시공)→`/(tabs)/field` · visitor(관람객)→`/tickets`. +- 콜드 부팅 시 workspaces 미복원 misroute 방지 위해 로그인 시 계산한 랜딩 경로를 secure-store에 지속(`AuthContext`). ## 실행 ```bash @@ -48,5 +61,6 @@ API 베이스는 `app.json > extra.apiBase` 또는 `EXPO_PUBLIC_API_BASE` 환경 ## 남은 작업 - 실 화면 데이터 API 배선 확대(부스 목록·검수 요청 제출 — 백엔드 M6/C-4 확장 대기). -- 앱 아이콘/스플래시 에셋(`assets/`), i18n, 푸시 알림. -- EAS 실빌드(eas.json 프로파일) — 본 스캐폴드 범위 밖. +- 앱 아이콘/스플래시 에셋(`assets/`), 푸시 알림. +- i18n 잔여 화면 키 추출: `checklist`·`inspection`·`tickets/select`·`components/tickets/*`(현재 한국어 폴백). +- EAS 실빌드(eas.json 프로파일) — G3 게이트(소유자 승인). diff --git a/mobile/app.json b/mobile/app.json index c4032f3..21dd44e 100644 --- a/mobile/app.json +++ b/mobile/app.json @@ -4,7 +4,7 @@ "slug": "kintex", "owner": "zioinfo", "scheme": "kintex", - "version": "0.1.0", + "version": "0.2.0", "orientation": "portrait", "userInterfaceStyle": "light", "jsEngine": "hermes", @@ -28,7 +28,7 @@ }, "android": { "package": "kr.co.zioinfo.kintex", - "versionCode": 1, + "versionCode": 2, "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#0066B3" @@ -58,7 +58,6 @@ "expo-router", "expo-secure-store", "expo-font", - "expo-screen-capture", [ "expo-image-picker", { diff --git a/mobile/app/(tabs)/_layout.tsx b/mobile/app/(tabs)/_layout.tsx index fccc948..f364588 100644 --- a/mobile/app/(tabs)/_layout.tsx +++ b/mobile/app/(tabs)/_layout.tsx @@ -5,10 +5,12 @@ import { Ionicons } from '@expo/vector-icons'; import { Redirect, Tabs } from 'expo-router'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { useAuth } from '../../context/AuthContext'; import { colors } from '../../theme'; export default function TabsLayout() { + const { t } = useTranslation(); const { ready, token } = useAuth(); if (ready && !token) return ; @@ -25,14 +27,14 @@ export default function TabsLayout() { , }} /> ( ), @@ -41,14 +43,14 @@ export default function TabsLayout() { , }} /> ( ), diff --git a/mobile/app/(tabs)/field.tsx b/mobile/app/(tabs)/field.tsx index b3c1912..0f6be33 100644 --- a/mobile/app/(tabs)/field.tsx +++ b/mobile/app/(tabs)/field.tsx @@ -5,12 +5,14 @@ import { Ionicons } from '@expo/vector-icons'; import { router } from 'expo-router'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; import { Banner } from '../../components/Banner'; import { useAuth } from '../../context/AuthContext'; import { colors, radius, spacing, type } from '../../theme'; export default function FieldScreen() { + const { t } = useTranslation(); const { activeWorkspace, user } = useAuth(); const role = activeWorkspace?.myRole; const isHallManager = user?.hallManager || role === 'HALL_MANAGER'; @@ -18,19 +20,17 @@ export default function FieldScreen() { return ( - 현장 업무 + {t('field.title')} {!isContractor && !isHallManager ? ( - - 현장 기능은 장치·시공업체(체크리스트)와 홀매니저(검수) 역할에서 제공됩니다. - + {t('field.guide')} ) : null} {isContractor ? ( router.push('/checklist')} /> ) : null} @@ -38,8 +38,8 @@ export default function FieldScreen() { {isHallManager ? ( router.push('/inspection')} /> ) : null} diff --git a/mobile/app/(tabs)/gallery.tsx b/mobile/app/(tabs)/gallery.tsx index 3826b97..03bc473 100644 --- a/mobile/app/(tabs)/gallery.tsx +++ b/mobile/app/(tabs)/gallery.tsx @@ -4,7 +4,8 @@ * 실 API 호출 구조를 유지하되, 서버 미구현/오프라인 시 degraded 샘플로 폴백한다. */ import { router } from 'expo-router'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { RefreshControl, ScrollView, StyleSheet, Text, View } from 'react-native'; import { AiImage } from '../../components/AiImage'; import { Banner } from '../../components/Banner'; @@ -18,14 +19,21 @@ import { colors, spacing, type } from '../../theme'; const DEMO_BOOTH = 'b-102'; // degraded 폴백 샘플 (서버 미구현/오프라인 시 표시 — 항상 워터마크 유지) -const FALLBACK: RenderJobDto[] = [ - mkJob('job-s1', 'S1', 'DONE'), - mkJob('job-s2', 'S2', 'DONE'), - mkJob('job-s3', 'S3', 'RUNNING'), - mkJob('job-s7', 'S7', 'FAILED'), -]; +function buildFallback(notice: string): RenderJobDto[] { + return [ + mkJob('job-s1', 'S1', 'DONE', notice), + mkJob('job-s2', 'S2', 'DONE', notice), + mkJob('job-s3', 'S3', 'RUNNING', notice), + mkJob('job-s7', 'S7', 'FAILED', notice), + ]; +} -function mkJob(jobId: string, shot: string, status: RenderJobDto['status']): RenderJobDto { +function mkJob( + jobId: string, + shot: string, + status: RenderJobDto['status'], + notice: string, +): RenderJobDto { return { jobId, boothId: DEMO_BOOTH, @@ -36,7 +44,7 @@ function mkJob(jobId: string, shot: string, status: RenderJobDto['status']): Ren modelVersion: null, watermarkRequired: true, watermarkText: AI_IMAGE_NOTICE, - notice: 'AI 생성 이미지는 계약·심사 서류에 사용할 수 없습니다 (시공 기준은 도면)', + notice, }; } @@ -51,15 +59,18 @@ const SHOT_LABEL: Record = { }; export default function GalleryScreen() { + const { t } = useTranslation(); const { activeWorkspace, token } = useAuth(); const [jobs, setJobs] = useState([]); const [degraded, setDegraded] = useState(false); const [loading, setLoading] = useState(true); + const fallback = useMemo(() => buildFallback(t('gallery.notice')), [t]); + const load = useCallback(async () => { setLoading(true); if (!activeWorkspace || !token) { - setJobs(FALLBACK); + setJobs(fallback); setDegraded(true); setLoading(false); return; @@ -72,7 +83,7 @@ export default function GalleryScreen() { setDegraded(false); } catch (e) { if (isDegraded(e)) { - setJobs(FALLBACK); + setJobs(fallback); setDegraded(true); } else { setJobs([]); @@ -81,7 +92,7 @@ export default function GalleryScreen() { } finally { setLoading(false); } - }, [activeWorkspace, token]); + }, [activeWorkspace, token, fallback]); useEffect(() => { load(); @@ -91,9 +102,7 @@ export default function GalleryScreen() { {/* 상단 고정 고지 바 (제거 불가) */} - - AI 생성 이미지는 계약·심사 서류에 사용할 수 없습니다 — 시공 기준은 도면입니다 - + {t('gallery.infoBar')} } > {degraded ? ( - - 서버 이미지 이력에 연결할 수 없어 예시를 표시합니다 (degraded). - + {t('gallery.degraded')} ) : null} {jobs.length === 0 && !loading ? ( - - 아직 생성된 이미지가 없습니다 — 설계 스튜디오에서 예상 사진을 생성하세요. - + {t('gallery.empty')} ) : null} @@ -124,14 +129,14 @@ export default function GalleryScreen() { height={130} /> {j.status === 'FAILED' ? ( - {expanded && (