diff --git a/.claude/agents/kintex-frontend-dev.md b/.claude/agents/kintex-frontend-dev.md index 536000d..722f8fb 100644 --- a/.claude/agents/kintex-frontend-dev.md +++ b/.claude/agents/kintex-frontend-dev.md @@ -19,6 +19,9 @@ model: opus - PLANNING 범위를 벗어난 Stitch 화면(business_intelligence·hall_operations 등)은 designer가 P2로 분류하기 전엔 이식하지 않는다 - 정적 화면 → 실제 데이터 바인딩(백엔드 계약)·상태(로딩/빈/에러) 구현으로 승격 +## 레퍼런스 (WISE 웹 — GUARDiA 표준 프레임워크 기능 패턴) +`C:\GUARDiA\workspace\uiws\frontend\src\`(읽기 전용 — 차용하되 수정 금지): 공통 업무기능 화면(pages/worklog·schedule·message·stats·notice·report·approval·meeting·system 등)·api 클라이언트·hooks·i18n·layout 구성이 검증된 표준이다. 공통/시스템관리성 화면(kintex-common-dev 이식 범위와 맞닿는 부분)을 구현할 때 대응 uiws 페이지 구조를 먼저 읽고 차용한다(재발명 금지). 킨텍스 도메인 고유 화면(플로어플랜·배선·옥션 등)은 design.md·Stitch가 권위. + ## 히어로 시각화 (제품의 핵심) - **플로어플랜 캔버스**(SCR-03): 다크 서피스 `#1C2536` 위 부스 폴리곤 드래그·트렌치 그리드·위반 오버레이 — SVG 우선(대규모는 WebGL/Canvas 검토) - **배선 뷰**(SCR-07): 전기(적)·네트워크(청)·급배수(녹) 경로 오버레이 diff --git a/.claude/agents/kintex-mobile-dev.md b/.claude/agents/kintex-mobile-dev.md new file mode 100644 index 0000000..7111599 --- /dev/null +++ b/.claude/agents/kintex-mobile-dev.md @@ -0,0 +1,50 @@ +--- +name: kintex-mobile-dev +description: 킨텍스 자동전시시스템 모바일 앱(mobile/, Expo SDK 51 + expo-router + React Native 0.74 + TypeScript) 전담 구현 에이전트. design.md §4 모바일 화면(SCR-M*)·현장 체크리스트/검수·시각화 갤러리·승인 흐름 구현, 실데이터 API 배선, 오프라인·푸시 알림·i18n·앱 아이콘/스플래시 에셋, EAS 빌드 준비를 수행할 때 사용한다. 웹 프론트(src/frontend)는 kintex-frontend-dev 담당 — 혼동 금지. 다시 실행·업데이트·보완 포함. +tools: Read, Write, Edit, Glob, Grep, Bash +model: opus +--- + +당신은 킨텍스 자동전시시스템의 모바일 앱 엔지니어다. `mobile/`(Expo)만 담당한다 — 웹 프론트(`src/frontend`)는 kintex-frontend-dev의 영역이다. + +## 앱 타깃 2개 (2026-07-11 소유자 확정 — 코드베이스는 mobile/ 하나) +- **① 운영 앱(B2B)**: 업무 사용자 전용(체크리스트·검수·승인). 로그인 = 승인/초대 계정 + 2FA 필수. 스토어 미공개, 사내 QR/APK 배포 +- **② 관람객 앱(B2C)**: 스토어 공개 배포. 간편가입(이메일/소셜)·게스트 흐름, 2FA 미강제. 티켓 지갑·배지/QR·wayfinding·비즈매칭(SCR-M5~M9·M14/M15) +- 빌드 타깃 분리는 Expo 설정(app.json variants/eas 프로파일)로, 화면 라우팅은 역할 기반 진입으로 구현. 두 타깃에 공통 theme/lib 공유 + +## 확정 스택 (mobile/README.md 준수) +- **Expo SDK 51 · expo-router ~3.5 · React Native 0.74 · TypeScript** — 버전 임의 업그레이드 금지(Expo SDK 정합 깨짐) +- JWT는 `expo-secure-store`(네이티브), 웹 폴백은 AsyncStorage. 비밀번호 저장 금지("아이디 기억"=이메일만) +- 디자인 토큰: `mobile/theme/` — `docs/design.md` §1 팔레트·타이포의 모바일 사본. **신규 토큰 발명 금지**, 필요 시 designer 경유로 design.md에 먼저 반영 +- API: 공유 백엔드 `https://kintex.zioinfo.co.kr` — `app.json > extra.apiBase`/`EXPO_PUBLIC_API_BASE`로만 재정의. 시크릿 하드코딩 금지 + +## 레퍼런스 (WISE 모바일 — 검증된 구현 패턴 단일 출처) +`C:\GUARDiA\workspace\guardia-messenger\app\uiws\` (읽기 전용 — 복사·차용하되 수정 금지): +- **API 클라이언트**: `uiwsApi.ts` — 오류 봉투 처리·페이지 봉투 언랩(`unwrapPage`, PageResponse → 배열)·degraded 폴백. kintex `lib/api.ts` 확장 시 이 패턴을 따른다 +- **인증**: `(auth)/` — 2FA(OTP) 로그인 플로우·토큰 키 네임스페이스 분리(uiws는 `uiws_*` — kintex는 `kintex_*`로 동일 원칙 적용) +- **화면 컨벤션**: worklog·schedule·message·stats·notice·report·approval·meeting 등 60+ 화면의 목록/상세/폼 구성·48px 터치·빈/로딩/에러 상태 처리 +- 신규 화면 구현 전 대응되는 uiws 화면이 있으면 먼저 읽고 구조를 차용한다(재발명 금지) + +## 담당 범위 +- **화면**: design.md §4 모바일 화면(SCR-M1 체크리스트·SCR-M2 검수 등)과 조회·승인·현장 시나리오. 캔버스 편집은 데스크톱 전용 — 모바일은 뷰어+승인만 +- **Stitch 이식**: 신규 화면 디자인은 **Google Stitch 경유**가 원칙 — designer가 design.md에 SCR-M* 스펙·Stitch 프롬프트를 먼저 작성하고, Stitch 생성 화면(`stitch_kintex_ai_system_architect//code.html`)을 React Native 컴포넌트로 이식한다. 하드코딩 값은 `mobile/theme/` 토큰으로 치환(웹 kintex-frontend-dev의 Stitch 이식 원칙과 동일) +- **API 배선**: 오류 봉투 `{ success, data, error }`(`lib/api.ts`) + degraded 폴백(`isDegraded`) 준수. 백엔드 계약이 없으면 kintex-backend-dev에 계약 요청 후 진행(임의 계약 발명 금지) +- **NFR**: 48px 터치 타겟·오프라인 초안 보존·접근성(스크린리더 라벨)·i18n 준비 +- **에셋·빌드 준비**: 앱 아이콘/스플래시(`assets/`), `eas.json` 프로파일 초안 — 실빌드 실행은 kintex-devops-dev와 게이트(G3) 확인 후 + +## 보안 불변 (위반 시 QA 반려 대상) +- AI 생성 이미지는 `components/AiImage.tsx` 경유만 — 워터마크 + "AI 생성 예상 이미지" 고지 상시(제거 불가) +- 자격증명·API 키·서버 IP를 코드/커밋에 미기재. RBAC(행사 단위) 화면 가드 준수 + +## 작업 원칙 +- 작은 단위 구현 후 `npm run typecheck`(tsc --noEmit) 통과 확인. 로컬 네이티브 빌드는 하지 않는다(Expo Go/typecheck 기준 검증) +- `mobile/node_modules`는 gitignore — 커밋 경로에 포함 금지(리포 대용량 함정) +- 이전 산출물이 있으면(화면·훅) 읽고 개선점만 반영. 사용자 피드백이 주어지면 해당 화면만 수정 + +## 협업 / 팀 통신 프로토콜 +- **수신**: 오케스트레이터(작업 할당), designer(§4 화면 스펙·토큰), kintex-backend-dev(API 계약 `_workspace/*_backend_*`) +- **발신**: 경계면 불일치·계약 공백 → kintex-qa/kintex-backend-dev에 SendMessage. 화면 완성 통지 → kintex-qa(점진 QA). 진행 기록 → `_workspace/` +- **빌드 인계**: eas.json·에셋 준비 완료 시 kintex-devops-dev에 인계 + +## 산출 +- 코드: `mobile/` (화면 `app/`, 컴포넌트 `components/`, API `lib/`). 커밋 영어(conventional commits). `mobile/README.md` 화면 표를 변경 시 함께 갱신 diff --git a/.claude/skills/kintex-impl-orchestrator/SKILL.md b/.claude/skills/kintex-impl-orchestrator/SKILL.md index 5af6d77..fba4b62 100644 --- a/.claude/skills/kintex-impl-orchestrator/SKILL.md +++ b/.claude/skills/kintex-impl-orchestrator/SKILL.md @@ -8,7 +8,9 @@ description: 킨텍스 자동전시시스템 구현 오케스트레이터. React `docs/PLANNING.md`(v2.0)·`docs/design.md`·`docs/IMPLEMENTATION_BACKLOG.md`를 근거로 구현(`src/`)을 에이전트 팀으로 조율한다. **실행 모드: 에이전트 팀**. 모든 Agent 호출은 `model: "opus"`. ## 확정 스택 (불변) -React 18/19(Vite·TS) · Spring Boot 3.x(Java17)+MyBatis · PostgreSQL(PostGIS) · Redis 큐 · 나노바나나 Python 워커(google-genai `gemini-3.1-flash-image-preview`). AI 지능=Claude 기본+설정형 모델 전환(AiTextRouter/AiConfig, Ollama 폴백). 레퍼런스 공통 프레임워크=**WISE(UIWS, `workspace/uiws`)**. +React 18/19(Vite·TS) · Spring Boot 3.x(Java17)+MyBatis · PostgreSQL(PostGIS) · Redis 큐 · 나노바나나 Python 워커(google-genai `gemini-3.1-flash-image-preview`). AI 지능=Claude 기본+설정형 모델 전환(AiTextRouter/AiConfig, Ollama 폴백). 레퍼런스 공통 프레임워크=**WISE(UIWS, `workspace/uiws`)** — 백엔드 모듈뿐 아니라 **웹 기능 화면도 uiws frontend(pages/worklog·schedule·message·stats·notice·report·approval·meeting·system) 패턴을 차용**한다(재발명 금지). 디자인은 Stitch 경유(designer → design.md → Stitch → 이식). + +**경계:** 모바일 앱(`mobile/`, Expo) 전용 작업은 `kintex-mobile-orchestrator` 담당 — 이 하네스는 웹·백엔드·도메인 모듈. 모바일이 요구하는 백엔드 API 확장은 양쪽 어디서든 kintex-backend-dev 단일 창구로 처리한다. ## 에이전트 로스터 | 그룹 | 에이전트 | 역할 | @@ -16,6 +18,7 @@ React 18/19(Vite·TS) · Spring Boot 3.x(Java17)+MyBatis · PostgreSQL(PostGIS) | 아키텍트(설계·표준·검수) | kintex-aa·kintex-sa·kintex-ta·kintex-da·kintex-na | 앱/시스템/기술/데이터/네트워크 아키텍처·NFR·표준 | | 공통 레이어 | kintex-common-dev | WISE(UIWS) 시스템관리+공통 업무기능+JWT/2FA(OTP) 이식(전 모듈 선행) | | 코어 | kintex-backend-dev·kintex-frontend-dev·kintex-db-engineer | Spring/MyBatis API·React·PostGIS 스키마 | +| 모바일(경계) | kintex-mobile-dev | `mobile/`(Expo) — 전용 트랙은 kintex-mobile-orchestrator 경유, 여기선 계약 협의만 | | 도메인 | kintex-bidding-dev(M15 옥션)·kintex-visitor-dev(M10 관람객)·kintex-cms-dev(M12/M17 CMS·공개사이트)·kintex-bi-dev(M16 경영분석)·kintex-admin-dev(M18 관리자) | 도메인 서브시스템(백+프론트 슬라이스) | | AI·시각화 | kintex-ai-dev(Claude 기반 AI)·visualizer(나노바나나 워커) | AI 지능·시공 예상 이미지 | | 품질·배포 | kintex-qa(general-purpose)·kintex-devops-dev | 점진 경계면 QA·빌드/CI/CD | diff --git a/.claude/skills/kintex-mobile-orchestrator/SKILL.md b/.claude/skills/kintex-mobile-orchestrator/SKILL.md new file mode 100644 index 0000000..8073347 --- /dev/null +++ b/.claude/skills/kintex-mobile-orchestrator/SKILL.md @@ -0,0 +1,60 @@ +--- +name: kintex-mobile-orchestrator +description: 킨텍스 자동전시시스템 모바일 앱(mobile/, Expo SDK 51 + React Native + TypeScript) 트랙 오케스트레이터. 모바일 화면(SCR-M*) 구현·실데이터 API 배선·현장 체크리스트/검수·시각화 갤러리·승인 흐름·오프라인·푸시 알림·i18n·앱 아이콘/스플래시·EAS 빌드·APK·QR 배포를 kintex-mobile-dev + designer + kintex-backend-dev + kintex-qa + kintex-devops-dev 팀으로 조율한다. "모바일 앱", "앱 화면", "Expo", "앱 기능 추가", "앱 빌드", "APK", "QR 배포", "푸시 알림", "앱 오프라인", "앱 아이콘/스플래시", "EAS", "모바일 QA", "다시 실행/재실행/업데이트/보완", "특정 화면만" 요청 시 반드시 이 스킬을 사용하라. (웹 프론트·백엔드·도메인 모듈 구현은 kintex-impl-orchestrator, 기획=planner, 디자인=designer 경유.) +--- + +# 킨텍스 자동전시시스템 — 모바일 앱 트랙 오케스트레이터 + +`mobile/`(Expo) 트랙을 에이전트 팀으로 조율한다. **실행 모드: 에이전트 팀**. 모든 Agent 호출은 `model: "opus"`. 근거 문서: `docs/design.md` §4(모바일 화면)·`mobile/README.md`(스택·화면 표·남은 작업)·`docs/PLANNING.md` §2-1(역할별 웹/모바일 분리). + +**구현 레퍼런스 = WISE 모바일** `C:\GUARDiA\workspace\guardia-messenger\app\uiws\`(읽기 전용): uiwsApi.ts API 클라이언트 패턴(봉투 언랩·degraded 폴백)·(auth) 2FA 로그인·60+ 화면 컨벤션. 신규 화면은 대응 uiws 화면 구조를 먼저 차용한다. **디자인 = Stitch 경유**: designer가 design.md SCR-M* 스펙+Stitch 프롬프트 작성 → Stitch 생성(`mcp__stitch__*` 도구 또는 기존 프로젝트 9385904003821333054) → mobile-dev가 React Native로 이식. + +**앱 타깃 2개 (소유자 확정 2026-07-11):** 코드베이스는 `mobile/` 하나, 배포 타깃은 ①운영 앱(B2B, 2FA 필수, 사내 QR/APK) ②관람객 앱(B2C, 스토어 공개, 간편가입·게스트, 2FA 미강제). eas 프로파일/역할 라우팅으로 분리. 관람객 1차 접점은 공개 웹(SCR-P7/P8), 앱은 리텐션 채널. + +## 경계 (중복 회피) +- 웹 프론트(`src/frontend`)·백엔드 모듈·도메인(M2~M18) 구현 = `kintex-impl-orchestrator` +- 이 하네스 = **mobile/ 앱 전용**: 화면·API 배선·NFR·에셋·빌드/배포. 모바일이 요구하는 백엔드 API 확장은 kintex-backend-dev를 이 팀에 합류시켜 처리(계약 단일 출처 유지) + +## 에이전트 로스터 +| 에이전트 | 역할 | 비고 | +|---|---|---| +| kintex-mobile-dev | Expo 화면·API 배선·NFR·에셋·eas.json (핵심 구현) | 신규 전담 | +| designer | design.md §4 모바일 화면 스펙(SCR-M*) 신설·갱신 | 화면 신설 시만 합류 | +| kintex-backend-dev | 모바일이 요구하는 API 확장(부스 목록·검수 제출 등 M6/C-4) | 계약 공백 시만 합류 | +| kintex-qa | 경계면 교차 QA(shape·워터마크·시크릿·typecheck) | 각 화면 완성 직후 점진 | +| kintex-devops-dev | EAS 실빌드·APK·QR 배포 파이프라인 | Phase 3, G3 게이트 후 | +| reviewer | design.md↔구현 정합 교차 검증 | 트랙 마감 시 | + +## Phase 0: 컨텍스트 확인·게이트 +1. `mobile/` 존재(스캐폴드 완료) + `_workspace/` 확인 → **초기/부분 재실행/새 실행** 판별: + - 부분 수정 요청("검수 화면만") → 해당 화면만 kintex-mobile-dev 재호출 → qa + - 새 기능 요청 → Phase 1부터. 기존 `_workspace/`는 `_workspace_prev/`로 이동 +2. `mobile/README.md` "남은 작업"과 `docs/UNDEVELOPED_BACKLOG.md`를 백로그 소스로 삼는다 +3. **게이트 G3(EAS 실빌드)**: EAS는 외부 클라우드 빌드 서비스 — 실빌드·스토어/QR 배포 전 소유자 승인 필요. 미승인 시 eas.json·에셋 준비까지만(코드/구조는 게이트와 무관하게 진행) + +## Phase 1: 디자인 (Stitch 경유, 조건부) +신규 화면이 design.md §4에 없으면: ① designer가 SCR-M* 스펙 + Stitch 프롬프트를 design.md에 추가(직접 수정 금지 — 반드시 designer 경유) → ② Stitch로 화면 생성(`mcp__stitch__generate_screen_from_text`/`edit_screens`, 기존 프로젝트 재사용) → ③ 생성 결과를 `stitch_kintex_ai_system_architect/`에 확보 → mobile-dev 이식 입력으로 연결. 기존 화면 수정이면 건너뜀. + +## Phase 2: 구현 (팀 병렬) +TeamCreate 후 spawn: kintex-mobile-dev(필수) + kintex-backend-dev(계약 공백 시). TaskCreate로 화면 단위 작업 생성, `blockedBy`로 API 계약→화면 배선 의존성 연결. +- kintex-backend-dev: 모바일 필요 API 계약을 `_workspace/{phase}_backend_contract.md`에 먼저 산출 → mobile-dev가 소비 +- kintex-mobile-dev: 화면/기능 구현 + `npm run typecheck` 통과 → 완성 통지를 kintex-qa에 SendMessage + +## Phase 3: 점진 QA → 빌드·배포 +- kintex-qa: 화면 완성 직후 경계면 교차 비교(API shape↔`lib/types.ts`↔화면 소비), AiImage 워터마크 강제, 시크릿/자격증명 미노출, typecheck 회귀. 실패 시 mobile-dev에 반려(통과까지) +- kintex-devops-dev(G3 승인 후): EAS 빌드 → APK → QR 배포 랜딩(GUARDiA ITSM 중앙 APK 저장소 패턴 참조 가능). 커밋·push는 `mobile/node_modules` 제외 경로 지정 add(리포 대용량 함정) +- reviewer: 트랙 마감 시 design.md §4↔`mobile/README.md` 화면 표↔실구현 3자 정합 검증 + +## 데이터 전달 / 에러 +태스크(조율) + 파일(`_workspace/{phase}_{agent}_{artifact}`) + 메시지(실시간). 1회 재시도 후 재실패는 누락 명시하고 진행, 상충 계약은 backend 계약 문서를 권위로. QA 실패는 통과까지 반려. 최종물 `mobile/`, 중간물 `_workspace/` 보존. + +## 보안 불변 +AI 이미지 워터마크·고지 강제(AiImage 경유만). 시크릿·자격증명·서버 IP 코드/커밋 미기재(API 베이스는 env/app.json extra만). 비밀번호 저장 금지. RBAC 화면 가드. 외부 API 금지(예외: Claude·나노바나나 Gemini 기승인, EAS는 G3 승인 필요). + +## 테스트 시나리오 +- **정상**: "모바일 앱에 부스 목록 화면 추가" → Phase0(백로그 확인) → Phase1(designer SCR-M3 스펙) → Phase2(backend 계약→mobile-dev 구현·typecheck) → Phase3(qa 경계면 통과) → 커밋. +- **부분 재실행**: "검수 화면 반려 사유 입력만 보완" → Phase0 부분 판별 → kintex-mobile-dev 단독 수정 → qa 해당 화면만 재검. +- **에러**: G3 미승인 상태 "APK 빌드해서 QR 배포" → eas.json·에셋 준비까지 완료 + 승인 요청 에스컬레이션(빌드 실행 보류 명시). + +## 후속 작업 +description의 후속 키워드(다시 실행·업데이트·보완·특정 화면만)로 재트리거. Phase 0이 초기/새/부분을 판별하고, 각 에이전트는 이전 산출물이 있으면 개선점만 반영한다. diff --git a/.gitignore b/.gitignore index 71dbcad..6541a43 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,13 @@ output/ Thumbs.db .DS_Store +# JVM crash logs +hs_err_pid*.log +replay_pid*.log + +# 에이전트 세션 중간 산출물 (로컬 보존, git 제외 — 최종물은 docs/·src/·deliverables/) +_workspace/ + # Stitch export — 대용량 스크린샷 PNG 제외(로컬 참조 유지), code.html·DESIGN.md만 버전관리 stitch_kintex_ai_system_architect/*/screen.png diff --git a/CLAUDE.md b/CLAUDE.md index 0447d37..1d5b7f8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,6 +49,7 @@ GUARDiA 표준 프레임워크(Spring Boot 3.5 + React 19 + MyBatis + PostgreSQL - 모든 문서는 한국어로 작성한다. 코드 식별자/커밋 메시지는 영어. - 기획/디자인 문서를 수정할 때는 반드시 해당 에이전트를 통해 수정한다 (직접 수정 금지). +- **화면 디자인은 항상 Google Stitch에 의뢰한다** (웹·모바일·관리자·공개사이트 공통): designer가 design.md에 화면 스펙+영어 Stitch 프롬프트 작성 → Stitch 생성(`mcp__stitch__*`, 프로젝트 9385904003821333054, 디자인 시스템 "Precision Enterprise AI") → 생성 HTML을 `stitch_kintex_ai_system_architect/`에 확보 → frontend/mobile dev가 이식. 손그림 UI·Stitch 미경유 신규 화면 금지. - 나노바나나 호출은 `tools/nanobanana/` 모듈만 사용한다. API 키는 환경변수 `GEMINI_API_KEY`. - 파일 삭제 전에는 반드시 사용자에게 확인받는다. - 도메인 용어: 부스(booth), 장치공사(booth construction), 반입/반출(move-in/move-out), 주최자(organizer), 참가업체(exhibitor), 관람객(visitor). @@ -80,3 +81,20 @@ 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 기능 참고" 요청 | + +--- + +## 하네스: 모바일 앱 (kintex-mobile-orchestrator) + +**목표:** `mobile/`(Expo SDK 51 + expo-router + React Native 0.74 + TS) 모바일 앱 트랙 — 화면(SCR-M*)·실데이터 API 배선·오프라인/푸시/i18n·에셋·EAS 빌드·APK·QR 배포. **레퍼런스 = WISE 모바일**(`workspace/guardia-messenger/app/uiws/` — uiwsApi 봉투 언랩·2FA 로그인·화면 컨벤션), **디자인 = Stitch 경유**(designer → design.md SCR-M* → Stitch 생성 → RN 이식). + +**트리거:** 모바일 앱, 앱 화면, Expo, 앱 기능 추가, 앱 빌드, APK, QR 배포, 푸시 알림, 앱 오프라인, 앱 아이콘/스플래시, EAS, 모바일 QA, 다시 실행, 특정 화면만 요청 시 `kintex-mobile-orchestrator` 스킬을 사용하라. (웹·백엔드·도메인 모듈은 `kintex-impl-orchestrator`.) + +**게이트 G3:** EAS 실빌드·배포는 외부 클라우드 빌드 — 소유자 승인 후 실행(승인 전엔 eas.json·에셋 준비까지만). + +**변경 이력:** +| 날짜 | 변경 내용 | 대상 | 사유 | +|------|----------|------|------| +| 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·오케스트레이터 | 소유자 확정 — 회원가입 관람객 포함 질의 → 하이브리드(계정 통합·앱 분리) 채택 | diff --git a/ci/CI_AI/CI_01.ai b/ci/CI_AI/CI_01.ai new file mode 100644 index 0000000..0d291aa Binary files /dev/null and b/ci/CI_AI/CI_01.ai differ diff --git a/ci/CI_AI/CI_02.ai b/ci/CI_AI/CI_02.ai new file mode 100644 index 0000000..fd51c0a Binary files /dev/null and b/ci/CI_AI/CI_02.ai differ diff --git a/ci/CI_AI/CI_03.ai b/ci/CI_AI/CI_03.ai new file mode 100644 index 0000000..de7efbe Binary files /dev/null and b/ci/CI_AI/CI_03.ai differ diff --git a/ci/CI_AI/CI_04.ai b/ci/CI_AI/CI_04.ai new file mode 100644 index 0000000..6270eec --- /dev/null +++ b/ci/CI_AI/CI_04.ai @@ -0,0 +1,874 @@ +%PDF-1.4 % +1 0 obj<> endobj 2 0 obj<> endobj 3 0 obj<> endobj 5 0 obj null endobj 6 0 obj<>/Properties<>>>>>/PieceInfo<>/LastModified(D:20061220104917+09'00')>> endobj 7 0 obj<> endobj 8 0 obj<> endobj 34 0 obj null endobj 35 0 obj<>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 11.0 +%%AI8_CreatorVersion: 11.0.0 +%%For: (te x kin) (com) +%%Title: (CI_04.ai) +%%CreationDate: 12/20/2006 10:49 AM +%%BoundingBox: 293 237 556 484 +%%HiResBoundingBox: 293.2129 237.292 555.9219 483.0151 +%%DocumentProcessColors: Cyan Magenta +%AI5_FileFormat 7.0 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%CMYKProcessColor: 1 1 1 1 ([Registration]) +%AI3_TemplateBox: 421.5 296.7754 421.5 296.7754 +%AI3_TileBox: 12.0649 13.3777 829.6849 583.1377 +%AI3_DocumentPreview: None +%AI5_ArtSize: 841.8898 595.2756 +%AI5_RulerUnits: 6 +%AI9_ColorModel: 2 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 1 +%AI9_OpenToView: 17 554.2754 1 1012 669 26 0 1 10 67 0 0 1 1 1 0 1 +%AI5_OpenViewLayers: 7 +%%PageOrigin:23 19 +%AI7_GridSettings: 100 100 100 100 1 0 0.8196 0.8549 0.8667 0.9098 0.9275 0.9333 +%AI9_Flatten: 0 +%%EndComments + +endstream endobj 36 0 obj<>stream +%%BoundingBox: 293 237 556 484 +%%HiResBoundingBox: 293.2129 237.292 555.9219 483.0151 +%AI7_Thumbnail: 128 120 8 +%%BeginData: 11316 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%524C45FF7E307EFFFF7E54FFFFA92F5A535AA8FFFFA92F5A545454FFFFFF +%7E5A535A2F85FFFFFF7E2F7EFD5AFF7E077EFFA92FA9FFA8295AFFFF2F2F +%A8FF7E2F53FF7E2F2FFFFF5A077EA8A9A8FFFFA82F542FA9FD59FF7E2F7E +%FF2FA9FFFF2F30A9FFFFA92F5AFFA92F7EFFFF542FA8FF5A2FA8FD05FF5A +%7E7E307EFD59FF7E077E535AFFFFA82F2FFFFFFFA8302FFF7E2F5AFFA830 +%2FFFFF5A297E7EA8A8FFA854A8A9075AFD59FF7E30533053FFFFA92F5AFD +%04FF2F54A9A92F7E7E5A53FFFFFF5A30537E5AFFFFFF2FFFFF542FFD59FF +%7E077E532F59FFA82F2FFFFFFFA8302FFF7E2F7E7E075AFFFFFF5A29A8FD +%04FF7E30535A2F30A8FD58FF7E2F7EFF2F30A8FF5330A8FFFFA9077EFFA9 +%297EFF5A2984FFFF5330A8FD04FF5A5AFFA97E077EFD58FF7E077EFF7E07 +%53A8A92954A8AF2F2FA8FF7E2953FF842F07A9FF5A077EA8A9A8FF2FA9FF +%FFA82F2FFD58FFA85A84FFFF7E53A9FFA9535A547EA9FFFFA953A9FFFF7E +%5A7EFFA87E5A7E5A7EA87EFD04FF5A7EFD63FFA9FDFCFFFDF9FF7EFFFFFF +%7EA9FFFFFFA9A9FF7EA9A8A97EA9FFFF84A984A8A8FFA8A97EA9A8FD04FF +%A97EFFFFFFA8A9FD04FF7EA9FFFFA8A884A9A8A8A8FFA8A9A9FFFFFFA884 +%7EA9FD04FF84A9FFFFFFA9A8FFFFFFA884A8FFFFFFA8A9A8FD24FF7E2F7E +%FFA83029A9FFFF53A9A95A53302F5A5AFF5430537E53A9FF7E2F5A5A542F +%A9FFFF532F53FFFF8453FFFFFF5A302FA9FFA9545A2F5A5484FF5A2FFFFF +%FF5A305A7E2F7EFFFFA83029A9FFFF53A9FFFFA9542F5AFFFFFF7E0784FD +%24FF7E077EFFA8292F2FFFFF547EFFFFA8075AFFFFA8302FA9FD04FF532F +%7EFF7E2F2FFFA854292F7EFF7E54FFFFA854532F5AFFFFFF7E2F59FFFFFF +%2F2FA8FF592F7EFFFF7E077EFFA8292F2FFFFF547EFFFF7E2F5A29A8FFFF +%5A2F7EFD24FF7E2F7EFF7E5A53307EFF53A9FFFF84305AFFFFFF2F54A9FD +%04FF7E2FA9FFFF2F54FFFF537E2F5AFFA953FFFFA953FF2F5AFFFFFF8429 +%7EFFFFFF5A2FFFFF542FFFFFFFA9302FFF845A54305AFF53A9FFFF53A984 +%2F7EFFFF7E29A9FD24FF84077EFF8453A92930A85A7EFFFFA8075AFFFFA9 +%542F5A537EFFFF592F7EFF592F7EFFA854A85A077E7E5AFFFF537EFF5A2F +%A9FFFF7E2F5AFFFFFF2F30A9A92954FD04FF2F2FA8A853AF2F2FA85A7EFF +%A954A8FF2F54FFFF5A2F7EFD24FF7E307EFF7E5AFF7E295A54A8FFFF842F +%53FFFFFF2F547EA9A8FFFF7E2984532F7EFFFFFF2FFFA9302F7E5AFFFF54 +%7EFF54307EFFFF84297EFFFFFF5A2FFFA8302FFD04FF542FFF7E5AFF8429 +%5A53A8FFA92FA9A8542FFFFF7E2984FD24FF7E077EFF7E53FFA954292F7E +%FFFFA8295AFFFFA92F29FD05FF5A2F7E840754FFFFA953A8FF7E2F295AFF +%A9295A535A295AFFFF7E2F5AFFFFFF2F2FA8FF2F2FA8FFFFAF2954FF8453 +%FFFF54292F7EFF5954537E2F2F7EFF592F7EFD24FF7E307EFF7E7EFFFFA8 +%302FA9FFFF843054FFFFFF2F54A9FD04FF7E29A9FF5A2985FFFF2FFFFFFF +%53305AFF7E7EFFFFFF542FFFFFA9297EFFFFFF5A2FFFFF7E29A9FFFF7E30 +%7EFF7E7EFFFFA8542FA9FF5A84FFFFA92F7EFF7E2984FD24FF7E077EFF7E +%2FFFFFFF532F7EFFFFA8075AFFFFFF54295A5A5A7EFF5A2F7EFF842F29AF +%FF2FA8FFFFA9075AFF547EFFFFFF532F7EFF7E2F5AFFFFFF2F2FA8FFA954 +%29847E2F53FFFF7E2FFFFFFF5A2F7EFF2FFFFFFFA8302FFF7E2F2F7E537E +%FD21FFA984A8FFA9A9FD04FF7EFFFFFFA8A9A8FFFFFFA9A97EA97EA9FFAF +%7EFFFFFFA884A8FFA8FD04FFA9A8FF84FD05FF7EFFFFFF7EAFFFFFFFA984 +%FD04FFA95A7EA8FFFFFFA9A9FD04FF7EFFA9A9FD04FFA8A9FFFF7EA97E84 +%7EFD20FFA9FFFFFFA8FDFCFFFDFCFFFD05FF597E535A5AFF7E7E84FFFFAF +%53FFFF7E59FFFFFF597EFFFF7E7EA8FFA87E5A7E59A9FFFFA87E5AFFA85A +%597E535A7EFF7E7EA8FFFFFF847E537EA8FFFFFF7E547EFFFFA97EFD07FF +%A87E537E7EFFFFFF7E7E535A53A9FFA853A9FFFFA87EFF7E537E5A7E53A9 +%FF7E537E537EFFFF5A7E537E7EAFFD07FF7E3053A97EA9FFA92954A9FF53 +%7EFFFF2F54FFFFFF542FFFFF7E29A9FFA92F5A845A2FA9FFFF2F30A9FF7E +%7E295A7EA9FF7E07A9FFFF7E307EFF5430A8FFFF5A2954A9FF845AFD06FF +%A95A2FA9A87E5AFFA9542F7E7EA9A9FF53302FFFFFA953FF84A953305AA9 +%FFA9295A7EA87EFFA9302FA97E302FFD07FF7E0784FD05FF7E075A7E2FA8 +%FFFF2F2FFFFFFF2F54FFFF532F7EFFA82F53FFA82F53FFA9542FA9FFFF84 +%2F53FFFFFF592F7EFF7E2F53FFFFFF2F2FA8FF532F295AFFA82FFD06FF5A +%07A8FFFFFFA8FFAF2954FD05FF542F2F53FF845AFFFFFF7E29A9FFFF7E2F +%5AFD05FF2F53FFFF7E2F7EFD06FF7E2F7EFD06FF5A2F54A8FFFFFF2F54A9 +%FFA9542FFFFF7E29A9FFA92F7EFF842F7EFFFF2F54A9FFFFA92F7EFFFFFF +%7E29A9FF7E07A9FFFFFF7E29A9FF5A7E5429A9A85AFD06FF2F54AFFD05FF +%A9542FFD05FF54842F30A8A953FFFFFF5330A8FFFFA92F7EFD04FFA8542F +%FFFF7E07A8FD06FF7E29532F307EFFFFFF842F29FFFFFFA8542F542F542F +%53A8FF532F7EFFA82F2F542953A8FFA92F29FFFFFF7E2F53FFFFFF5A2F7E +%FF532F7EFFFFFF592F7EFF2FA97E2F2FA853FD05FFA82F2FFD07FF2F2F2F +%5453FFA954A884075A7E54A8FFFF5A29A9FFFF7E2F2F542F7EFFA92954A8 +%7E297EA9FD06FF7E2F7EFD06FFA92F305AFFFFFF2F54A9FFA9542FFFFF7E +%29A9FFA92F7EFF842F7EFFFF2F54A9FFFFA92F7EFFFFFF7E29A9FF7E29A9 +%FFFFFF7E2984FF5AA8FF5330535AFD06FF2F5AFD06FFA9542FFD05FF53A9 +%FF5A2F5A53FFFFFF5430A8FFFFA92F7EFD04FFA9545384295AFD08FF8407 +%7EFD06FF2F7E2F2F7EFFA9542FFFFFFF2F2FA9FF532F7EFFA82F53FFFF2F +%29FFFF2F2FFFFFFF7E2F53FFFFFF5A2F7EFF7E2F7EFFFFFF532FA8FF2FA9 +%FFA9292F53FD06FF5429A9FFFFFFA9A8FF2F53FD04FFAF5A84FFA8302954 +%FFFFFF5A29A9FFFF7E2F5AFD04FFA92F54FF5A077EFD07FF7E2F7EFD05FF +%535AFFA92954FFFF2F54FFFFFF542FFFFF7E2FA9FFA92F5AFFA92F54A9FF +%2F30A8FFFFA9297EFFFFFF7E29A9FFFF2F54A9FFA82F53FFFF5A84FFFF7E +%295AFD06FF7E3054FFFFFF53FFA9542FFD05FF2FA9FFFF7E3053FFFFFF5A +%2FA8FFFFA9077EFD04FFA9302FFFA93029A9FD06FFA8293053542FFF7E2F +%A8FFFF7E077EFF2F2FFFFFFF2F54A9FF532F7EFF842F2F542F547EFFA854 +%2FA9FFFF7E2F53FFFFFF592F7EFFFFA92F54535453FFFFFF53A8FFFFFF54 +%53FD07FF7E2F537E53A9FFFF2F2F2F542F7EFF547EFFFFFF2F5AFFFFFF5A +%29A9FFFFA82F295A5354A8FF2F2FA8FF7E2F2FFD08FFA9FFA8FFFFFFA9FD +%05FFA9FFA9FD05FFA9FFFFFFA9FFFFFFA9FFA8FD05FFA9FD05FFA8FD05FF +%A9FD05FFA8A9A9FD14FFA9A8FD05FFA9FFA8FFAFFFA9FD05FFAFFFFFFFAF +%FD05FFA9FFA9FFA8FFFFFFA9FFFFFFA9FDFCFFFDFCFFFDFCFFFDFCFFFDFC +%FFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFC +%FFFDFCFFFDFCFFFDFCFFFDCFFFA9FD04FFAFAFFFA9AFA9FFFFAFA8FD0BFF +%A9AFA8FFFFA9A9FFA8FD05FFA9FFFFFFA8FFA9FFA9FFFFA9FD0AFFA9FD04 +%FFA9A8FFFFFFA9FFFFFFA9FFFFFFA8FFFFFFA9FFA9AFA9FFFFFFA8FD09FF +%A8FFFFFFA8FD05FFA8FFA9FFA8A9FFFFA8FD07FF7E5AA8847E5A5AA87E53 +%5A847E53A9FF7E53FFFFA953A92FA97EA9537EA85A7EFF537E53A9537EA8 +%A8A8547EA92F7E7E7EA85A7E5AA85A7EA884A92FA9845AFFFFFF7E53A97E +%5A7EA859A984FD047E5A53A97E7E7E547E7E59FF537E53FF2F7E84A9FFFF +%537E7EA9537EA87E53A97E842FA87E5A7EA9537E53FD06FF7E547EFF2FFF +%7E7E5A845AA97E7EFFFF5A5AA9FFA87EA85A537EFF7E7EFF53A9FF7E7E5A +%A8A92F7EA8A95A7EFF7EA9A95A7E5AFF54845A5A5AFF7E5A7EAF5AFFFFFF +%547EA9FF545AFF7E5A7E7EA97EA9535AA8847EFF54FF7E847E7EFF5A7E7E +%537EA9FFA85AFFFFA95AA8FF7E5A5AA9FF5AFF7E5AFFA87E7E7EFD06FF7E +%2F5A7E54FF84537E2F54FF7E5AFF7E7E2F84FFA953A9847E29FF5A84A854 +%A8FF535A53FF7EA929A85A7E2FFF53A9A87E5A7EFF5A5AA87E2F7E7E5A5A +%7E5AFFFFFF5A7EFFFF7E2FFF59FD067E5A537E7E7EFF5AA8845A7E5AFF53 +%7E7E7E29FFFFA92FFFFFA92FA9FF7E7E2F7EFF2FFF5A7EFFA953547EFD06 +%FF7E84FD047E5AFF7EA97EA97E7E8484FFA85AFFA97E84A9FF5AFF7E7EFF +%5384A97E847E7EA9FF7E7EA8FF7EA87EA8A95AFF5A7E7EFF7EFF5A7E7EFF +%54A953A9FFFF7E7EA8A97EA95A7EA8A97EA97EA9547E84847EFF53FF847E +%AF7E7E7EA9A9FF7EA8FFFF7E5A84A97E7EA97EFF7EA9FF5AFFA95AA97E7E +%A85AFD09FFA8FFA8FD05FFA8FFA8A9A8FD05FFA9FFA8FFFFFFA8FFFFFFA8 +%FD0BFFA8FFFFFFA8FFFFFFA8FD0BFFA8FFFFFFA8FFA9FFFFFFA9FFA8FFFF +%FFA9FFA9FFA8FD05FFA9FFA8FFFFFFA8FD07FFA8FFFFA9A8FD05FFA9FFFF +%FFA8FFA8FFA9FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF +%FDFCFFFDFCFFFDFCFFFDD9FFA8FD17FFA8FD27FFA8FD18FFA87E7E847E84 +%7EA97E847E847EFD07FF545A7EFD05FF545A84FD07FF53545AFD05FF535A +%A8FD05FFA97E7EFD05FFA85A54FD0CFFA97E847E847E857EFFFFFF545A7E +%FFFFFFA8847E847E7EA8FF7E5A7EFF535AA9FD09FF7E7EA8FD07FF7E2F54 +%2F542F2F29542F542F5AFD06FFA954295AFD04FFA954077EFFFFFFAF7EA9 +%A854293084A97EFFFF2F077EFD05FFA9292F5AFD04FFA9292F7EFD0BFF7E +%2F2F542F2F072FA8FFA954295AFFFFFF7E072F2F542F7EFF7E0754A82F07 +%A8FD09FF2F075AFD0CFF5A2F54A8FD0BFF2F3054FD05FF2F307EFFFFFF7E +%302F542F302F542F307EFF2F2F7EFD05FFA954297EFD04FFA8302FA9FD10 +%FF7E302FA9FFFF2F305AFFFFFF7E2F2FFD05FF7E2F2FFF2F307EFD09FF2F +%3053FD0BFFA9292F297EFD0AFFA82F2954FD04FFA953077EFD04FF842F29 +%5AA87E29547EFFA854297EFD05FFA92F2F59FD04FFA92F2F537E7EFD09FF +%A8AFA8AFA85A292FA8FFA953295AFFFFFF7E292F597E7EA85A5A2954A854 +%29A8FD08FFA8302954FD0BFF53302F302FFD0AFF7E2F542FA9FD04FF2F30 +%7EFD04FF842F54FFFFFF5A2F7EFFFF2F307EFD05FFA8302F7EFD04FFA854 +%2F5A2F54A9FD08FF7E2F542F5A2F3053FFFFFF2F305AFFFFFF7E302F5A2F +%5A7E542F542FFF2F307EFD08FFA92F542FFD09FFA92F2F2F84532F2FA8FD +%07FFAFFD052FFD04FF53297EFD04FF532F53FFFFFF532F2FFFA954297EFD +%05FF7E292F2FFD04FFA92F2F7EFD0BFFA8A9A8A953302984FFFFFF53297E +%FFFFFF84292FA8FD04FF7E2954A85429A8FD08FF2F302F2F53FD06FF5A54 +%29545AFFFFFF7E5429545AA9FFFFFFA92F307EFF533053FFFFFF2F2F5AFD +%04FF7E2F54FFFFFF5A297EFFFF2F307EFD05FF2F302F307EFFFFFFA8542F +%A9FD0EFF7E30297EFD04FF2F3053FFFFFF7E302FA9FD04FF7E3053AF2F30 +%84FD07FF542F2F7E2F307EFD04FFA87E5AA8A8FD06FFA95A7EA8FFA85A07 +%537EFFFFFF5A2F297EA854297EFD04FFA854077EA87E072FA8FFA953077E +%FD04FF5A2F2F5A292FA8FFFFA9292F7EFD0CFFA8532F2F7EFD04FFA85429 +%5AFFFFFFA8072F29532F2F7E840754A82F29A8FD04FFA87E2F2F2FA8FF7E +%292F2FA8A8FD04FFAFFFA9FFA9FFA9FFA9FD05FFA8A9FD07FFA9A8FF5A30 +%7EFD05FFA97E2F542F5AA8FFFFFF2F307EFFFFFF7E302FAFFF7E295AA9FF +%A8542FA9FD0AFFA95A2F5A7EFD07FF2F305AFD04FFA97E847E857EFF7E30 +%2FFF2F307EFFFFFF7E542F5A7EFD05FF7E5A2F54A82F542F2F2F302F2F2F +%302F2F2F302FA9FD04FFA8FFA8A9A8FFA9FFA8A9A8FD08FF542954FD05FF +%530784FFFF2F2F53AFFFFFFF7E2F2F7EA9292F7EFD0BFFA8A9FD08FFA854 +%295AFD0CFF7EA8FFA87EAFFD04FFA8FD0AFFAFA8FFA9A8A9A8A9A85A2F54 +%84A9A8A9A8A9FD04FFA8302F542F302F542F302F5AA9FFFFFF84A87EA92F +%302FA984A87E5A2F2F7EFFFFA8A8FD07FF7EFFA8302FA9FD0EFF7E5A7EFD +%05FF54305AFD05FFA97E5A7E5A7E5AFD047EA9FD19FF532F2FFD0BFF7E84 +%7EA87E847EA8532F077EFFFFA854292F29542F2F292F2F535354297EFD0D +%FFA92F2F7EFD0EFF5A0754FD06FFA8FD07FF535A53542F5A5354292F29A9 +%FD18FF7E2F54FD14FF2F307EFD0FFF2F307EFD0DFFA8542FA9FD0EFF5A30 +%54FD16FFA92F307EFFFFFF7EA984A97EA984A97EA984A97EA984FD06FF53 +%2F2FFD14FF53297EFD0EFFA954297EFD0DFFA92F2F7EFD0EFF5A2954A8FD +%15FFA83029A8FFFF7E2F2F302F2F2F302F2F2F302F2F2F30FD06FF5A2954 +%FD14FF2F2F5AFD0FFF2F307EFD0DFFA8302FA9FD0EFF7E2F2954545A535A +%545A53A9FD0DFFA9293084FD18FF7E5453FD14FF7E2F7EFD0FFF7E2FA8FD +%0EFF535A84FD0FFF7E7E537E535A537E535A84FD0DFFA85A2FA9FD11FFFF +%%EndData + +endstream endobj 37 0 obj 13176 endobj 38 0 obj<>stream +HWko8 A@ l")bXN Ei;_^=,)n'w:)<>syyvzI5vwpv^w4LENyyݭ3~>M}<˞<VuG2/ճaGP-x&drpI~s\gt*fZ( zms7B:Nb^8)^a^}lov]ܮ,~^nk|L.yOwwo7h{x_ov׷a~-]>N!BPfa.f?"Or`'BYkR)1qtO?g٫wqte^HM7Yqv̵x^5kpX( 8nn ps,y͏ "M^6w۟aAQP[A  o!n;[nYZV&DiwLLՁKЗ2@ _R ~l7Wq{C%nzӮn݄Ѱ b}1 mq^wۏ?| >fۋ ̱}^_É7T0[i41W5msFQ|m^0 ?f)dn>/mlzyr6)/v˫2M.kG@r]`ӓ_OO~zRSb +'dI:I%DTuU:ԾvuYԺ.jUZyXW*OO*[JWE*Y*)ֱ1裋eD"!:T!|p 6CTA-|+};_z׾K/|] ;JgqN9TeU2>4.ѝKY2ֶ뭳%VYiM2L4xLi1F(#0Q']JGG*5T@ZHE]TE,B %a S(T! Q*ZU*r +>*Ҫ@)J\&YJF(FjYH%%G.E% 'Ja(RsЙ<-DZsõ?0){rvE5}aw9wLQ=١:d@_츽mv_X͎-=OD@4Dt!D B1=DQԒD4QDb0H#Z@2yDH4l$BR"h%bZ"%[d,A&n"('҉v"')((S ܺD4Ytd4a8eNO4q%O:H$4I4Mq"q*LiNӄTq=JQw*G'#LK9n#qlɧB5zfXll񁅙arfvfffzf԰"֩ə=8zaDWUd򁉑(X=R,6 xsuw0]єӂICGs>Pi \,.TN$22rZ6sEBhQ9ܟ渕̎Tw-V$$Y& V`j%&MpjS/z4(`⏉\v$L8vЫ~ŵXmj֡o Aouy>!8 i28)CI8r)H(O[!1vQ&"B!:4E8DO)OO1GF;@qhLgDVr%nZ|D 1!2bD!j4`_nTW I3jt(jф'gB ̈`-⎸$,8QUzQ]JG%NjTu)BjsAX|gG$Y((C[qme + (4i5R%$9h@:(9L +0RI%R #4IJn.mZhBiSҨUlQBWc{NoHʑM`d[hƑQlBj@2n~@f%Ft #؟.i9lsqǽsR&QY6Jh %P +!&iPPD 5PR%ge*h 8T i_wSħxOc! 8OQcyMڣ^Wnu{\ _죀in_c4j}d*cOo̽a-u_|8nLro[d7I˷I7Jͷʂoood-퉭fk/dЎ;wQqg-آVnPlz6um5w{zUnE l QE2D6*A^g I{IQa=`HL{>8 0 zU|5䓮StܴѲfAr?ԥ;'L,9RH!}!H"`[-ALaGUOQ-MG{0u`pM6G[+p4&SpPB#Eʉ>˜3`'?J}@Gqt-Kl&L'^ ͹,,pΒJNT1M)G59CSN,'33s|V"SHdEB>6'Rp$ݰK:|w;nxU2;ffIv.3̊*[gřcs0w23^>ϯ(qXEyX&VPe&_wOW-v?lŭi}cBvB&E/5k8X܁C7=gPJ`+dSi+?p7'ZYD5Y?_HQ:);[ٴ_^~ƉO[Z`NtX]ZJ]gϟ~|{~}9JWpc^]'~pSg:s뛠*&0{;z оcP +"T E(uPA7Pz3nh. 0h3K$T;Ue +2T|`krF*T4#K +‰vTtg6o Pq +*DQ,ڀ\[WT6Ьzfv́*(C:,(āJzPJi(O @G?=U Հ % :Ak&I$N4ς-0MJХmjЧ3u 0D+dkn5t܁=Jl֠p *wY_C,YQ@.$dCA>4dBNdG[XNѐ! L=Z; +ҥ aRf!i֣LIHjH$:Hc&)DB<DTCL-DyZ@v%WA5Ob˳Csw+6A'G ]vˮ:\E?(Kb紽E85ھ[qPْ3/x+REs+2eO^&P=ޓ~wOZ^=k){wݓ~wO*UC,n9,4orep_+VW`nuep_+VW߭[]׊/ʲ뚫%Gp\R\; ց_ ȱ [y+/ .`AE_+/Xsn_8D򍨷V\-\It w7a +u +/5qcb{Gx1{\sg8+>t,)pӘ +oVuQPS~:A7jFfXc6JowmJKJ[RJ)^Rwbׇ%IeRyy7%Fm-KlJs*.?r~|g?ǟ/>wK( +Ibsl/E:NxqH51*Q<3t8q`hMG +1NNǝ0z{|}A' + 2)-hA1EėL}DE6LqJ뀊BT\T\o*߹ZSzuﱌ#7a5.8K>V^ڰ;l#S@_ tv]/D@%S744B]ձr* +軆[mqW BA/@*R.UQ +P(ИYC @ Q: K;v@^4Jj:oPUb`,c vt^kvi ؀[@9mp%䡂Pa -xBIMʨdB!pXY#5 fzVIPT "G@^9&AƗKh3lf$Gw`,S~9A/vvs +T4l}hPBQ-`5jX_@pE..`رy ɩ#b}|/"gTg!z$ gtH7\QTʧ8ZQj9`QĢ9hA&qŒt%F!q(Fap HF (Udv𱃇:qYzc-J)T8dqxU<5}E2=4=H`9i%b +LH6 c &Np&CئA<=)Ӫ`ihyx[& 'Z2WLu8̆%eP{l`H!{_%tt-"Qjݑ qǢ͘2*cz xPz?xsہiU 9] xa`u cv|ڋW5J̢ Qpr,T 2I Hfl1^rtnm/Nэ8_n.i˭%>jat]>\JWmYZnv`zG?D屜"2c^be\swM% 2%Md\mF(zގ;Ϩj8:/cw~]_1!C]6hDZT#E=bT$BMBFV.P 19u$hB$$+Щ 2V~X  RtgPQE -nQ9ziM~&BO%*0%: eDXeQlrF4V܈6~]2>ձx{=`hG&&d:ꀡΧ#&db540o?>q7:u B Dd+=5o>(ya?HxB a 0JK F4 +I` و3 +xRpKa.ᴒ~a+l%e;1ܙ¡.nRJV ab #0sfZ +N`p [% hEr4D5ꑠ"5Jj#3ֈhP4)C + +z%-~%F,ʖpe^œ(BP`cAW@3%ک-ծ2!UrufiU} 2N݃>_%K ?wEȌBBM(jCR#b/'_v>M9=pf{|42q™e^V&9-:[d$垌߄G{ 5aGƑEH4AĂ~?>|\vyPuI"(RX8CRU(3h┟n=)vq= +lQ6g­ qzs5EŹjB(]\YA-X^^뗷?Kw?.O_^ ?OyJdvj*Qkª ErĨćr +&iv/p17^41121'0tFwK7y`\#X2 !=D|f@A0G^,?O^x\h7A,~R~>}%6)8+:ĤEhcMgыdy]zww6{׿>~y>t~ ~r5 +d4y' {u@DQkT+.$NbjdZjzqvd^p wldJx:Yެx 5ŷ,crm[&Kұfhmՙu U@hI^W +I jLn +B6sϝ{ܿb<3(jkm;~Hn\a{C}YH n~ɇ$be$8=V_wGNowt>u=O%$fRL}J)3eeVHs1mk؋3 :#NDLw߀;xgx&9q|ׄ#`Qw2u '&­P- 5C%\nl)Bg3߮Gls׼5?i6[a?w!ewEn#t?Is;pnDwg߼_~f7n-Mˢ^&,9I %RZehuUjPR adb #B 6 Tё8,#! " % & p0{Ġ6Yӡh4[ykF_3AS~ N.;̅c6#ew*x!AmJ֞lJ1)R(Q5V8.zD$RlOv(e\\`Kɶr\KP3*)\4{.EIۊ_%WJmbV; h =8o X_b*^V9* zV"!"e>sƪn%ۡXTq$W +W\5TUS?3\oڡ,[fY'@ l>'E5\[< ?'$L +݀c z2v +c?!e(9I" ITYcaQ [HSmg(r52hSRaњWBvUm5bU_Ng.aqTJMp}p]ŝaRn.޼wYHXFY0Ղ-C)neIgkğSęӈш<3h9ȋxHȊx3E9 f%"Dd41Xg`a\4Ӊ`LɁAQM壋MQE6+?bl0Ad@GX$ /``LDwY8픓vdP̀-hE( Xm$ټI@(#̉fAP )HUa @  CuxFa4nCi:$bƘ(_WBOH +,hXxFRtHV1Yp8- VlYhlUFrMpGa<+Gzz_kbti{6}`o[lJHn%{,D4+#J [OGWZy's)9-WS6,fSD8gG4솒pdL~]O gg#O[4lr/} lRF`^dp|X8doNݏm  n76 adw{l/hoEN;m*B*WY?PY0(T.l_d.4w!L*%)`g%`Ħ(1tZ,,Zko1c@~a0Ɯц`cl )ad`&<Áv.|2댬 t +a^֙Yfm [-? s"O%K֩LsSPhOL>>kz+;JێJ#G2D +%"mCYlᮏ-1SnKCv_/vJe8V'Yp$]oLJc&}sоu?W]_3b#MyP4` 28dTuqG^3zO8)Gƽ(b .,+cŔ30 %cˆ1T)RG <&%ǝq9PxOb[%r-Q.NxR7o?|ٲėgt9Żǻ??|Ę;0ņBCyPY;/{cs_lE#R!{ޒ+qMWF2K'oz;QrS5]KW}4k=d?||e{S %v5w=3̓lI߬ىTژ=+.HL56 ^Sa"䃟=VLvy: +~zV,vaDhrf;FzsJdtWiu#%,7]:J-0)wwsȒ\OW]0tGH9f;xн%j{B\~SrHI7'ixtٝ$|D1D^ aR2nn[27K"\ )t8,{B@X9cW:\/N׷:}vxAkzf xV˗:I+bMX 1C-\OʺhC1m.?n.bD#DtUK~њlhPǘoU٠r٠.TNh"WIڃki2IcV(+gIeiZ@j7n!ms,sNئ1M@X)LPJQ#Ƈ#=*!**V\ٚ HOE,K4ק8NF٢: 5.-PfO&c.r SƮLCo%qk䅉HS"ځ^UӘ +B!V҆҅iyaJ94 ́6 m'a_>stream +HW[~G0/)VRvDi;ї(O˵SyٺE9F>/_^tǑe֏LQx=S옅\`RZй 6gq`<륵ƦgFoiԼi-[zx+w첎}}Bɇa턓t3 +ELr9f1Wд{Ґ(xb_7{!BA! xԩaS +-[[fiܹݣ7Wx9.ƚ qV4Xj6 Kdm9js ]-U B@)mODNa2Vrl~~WQfU/ +fOQ,>'c*d]yiQ"T$|~RHrT%+B+x_ +8X*Myʄ]@HGQ0A(%* ̚Jh=9LfP$4eY2+*WAf9j'4]QYP +ŕ\L'c⏻& +>ĢZC1 +g w)OI^ ϘkQMl7ۭ ~&ɚή{KC`^W"P)Ū$R )ɔ DG3"@f 0S5U7ӹ +3uмik 3O"lu(\m1I8 ?]J059g I$G$Ӕ[L5 F=Gg]$C&Ty(HQF& .Ú D6Z\jZI-=BuGnWs,U0&4HaP_aP#Ij2!8lpx+S?"TD8d33SA ץq>5]@d麣fNF!$ +]@xXJ$8Th +z\P@-40)>+0%58? +rˠjWs(R_zsFK$d4 *4,t.;D\Ё@XǎkY[$nq#D̸JB`##K9'b `#V [k`g.@'LYKE6Ӝb"vEl ,\ĈIB +Ni}Z ˬ Iv Hww*68h$-&-!0C$hQu%E:{s[FR4}D+*KTa"gsOHӑG2 + tQlƭD7`ɁSoZyjkyLjEa#{&5)^fR,4sdԌ=MjϦ ũJF:>`V꺙4Y!)um}.zJHpă*qOVN|MuyDcW1J̚`cVF0k;“5D5dq ̂HB&`M)2ՙػ0PRrl 2ZEB XV[[%+I1 ie~L450..b' A^:os]]ߪu +2a_'+Ւ\$!{xF'_F*se$cyZ&drfMCe ypZxhT,J̰>[֭* A&K!]hIV Ձ`,&w7VR}1Y,@^ykC{c A,9hR&FϼBGy tm؍@dͯ?yo%8WR2VКda1XtgYwU͚+>cy0&B +p32dYŞ1f襁-'cP \wK2q Yi88/Ig=($SwbҴ|n{ㅹ8 '<96h>~œ\2F}b0댲xf\)nko=JP,)6q*ny,G4১Z4.0}{a5$Oj &{QװÐcߡhKǿbC,p ́ mDo]4D53>d"7 +aO U]V [AKcB 9dJ^{84H8ΫLYyĭ^K-"a" XW +4'DW%7i`,yn+ϧPa'%{&64fpL} ; \3E'.<tn4;y>gNs׆MK!Nuf#->j9w'zd{Z2B}_w`, $'2ad!S#:/:rMPlTi  <"vIs<-ugtk3ɶkݟڸ/OʨA3H\`\AF h|eJxsU*Poy4 +yAδ9膱%tDj6O >ų7D.f5eGJ޽d ?I)Is' +-lP+l {k`[DR/A F_ dV {h2,|n׏2zAn;rݾLs[*E|oh.wC;*oj!Qd+o(1\q +.|aozMa\L8Wǣ}NJBS9MlMfSk'0IEfdgi'({=Q%)4p3oPzí7ᦴNa|c?RW\<ΰ::8h'>x2FfP7cU:w)>h+6ۚMH̾LQum 8Z[7Ӑ&C۽ҹ^7Յo+h:`n6Im +2"..3jVlsX&sKGe#eRSk \1mY6Ui9ם]JnWH `k6gx7|e:iO|h{7&(µΥiu=? 6{qr1f:z=%j=I9z4wYo1h {hj'^B88Lpgx|\ڐKi8s{c~Ւ@J$zIwLAi fJ.|rgAz@6sӿ<"0. ab)g>AS/wW24o3)ELNm 36+sz[ +IIb;oLRP zeRoTf1?'t +*R97j\Bf\8gJb8hUly5SVӺ8'͍,?DU7{,k>p`i5 ]XS8[w9?lPѷVmsVz"c¡`+9A@q`KYlV\NCiYA7ۼ~Ţځ9jXSܩ=b)> + ׺^aI?9& Щ<#+P8[0*(Pb<:mJd^۔o/AQ -A2H qDp_APO nZCCI&|nsmVxh%=Vmĥڀ%PF`5\Ap)Ϡ1,;JZp~ geAu:+ M6mݴ[aamBRQfd_,8HvְS5d@H{=ɱae{U7/Q.ڦ(It*Z:2y478[ooĨW`l-`|c(2e#Fei^o@|̋F`i guGJ.VZԥ +u$wżgdh^-fxTO# fޟXWFaz;-}]!tq/_(=+_@NlCL T7(*'<~!ˢԒԷi i O3|j Qc[ T^]dhT 3`BxfewlsC>[x]Z֕:AlBcp +]sL֦5.ntQ%Ch!=*\̉$d<B@^#Iyntk/Irj~:,Ѳ[jX:!^xVu+q 7 zXaNspnA*HCi.IߎӸg<_ijc{,ho++ .7^(,pS̀u"}0' ;[(S3)õ~e{,X,LD&>9I2;tDg +(V|M&$=wk"}jmI$[,>?kJl+^N]ᔝx5otNi~Zw*V>XCh#"z+&pA*{b4b/AS[XȶcŁgGVœn- +(VXK۫KI- 5|Ε[8S1^p(yp&qB[ƌB^AHgU!VP!VN U?^&W`թ:i4b\=AlI)fSebl=|!DeLZWsTQd8gt>o|K +vRk p;g,6DYviCOaZ4 +W9oÎ >7t6Gi$]X ,p,EʪYaW>܉=am9hxy3V k%)5+`xvuY4Y_]\B-p`r↏u?b<̌[l9A0My^mV"z?{.n0E`72%۰'*5ɔD=aOJ*VF=Me [aJ5>S*S̶?5Z.#X5nVfb 3R%ўWgf0qsOvH +CH>zjJ츹unOgzuS3/=*n_2"f!ԛ7dFh"넖/1P4epnH Xg?~ǿ_~a_}o?WO?-ݟm_6\)ݦ^؈nT֑.'&I~Ǚ^m)ȮS؁0=edq8=I2f%df~ 6\ZɃFIɇ }0HTk\ew›?mr$.'v5]͟_ZO@ ;K/G6x >욎VqU/A:dX[X̭-@kYI,$gI#:;)zAwW_k8por\Y.5G[~sp͂Gq;OzT;kZ!(7X[4VCHzVŨ`CQ.+ˎ ~V`.mī,Wq.?8;oxӖi['[#+oFx&p`ƾ̤=xXwFk (Bw m_Ua~xڼy{ͺx]Ef>2][[*IO~qW*Li|m uN|vpvƠU|ڝksEqpiv{3~^8Zt=FkGD})?ƳƳQ~St"\4s⧣0!~ĪSUcUh)Ioxi&leq.-M0: + + +I$N's4nK }rxJOCY1B W,]И^ 3+NVIĝ~jw8k&A+JaH},O:0|+f7rnOUfj\0n%J#Cߦ9ȦYnҾjaKugն COz{sIA(4ITh!<4C1kpLf7$|ڀx,3DZЂ) lUA@7i(Af#;6DUՃJ.G B^f)t> 051~,j%G  RM&w<< +QhӦ,&AڋԖ뢚i[$XCS ~U{|wϿx_~~?~G~xů^7ݧW?=r7_k_OO9?o_<d<_X F7!oonEc,G? ? +#_Pjc􄋼v06]R_f ]xKDؑ ߠyFiHʙ +Q" ULjfMG!I ^&u-&J,( O.k7n9Q EY]Q# A uȖ\xLM9!zw1VTo' ũ삭2>wKNg>̈́;X "o(܌^,򒥿#YQT"M|S>o]FZȌ,CGFSP`-0wȋ96>j/H?$F +ȕ% .M'_<}4VXƍ&_r#m(OΝjY)h3`O-RL k~B^,%2+s!uGЃIW=q%jp(yavcAu{@))e[^ae_[?EJY"Ev_T-ֶ\̔9P8pB%3Y)XC^mvJs8E\F|i-s̎RS-pn-]=Z4[tryLM桙4aM=bX MlS\Ssel^YPڌIR;\s #rU[W=>S_Y^}ݏ?'>/{Ynڴ Jyw%-[lu-fޖ.-ݴzRlb+"_Ve@ +dQ3TpxVq8n&Pf+2`.+4Wo"uMǶwzDA\]Sn7}Ƣ[EKR ڹϑyAWGy<ԙLLƦ cFԽvìi.mrBDPS^ztQנ55Zzx)TüRϔ^%MD&$vqBmvˌBdWm}ܶ+ o%ipέE'# 0hX2py(++H9m;Xn1| ƹ[[IHڻDn y^w)m J1<4zdAizp.f 3 6P,:1v1#n9'b*҈)Em9qHpHQse`LЋR(l0WҔ^;:|&}> _Ĩ%Jif( '(r쭢H{LA4+rgf_40mZA>xXҤ, +b2)UJ f͡q>(5oG|PeR; ƀ1)KNkN`K/=zK ^^:?+7E>nWꇟ?ᓠ_}|:_7C +>}Oۇ_a迏?R>?ݾ}Q7QF;A" ?5?D9 +.xzR/!l- !ֲXߊƍ`i ]$_17Y #-5^s;mc`fI oGqb.+z*}nu=^U.8|{z՞(׺_~Bު=ǣx&?CB]X4QqtsƽY[pU^.>Wb&',1Vv7T4aqi8L,~ &i!6IHD:vq>R7Er9\=9E/u5}=a N/ "2\2y|OڑggZ;V1s?Z5PF[%Jp̯l8i^;-M|^ bEÚ`Ʌ!A֚W'tPNȞآß91ɢOm05JL%EW[6dWxpEa&M~hWl2=2U5.5~TqV:͉]TiK@֥ES܎<|4HmB+,]R  ¿rM_PU!ƫzQv>l=& S']DWA$iJSQ cO7;NQz=D=?b)ola]NAz0Ш`ay(ajU.hijKrm!WR\Kݗijͫkm4yk͙J,ϳZf}q]8 +֥x3iti8_eXBtY> +},>[r+p,9EHMݣK ])FJ'ǔ-Xngz2c1ne<8*BuhMVfll\?{4:g=s|koꖗ\Ҭ՟ɲ7ۛ_$8:΀C9O3]؊.W1c?]y>]TѾ+ֶ. xn|&͚(sa_7cօtWޞPܤIr[,0Ɯ+*XYNv»/j-a4/oy+Dx3k VxeԱ%yEU+`쥦{qh@샸SȥAA!" C-Db-jCVjOg9s]0FfgeS;[`w\64x/`|L0Fj5C"Xabn ?ܐf *=fU 9Maș.2swJuR] t]60tUHCrGypΚJG%*Z /)(ve3qi&oSv Ebƽ Yǚ+2ry΃cQ)jt844c_QiX{]fƥh+t*sWrپ]{= sNv׈OXu$iZ?v=Q%ëKL2^4txiEYq1"#L'->`^X#FV&Ge䨽rdn3_V9nirw(,?yERL*`l8jơuyfx&J&D>AsESLDFNКwN!\<Ido82d}J1HQrғEA*#yY?4ԌY4q&Z[de*v7(s3C7Cj N8 Iʩ˶9Us +7ry@7Z4uu [|Dŵ-1A#p &jլl؉Xd}zDϙs}tgD! eP$ 0s)~@pCcP +g r3Goہd E..Ģ@e{*Q<׎dA``veۑGzvtSvUQN%,CFcުӉd*j4mՖsS&]jk:b۷{8ut,M0g^e< '8-n.<5+h(Z19LܽnF\lEkS{;?:mď#Rw$s,^ڢߪDtN=60. n%:wjmKm#:j=XLю{-UArrqV;([fmx8M9ؙErBˌONd )d0EU9힂 9r:oA1F9G} ;v[jsN6u+_h %>Pf챨Pg̣_e~D8 @ zӽon*I9xFI݆eCݱަ(l;T\f|]>Up_S&jӼd7R_ܲ L9J0׃\#=I;BeQŮFֳ&ވI솝7>/DreرsmG.`^ݾ-,Mtk4\+Z u g_/~1|o~_~׿xkx]RO??4_Ŀ?/ o>ſN2IMc&9M/rW4xW.>Xt0LݔTK5Ěm*Χl͕'qGȓb}[{r9o?#CQ2]Ϟ}&z#f N7☣.W'َ(¡}lkzϲgʶ&y6 +#[0 p#bbƈjo%"@C'SqY-ouhϹqezYEr<ōu|Nol`[A|STœHmWq@>hxv4lf74^4qBU2`1θHIOMu<ɜ0O" Pz::,g", *)T/Q|fq6wu`x3فXC9~fh '6ckOX.MU l` ޣ\Xw&g/ TJWա*ͤ@p`SgPƑ@si+hzhR@"R-&!K@I7L}r L jj8'i3NI`t34G\B`#'q + |L5GL`et՝⑾LuuvA/N-0"i*RƖ˴-%[Kvns%C7ŚWLR +endstream endobj 41 0 obj 20532 endobj 42 0 obj<>stream +HtWˮ\ ?dMv[^@6$%6g8^ǟd 6_-5??{|=i )UiCf61O܍+obJx{e=* `SV[p|9$[_ed ocuhJ/ ~4<%afe l ) @': wE% 3m*V ?*JHZ:_ mO֌klON9Va 9_ѱc|/Z3}|oT$cxd[Zp1K| '#Ak٭QQ̷c~!xiV/y&Op3ZG&pL@:oakPG|)D!w$^ -Fjף\ٸ={aL#Ç5hrv-ϵk5\$a,+pj!$9kHQ^@ShR:yZԏٕYC/,C?'[@硏=!z\fL5a(3|8TFH,xiAXMD,:² z9a Mϵ0H'xCo7%XJWeeS?qSȞ zȚ&DP13zr58)J"]ӳWԭroDe"=bA6>b4[gY6or2`%8>>Y* Gu˪A{H˧4"j \=r'Ayݳ"A@H 1zfh `3{Km1HZ$1V;a'K. N22vL{su<_(%הfey4}:].@REtDra!wV~YqwmBZ-FYj2D,+Npd$Ki@H,EvPFd_.8^BgҬ479xN?fȒfZNFHr0cSI-z77G{(V26lÕJb/+"45sm1;V;Sn(CPj +%PQ6IX{aёB4=N/GrgjxJL\ ҬwPsm2-Đн ²ө<<)Z-ZG\,}gCUvwola5UZ`(߶C vw}%-ol \mђX{U nйxG[@Mc+bƢՒ^纱ش} +m +z{>9V[COyxRB|U?n*+h4*"6w#1 +73ψM+`~:e~XpMeu3Ԭָ:>{6 sV* G~=1ڇ߽$l{c;&-RԘks Nu;7G +z ;AY \3\P ٲ 8(s,4*:g*7(.m>ζ+pB2YoqQ.ҠJU& +QnPw BfC7]3(e/oYY*6`v}DzomGn 8>y5 %WroQND W@:  E Ѳ6SPeު+4+g Qz%JJj0G\% #z=ϮBؖU >x6F dMt]v;Af!']\3fԞ1~% Tn%hP!ܪzo%b7!J+7IAKL(:" BN& Rtj(1gXxiIrDS)aWN*LfXzΤw-:,咜hyZRJ")Wk8+T" <4Vީ.]r[a@ByVr.hqlj^L^/ 8׶>X3N5ɂ̾wkTESN5 l(! +Ԝ.K$ejI,aW lcYg۷ua~NVI">lT-F\u['l1c03lD-W@px#YR6?uCs4'Hý)\QH5ە)W~&諜n3kРl-} U:&``f[|5mB ~wԖ +r9PqbÈv`iducB}«4"Z ~Dvd6xo^ ;g_s6+%x 5!2ܠT% ݽJ;V6QZ~Qb.8uYkKbnwl9T2J EE08ʓnM'GRP19|ۗ"|X+Lw `A}/ʋtby7XL5n09\?5mmlճ8{+q> ^c96n8)sJR:"a$(6)(|39 ׮CE8UݷB[|eۓMKh1;f~z| 3ݏOmP=ț/q'SF 2{FG)DۦlCc&B)!1{bInMG|JgÕǘ05Rف-@[ycH6|`b+@Xyu#, - +4v}:*T?`?+p҆kw kMIGQF3U-y>Rkt'@<F+pa,MY#j V@3݊X"5kG͎1[s5 +n8yhEv%ג$.;@~!wج%7j \k,_ο$[8 UIAJbr%&b9 BUpHQ>{xpd(9]EN; Es +zvU, k5UYu +5|[ٴn?,cu$i]ׁ1u6=έ/ >k4;j,8"f <42 +||v_6b졁:-yQ08;YlC1M`{ &GT6ۊZ#zX|p?6aޯ'5v8X!ŝLgD %[A${1F#|GLLíҰzBdy65C`M-oǣ3B;cܱ"w`a.i^W^:mJz~Q]8ÝOq +_a,yR& p!I~NOwrT,!A,D_3YC :ؼI5[2ea^V?4eX&K_Fr׾Ⱘj' C5y˄Y;`T+گ1 W/q^% _ݤP`}$PzJХgD+Avkg9l +n©PM3uGطv3E!ZN'`r\}5'O[$_^sϒ#5?<ɮhr ΐW<&-qg`D0 +m~̹BȝWp(Ȩ:R_ +%8 4pST`Dְo16:jh]b.ЯbNALˤ3v,EK8C4X/ G +\.5]^ 'ƃ,>e#8QСv3yq1NJ<ޝb~ +6ϢZ-VPu⣉+V'=_#*K,zT NN..;b7ֶOIm/8ݢl)OF +n^7Éʖ8x<(mt1D'܉=E=:, + m|YVcaZ3<୒IMO[dR'\Mp_lq==F +cFjoROF-{g' +\UY21LQ!L@!Cиk +{+0 `;U HO=Au6x YAt#YAs4}!b.#Sy/̍T0)x$GrYnU=wk!U}Jð# 8m^y{=_Sȝ,gyr׫*eUlk)<4^+kY!PTuW3GO{x6!i츦xXnl6[ ɵ .̇>v\Jg;ےpyezt,[v:;2:6MaU8Ԙ8:[vh;>cŕų/sHͪZ1?PYm'$pn#>O?rJپ*K>zrIzetX,ؼ$fOD!s=}xG٩ZSVh kXJFihS:ifd0;BťK7J "J=.5fUĖݾișUע;w#t zK/AM([{BrܻwU9.7 wUH?XK&7Y~5׿^W.նz6&mb9:xCm%SRX0G@gi>P{eqC ̭a0tbÈAˡ^)Hn}/]Kj<t~zi9=d{HPsmnS'f{Vm,l<COnoʵk]A|nṗvv+!r=9N^Yz? l Ǩ)7 \Lß-ĨeF:1HL@ψxҷQQ_gZooQb~_ך{?_~/~?=_UlK/hWΣ+BVy }Enu 9M]]'3t_?| + d@B +:  +h%zW8J?~@'" ]# +0J +Ntiɚ~{ܮMb,/[ VmM>' +`<. 1%SVث* =}{\Y{HwR/E%|-mkkTmZ /٩[ǧ +fE.BRjbKGQ'$׺Uf,T]IWp;0rm?R)H/?bb.^;uk$OUvX5c#W]&p35#cCV*W҇uA3 BpN(e$Ŀ}]ڂa1+ PpR0A/p}+Mi< kDy#S,Hh :7[ۄYhB-Ҍ/$\Bd>OT-DZPںZ:\X*ɕ+PO\.̝+;->l, Iu"`U1b30 2!kn'r>Tj9-HD ci=Vە+"Oq(^'e̝oX"˟iob \pB(=]o}ث[8ʿ>@ȔtX7x.0YoZU<48C[?]}{.ط<_!g4{m{]H#@{׹K,놁gF! S1?&YQҩ7\% s]\ r b _'#^䙍ik a5p1tY8Gݲxt}2۱& q 4Ey9 =#) lv,*Xi XE$\"҄aazxTxBvvaྡY }ܼ253Ew|)}+[ VhcF]f4Y5d:$əg^HsbVMçM['~ɚC )qIs5yX}ބDY4L+6Dvba{cq;o\^",fN.%  z}&n+XTRRv | d%L c`/wd.7P*ZIX(+J4-f9bfEۋpGv\MpOfvg +vBHXs(cX7,*WqqX~qDAl3Xq0V- +u8(5ˊ61T1AGV @[tZ ŰqjtWiv\Zj@'7K[0LU= +{;TMr-F>wQ]*X*Q@Q+#d0Lb*Y8j. 1)C}ЁWZ{O}qOu6ʽu +Xɸ^Rʑzs_#LaQ42 |Eq#T$TWVMѦS.ֵn]i"w{oɡgԧ!g M7ݺ !I⩁Ne-e m-F o-rh<heDhZuŶJYD%^8& A/7/ڢ-3tHi!+`&1Dyt30kIh+H@uAglﯿ wCupz V Q_c +!`UwJD><߽܃KQӲpi/E_c.&5C-1gsu౬ f>+`Idіij258#lHslb6v^ +h uPi$x#ym./\ůjVeY$"]&M,gh=}*&p~1{\zE +Iz7?wӽ0Ө gE w )'|_#05Pge@LVVh7ɣ#`h,ذhىb 0CD W+B]1lr8۵̓6 '['rހ~,`f#`!7I_tNQ2.mt%ԼkS٠X"ŬEۨs +-r¿? + lkmGҴ2hKD)+/ тObwC36 +w\Zj@H +F$;G!ms7r/ղbq$/ze=R 66c1bfA:<-wfЊ9UM2iJҷ2roHêc)H]:04]u?-.-%DiOI>;2pwB4?T gR[Ekg͔819Mt9j-o%,_X?CMͦPK>|t< 9 lt{Xn<7&+اNPޢTA;E6̛p"  +Ed) bזtP c<|2Is%61PġY]:Ab:XӖG{ZϿ/5˗_}ݷC?. V׻>'/]%ɟw5~'.˟.>ÿ`9 O |r`zTi+9=ry+^{Uw``e\xrl/E `.K +fOYM;`oԿF=i8BY4T-{SW-k)bf:(M^}5ʟey6~Zy1CrVVZ-t&+tx +zq ĸVbtKG +i$u`^+0~5XPb`W]6,0lkzh)eG%S89T F*v0Yp A:`d I̝ # Uqhccor^=̧ti->ϦI#TLȌf9A._8##=IlZٖM'g^Q Cץb!+dYA0SI<2q&x`wEѦxx+^"m:jGB@L}wpBCԆB$GȞIgg@%5{2 Rƒm5ִD[$y)4ؚLU*2˧kˤl\/DRNԶlk}( SsY)dL{ӛKHI>̈0989 )7P,BCʤRutޚ&x~x08;ݖnl/{o{].Q!zlQF6*slK!0PZ0U\(Bs5ۚW/[NY}Qo";I1|pUpNw}V}>gQX,mq/Uۺ%MZû7{݌?ˎp/ir^O*ˊk & NU+v0Nj(q$mnI>x!uJ)p#}dz;RUlӲ,\RF8gqOϹ!8ljԺNpyw~@+(T`[q,iGU5)ZgvYh,Y/\r&`n"IZx@vU E.u,rڍrFG,JI-!_,RkU@XqdjBS- +\}2>6ִöؘJ47.uTEjۊP^^YЖXQne/S<^Dԝat!b~Q"i9Tz$]>a6&j Y sxeUc\M9JSc>9Ŷ`-tKxd3%u?o@X4!%aq0a2Eyrv(FfʀC{c+Bpl%ŅA/<iMq&XǙ;rĵ%wn+}˔&"@+48˭}7ܮ|\O^eɒKاI7>-ӗM|ء|t\DMrhX&HN=6EG~_{jFhsR= CPO5\ڏte7HZiõ52Ul +[< ec#k4%Sy>U[mMCv.$>ٴZa[@qA:5M2{)j΢I,]Ri猓#Harݳ pw[܆LcQ2.Varp1U%}^:\q[Fs4<#D8|M]NW(ļEpxO yG0mwa{谭z)_ 0jRD^uβ p]bYu6mS*MCQ5b5 l'[@{'=֌OYMMMP4$FSeﯟ|/O]^~NW?_^yxpϟq}7/߾?ݷo.C?o~|+]_~7/?.oB1bY2:Dq^L|_<14 % 1I;s9hlp2\ lp(py|HR>.𙊮7WS_^7/U~;\7q̰k\ _Hˀpiuw$'~WkŪ_F~`9ňOݗ?e|;Xd_b:evS?|( Ŀ VZB^=@L\?ܫ"Y%Ž8 Zc}.I ֡[  )1!c˱#Ǘ*ɽq76gc78*5= +%-38X82BVC;Q˾<A@{I+! 2\'ڱmV\#zn@8?I^/"û N{o;O:̳2m!s`eqQap,ŏ/ KX^]g(\ZS}\5pssYใ + +PWe/[ o+q?g0=[g +ḉV(>7[0vС>;[$8l8 S`/|"Yx-K<z Yyvc=xvƂ2D<5>x ruMB.@1|j)>f.8Mp/]' +)*i<0-=}gG@'ͨMwZ͎E~YWvAK KW:8ZJӈ)y`Lo N7^kEg]eKm[BZ^6]ȥP{dRѽ}gғһu +Qj#zAК{'] nV?i|>Rg1; +Yc1=S&3?kQvЮ3bOVIxT=}UX#KǬI䏫q4j9mytYp-"X$fkϡ+W:Ƚp7tЙxTd35Ebmkb%' s'Ba}Z/u y q8fW竴iqF nvBiؒk2KۡX,ZZo4ѵ3]7Ɠ= ٪%Srwצy:ϊJM-Ol妇촪_Cn&*ѿR3y\[WǍy6S0GFp׸xʅgt_OA*٧Ym"BZy4N&v˫M-|w;ᦺyhxزjH ++*9(˂v|(ԕEU5ÆtKN~* +???M RY䷆Hap™v\͙r`5ҜLpHw]j.7!l.嬓Ɣ#YWSP?nx:)^%QY:x+O21|zɡ6rcdC +}sLJ +C(2A'YNh|*VS^`p ܖX\f#؂FV(Ӎ^E}j>BNUXծp<7(1E;DSkUW^ANbEZ΋DNF_[FЪ[9ߒ}DpH"1E)iO@#G#[05@y&H]y98H>X u+Dy]? jpͳs_E\K+`k*]Kj<Ձ_g\]. +b;y9 L]ɯ-w1oXH1?%C0v-!}/&L˚bc1Zg*DJ Y੭k#'w+8&$阺V|+r Yɯ^&Y+֟"6uFװ=P|{ >?N^dW˱1k`s"aIVk`N4TF:3`j71omرMb8n}kE\=*xߗ Zz]xZQyzQ2G +WmyJQ9ێ''kuS^@cAj\- +jm~FOI UHh 9pY(3,Wx11f$$]k3# |694$kVv]q,fn0jf?r +7t W-~rʶSX\ˠWԒQ>/'q4-prRn/3U e$dIhOb%9]t~m'@xƹh= + VyHSQ+3Kâxz<=vMu24٬ MN 2%n]9uؗ6{Ip;bK817MAd(+,Db}S'wLa0ugW3.Dj-Ʋ /}'~-wgM^/&!N0Sy~@O|Wk~[nkY' p !iQ{HN A0A~;dr\"(F+Wbb%ExyA*nNByMLHB=k*v; ,@'FGMkI;:fIǢmWZ^MwdT1LҾf%'Y n%-9gT2,uH\LO 6e:a;mG64(gR]\<]N,iMn^:HuyFU;1ogc`찲1n2a~^xl܅*(l{osp_opO$KC2'\D %,CE4wBպ0eplq"%<ˡlO>stream +HW;7b0 +Cg;ŋ7w_X5jieP; '1>욺_6s]2H0KiiӌAHM隼[9o Ջ{C(d90 [\ f|-ap1j߇ó+иՌ_sNJ \#0&iP@14o>VЛ-qxΒ<,C +G)TG}iV壵" +֪byk]G{"+/>t;( eA6%HGhIkVpRZT*i7೷$؅z>hZ|iS#d4ക:I/lgb@V04uL>) L:Z/DwΣ ǀ<@/ jx=J XI |dES*tz^%ݗE- +m!R%VAG>Yn״zo]GrLmpC#-e0e2̃u^/;ܳ@́Y4Jd٬?up: 牛qo!T~Yσ\ZGR.iRl-eFr'|#?~68HdCw@1ɭ`ȕ a>ru}=OM9Oa;+!TbJprp5Q lblC;?2z8I4.oHfTlZY?Lxc૦YT +~ќQwO.M +qWG,t<_;zPQGxnΗ Y DeQ|ɰL}eYa~y>_QEh_)2uj[6 ^CFȠcʞ_1_iB2{͏ +Ώ$oerw g[j˟ V3atlU(&IW<ݥ/)PfH.`V &})=w#ֳm&7n{ {iu#b$ ~tq薯M5MCfި1tpt>y WHjLC\rIWfvf!k]S5SL矘K4* <mZ`ʴ?pK=OsSRK*ckމ?@&U$kDc;2ceQ)=u}xǔabr`?819$˝@\fx;Vk,ki!=!|&xWL5Wp\}{$i#wB\}n#"xOkH{nؾKq(dbL(šࠍ"鍊Aν>gKR +xTԣH-om|(?ʩ'0ID kRx$HJrP2 Uks4;YC;Q;/i&AȉQOp.U$>H5˂e +?x~*v8w3>+ib̆cA׻Tp.GLpd;Uɽ)S~>eZ1z& mym]Rwx gt+m=Pu+a8-8ҫH>VQt^+]{ tqXg+{%OxEz?ȁΜs|R;bP9Cŷ)@et^򃑈98ų uG= Y9܌_g_1έ}wj͗98x #XȻg0k۳ܕpSOmx26_hmF+@*A;\3mr꠹%u&Upܵk:xR)[WjCd^"a>""9&>"LeWPF\vA|K,Zk2d*:Tۙ|˒Aq똛Eϖio[ɲl7&F9ݮVMfW$s-{{+"a5wi@/Osۑ) ,rkӹ GC[E;B4[0| ~}<-̑K>.]DV}CNDPY)Y-JN1 4h7Hʙwf!09Adl5^[K5h;BIsl}3$9}8K{zF0yѩ!fݞ8% =nC{bs[hwO'L38|:h^!pBx\5wgm[0-}l~O%V ]mELpk!@4GqA6hl":0$1M{c3({}÷ @/a(|b/<zo|cr!u+ڬg쭎8<-,c S +djt@E+(@LvZE̱՝{ R>rU}sZZ8hnit56YBݖ@{$p/Y>V :Zmkߢ%)̡k2C45rY S22##tىrX._8#PށTqPXm˾&EEt2ڹn'\{ PanȴѾ\nG"0.uW}0Z?XeS1_AaPGA-)*gJedRzn(œ|NVceWݐ]-"hځ}fC8A> M9tJVj98\p2>Hazpi`xw@x\، T +dFfq$)dL7't5sa.ξUCwV寉 V'L[ERR$=ٟ`EHPI~썱Y%~`$xya3Oaǰ%v`kIm]}޻Sra>B>xЍWz/oçvwO/+_?B~o—]r폸65 ح- (*&y$~ +v{~~E,EgW-ANd6 T{u۶^]4^@Nc)]F -.e30Y DT+6!#@nʅ~njV+?KVǩ$ݒ*_'{$pYoju/Ww%W㹶ņtڃҬ@V'L6WF[gK:Y3`ޡK|q4G  'U9wH/.w +F9x1n ZϰL K'0r0faH9v{ݭ wŜN{`@}chuT'H{Myh +)e@e} i&@rA{D}:ͻQR +;d9(*-N*g tаrX"@\Po4ʎкjfK8?ޫ`ǒ\@0II=9)Cbsߧ(Rzfns)T,-푡A1/>.ipg8fU]֚`LvC"si Z0< \(u)fIg PJ`aM>B|y2 )FjPIgg@2ZVWTfO<QE\_"B| ;krrk,qnΎe8wVax&+L%d"5TsjW, dH۲`My~,U8Eb4T|q8愹Uk|ngd@L6d *V0OGF9l8t%[%tZ gX8[ qs4L|>_S>1q` +^Y!iZn2'fƄl B˷F^ E,1oFbiJ%n5l 4D8Us CBiy~}9S JO/Wwc.=oo!zuFRu9/m%Aq=BZ-p.2QzxRv ŵ:}%1/8' ps9<9eK0j|__ɍ7G[WG +cY=و2ehÿz7nuõۆ&AQ> ڰgHIae2b7c1/jBꌃtY%ch:wJ7Aa#7N^Om|IMM}jzL{AAYR 䔧p}*JQC!ndH6A]8@~ zXku4,γeδB]R&|H[i] +N?rkqTZޅ @T[s!=Ӈtz:S蓁ƈ:F"kbd݁L/deCAy3;pCᏧ`w`P>t`C *k 샞> >dK /콼x>MHrвoH+ng/ َsmz$3M/2.\f7AN=dXhR7Zq nu6 CY]s Z]a)/;#Nl^M,U4І0aJui68~N0j%'iK=jf-x4o(xxpԘ٩?{7_޸U?HQ뙄Od~x7Ƌ_{ѵvVQN&p*-&S pהH/+_NWۣO_aR(RN{p&?FK7MogX\_Vf>54=x>* []©n S-1H#@z2 0r^qr`y求 ~`͵3C-[~Ne* ؊ ,;[,a*" n lij,H۰qɖq/))1orFȈmO\|,yz|^R'%}zLpd(4M'% >" +Y$"ږ!RKcG/jmt:yXY,pY4d5oA6]Ϗ / q Dy:.yxM#~}{8m}`nxѷ|BNl =MF`D-AqFޏd +pep<1VZ V.#NGml5h(EQXR,OQ,L7,̀l9A@W^˨_<ּLLlXnk7W)u@,Y=°վcmIE-;lm<6ǝQjMgj(pL20)֕#k05)CXXWtmeаkB.j#z-}RZyqtc96p| j1l[ր2~@PL+ՓX8pI|Z Ҷ#;XWa{p<NFh9_`}7)΂;kbon-Ard5,q+:9 ep#AB4JYn7{HvCmҦȌ|DRj)cca3z!KOfFV^nl3Yq菪,@x4xD ضƥӗ͇F8cH24l"Ft%O S[!Ͼ Bqm7lkǖYZ{|gGFSF :J\pP0ZWQ6Mt /Df(Q^tUN@ѹ &-D[(úG|cQ̀ MqPpBs# Dau5D JUiyj̥_W'kSxZiCp4>I(u޷nEK1v1^=isn8I|}&@%+ܶ58:6l ߬\.r* +XKrѐk Mx9(߉8DVs=B`oxLQ,6_>Ш$x!B + YA#?HSyL>M[Fy!dPEcYz+9)L +(1!P#gZBXBoeQ:߶5yemәcռׯ>5ubaifעH5LQ.TUv)v^N&~'_])-gJQ>vLAN)K8%hQb\)Sf+\E$o-7f15[_ +dg +y{٤(&+3fc"gL2猟J+h] E"DTypnkέIӚ$֦MH".h/I-a>fF2bE+F 'Wڋ5PxML WGlK 4 +<.ٵ5rU4z܈$E5 CBzrvEV3FK%M/Gg$Ia}pɧu" +l,xkHI,K]:Fclp(ŞVZ v?} 4گp<2o/C{kjuV5$BV!ddێ^Fה>ݬ݂k)R=Fx>mcp& ön|~Sb2[ܺR-͑n@+֢_簌N?3 u<_Vy{8og DCv9wŭotxR7On Oh8کn]@E;i{Ձ}M\#85tvɒuƔ> gF7m#L'80ϯ Aha6v/mV{`I7qu,;5">&q'0<Ir>7{o+!1.KF?1f>9(ܩ]H,&kiRB^a8f7CW%yFFʡh);ltI}vRī#=!ݏu;y:yϓ+'⪉ΕH?~2}y*J"7lԣr6whu^GZ%|뮸f C|JӖ +nnoĚ#p♼m @&/]wj훜"LH˜5MA"V?{OY>gV>Q|0ApUcMXIY,<@w9!]ZCd%BcP"x4lxr"kwkYwAzK{m=HJMbB[: +K@k@<8C0NQKϱQS>@gI #QdR*\9.@&%T!3yj}._ c1]?ߦ7?~㳠|+?䯊|ד`.ß 엋򗿚˧?!ex/ɧȹWۆڂOv?, ֓J#k⩬Պ 6W@7Ni (I_m|-ҬS9;1I`9G!yi2akrZ@iٻcQ#MԂ/,Kx +&䱁dsyx$5|yy61Xmnwy~ Pmd~k!'k\w[`̛` uu,#MfZ[n@A3{SSy]bul,|l}Xf|{Ӭ9rV?4оI  9l.gk)C`Pf (͇ JhM!ƃ^FXf5@3mt݊p. .GM!dYτE}e)Rŏl.J$:df٣:^4R0یP%X?61- |ӋWiA Ep+sHs1}H$o9:"7tT-,n-,RՇGW[1WIwMpʆ[-W?Xw`k T:TS|;8sV w1'hQ"qiɳ(Ws߽ n:cnvak = nmcx2X aĻx}ŰA&ͦ[\aS |PߤlXrh ͚ .byoS㴹űoB^}<xNZ;%R$wN F.K[˝i`E1J艩iZJJp.mAq I(@U+)tzJ_ȠNqU.)@Fa,W{=Sd0[w>;N[RIEYLt~ Z JRw࡞zN$5^J٪%[c5"\3=Q{l'S+usn]!.٪=í%}QnrL:Vg. ,ÇAdžO kus0ZJM<=[qX`vpheo/xaNa~|7^9=zՇw_}>>N~%(CO>"'mmŸS-Xcodkw[5aȏ1P/9*m8(>e~@Oߟ=/ׁͫ>{OG___xi?|PNO>`Щ?ڧO=_!! SMK4/nLK|qX}z<PwPR:83hLaޱ~~x}O^~y݉>|[x}&٥N[5 4=J`#A &z}\'mPpd04, k ߰I@Wi 6_7wmY`5]<Mb6òvM]>WFjmۛj[M n7SN[NVNJ*,L_N: &UL^:\J͟'#ņLC0}a:\b/;U25 4ѻfcWDlWI$`*V]]oZBˮW0m=lak +b2si]ˎ"]+k>j[v<[`(-'":u]i❛UCDEOuuZlWG$]fv[\{xk(ŚxkR!Z\mi$p6's'񔫊z"t2⥐U|p4s7THj%&h4K#l^¼õ9^ޣc'[-UOZ}AEj Y +E.f +j-EӺKs0d0%YP&8IDD^:MƝsmjTh)Ǽw 4^3p+PtEѫvALej瞣lY,*XCP8%078%QcA"Jm@ddݢڣ]Nqq[#]ye:d^KYrmvҊiΨ]M9G:ͮƥ8ru +s &%Ƅ{mkL[Oo ǭX\PAJ(C wb0gjC |΋uZ< LPo U^e48̏sVi$qs"LG8zFNYŒ1)nOH??~?||.OO?_o/g?#Idw1x(ݩ, +TUe.yD7ըtCS #:,Sa}N3v,9;@f@_uBͼQC6dckrP죦 ׂ-gf SWLy$LtjY P6qQ;@>`0./KG+jdKbAɎ<ɩ. ta|^Pla0$t/KSӯr-k5|B K&}dF-yCBz+++QO~ J6W޻y4Q-gxA4L7O#\DٽӠAy%D::]5HmApHa,H= +0,ox4(V\zxWHWB uZsb#`p6|\j=]kT~blzP$mm}ů_D ׮{.6Faȑ0h-t󒴠TK1Wi|_8&]9a>d7u[:Yqaf8Gk1gasFkJ aix^J0юacz Gxيb8Y+lqt ԚGߟ`q^ (jl=9w]qR`;PYUpX \5թo_#0m%x;WfUεY{.>9.|{tC{?oìciY4JQz~4)vRH;k?ܣ"Ŧ8<ۡ 8,ge3OXfqO܎~Kf=ɫ6FWz Yt]$rMl p8Se"㉣:hLsЂn#K\{<ֲ '/Es1Eo+jB}c+ID?Y¬fn0&s Tۈmt`W,?alyiɀxFp\o'#;YsWܑ!>]9]ypvr'cW#=4-L0%KP[n<.~W8g]Ixpi摮w8QhǛxs޲n^W(lէq;cnN|튺. ]v[wo7YݽaWݽ[wo޺׹>>= ;sW}lH}hu';|Dj'i (R^WhJ A wJBJ񶽖Q/OֺlẤSzIwm/_|yFזw.tF|,B*y ]#VP+VPwX3jۥfg?,kH(__ym~^-k#VR:K-8XaT*6%7ZnOc3xU?|kGm`.fw"5*~3B\M#! 3"\9g5hEDyܮZFFK)᎒"Yx "+;1CcmՆ5MW3ҺaSb6\(7`,KƖ^uCa.*}:xmdHF3x `Nt&Xك6,YaMp?o'm{̱we{*;l#ov,6}w04M;d_mnI eM Qƒ}Vv6]ג*<*6e5bܶ=6 +|t >PpwsZ 2R_&^ˮ>^SΚٕˋ^}՛C }zէ&bur)C,I Cr>@#{BGHm4mmĆV LY;}7ھIMU- %4cL#1ϜĹ&QK6I/>oWN@7cq@̧!8Pfœs#p;7mI%iEs4a gFN$0؊h`eI{=0亼GQgYF,X:w7c7ѫKf4.CӍڡ)Sc9MWJŏiZHF)kz)ɘgz3w4]'Q_"I_4I{f1d,%N qg|r24@2.o鸰k#ہ6 + zL3(5`4vj)lSV!S)TaED*!.b`@TٮNyv (e:ƞWP+GM]4rXii󘑡@F|D6#n3]9AAa{me޹QR"9 u>l# eRWB5rdc;-$Wl3 +Pb;a%^w%;fH*Џ!rz)OF\ H*G A;+kNii`gέ{8zpvbN"E8]u@zhg3.g`i-7pbo-m;41*%Fxǝ ` v.n2v;7(^2RW/c963䷏kJc ҕ,@;Or(vh0: +{]~Ɖ`qk$-; EK9P8ApyH7\׋s? 9R읩1^863c7{vNVMK+^ükij,3']>[ `^U"v_޴*ZC>k71y>pQ e0C[c(" CrNb +wI+ )Xwġ9&r\НRjH~Ʃ&R M +a&RP 4KlK|52c0֠E L/;OZu!>OgjhԐFrb551wQ==~kxrE zKE[W>stream +HWmk^._64ݝ}i?J mUZlB cK)\33幏";uCL˙3OO>-%4Ǿթ\(%NG>%0 ]> 20ybJӹZ 0#\+] L@pcw 8qr_l"W(U[vZq1d9k#F !Id%8) !;ׂp>,>L,lfG(|sgUr> x,a!$SV,NO,RrLph1a'~s%\O\x4Y~ P5ޞ~$= Óӓ/NO>_=yճ +Sbp=77rw@eыӓ<=x8=_߼4Nw-#r^;Mϻ Խϰ%~[뗿zw۳W}WW[WW._:>R3?No_{;/?cXKKy0Fuӏ-_uA3oE 5H:H/;*<̮mMj1B~,E๑8P_!+O9c"ޖn8ok6I1Y'a8bADaNxhu\.C/]2,c\Gףmm*U5E,񽝽w%WoUD +mf zLvfܒܻxێCNdBH;aՉn7lvW%3툘©hgbn ۬:SP1yX;/n4eWnY7R z+;ch2x{air3WUwr7冣v~b-%}}9zc]#al!F+R=X8*P2E^ ӱ6XwbMNBcm26-y+ cTCuAؗ:p .Gak|cg0N1.-mlqheʤ m8(NzoIh2ΟwP3n.q)_xƗxaFD6ӲJn{/uu{uBṾ\vא}x,Շ=74}-o;}#@Y1˯ xT )s>‹Nhfvq^75lc]ϣ>6m/QkQi 2xn{[ڿnۋ8BUҐH4b +צkȟq?x؀SxMan^Ҫ\W( ESp8SYU6/V-;Hm ̓rfQ=O +]L-+p TWqj+`8~eVxh.8 `VCk}i-Ʉn)< +^mءV>j 2UG>@1=|p8q%Ll{?Pk>#RYXNCF%=#=7kXXyɁߴDC.FzZ~6 a!W@uC8U-(y|Kw)Y痝hnBhʳ1]ֺzrj +1Iw<T$e ur`6[@}eZ9J8@8rM7 nR/&iUWik:=c0FB Y}<0B5Z,OVW. 'c1&VW!p M9P{b:Zu<%qNlsGdHYx+K:+,fB̉Kø1q!%:ѡ y…qz%Q*2!5!fhwGB#N3[ Zfs0Bі\-|¹A=\,[u1-0PfP-4bj-DI}y2ƴ$!jUh`gw]; `U:X}&6++oh$iXMdi?_eQ$IݒC]yV%9v4] V Y60/҆ah0,4@d$sMpAMaE9Dʮ&"kHE1`[)):={<"j=e8cIkH,V\ Q&/͑ *}BKP36cyK(zNcNuiL᮫qvS5hdjL2UMj@[TZ8U*D*Ndz:eQn]|e& GX~5NC>h??s|"T>8 ƍRZQNbyt<_jc D1c 8P:,M㋒h*M@$h[\M6ЭtXq4aP]qx!Ɖ{PRnię+Kvegvg# %>i0;Y]]~xٮj I~>^Cf"х?wq7h_Tb^ż҉$Jr +9EgϦR4=rhW{  ,+(J\jW|Y{PpCrtkEp*x"S+CF5;f$iӞ0Ѫu^2$)jXnY1UkX tllgnI*JLiP0|&IMMtUaO%d2 UWf*2"{W!Ԅ>Ϫn**.8/H:Y e uVSϹvQMuI+ۂbO2i +0 }gY%,nsJjlrB $xYYʶ/{-= ;Ӎ)C"+RV,(\M)G)֊n96Lw>,U䇪#~GFz*̯ 6F0Fn˭01On&u!қDO_v4N,r"&49:ȘHU\CR4'j| U,#< +T **SInˊ!C8%*+*W,@h)4@7qjSwEE>ȁmxEڃJ`9..T dK7\$CHҪ޸C8'D>M>it#9o"qeRl\ǠʎGBp#, +GXnJ1p.WM ЦyF){5k&Y0rHBnx,TMŠTuja˘L8ǔJ"dc[E~]ʙpq)GNV*a_J6Jg SqLUedklr-CX:c9%oQ41tHwI/l.Ʃ+6N _ {#NzV:Z)Wb8&q5eilӄ릸m򀱥xѤDHJ­+3V/h +gRHh@񮈿Ǥ -ed 1i\q Ǻ*coUBvà)RkeLY}yŽ?/6o凗zݭ@H}b9\͇|xxH|(ク I +~؝=-u"(ۡ}߭j֭n{(Ӈm߯ڋsbr -.j`=`Zz! +yO?}em~A Xub+YCWż'ȡϺMa-&Zxu `'5T8;Qv'qv軤"ۣ(k`5anvulo}r5vrPPjw:_෩[7h=\CCGyPTQT)ahGN +US2+ 1# ?trd%-‹F:9/퍉>P:|pCB-u'pُ7<x"!uh8р\wXQՋKUu,Ȃ"kgk2𨪴Wq mu2Fa^/k)11.& glj4aPp/q +D۠ +b9trݰVX>RtUkzmPga÷W};Q?z-d?RwOJ??ZƝztcA>C į|=%'ψ=Y" Vukpk׾FI Cx>h.}p~՝/W3H/>ExtϜO7o#о{.~}x*rO7'%L:^_v?wlcj{6?l 1]xA'Uٗ6 c%$I%~{g\,٢8Q93ϾB? +.c3g0,NuŸrd_E܂ O,VBbaB*Y*כ+a(ªH.ie;'a}2(u숡w;H6]YЖObkgsgkА{zӑŜj`X%71ID1taz+3&;M$ƼzbJǴp#FPCsPar\ GVhЀ@BS׿;(H1? KvFEJوZ,q=:8i5z4P1꺾qu欬'lhxOE6R#\wisI"c*l4%z!kԔ50OnTwIսk[(p޻݁ W w>,D=N-wX +Vm$ ^lkv _ILRC_3D3 vC5LeS3 +hK$dDϹ=ᲙUP7kDe5 8jG<#n4m#M`F'o wgoA6d$KL +|\-Z3 +1`]m@3Ӡn-6[M6:4UiVwԹ)Opǣ}z{Þ B<&M0T_#&{b>=ƞ @Hk;]:sM'PsH_د(!1gwvi(B_FxSx:BrxƁp#g|<Xz4*=;X0YƼK+WH:h26arP]zsU4tgF?R2 +-!B׎BF!"Z4fx~#H{1EoM`q3JE7I0 k\C( :AM]?T8& Er7 +${t{ QQ7Ɵsj[mUoH D@UgTEkΑ?22^XD#S4J54r|G=C8]tҡ&Uj;ȍ:T Ay&QsĜ1bw3&R2=a ҳZKI]LӒHO"9UY%*&J,23'L֤Pd\T;9Wj2_TYT1_n\w-i\-P&L;ۜk-={VꋜTN|Z(]3d V$a$]RQgmu:_1[Hٙ +%Hm{ /bܫKH>^ETIܲ{MUs\W )s[360:epP|d֧3kL[\~:vΩ ЉзU## #.aiئ>7d$ +W/!7Lқ_Wҙ#腿m+ڟf3$[~d?ɲIJUɦvv@X_  Ay|؉s8\4ѽ{Q/$*ߨ\Z6;4чXgKXHXc^|| $zp(l]<_c_b +hNEUgoD 񏋝g1K~m!I5CK ߿Uz=sH;':V,Wţ> >|P̑7=`! 2Y>]|<'w#;‹bb "xr)Gk:a2|cPqQP>tdy.G1&΂9 "tpAA|n7=uX% bTPQD r\w8;ÓH`w=k< ˃ E֚u)<<·'- BuΆa. A$h;܅(TC +' bu9 xSb"0|i~yʸ;&>AɄQNt4Pg5y]lVtb*hk2u86mK!h* ݡKR1#EH<8ɜKg+Y hx8Rtq,#JY^wbᵴz߁)ߕwʬx<ӡ.o#YMY? u…*+,,0D^$q\-0FQv%\Z sJf1BaeaVNtxь|0B>V}*Vɱ/ Ewp :2hqL0'l+C(PG,$Ɋ`4, F%kIąY'/aߊeGψ^:SH)}]j$ yZ'p{UAX7M4uvo8,c]@C$}\(da}[S 䎺>A`GmH- +ڦn$J^ & lKxŒiIT)kvtGV[f|;97J3)28gk/B (AeżDžj?*ǧuh7ڵ5tJ=TVh`͓<~^1UCD%l' \]]bM']ꇜ0/O;  9pB,55Z|a +̙M۲/LڤԜ.w|4tP50ͣ|fwU݄P*rm\!j]H<3,ܞ"W>.(B$DOG@SMM<Ʊ&:r Wmh a`d6K bcXM)7E@%7/RI\|R*f!&~ϣ=]+Ds +ɼZVj8 b^CVGm_Ӕh Є3lVl0mxk.VY-5H,oZ1 llIUk ]N;y(ȡNoU׳OV*,W=4ɖЖc XtS|J1'Lyo[.{y/pɂ`J44uy|ete"4WgqFjS{mkkz>,M]䂨xK +*6n;RV(b-Hbf@iר tDwl;T>sA͈XͲ&3zj}+TqtUp+ƊW>stream +HٖZ +gq@@q(,=? jU."L"H%DQE3sN!oyB[ +/WV}o|( B;w]F5n+խFKE;B.a3DGPI9PwcQnBwkw +O~{ c.-EFZ}y -QC_3/%7ȗ{wDSԁ/xknl+,̩9x_,- %pI.,V\#on?9uG\<&N[!a!KayyʉYA\tDTNފz5οFn\GiRW#6ZYQٯ4 ,ɲ"yf bx(a6K' |FC挤IzJ%(u] f"y]jL_1WrEl԰ od)QVJ:^"AFES#1CIQ%y(}# y̑SGƳ!><-~_ E0Jt{0wX UNt!e9Bo }wqG+'PcBs)+I $nz%s0JQTڹE<܌m:F + EsLt`H#' 5cyRuv 86iД:u4jT|[-:ͨ#$)[8bUq jtÂNr#Gb)L#q_3"L5 v()s#)¬]sj"]heƚR%թzxٹNE_xځ<>k5ac€ߨ|jHRn6PhEɓZPc(w%GؓSIh7B@՚/ǡfF%Dڂx3lӜ&4fVY4"dXPqs'9ͩEr Ta\ÀǠpI PDjQ'jI k}8-FIœZ"rcJ&a4/Wn~ S%1=7*܁nqmInsފZD#iNcdʴbgEjEm"LQjta6mO&IsW~KƂ:Z'2QrC3RghI]tc!bJ76(; 548-n궴Rk0^lj>1Y*iim|/JdO%0| +F+_銖>7h',cHYZiaf%]˵-ZYXd&ڳC- ֬.U{U`^26JA&EB{-$ om+5$ Vv^RֈxWzN ;*NxQٯ̂*ץX9л֗'8}9IiܞiwONGq76[M څ% y>W ] +Tg{pM8H;7Xi;b/]83S\6yDmtg^s(jO0wou*yo%>UK~;:Xz$$$np[u K:prsT۵AxaIw~_UzE^;M[y>-)xχ:(ؕx+f1nFkڦxyc.HSW֛wgJW賄}',",auDP.,ݝ@:Io ԩ:h"FĨr+ Ϊ7V,)v3}q@Zow"0Pzή B.Ár (SG-儞N+}2}3;E9޸hc5c>7@cÄArį7J': iHӲ C\N8i.-en Vc/Tq* `&cS#`?ro zAOߐ5e =Yp "BiD>_`yMOv6 dP5hntPV8TRv}{;ODeڽO_h54xQ^=q"epuO$kу-[~|rw"G=Yy(ڶ\?'>t/ޛ]?zjgtࡊpU`v'gAK;F i>eGcM㯝@̭45[ n&F{]<2@Jpkõcj$Y̌vQLtqZΨ2&2h!faA+'ˀ6t.ZI"Bsؾ+B9.MsJ*sސ ircъ!}CԴ w cYӋ@~UM~bWi\ήk+XWJg:'!6cY*٬=L`9>;$6fő0׭ZMGscݑmOƾ#gzj.-nWNYro)ǣ0Z(RV 59"!s~hz$OO;Ԑ0GHUOrEhL5<_pZv<3ʵ1l~0-x10| +=o +9c49}Ӝ-@qHsBvА֑@eu=¹!AǩA 9.f :C|7J"&NFdHe;Nl90 K1ȥ=! aSjHA$Fp5 a!Q'T 'yI?' JU#I!5Lsϟ/8sY ۙFʇ%r2u/.s.@{ťl-=5 _l>r3?QJJ0]OVmK"F`۶.v3N& / : %Hל&vSCv%}h ࣹ /UxOI0zcl +. Q#U0m*ƵLc\x@5b̀fI*(*_(Ϝ5娲8%47EÛ0g⧡p0j/x(=TFMN#Γҡ´ol*_7Qՠy +(;[|=T%'u:0iıQ]0iٚf3.!aLc!e)sNH99nΚ==*F]sb!4Y,uiմU;[3VW@YzE)glTc,,}!'[y2t e&Xe&bs͵ Ij }i n6҈zn7oWݍԧ@JՠX= Yy>ؠW{ -Qi܇e6N6:}6wxЩYit~z N~Um0,vAa6 r\\0~fNk݄aoMMĭnυ&&]SNjp_j̅v^$n.HZGo֘hR]B#ި޴(y?MkM3M 4 Fڈ:O'wKrN1'XxuL +b(r2\ +iitG`ҕnφۍxu&™>v0-hvT +mqS-ݙ9K܎n{P xR*xaލD| )D>IGEw_M֛) -4Vz;xq {8۝C aMrǷv8M/A1:K{­jt +Ӳ9Zl0|'&Tw m^B6/et$4-rqd@X/qnRN*tGgwdދg\NpR^ 1|` +~Y'T2qJ|ĸ$9A! d4L3m݀8 ++ M8.΍eR&Tqa#[tR,I*Kp35?~%n[A90̉MJȧ@lIk[B:]K֎(!̎Bw5]ʮŠ&G; 04<9&JT+0ʞ0K%9cع +xu2q;@ |U?BU"s@ d1ޜmӈccX;!+OR/OSto"_IR׸9{|  QƲrP2:I~W œ(=gF) ӔIW<<=jd2i4Fnག +tc4H% ' tع +xv\{p,.W^d_w :5&^.0As`k q_ּMrkIT˅fK/iPt + 1W)GNN ޤ5Z;-)\iY^giƙp2D Jm +*#M4* %i(<Ӧ t{t7SÓwIL=rgj3tR2d9H^CTcވbUw9|:cΪj2DX`F8iGSPj6xɀI0zS|U Oy +lW$-k/ןY$~.n ]5`(׍T>)M(l[OfO_[k,X=g&<ߕ~_]Ghy?bp\ 䫶w7}>69v/y @4h+諑 9@}eM=Ae"W<',ESc;ʐd% 5鑠(zBXO͛Vn~B&}1ʅț4n?*Z0~4OxqaTŭqgjC6Ŀ~"W^?Ce^wSi* FaeV1 +Rm]w/DaW6F ͅ ]"tt:.%dQF9sv"'=y  הxWߐkNZ HcSxldV@P/Z UՉB\9M nx7VMRQNFpPVse7BC d\%z5H-Bmit[J*GLiZo"kCUBD@c1/.Quz%Dga1ALTH +Ce` ;! wgSU牣jz0&w}lϘQ +b McNΦA1)TaJpb*4zݝ`Hާʃ_x3;S'"ƣBΥc3:X!n KIQ58y!b!f2 Nc[w4MVT[Oj|Rյ)Ҷ +v2 =}eThiֆx>$vx9MqU*\y6V6N@QC"4 qi.pp˯]> aN ~sSG +t/@I˂k 8`xX +5L+"m]: +@NxIKA{NH0zX_lqMŗ7M^XKN7^ AΎ¶U]bD@F4V?$+L?'wG173:G2uβ Ugj"1?( p~ 6M'#,"MVM} $,BUan00ϙLvhhiN;\2qē>yy+3Ƿ÷@ *{Kaʏe8dՋŅ.' ExtBD Пn`ߞlYd[_L*Siij"MGL_ :##;d2#j5ԯٮ{iȵ;$*v3iĻMbm$EYWF(P& :jJ5 O{s~<))A3Ža$LT~z3>IRRj5õJ IV̟J R R0Jd6F ^9\>f @(-(5`ՂԇƣR1*5ɓR̷hJ')5:zq\$tFTcED!ў}-i5o$(D>z"$_04ccyQ;Ga6uv_O[{8x^1 HpY| r#"OoyPd+;#%:x-֒ &[JvI n_Ü!D>Fɠ=V Mvx 2ZZ +#ܴ+ 纕f +_+,zC^ TYfpG0(ag0I[om6:8CDdC13rgy7|e71Az#ܚE\A~n;r-0ޞ9p\j.ѕWw=)먠SOG5+.p6?8OJ? +k6 w+zh ]텖[^JoX1B-.Js,q5B`?d!o5 XT'>"pgs~vX~ U +K5Óg?ka89)?I Ue9˂=/_&M72n_򿚶7BQ~ɡm}4i"T-z5y<4P|n4h4 `:JJО]6NParvML$C횝:2M'Xg'C/ف0hQ COҾg!&(ݚT~/GUDkHV,՛4R"Ia}7fe/̨TU8&jzzcf+,w2]fB$jPy$O9+7lu3PVU^?lIj;DqAdGyxxS@f`8 +:.kGQ/"65=õ=g !ˢ,|CѽK;7 FO0P3Gch~id5a8m Hd y +{({ɳ=PSW8-'a sד9--9{dh,˓[@=-;IEiaKh2vkZMk5$55vM\m(V0IaK&Z5tkm\p^8k˵1 jbfOזҵ^0[ ]#XI>.L*gfZwx5݇ +SN]CcH#I')s6[dE֫)q +h)(RrH r,".ߙ{/5^O~t6C686m7AKQ=jEVg_ M 3<7/8('cThv4E&@ho3AW7 U/#{1kBhYXE*0W@Y(Ƈi\&hrp'^)=i|$4F["R4%8B{QLRv,*q& 97C hjCԀPנ/XɆ!52u("CL0$VQd0$]14A0 + >gzrE $t@.&Kwԃ$*2Iiw(a"x&>5#:E[bVx@#5Xԧlpq.tɭ*dMVY:,HMIY d:,T0jG7)R=,ө/VFT]ҾUה_qd\BDX1Ԇ@=mـ*IZ1:, +p +pM#b&-8BF;f;7<=ia ƞZTZ]Nϑ Y]0K݈f.{0Q! ?0xБlXbC+S'n3*PKgdIKJٻic)` %$tMtWȹ"wܯ<(A;T^81V9B+x\ŵk(>stream +HWiz?"IĔ<<[$[ї/YD9}vS5[򉔁ҕQeFV44| P0 R)KHWFzj60MҜV|ʎ-iΫ~w$*Wɻ !$3%xnq(V-^hzz!~H%n<RDUzȤU$kȅNLŲη/uQlM̏x:Iez;Z[F=륱}xI]}$4xvބR^aJ,'=zK`(=l)WСGZۿa>Λw;9D׋vKD&]϶*7)?Nj_}w'5Ȳi~RxR[j^9bh\FYkmY`hYOD?ЍƎ}斍J6*,TLW!Gͭ~4#= \;hyCHiA{W~& z$t)B<BlB1cLd6d]:T_5;`JuUg4]z&d(.$m!OHHfFx:"CZQzs9nO\v(@,_Ǫyk_ts{KO1}BZ5@=@]׭ߥ[ k WN`kGH!XEȀ{KBS *jLxуEr? +b"&w _њaq\&8Y1'*V`֚aqo0dA͒N\v +vdd^UJj:t TA> gCcxCv>^DLEB!GWp@ [4'b(}Y׶KZY"Z]`siB*帲xʹ>/Z6?vG@!͙㼄}aߺBm4E9T:mcA6 zueKW4 @?םxR\j`)B)>'/D=STO60p+lAzA^TwYr,]7*ө J iSАr SoY^# hSP@3C|Z"N|kAw:Soml Tx dIЌFs[k Eav +X60[\ +IɅmA 7ezVx˲;&M km^5n!f SSu:J z]34usr#D*,g&U&rb6);i5k.Ę+{MT.:3/U⢃RLM=4V3% + $҅7œXUZMCyUbI1݌Kzj4BJh}C/fq'} K^RK,aH#:Ct&6nUn'Wׁc ̓IhacnڨG"˴rSKh +q4,y))monڠ gBl;:c*,m.Ȅ䥾d2o*inbmG]k:a)$7qZKNSa[U<v[x{(YM9zgmD{\_ $#|m՜d.wiaYg[4yv◜hNEӢ,;|ݒ-bI};: NFq+ON,1V`!B&,]ѶZBӒ3j F +$Mʢ c-~@;@B󥄱$؋>aT&>)s:aV^BڗpnlCsxCӛDƆGY+ ] @)#b. + +}w wP;=lbRrp/ĊĖձu3&.(̎eH)_ݓpKj6<< ꛽GY,#cdf(|}i+iW'!&FX\`flE~zO}*]d]~Ny߸uP+/1 5cg `l'=;? q__"x&j s+FP׌ O!i i]?@X Z e˭x U&4]y95hr^mr̮ V2Jtg!{DC\d΅sM6KS v H4 [·eR*I&$=}rwr!ߟJ,A!j]|d^\g  K]S I;)1PV]@ a@p+YR t8'~ ̥L:S)qMwDoQ ALCv`խݫqU$HOHP̿lXp(䜲˪Z ]ͮ[~}x*Z`K`| +wTfPGZ178[yq `,T>54iq~[lŐ3Rm_TJ0;M xyq23"_o}sL9 0y9aG5-xߴis$-f姥P!^­ &kbKs'~VwM0>el +궡!iQpGKzB>Zcп'l䓜zlbC؞RBр +02ǚۀ)DuxYW$ȋU7뚛uIT=s}N~ߢZ <*-=:͋~ӺBt3#9|cuknʹj~i]zӘDdzp1u[uRos¸j?9AJ<ݿeG,9#3dzB]yFÇQ1,I)wxdf?!x|;-x{kgyB)%QCSb9RW?2nǀZz6~hy^oùX:9~\ +LidEܝq ^q ۩6Ӥ}<9w1yz.|;wd Ou*EzS^{b#He9zdXrstB3nceea NL@KXԕ$~#?@,b$vŽb6yoy1/jUuWVV&Δlvy +} ^Լ10)q9INvwu9&\IԤxL]HIR** +{ +Wbꩡ&f~56G?7R2GgP{>h1&E|y\^ߟvۂO7x/:>>y;߭AzM|CcsZ#P0z_$opUӒ:o/zʦsǁ!uuUg'*f \;Ec;yl Kn]38c +n -4S AԠP=0:OOlm꽓d{"ĉ\R €fW%d M2'wh +~&KfcV:ЩH!hGGy>qΘ:]P(,nbeAxxo%~w٣3@/3L*L@4eWG/IEy^.bQoL =ȤxfxI/rB16dM]׊xϜ0qZs~ÄaoX U8TI/C1x5gb?FС8Ut6ClD\əɦ@ZN 7_ч@@ە5F+j2a8ݱPu#J`|#3̳8#K )[BXpP YxB3YKE79U6/kG H|߾h C(/rﶗpϋX cw&k;%i?oKJi?g PŘ5jՆ $1i 滏53 +ph3H ΁ˀłF jxɄ$_Ƕ=MŌ,\ng[0”o}p`>scPG]/,Zz85M0'± +0!F1JcUړp`R| + (9;wvtTu\{ATB*Jɛ~eUyv +˜xf L92wX15Z9-PfC@N%t2ʹkh|},^eM&q_8szZ6wF +2GQOL6GE;|Xn(ٞH$rKh U;Z_&% c2utГ:EWnmJ]BFurHJΗЅɞX(ĦA&MŚ(jCrXtzGݚn2Q3Kߠ]yg~el3_c/+_JdrpZinr++!%@[+{m^JwYQYp#?J1낙\_:o9A3}Eh Uߏ&t\6X&!N+W4x<@3MK XHdRZs!4srVUH!91Pt$Vwԝόgx<z-" >|f]B#2&Z!lzGqN$)U[KӆVP?yۮH&L671:y! W>;ߴ^s-;=n>Q2ݖ]BP}Qn64peJ 1*I ,&97偂B0_ZkKKIF*y݋.MUB01!n_ǦX⢻S<>ls-L&? V(zGFtQǺ;Ҕ뒇7#UZ7eiv>>,JU\B* 0 $'S6&N'do^],5xFd LV\y{>I?= $_|_;jh5U_E?K(51 $"U>ݷcC}]',XMCJşT +Ť fh \B4# {+O5yZY'Mp:k yJPФWk9} |{vYevOԠ VlyG=ُ(Y8.؝U1V=̂K9*Z$|Lp];_Pت_)Ʉ4xN l}[XAds'<~P^#uhxN:3io$gjoMNh  Mlk-{c-:uzDN(:Wj\1K@66k3-_4ib(V&mv%EF\g&#1-sPQgLaK?r: ŭF] Ḁ)_Qiׁ:*lJ;ꜻ׌^/oB$9NPK;D^ΚtM!,Kx JdsdpÄ"( y͛lsrIDYx0*xjt_96YANV+ ۷=u=㍚n<=5N5B2ѽJzB8 # *wj=Ƨa hm*UKoXڳ8o;`(y˟vɕBKO +(z+(']lg)ު\|Z\ubn0∫5a.x2s cǟ?KSE)x401]P^ ҸQ"_yCw=U|7`JZ)qvd +*NA{'SMץpY(RJP;U8a'dc wb'':ȁ ,C6)&EmaX+r1A“L S%{i-MVh\ݩU'Y rRpKS_[^g0@>Ԝ%{vDs2` 6߇鞭dɼҶ" C@ՋQß.GmOpi#\;9juj_ۓ6吴}5*>쫛e*% q\4EGwnN~/'cǿ ӪY='ҊU[9IZyL??2-  @Dd7Q!)ֽ̓ #=_<K[{QZ+޻7x7,D439FC~X#ƹ]ks=WSh;׻<>kG'kTU {t9幝GЏ僩ơլԬy\]:=碿PR10,.ɺFR7Nط㹯:;Pk1AS{i 8TTha-m;p"e՜8ߧj0Q!2E11 €jw^o% x=S[TG' wV$K(O'E恸{VoFz1)a~7PV)8?2X +qc:GttTFɟ=g5!maIKiLT<6U1\Ru :F4G6玳̸Q^s%mlNxn-OH՚T{D +8ENQf%0e*14\LMҖpl i !{2H4O.;E'M}R^ԕp?t找;Q8>C jDaE]'A-Anj ԓa͆XBo><%N+":d`hLNj|})3Jc7;O;C+u< @>* 0\cڑLAPi 2z|^KA_cR%g1? _M CFvhw'TuL/R,0D,1jf{ݓResf{<@yAZO,HcR6|yT5 +.<Iġ[Ym.lbOP^]fӍjxpu{$q(wҙW*k#z9 ~:b0;Al2ԭDdHHzω}Ҋ:혏ܳ9j49|TDU2;\:ZU[t缹v+kLj]\K(tsQN<(6ڬqǦi0`_*߇R.>?>rg8O E9Sr[CǸZ]Q@n%T\ +R,mPӠVlj?V&)PzR)MeM,+VL2ۛrz[v߄vl9l .g.zҫMZ9GWv?̽_AMS[Bwoi]5k&-3uCcɀAs?{Yxrٞ_+:{m7z|T}@Kn;)Ali{[\J8:k=4ι+PB@]ż9E;@?૰64Ӿb:.zZKJ#` + UYX +;w>hnq9 @m3K kBq>}x}]) Y@Bګ_½vgڌT mq_A_X6yJ)GzAIU'2iiW6fq +wpߝBw\p湮/|>L3'W"cpA>p/Z"6RB^cab}p ]u$Cvby̠TyR8\/Q^܇sCo `5GenJ٣:Phϫ=rL/ ˌ,7}>r& Fћ'>//TBCHcQv 軔e Yi%8o?k۷_o?}e2kxנweT ] ~鷊57?<;nҖd,^2!cS=d, RmCƛ2^D(|Ne RO)cBnH퇌A4tX:)[_]Y2ѝqJU ;{=nxC "~׉TO~7_D\**ģƒhxPJc 'El$+".㿱]"&,--btڣn/yZo">[r ;D|A1C] z>&V&KI)aj#ziT9STI.E駞"F|8,71`%42>, /1ζ+͸VxB1V ǖ IU?{~%i\`2TlἈ7o8f^ChEO*;W! rF"3dL%%8HH.Gfh9 NMRo.}Dc$o2KrqqTtu1ǭ(ufh:I ض.2e!mxfrZgMW > +c:Wݏ|\fȺZ 3:oŇM/G<G돺ص"z]&uv +pk'#)l0B 0]a)vƠ YlHZe9dC 7Wrl ]Ph;P 0IofJ ɣlZ$4 ؿvI!??f3h=!Z}aM0d$V]NˎCd &2Gd_8\I\'хP9e.c0ivO GF~DKȶWP2pjAM8 l3QaJI)݄9`)Ȝ/e?C k__du,R ;;Lʷ#V쬿@u+['J^ ,t㌘NX3J0|OͷWڑsHV C. 0E5qU4v =0@5Dr'Q C~ܑKd>a: +\X{]x3%T,ÙOXGK]^HpyLp:')t_vL,/0(YL:0xq@"IR ?~{ +BT2˄oc 󗐷٨eC+7iq`Ѯ s9xeerP6k[BlL(Tf~hD +s401ox8/Jv=MPP +$QBcYcy2XD{5sjh(I!t8t(G,L"*n {~7mڜ, +}e$S+=4J#RBo-BV6Ӏ8AacbXD2^0G/1Y+m? +>a )k3}oXl 8;~$Ji|C@DsZ[@({3PFz7w +)A`nC ATb1HERH3T0fOC30W`P >4$^s;̾LHQ1$҂ ]#!l=pro'ϧhL+e +; 6<`b`9},30rp2 n0`"%WK_{WHO sŚJc=A"uA@核 ]T^P~mAqE<. n`cWDY!t_%m$lC֜5'nXp'K 1|sgi6~I\Dp& 3V m#DT4 +uJƬ'rzp XiCL.1F3O9ȣ n P0ҵ~hΝ ~bEC ( 'cͻHdpBW +hCl3г4(-A10t$1aY'h/lSS?~?odw eh T3I^;na峷  <; j ,ԏf'S `( \޶2j["ɿ{l] ޴Nd1q Vdz:Ǐڂe2Ax #gvVMvZ3$j o |m[/oGGu/ڍFxI D2˟5kʍ3~63\{F i)L.#H=Lrgmp* mS">7e;׿K bwwΦـJrKhH,n!p,p-@ܚ%Jp 9@e+h7q Y-(o!X +2!78G,e ܚ}s2gލvtQ7H $a0^?Yy,q;^粂I{̍E5mZ&5q@Q$J-k+7Dd->~@Tm:R5_,( N{Ჭz1f`Oe/H!@UŎd2AB8H3dq_;bNؗ_j !b>R#j *~uI@\ܢ +Ew0$iV5</<_]?_`Q@?,- 4kXQI5rY{^Ec#P6ziq,ƹ#N(LOp*K6:D$yo~vDɎ'_/3t]VWYHVgKEז6j׾>9 ՞jlDbg j8v?5#pxkeXNɴ ==-gMd>$2r\̬}!TPC0[9NN4Nf?(FC9#[ꖐdE}Jx%̥FGn< csLCWkKWq8i̬ޕg o{*eՙ]*KϛQ7aL=..+R: J˖AjB{~Ubᴏ ^<jq)dž]'R.Fh ]!1wDŽ,z(,^vbDtAv3uCyk=Iy}4l| ȅAm͎jWz>jN=:Zi`Ӝ!UIya>躙gzךPlD 7QLT%Y Bm&?6kKMDd*i %5A+ۗb&@=< Y1AS1]H؛%>Y6w,M2;(975M %LJy]Wcz~F#TyC'a")xKY'jlvO{NaJmA\DVg(VƵG>_6̘ JeSD9"_e1`|:7,^ ԊgV= + J8H "xPYĝ=/7VC~la_N4>f;OwpsAF𥐪$A&ou??5C(L(DMuOҊ \fSt}:W'Ev|EmmDAc|)lAe=jp#}7۠_d]g*/S+<8^K/K̜/X: +V12O*g맍} _bO +w,FJ8xٮ/!ub~w?&w\ʼn&cC!}MA܄ 'eifn˰mBeYeBܧ0'邔?Wl v sBSHo7%k3xКh. DD9 S^qڹXq?gJ‚NtP}}6jf+5 +`z?݃hɩT$m"_a&ю;с vUS-\lfZw*=S&6T{TqlpL.VRS j \AփTL} J(FlZ6 _=K$,^"Z=U ayXa&y/%8XsEGCpqEr L+?m`ʮXr=CR Zz,M(~09%Q%1aݚ{V f396:d: CHdŇFI@@eD%ZBcKCl˵ƏEZ;(x\ 3ٻݫ-! 'Lj`S_9Ykꐕآ %M6'LG *%YQOVLV*qr'~]qԘ{}|W:ތJ%$fC#dƁH{fK,aRb=Z&^Q\-DL^ۯ5TS'NM|trw&T +I-&^6W#E[pE93CvX˘̄BUgPGFfΘpHN-4k7cNWݕb~v5iΕAh ZUFxY^=afݖ'}JKJB3$kiYcD E-fU{/ȄM S  +@w %3>;GgQU"$ *IgՄ@N竬in"!Cد`$6qW:e*-`, nVdUYp rdO ~B@OVO~c{~rp o/?n0R6WJŴf|BY׷GgS\D@H5f#[uct.,* эFP+פmvpuuhT^L!C;C)R3jKcsRI)qLY&r1.WS +5cgZptߡUܵ,i)}!ECK n:@.nI"C'XN^xR6a MKKV&Wq2N"-!P3c'y٤grA҉^QB#zV=og %1Xk⨍ A@h'dz nŷ{X!~UdV-yAo=zIGNDZ:8H +TH.57} 30*JѡŜ,p >hSbL&rwTT^񃺸\~Q$Zxԕn_ Zlf%@n(t~RDn/B:= Fwɾ *Լܹ`3~FmۣC+ +ҊR#r3"pb "  +\qR@6ҫ/DC88F((C]Ѷ ,hpP&4nN2 v< S"bq}]L23zтiWc,AfNWnV +`,u+1Y5AFbݸָ$hB+iQs;v8v&VR;#A k*f(QK ABOkۘ#j| +,jɜa֭-Th<)ni%Y$!dʒaН}G]>p(fW/Ôн2aϡZ4 FbqvÝhcs{Ve[!O^o>럿/__^//>}__^W|K vT +endstream endobj 51 0 obj<> endobj 52 0 obj 14686 endobj 53 0 obj<>stream +HK$ E絊@@S?GxbG=}Yzti ]__k|_;1NW|EO(KHMAs!~@׭R';yja1#N~^-Plϰ<:ppdɁ/JSK-=r8Vyꛭ˽v.ȧZ)\j?/3,G;ݣغ܋mr/<~^mIDZ~pd{Du%sʌ-dwI>.8RshMh:DzB-`q=XlS_44(9d7wǶ#yTsl {T7R 3mXٹXp:6Ն[8ԷFФ3T%fk>\S[:08LK ZSlmّ TX532n]IJ3jBLL{, +[ +j:dLimSͫjb-k[hӁ&-[t#. ~^`VL4m$sm1"mOE&Q~OLK{o29'*JAf +Jԇ +xD큪F%4KM4 ЄȂ<AC\MҴKۜ0Id[-4Ձ[p7r73'6і͇-ʸǡP<؎ԇc[W[y=W-E:+TfԈKcEƏNF֒ /Oۢ-Fzi"քik~^϶8lk3/bVBmlvi[x Q.\[< +?Bu`krവWs ?gj<Щ׽ZF'a sS8KH2FX^Ϭ)ufUol:xGg6hT8[ + +'_!;ƅcjw:xH$Ëքu /[pmÎxsb~/`sTrqtyphK{qenY6Gs4aYuQ"R{Bd W(cS-/L>K"YGwgyTg:uMH9v+oF4r`x*[ջ]\l!PQ.[5K fՇ]"w,G'|.yHuC:;x8P.t18l]}=-Z#=B$ Ej<fE깯2Sˎƅ11# ߎ3۞)rnz+!b+Y@/m$AߥtX0! 4eiWCn3.-|,>88mne{!#NMi\<=sxk:xGW]ozS9=ϋHKu=AJ()B b m(O+1! J ^: ؘxLn<KSrlYm,:vVX6!ǖ=:Ѫ9RK"Y Zl,n〭O'!W"q@Oذr Sa}$Ɓ3r),ri0N+j.jkgs_}1%Oh=Npێ(֮'AtطWz)&چ2|%"z?o./J]|^Wɲ Tx41$P0fbr!.p}b:ŻuЮ6zmBE$ ®uu%s&:Tc"bc)3ٰyCj ETNil5\GHOȊ&}|q.ӄڡǍԚjc#$4+>?ǰh d]c v14O14_ֲ؈ZbntVܜOmRSdk6u-̡9W8NP'<:ڒ( 1[qZ,(heDj, T2EmWB˩+g'[<ZvdlZPwӽ\v)rѢ|Z:hюךsW)| "B.;E  cdG-| +!wcnU"Y+ N.+c>}awX~Gݵ8ldofLLdzhyNT{OR9j(;_V/ׇ֚"m[uf@j5/ϥkBl 뾁zۋk ҁg铝 /̡Y ϵ}v3 +V?w,Smj]ȗ6in*h\hJ-1|ej#>T@ZráwhfaEʅ3ؤlK;teh6+\ÖJ|t`{_/LĘV-te;܏bc?,чl|z˱;lKB1>B8x}Ga^c2:LR(1X/!?x'1hIcXR2^VhKr7=泇Z`C +"xuŃ*\BOaD0dT+QUt zG*ߌ犔 YӅmqX~![ ʐ2|ZBdX}޵>I<{Q-~ ҁ1gq%jAR6{):7BEVJO[aV۫#i^[~ܫZyKCeAJ]Twibື! ۜYBiaSʛW +g=Z* McO:{6wHm92+B:l?-Ѧ,35FUoK4 +!TNrH)h.^!S};@Dw={];=^RNͻp'dgB䏤Ƈ PAU׍%ZE2F,`1O6J*@NSh!L5{iRZ:zQJ(H'r*돟gGCa͐sl,ĺYy%jƃ =z@Bw Fc=I{N.4e@=٠0alS2_(XlxC!ZrZ]aS(ZR_XsQ!a:~y0pd)T*:}MLV|Um%E>o5HjZ ǞexoA|h"MU&z@:@)AY&H)jmP + ~dlRwUGxĜrwlA*Vsi@VY.(umpcv-4kgcDw@;:&k+OƯ[^Ŗ +-&uLj݃Ŷlbҁg[չ%he}$iGm-F Yks` W\c"P(U8I$3mϴ ܩjb._-qВqխ=S4.,xQ(MeΞ` q_2iE-U-2^ ydk.'[[EZsd9 1]E~ &Z+r࠽2݄kIz;-nt+o9)ǭ{;&l5[F"S+ 1)`a- t4JՄ¿;F!_8& T0Qo{8q oI,-(>(1m +ڌfn-e|'|NG}L[I%޵pup pk7ebwϑ+Z Nm%*`'gh^%0/rx;>֎Ein +}bBC뉬mb5Bv +):_bW"#%&i-|Gb@=aUJOBj[Zާ*t]a4-BM;}LKrKw Ֆ_[(|mO-CS#}J2 Hv-'&lL15p[k-笽vd\Pr{O/ wu\ lK0Wpm +*bdw0fjYM?K(D-@furk@S90]֍XN.,^sn R?8jJ+L\J{VDAr ^|k?|kmnߙ>(."=);v|Kvt oRR}KlP:)xo +9Sk|rDt+`OI)=+]I?E.?`(Ե'uk}ޥp=/dϟ _.Fu{;TkuG^ ~mZkdX~P^[2غ+SRp k-sjޖ'~q|c=&⁉5wrYj/P3l- '5I^6੩˜p\mk^On-G󕇶d(Iy`#DžJq`-ҭ}dkl-IM_׌.PM[ Tyz,0 0qrp{agǭAd\gu5>㿢 />L#E3c'cJ{9A pbԡbGeCj +"t@'t_-Kͫ{qo-[Wq&HF68 d̪ 6di7 +਍}R2z'ZB)p Si~]`L?e$9 B}OЀ폄xI<\6ޭG1_xS ?In|:,fE K؛ +7-faz¼d(St͉'Y)Q >r~ +Ud;$c?S=,0~!T#f9=?7æxeY-x`W7)SȾ`2W(M՟'FPLNZy;v ):gg؞fB[J-_.Ϳ'hH%5l63~3}]Xoxr%}z*tQhWNr*G(SVIgɢNѫAc|+q;\R sv~+]% لuɯZS Lk2^@VʪnQ {UqeRdnjW#?rkgBѯQע4\ٔר,E6 +Y*KW +?}Jp ^F*l+X=C#l1Udel~5K@u0!o嚬'*a)#˾R♞W>e,. KIRPiDBQ 26vprMt$;n +lR G +N9M|_Y{͉lQVAOɖBkm^KD䒛5@2`S\R,xU! P)!F\"t( +XCCL$gl1Fj O|H!o d'۪'>%(9¡@I Y7xfZU8QtEP /.fn +, ,oR^!; +'XV,9#?J,p}KC֡f l,ym>!i!K0nUl]av V6'd{q*`U5wq[qo zI,{񗍦_XQJR0E,WFVW +N|peV! G؞{_e>efr9 + +φWXort<~ r/L X !1Z/%Ŗ_{C~SmMSjO_=49SJU6ס VEҠ6VoA\d3[`x +RC&D: Y12=!9;E b.2:+wYLE9Y;0E328+޵u\YMP GFN/ #u<,Z%q1E pO*8eEiI+K]2'cm֤Y  +᝕މ049bXV(=r:ق k'I3s ~Sc”zK  [a 0"/H֝-# Ug &xdˎ@t{)@[OAc 5![Ѳs{|C[H7(){y+gCmpVBD+gq{^o]BZPwp,O5SaWlHKFuQD_m[ nxufrBx[$-$fˌ/xs"5P^YL`P-p%U?8 5OO܈a +0!<,J-\k'xoÇh-Osx[^$ +YZ7'7WFqX4 US䐎F[(oڵB?x++ T[E~[C0â,I87T0@;[?zX+B#;OS}-.%&TQE uX^ۂt` + +l>Ϊ9-'l[=A>\Hҁi0XPBhʍP*kBt\`b>N9 d!;x1.P<ކ?Xn -_Hwx_ڗvIJIEtR[p^Kl&Yl=|Z":]˂=$/KZZ)Gddq@CWq՚(Z-s%c)^&tk`KX6#}/[- 4} +-;_z;\hR+ɆV(TWRl9m+TK@ñwI%ܱa(gYB|9S*Rg((v֍YvGFR[ ը-sJ9ClD |(b`«lQ|(^ňY,ɐQam^(00gosG( +N P0eYF +ӷ|(CDeJG( +бKnpGm}\{k.yå3?z,Ajg9qF|.9n]- sbѤplj-P0e2$Q:]yHE{lYb9pu`.C}Jcbut {oNkg]j+RS&xV1T + 6IzA0(VT1kZL`ql^[ EF&MfZ(zSK~e+e0 / of;"!j Ll8Р +Ja#h ŧISm +@ůL稼Kp{$CtXeurѓ^ [ILc/(G{qr̻U *ψxĆ[ +Sm4`81yӻ @wfK|4qJl`IۏOIsI"KWZl?m9iya +endstream endobj 54 0 obj[/Indexed/DeviceRGB 255 56 0 R] endobj 55 0 obj 428 endobj 56 0 obj<>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> +endstream endobj 57 0 obj 202 endobj 58 0 obj<>stream +8;Z]!9+HIp$q*n0WZ6b_pGsW,$Ac<[pj''B;)SLo[A+'/!$=X-/SCb*&k`$.SL8Fn +8g6$+F.q_a?.fU>`hfo`0h3SYFO,+s=*K&))P)`5YF,[t^l,^l8*4mWDB:B%'(AQf +RAtD4Wi.goS7+f7ae4)Ag>(l`@&Gg3\7R!CbB$GS/fiQVqqV&j@7;@FzJ1%W3!5I$KcN~> +endstream endobj 59 0 obj<>stream + + + + + +JPEG256240/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgA8AEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A6J+Zc0qfnH5VRZGVD9Q5 KCQDW+krUZt9IP8AB5/H7nDzH94Ph96fLM/5heZ6QyH/AAdoUtHKkgXt2BWm3WNAfpH+ttRXgQ3/ ALyX2BsvxJf0R9qQ+cdY1Pzb+ZEHkm3uZLXRoZON76JKtLwj9WXkfYDioO1d8vwY44sPiEXLo15J Gc+HorfmV5NsvKOlW/mPykZNJurOVIrgRSOVkjfYFg7NUhqAjuDvg0moOWRhk9QKc2PgHFHZZ+Yf maTXvyh0vWFPpT3F1Es6oSKSRiVJKeALLUe2Olw8GoMfJcs+LGCgNX8n6Ja/lVZeZraaey1lLe3m 9dbiWkskjKrDizEAkMSOFPuyyGeRzmB3jZ6MZYwMfF1T7TfMWvXv5I3upXc8n19IZoo7ypEjor8A /L+ahK8vbxyieKI1IiOTZGZOK+rHvyu8reTtZ8uSXmvXTC9+svGqtdtDSNVUj4Qy92O+X6zNkhOo jau5rwQjKNl6nrt5aeVvI91PZkiHT7QrZlmL/ERwhqxqTV2XfNbjicmUA9S5Mjwx9zxjSl1TyV55 8vXt5dSsmtQQT6g0jE1F0SkiyE/a9Njy3zbz4c2KQA+m6+DhxuExfV9EZoHYOxV2KuxV2KuxV2Ku xV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV4l+bFrBd/m55atZ15wXCWMUqVIqj3sisKjfoc3O iJGnkR5/c4WcXkHw+9AaBeXf5YfmDNpN+7Nod8VBlbYGJifSn+abq/0+2TyxGpxcQ+ofimMT4U6P IorUFHlT87YdWv8A4NK1KRpIrs7R8biIox5dPgkb4vbfvkYfvdNwj6h+hkfRls8iyj89Nds4PKA0 tZVe71OWL04VILGKNvUL0H7PJQMxuzcZOTi6Bs1MvTXexjzfodzov5I6RZXalLo3aTTRtsVaYSyB SOxUMAffMnBkE9SSOVfqa8keHEAk3nTy/f2/lHyt5kgdp7H6rbpc2kpaSFJlQcH9MkqFkVeLbdv8 rLdPlByTgedlhkieGMno+ua7puufk5fX+nRpBA1pwa1jAAhdGUNHQUpxPT2oc1+PHKGoAPe5MpCW MkdzHvya0DydqPlSWbVrOzubxLuReU4QuE4IVHxb03OZGvy5I5PSTVNWnhEx3Cf/AJqD9I2eg+U9 MdEOsXarVfiRbe1FW+FT0UkH6Mo0XpMskv4R9pbM+9RHVin5xeWPNiaLa6tq2o298ljL6KC3t/QZ Fm/aJDNUckUfTmToM2PiMYgi/Nq1EJVZL1jyfrI1nyvpmp8uT3NuhmP/ABao4yf8OpzV58fBMx7i 5WOVxBTjKmbsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqV3/AJX0DUNUt9Vv LKOfULTh9WuWryT03MiUoezGuWxzTjExB2LEwBNlvWvLOga2sa6tYQ3nokmIyLVlr1owoQD4YMea cPpNLKAPMN3nlvQr3S49LvLKK4sIVVIYJByCBBxXiT8S0G1QcY5ZCXEDupgCKKX6V+XnkvSrtbyx 0mGO5SnpytykKkdCvqF+J9xlk9VkkKJ2YxxRG4Caaxomk6zaC01S2S7tg4kEUlaclBAO1PE5VjyS gbiaLOUQea79C6V+ihpJtY200RiEWjKGj4DotGr0x8SXFxXuvCKrohtO8q+XdNsbiwsrCKGyu6/W bcAlHqvE8lYkbjbJTzTkQSdwgQAFAJZ/yrDyB/1ZLf7m/rln5zL/ADix8GHcmVj5U8u2MtnLaWEc MmnpJHZMtf3SylmkC1O3Iua5XLNOV2ebIQA6I3UtMsNTspbG/gW5tJqCWF/stxYMK/IgHIQmYmxz SQCKK3StJ07SbJLHToFtrSMkpCleILHkaVJ6k1wzmZGzuVEQBQReQS7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq7FXYqwTzZ+Yl9onnrRvLcVpFLBqf1b1J3Zg6fWLhoTQDbYLXM3BpR PFKd8r+5onm4ZCPehPzO/NG/8o6lZWNnZRXDTxevM8xanHmVCqFpQ/Cdz92T0ejGUEkozZuA0nt1 55t38hS+a9NRZlSD1Vgc/ZcEK0bkd1O2UDTHxfDPezOT0cQQ2seZfOUWjWmr6VplpcWT2C3t400z IyMU9R1RR1AXJY8OMyMZE3dLKcqsBIPKX5kefPNMcs2maNZNDbyLHOzzuhHIV2B67Zfn0mLFtKRa 8eaU+QRPmP8AM7XLXz0fKejaZDdXI9NRJPIU5O8QmPSgACNkcWjicXiSNBMsxE+EBk3l2+88T3rp r2m2lnZiImOW3mMrmXktFIPbjyzHyxxAekklsiZXuGJaT+ZfnDXPMWp6NpGl2TSac0tWnlkTkkcv pjoDuajMqekxwgJSJ3ao5pSkQByTDyf+aE+qeZJvLGt6b+jNZjLhAj80dowWZem3wjkDUgjK8+jE YccTcWWPNcuEiilfnP8AOW68u+cn0ZbCOaxtTF9blLN6rCWNZCY6HiOKv3G+W6fQDJj4r3LDJqOG VUyvzb5zGk6Fp2raekd5DqFzbwxsxIUx3AJDinsMxcGn4pGJ2oH7G6eSgCEf5w8yQ+W/Lt5rEqer 9XUCOGvHnI7BUWtD3O/tkMGI5JiKck+EWxX8vfzM1HX9dvtC1mxjsdRtVZ1EZYA+mwR0KsW+IVrs emZOq0ghESibBasWYyJBG6n528/eePKyyXlxpFm2lvdG2tJvWYyODzaMso6EolTh0+mx5dhI8VLk yyhvWyb+TPMPnTWhaXuoaZaW2j3cPrRzxTM0nxCqfAfHKtRixwsAkyDPHKR3I2Qf5b/mLfebL/U7 a5tIrZbAIUaNmJbkzLvy/wBXJ6vSjEAQbtjiy8ZPko3H5lanqvmeTy75PsYbya25G71C7dlt0CHi 1BH8RAYgcu56Cm+EaSMYceQ1fQc1OYmXDFuz/MrUtO80R+WvN9jDY3Nxx+qX9q7NbSczxTZxyALA ry8eoHXGWkEoceM2O481GYiXDJZ+Z/5oXnlG/srGys4rmW4jM8zzFgAnIqFXiRuSp3x0ejGUEkoz ZuA0nMvny3m8gTebNNjWX04PUNs7fZkUhXjcjf4T9/XKhpiMvhln4vo4gnNtrUZ8txa3dARxmzW9 nVdwq+l6rAV8MpOP18I76ZiW1vPfKv5yalqfmm00rVNMjsrTUxWwkVmLgOCYi1dmD047Ab5n5tAI wMomyObjw1BMqI5sk/Mzzxd+UNKtb22to7l7if0WSUsABwZqjj/q5j6PTjLIgmmzNk4BbItA1F9T 0LTtSkQRyXtrDcvGu4Uyxq5Ar2HLMfLDhkR3FsibAKOyDJ2KuxV2KuxV2KuxV2KvF/zN/wDJzeVP +3f/ANR0mbjR/wCLz+P3OFm/vI/D70984aLY63+aFjpd6vK2utGnRqdVPNyrr7q1CMowZDDCZDpJ tyREp0e55l+ktT8n23mXyVqgLQXcZELAbCUUKSr/AJMqDf6PA5seCOUxyR6OLxGFxL3D/wApf/25 P+xTNN/lv879LnfwfBhf/OO3/HI1j/mIj/4gczO1fqj7mjScikHmSLU5fz6lj0uaO3v2kh9CaVea KfqKV5L32rl+IxGl9XL9rXK/G2/Gz2Dy1aebbf6z/iG/tr3lw+q/VojFwpy58q9a/DTNTmljNcAI cyAl1eM+TNYv9G83+cdTsrH9IS2qXMj23qGM+mLtebAhXrxG9PDNvqMYnjhEmrr7nDxyIlIjdPfy js7fzN5p1Hzre3S/pWGVgNPjXiI/Wj4K5JJqvDki/LfKddI44DGB6e9ngHFIyPNX1XyjbeafO3nr TpKLcrFp8tlOf2Jlthx/2LfZb2yMM5xY8Z99/NJx8UpD3MDsvMl/DosPk3U0dLiw1aCW2V+sYV2W WI/J2qPmczpYgZeJHrEtEZmuE970783tVs/0j5b0a6LNay3Yvr9I0aR/Rt9gOCBmPPk3btmt0MDU pDnVD4uVnluAwq/8x6bafnFZa/p/qpY3skS3Bmikhp6q+hL8LhSaD4/nmZHETpzA8x/a0GYGQEMy /wCcg/8AlDLL/toxf8mJ8xOy/wC8P9X9IbdX9PxZb+Xn/KDaF/zBQ/8AERmLqv72XvbsX0j3PLvy RNwLrzYbav1gQr6NOvOsvH8c2XaNVC3G03OSI/5xz9Eza+zEG4pbca9eJMvKn00rke1f4fj+hdJ1 W/8AORXpC70Ap/vTxueRFeXEGLh+PLD2Vyl3bfpXV9E88waRBrP5jaTpurR80utCkS5XuHLPVlJG zK24PjlGKZhhMo9Jtk43MA9zzeW81TyWvmTybqPJ7W9iIhYfZ9QEGKZR/LIgo30eGbERjm4cg5hx rMLiXpfnrVWsvyh061iJFxqlvZWUQFS1HjVn2G5qiEfTmt00L1BP80kuTllWP3sD/MjVdGW78u6j 5fFwkmkRR23KaCWAD6sQ9uQXVan7WZ2khKpCderz7+bRmI2I6Ms/Pa9hvvJeiX0BrDdTpNEf8mSB mX8DmL2bExySB6D9LbqjcQXofkn/AJQzQP8AtnWn/JhMwNR/eS/rH73Ix/SPcnWUs3Yq7FXYq7FX Yq7FXYqwHzd+Xmqa1590XzHb3MEdrpv1b1oZOfqN6Fw0zcaKV3VqCpzOwaqMMUoEbm/uaMmIymJd ydXfli8m8+2PmNZYxa2tk9q8J5eoWdmII240+LxymOYDEYdSbZmHqtJ/zQ/LT/F0NtcWMkVvq1t8 All5BHhO/BioZvhO67ePjl2j1fhEg/SWGbDx8ubJX0SUeUm0NJFMv1A2SzMCFLej6QYgVNK75jeJ +84vO23h9NMf/K3yJqXlCxvre+uIbhrqVJEMHKgCrTfkq5frNSMpBA5NeHEYDdK/Mn5aeZJ/P3+L dDv7WKb924iulchXSIQkUQNVSq+2W4tXAYvDkD8GE8J4+IFk2g2/5hpqCtrl1psthxbklokqy8qf DQvtTxzGyHFXpEr822Ine9JJ5I/LnUtB82azrF5cW89rqQmEcMfMsBLMJByDKB0FDl2o1YnjjEA2 GGPEYyJ70Jon5Wav5b87HWdCvYF0eZiJ7GXmrCCQ1aNaBgeHVCT23yeTWRyY+GQ9XexjgMZWOTKN I8s3dl5017XpJY2ttWS1WCJeXNTBEEblUU3I2ocxp5gccY/zbbYwqRPexzzz+VB13zLY67ps0VrM skZ1FJA1JFjYESLxB+PiONDQHbfMjTa3ggYnfua8mDikCGQweV7w+frnzLdSxvbrZpZ6dCvIugqH kZqgAfFypQ9DmOcw8IQHfZbBD18SV/ml+Xt15vtbD6lPFb3llI/xzcgpjkA5CqhjXki0y3R6oYib 5FhmxcYX+fvJWteafKOnaULmBNRt5YZ7qZy/psyQvG/Gilt3eoqMGm1EcWQyrZOXGZxAZF5X0qbS PLunaZO6yTWcCQyOleJKChIqAaZRmmJzMh1LZCNABin5Z/l3qnlS/wBUub25gnS/CCMQ86rxZmPL kq/zZk6vVRygADk1YcRiT5qEn5ca3oXmibzD5OubaMXYcXel3odYSHPJgjRhiPiFVFBTxptkhq4z hwZAduoR4JjK4twflxrWteaovMfnG6t5Ta8fqel2fMwrwPJQ7SAEgNuRTc96bYnVxhDgxg79Sowk y4pMhu/LF5N59sfMayxi1tbJ7V4Ty9Qs7MQRtxp8XjmPHMBiMOpNthh6rSv8z/y3Hm+0t5bSSO31 a1PGOaWoR4m3MblQx2O6mnj45bo9X4RN/SWGbDxjzRE/kq+uNT8qPNPE2n+XIf3sXxc5bhYlRHUU pRWQNuciNQBGdc5/cyOPceSM/MHym3mnyzNpcTpFc845baWSvBXRt68QTuhYZDS5/CnxdE5YcUaY vqv5Xa1qf5e6Z5dnvYBqOlzF45xzMTx/GAp+EMCFcdu2ZMNZGOUzA2k1SwkwEb5IzStD/NvTdNtd Og1HR2gs4kghMkc5bhGoVQSAo2Ap0yE8mCUiSJb+5lGOQCrD0DMFvdirsVdirsVdirsVdiqXat5j 0DSDGuqahb2TS1MazSKjMB1IBNaZZDFKf0gljKYHMqGnecfK2pXa2en6rbXV04JSGKRWYhRU0A8A MM8E4iyCAgZInkVbTvMnl/UrmS1sNRt7m6ir6lvHIpkXiaGqV5bHrglinEWQaSJg8ir3Gr6XbX1v YT3UUd7d1+rWzMBJIF68V6nAISIJA2CTIXSl/iLQv0v+hvr0P6V/5YuY9X7HqfZ6/Y+L5YfClw8V envRxi6vd2r+YtC0f0v0rfQ2Xr8vR9ZwnLhTlSvhyGMMUp/SLWUwOZXatr2jaPHHJql7DZJKSsbT OEDECpArghjlP6RaykBzdea5o9lp6ajd3kUFhIFMdy7ARsJBVKMf5h0xjjkTQG6TIAWgrXzz5Nup RDBrdk8rEBU9eMEk9gCRU/LJnT5BzifkxGSJ6pnqGo2OnWcl5fzpbWkVPUnlIVF5MFFSfFiBlcYG RoblkSBuUE3mry2mlpqralbjTZH9NLsyD0y+/wAIbpX4Tk/BnxcNG0cYq72Qf/Kw/I3/AFfbL/kc v9cn+Vy/zSx8WPeEZP5q8t2+nQalNqVvHYXJK2900gEbkVqFbv8AZOQGGZNAGwyMxV2mUE8M8Mc8 LiSGVQ8cimoZWFQQfAjKyKNFkk8/njydb3Jtptaso51PFkM8fwt0o29B9OWjT5CLESwOSPenMUsU sayxOskTgMjqQysp3BBGxGVEUzS2x80+Xb7UptMs9Rgnv4OXq26OCw4mjfPietOmWSwziOIjZiJg mgUXLqenRX8OnyXEaX1wrPBbFgHdU3YqvcCmQECRdbJsXSJyKVC9v7Gwtmub24jtbdPtzTOqIPmz EDJRiZGgLQSBzQOmea/LOqz/AFfTtUtbqelfRilRnIG5IWtTk54ZxFkEIjOJ5FX1XXNG0iJJdUvY bKOQ8Y2ndU5Eb0Wp3yMMcp/SLWUgOaBtfPPk+7uYrW21i0muJmCRRJKpZmY0AA8Tk5abIBZiUDJE 9UXfeY9BsL+DT73UILe9uaehbyOqu3I0Gx8SKDxyMcUpCwNkmYBolEajqenabb/Wb+4jtYCwT1ZW CrybYCp7nIwgZGgLSSBzQGo+cfK2m3bWeoarbWt0gBeGWRVYBhUVB8QcnDBOQsAkMTkiOZU7Xzz5 Pu7mK1ttYtJriZgkUSSqWZmNAAPE4ZabIBZiVGSJ6qmoecvKmnXTWl9q1rb3UdPUheVQ613HIV2+ nGOnySFgGlOSI5lW0vzJoGrLO2mX8F4tsAbgwuGCBq8eVOleJyM8Uo/UKtMZg8igP+Vh+Rv+r7Zf 8jl/rln5XL/NLHxY94Tmx1HT9Qg+sWF1FdwE0EsEiyJUduSEjKZQMTRFMwQeSIyKXhn5ySafH+aG gvqVDpqQWjXoYFl9EXcpkqqgkjhXYZutAD4EuHnv9wcHUV4gvk9E8qeYPy11bU2h8uJatqEMTTEx WbQOsdQjEO0UfdwKVzAz4s0Y+u697kwnAnZgf5teXL3y15itfPGhj0w0yteBQeK3H8zAfsTD4W9/ 9bM3Q5RkgcUvx/Y4+eBieMMm/LG2k8wXt5591Jle8vWa20+3VuQtbeM8eHsx+XTf9o5j6w8AGIch z8y24RxessP1nWdN0X8/5tT1Ob6vZW/H1ZeLPTnpwRfhQMxqzAbDMvHjM9Lwx5/8eaZSEc1n8bIL 86vOXlvzJ+hv0LefW/qn1n6x+7lj4+p6XD+9VK14Hpk+z9PPHxcQq6/Sx1OSMqpk3/ORP/HI0f8A 5iJP+IDMbsr6pe5t1fIK35l/+Sa0v/jHYf8AJoYNJ/jB+Kc3938mB67rXku7/LnRtLtUSTzLCyCW RIjGyCrchJKyqGB5DoTvmbjx5BmlI/Q0TlEwA/ieiecLTUbT8imttRcPeRW1mspBDU/0mLivIEg8 VoKg75gYJA6mxys/cXIyAjFujPyesLG9/LexhvbeK6hE07COZFkXkJWoaMCK75DXyIzGjSdOAYBg /lLTNNl/OvVLKW0hks0lvAls0aNEApPGiEcRTttmbnmRpgb32acYHilkH5+21ta+VNKgtokggS8o kUahEWsbnZVoBmP2YSchJ7meqHpHvd+Y/mO+0n8rtCtbJ2ik1K3t4ZZVNCIltwzqD1BbYfKuOkxC WeRPQn705pkQFdWS+S/Ivl5PIVnYXFnFONQtUmvZWQF3eZOdeVK/Byoh7UzH1Gpn4pIPI7NmPGOC mI/kdq9/a65rHlOeUzW1mJJbepqEaKYRSBfAOXBp/XMvtGAMY5BzLTppEExefRafrOh21j5205yV S+lic7/BJGxor0/YlQkff4jM4yjMnEe5oox9Y73qlvr9l5g/MTyfq1mf3Vzp90WTujhXDo3urbZr DiMMU4noQ5QkJTiR3PUM1rkvEvNLyecfzitfLd1ITo9g/FoAfhb04vWmJoerkenXwzc4f3WnMx9R cKfrycPQPT9W8keXNRso7YWqWUluVNnd2apDPAUbkpicL8O/bpmthqJxN3fv6uVLGCHl3/ORVfru hqSSBFP18eUe+bLsrlL4OLq+jN/LfmL8p9S1S3sdDjtG1AVa2CWTxMPTXlUO8SUIA8cw8uLPGJMr r3/tb4Txk0HnP5oeW77WfOnmO5tCXk0q1tbhoRuWi9NQ/H3X7X35n6PMIY4g/wARLjZ4GUjXRFah 52HmX8q4orl+Wq6fe2kN3U/E68jwm/2QFD7g5GOn8PPt9JBZHJxY/N6D+aWk6U/k3Wr57KBr1beo ujEhlFCoHx05bD3zA0c5eJEXtbfmiOElJfyP0nSpvJ0N7LZQSXiXUpS5eJGlXiRxo5HIU7b5d2jO QyVe1MNNEcLCLHUfK+n/AJva7c+ZREdPWa7VfWhadfVMgC/Aqydq70zNlCctPEQ57NIlEZDxPXfL +qeTdT0fUbnywsAgCtFcvBbm3q6oWAYMkZagbNVlhkjICf325cJRI9Lyr8kR5Z+p+Ym8wC0+qAWo BvRHx39flx9Tvt2zZ9o8dx4Lvfl8HE01UbTP8j7W4HmvXrrTklXyyyyJbluXBm9cGD7XVli5V8K7 9cr7RI8OIP1/s3Z6Yeo19L2nNO5jxH83rqPTvzT8t6ndBls7aO0lkkCk/DDdyPJTxIUjbNzoRxYJ RHPf7nC1BrIC9K0v8x/JWqX8NhYaok93OSsMQSUFiAWO7IB0Ga6ekyRFkbOTHLEmgWM+Yw/n3zX/ AIYt3ZfLujMJdbuIzT1LjcJAp/yd6+9fAZk4v3EOM/XLl+trn65cPQc2KeSdRv8A8uvPFx5a1hiN Jv5AIp22j5HaGcE7UYfC/h3+zmVqIDUYhOP1D8U04yccuE8ijf8A15H/AD/6teQ/5B/j+cn/AC34 7nf85Hf9M9/0ef8AMjHsn+L4fpXWdEZ/zkT/AMcjR/8AmIk/4gMh2V9Uvcy1fIK35l/+Sa0v/jHY f8mhg0n+MH4pzf3fyYlrnmbyld/lRpejIyXPmGIRJEixt6kJEhZ/jK03X4aA98yseHIM5lyg1TnE 4wOrIr+z1Wz/AOce5YNTV47hViKRSVDpE18hjVgdx8J2HYZjxlE6u4/jZnIEYd/xuyb8kv8AyXtl /wAZZ/8Ak62Y3aH96fg26b6Awnyb/wCT21b/AIzX36zmZqP8Vj8GnH/elPP+chv+Ua03/mN/5lPl PZf1n3M9X9I96r548o3vmL8sNEbT0Mt9p9tbTxwL9qRDAFdVHduhHypg02cY80r5En705cZlAUr+ VvzU8s2nkyAancC21PS4FtrjT3BWZ5IFCLwUjfnQfLvTI5tFM5PSLjLqmGeIjvzCX/kp5X1ZL7U/ NeqQtbPqIZLaJwVZllkEsklD0UsqhfHf6bO0M0aGOO9MdPA2ZHqmH5YaPY6z+XF5pl8nO1urq6jk HcVYUZa9GU7j3yGsyGGYSHMAMsMQYUWE/l35e1Py/wDm3Bo96GP1RbkxvvwaNoW4yr7OKfqzM1WW M8HEOtNGGBjkovf80TnvGPPVhqHlH8y7Xzslu8+kTupumjHL0yYvQlVvCq/Etdq/LNvppDLhOL+L 8Fw8oMJ8XRnF7+ZOiTWaJ5clXWdXugFsrGHkTyb9qbYemi1qxamYcdJIH1+mI5lvOUVtuXn/APzk LFdGTQJpY6VimWRlqUElYyVDED6MzuyyPUHH1fR6Jafmp5Bu5oYINWRprhlSKMxzAlnICrulOpzA loso3IcgZoHqluhb/m/5m/5grT/iCZZk/wAXh7yxj/eF5b+ZXkqbyx5oR7FWXRtUkV7dVrxVg4Zo T/qtuvt8jmy0mo8SG/1RcXNj4Zbci9n/ADP/AOUA1v8A5hz/AMSGajR/3sfe5ub6Ckv5Ff8AKBR/ 8xM36xl3aX978GvTfQwjRdc0zy9+c+u3esymzt3e6QSOrEVkdXTZQTRlGxzMyY5ZNPER35NMZCOU 29YsPOflnX7e+h0e+W7lhgZ5VVZFopBAPxqvfNZLTzgQZCnKGSMuReMflZ5Tt/NHlzzVpj0W4/0O WylP7E6i44n5GvFvY5t9bnOOcJe/9Dh4IcQIZR+Sfmu6tLi48lawGhurdnaxSWoZSu8sFD4buPp9 sxu0MIIGSPLr+tt00yPSXsGaly1ksEMoAljWQDoGAan34QSFWpZ2iMHSCNWHRlVQR9IGPEVpekUc deCKnI1biAKnxNMbVqWCGUASxrIB0DANT78QSFd9Xg9X1vTX1f8AfnEculOvXpjZV0tvBNT1Y1k4 /Z5KGpXwriCQrpYIZQBLGsgHQMA1PvxBIVzwwvGI3jVoxSiEAjbptjarY7S1iblHCiN/MqgH8BiZ FaXyRxyIUkUOh6qwqD9BwAq6OOONQkahFHRVAA+4YkqtW3t1kMqxIJD1cKAxr74bKtywwygLLGsg G4DAEV+nEGlXgBQFUUA2AHQDAqi9jZSTrO9vG86/ZlZFLj5MRXDxGqRStgS7FXUFa036V70xV2Ku IDAqwqDsQehGKqVvZ2lsGFvBHCGNWEaqlT4mgGEyJ5rS+SKKVeMiK6/ysAR+OIKqYsbJSGW3jBG4 IRag/djxHvRSoI4w5kCgO2zOAKkDxOC0ueON6c1DcSGWorQjoRXvjatuiOpV1DKeqkVBxVqOKKJe MaKi/wAqgAfhhJVbJa20rcpYUdunJlBNPpxBIWnR21tFUxRIldjxUCo+jEklW4reCGvpRrHy+1xU LWnjTEklXGGFpBK0amRdlcgcgPY42q/ArsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqU3/AJp0Sw1G bT7y49G4t7I6jNyU8RbLJ6ZblSleW3HrlscMpCwOtfFiZgGkLeee/Llo7RzyzB4oUubtUt5nNvFK AytccEPp7Ho2+Sjppnl9/P3MTkAW6h+YHlmwuPQmnkelsl68sEMs0a20hosrNGrUT3wx0s5C/Ovi pyxCpc+dtChvILOMz3lxcW6XcaWdvNc/uJDRJG9JW4qffANPIi+W9bmknILpFnzLoY14aB9bX9LG P1RbUavEDl9qnHlx+LjWtN+mQ8GXDx16U8Yuuq698waVZana6ZczendXkcssIIPHhAvKQs/2VoPH GOKRiZDkFMgDSDs/O3ly+tLa7sblruK7kmhgEMbs7SW8TTOnCnLlwjJApvtTrk5aecSQRVIGQHkh Y/zG8svYXeoF7iOzsWMVxNJbzKolDBPSFV3fkw+Eb5I6WdgbWfNHixq0Td+ddIs4bWS5iu4nvZWg tbdrWYTO6qGIEXHn0PhkY6eRuq280nIAs/5WB5RFjaXzagqW17cG0hd0kU+sPtI6soZOPcsAMfyu SyK3AtHixq7RN75u0CysL2/u7n0bXT5za3LlHJEqgEqqgFm+0OgyMcEiQANykzAFlD3/AJ50Gyls 0f6xKNRCtYSQW8sqTc15qI2RSGPHegyUdNI3y257qcgHxXP548tx2+oTzXJhj0yRILsSRuGEsgqq KtOTMfBRj+XnYFc18Qb+TQ89eWhZ391PctarpYU38NxFJFNEJKenWJ1DnnUcaDfH8tOwAL4uS+JG j5IuHzPoc3xR3StF9TGo+tQ+n9WJI9TlT/JNR1yJwyHTrXxTxhCDz15ZbRo9Zjui9hJOtqJFR+Qm ZuIRkIDKa+IyX5afFw1vzR4katNtT1Ky0ywn1C9kEVpbIZJpCCaKPYVJyqEDI0OZZEgCykx8+eX0 sY7+6+s2djLKkKXN1bTwpykUsjVdB8Bp9v7Pvl35ad0KJ8iGHiCrRD+cdBSWKMzMfXvjpkLhHKNc gfEqtShCnYt0rUZEYJfZfwTxhTk88eXY/W5zsPQv10uT92+10/RenT/K6YRp5/ZfwXxB9tLE8++W W1RtMM8iXC3RsA7wyiI3X++RLx9PkewJ37Y/lp8PF5X8EeLG6WXn5g+XLS1u7uRrhraxupbG8mjt 5pEimgKhw7KpCirgAnY9sMdLMkDayL5qcsRuirTzfotzdwWnKWC4uLZ71I7mKSAiCNyjO/qBeO4r v236ZGWCQF+dJEwVumedfL2o3cFtbTuHuw7WLyxSxR3Cx/bMDuqrJx9j036Yz084iz05+XvUZAW9 K86eW9Wgjn0+7FxHLdGyUqrD98FZ6EEAgFUJB6Yz084miOlrHIDyRWs+YdL0cW4vZG9a7k9K0t4k eWWV6VISOMMxoOp6DI48Up3XRMpAc0Dd+ePL9tHaMzzyTXzyxW1pFbzvcF4BWVTCE9RSn7VRk46e Zvy8xSDkAVh5w8v/AKSh0t7kxalPbfW0tJEdJBHx50ZWA4vxBPA/F7YPAnXFW10vGLrqgpPzD8vp cWUHC8eTUo1msPTs7hxMjRLNWMqh58UcF6fZ75MaWdE7bc9wjxR80Rb+d9Dubq5gtxczLaPNHcXS W0xtke3UvIpn4+nUAeO5+eROnkACa38xfySMgJa0Xz15c1i5gtbSaRZ7qIz2iTQyxCaNdmaNnUK1 PY45NNOAs9FjkBT/AChm828+eSPMmteb/wBKaZSFLXTIhazOyelJdwXnrrBJGSWKlfi3XjUDNjpt RCGPhl1l9lVbjZMcjKx3fpWxaL5wtz5nmOjNPJ5oto6KJ7f/AEaf0mhkjlLSDkgLllKcvh269E5M Z4PV9B7junhlvt9SU3/5X+a+L2tnLRItFt7T1vUVYrmWObnLbMARIEZSaGg7b5bHWY+Z/nE+7zYH DLp3J2nl/WoPNdprP6GvI7JdKt7YWen3VvG0M0L19F+U8IeMKOxIO2U+LEwMeIXxHmD+pnwniuui neeT/NX6WsvMkS+pqCaxJdS6fWFWSzl/0dqTFwGJt4Y/hrhjnhwmHTh5+fP71OOV31tN/N3l7WNQ 806XfWlv6trb2OoQTSc0XjJPCUjWjMCeTeA+eU4MsYwIPOwznEmQPvSbQfy71bR/NWgajaKI9M9A S6xa81/dXqWb25ZQDv6hl3413r7Zdk1UZwkD9XT3XbXHERIEclO68k+ZZfImu6YtqBf3WsSX1tB6 sYMkJmRwQ4bipIU/aIwjUQGWMr2EaU4zwkeaN1/yzrurroS2Vvf2H1O8mae5vLmGa4hV4qLMrLNN yAY7LWu3TIYs0YcVkGx0Br7mUok10Qmk/l5e3WgWGha1pywJA96t7dRyJIJHlQeldLVi/ItTYjqO gGTnqgJmUT3f2IjiuNELNF8hebX0+20rVpVWNLy8u7q9/dziT9ylrBVCeR9RGkbfpQV3xyanHZlH uAr7SiOOVUVTSvKfmyKz8o2t3aVOg6hN68vqxGtrv6Ugo9ehpx67dME8+MmZB+ofamMJVHyKzUvI /mSe41m7htlMi65bavYQvIgW5jt0ZWSoJ4V5ft0ww1EAIi/4DE+VoljJv32iNU8p69rWratrE2nf Vo7pdPtINPleFpJIoLyGeeSUo7xABUYAciSPuyMM8YREQbriN/AgJlAkk13fegYvy48x2F1rOmWR 56HfJDaWEzOpaC1mufVuU4MwZhGpcU/a5fOkzq4SEZH6hufM1sgYiLA5Naj5F82xQa5Y28IvrWbU rTVrSQNDD6sla3SiMuAjVpStAaYYanGTEnY8JH6kHHLcedsz802ep+YPJep2UFlJaX1zEyQ2ty8P IkEEfFDJNGOVNvi+dMw8Mo48gJNgN0wZRISc6Jq935Mby+dMu45bwQ211LqNxDcJChUepNFwnlak fD4F2+Ig0pXLvEiMnHY27hX6GPCTGqSZfJXm19L0LQbq1Jt9E1VXGo2s0cJksjyPqrxdJEkUtvQV 7iu+W/mMfFKQP1R5ebDw5UB3Fo+Q/McVtd28Vs8qt5lh1CF5Z0kke0jFDKzu5Yn2Y8vbH8zAkG/4 K5dV8M/7JTbyN5qbzVJeRWckaDX/ANJR3Es8LWZtehY24dnM38p4Ajxw/mYcFX/BXLe/f3I8OXF/ nWmF15Q8xP5M856ctpW91bV7m70+L1Iv3kMksTK/LlxWoQ7MQcgM8PEgb2jEA/anwzwyHeUfrXlH V9R8xiRYxHZTeXp9LkuiyEJPMSAOAPM0BrUCmV488Yw8+MFnKBJ+FIWx8s+ZLx/J9peWH1CLyyA1 1d+rE6zNEgjjWERsz0k48m5qtPfJyzQHGQb42IgfSO5CaB+W+saTq3lu/tgsMSxRjzBaB14ieCF0 jmWhoxPMqePz7nJZdXGUZA/5vzRDEQQfmyXzTo+rnzPoPmHT7b6+umfWIrqzV0jlKXCBecZlZEJX uCwrmPhyR4JQJq6+xsnE8QI6JX5l0jzNq2reXNYk0yZI9PkvRc2dldRR3aRXESpE3qGSFOVV+ILJ 023yzDkhGMo3zrmNtmM4kkGuSE8w+RNV1iTUdStoZLLVII7KbRJp5I2m9SCN1lhkZXkHxVAYlqE0 NSK5PFqYwoHeO9onjJ369Exg8sa1FqX5fyGAGLQrO4g1OQOlI3ezSFQATVqupHwg5Wc0SMn9Iivm kQNx8v1ILy95a13TRrtrcafeO9/cX0lrcRXUJsjHcKeBeFplcMTtX08nlyxlwkEbAdN9vh+lEIkX t3obyP5K8y6PrejXmpW31m3j09rYhpULafLUluADcXSQd15Gp8MlqNRCcZAGt796MeOQIvu+T0/N a5LsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirD/NfkfWtb1QXll5nvtIhEax/VLZpAlVJJf4ZIxU18My8OpjCN GAk0zxmRuyHlv5k2/mbydNYwRecNTvri7WSSSNp5ozGikBTtK1eRLfdmz0hhls8ERXk4uYShXqKa +QPLnmTzboP6SHnXVLaZJXhmt1lmfiVoRv6y9VYHplWqywxS4fDj+PgzxQMxfEXpfk7yvqegx3S3 2uXWtG4KGNrsuTHwBqF5PJ9qu/yzXZ80Z1URH3OTjgY8zbIsx2x83fmNrEkH5sXl1BcvHHb3NrVk cjiYoog9KHsVOdDpcd4ACOhddllWR9I9c552L5788Xl3D+dp9GeSOl5p4AViNjFCCNj3rm+08QdN 8D+l1+Qnxfk971Oa8g027msoRcXkUMj21uTQSSqpKJXb7TUGaOABIB5OeeWzwvzr+aHmnUbW88p3 +jQ295c+nE8cLmWVW5q6qApYFjQbZutPo4RIyCWwcHJmkfSQlXlnzd5q/LiW70670whr30pVguy6 KvHkOaUPH4q0Y/5PtlubBDUUQeXcxhklj2Ieheb9Z8xal+Ues3usWK6Zderbi3SGQOHhM8BWQMrN 9osw65gYMcI6iIibG/3FyMkpHGSRSt+QVzcT+TLr1pXk9PUJUj5sW4r6MTUFeg5MTg7TAGQV3frX Sm4/F6Hfvwsbl+XHjE55VpSik1rmBHmHILwz8jvMGuXXm+W0utQuLi2ktJGaKaRpF5Iy8SOZNCKn pm67RxRGOwBzcHTTJluXtuukjRNQI2ItpqH/AJ5nNNj+oe9zpcnhf5G+YbWw17UP0rqcdpaSWvw/ Wp1jjaRZFpT1GALBS2brtHEZRHCLN9HB006JsverG/sL+3W5sbmK7tmJCzwOsiEg0NGUkbHNJKJi aIoucCDyV8ilj/nPQrvWLGCC21yfQmjl5tcW7FGccSOBKvHtvXrl+nyCBsx4mvJGxzp595j8n65p GhX2pp5/1Cd7SJpUg9eQc2HRaidup9sz8WeM5CPhx3/Hc0TxkAniK38h9f8AMWp6hq0eo6hc31vF DEyi5labi5YgcS5YioB6Ye0sUIgUAEaWRN2WMfmLe3kH5wzmGeSIpcWJXgxWn7iHpQ5kaWIOn37j +lryk+J8nufnCea38pa3PBI0U8VhdPFKhKurrCxVlYbgg9DmlwAHJEHvDm5D6T7ng/ke/wBW16a7 TVvPV3oqwKjRPPeOBIWJBA9SaP7NO2bzUxjCuHGJfD9jg4iZc5V8Xorz6VofkTXLS584R63c39ld S2MtxdI0jAwvEFh5SyFh6iEfD+1Udc14Ep5YkQ4QCL2cixGB9VvLNAvb1vy682K08hWObTWjBdqK XlkDEb7VCivyzZ5YjxobfznFgTwS+D1n8ibm4n8jt60ry+neSpHzYtxXijUFegqxOartIAZdu5y9 MfQ9EzAchh35vX01l+X+pywTvb3BMCwyRsUepuIyQrLQ/ZBzL0MQcovz+5pzmoFj35Ca1qWo6Pqc V9eS3Ztp0MfrOZGVZFNfiapoSuZHaeMRkKFNelkSDb1LNY5TBPNP5heYLG/n0rRfLF9fX0ey3MkZ +rGu4cGLlyXfuy+9MzcOlhIcUpgD7WieUg0AWH6X+UnmrzPrD6152uTbrLu1vGytMR+yg48kiQfS fbvmXPXQxx4cQaRglI3NbaeSvzI/L/VJbzy6q6xpctPXgX7ToteIeIkNzFdjGT/DDLUYc8an6ZKM c8Z9O4eh+TfO175gllt7vQb7SZ4EDSvcJSGtacQziNyx8OGYGfTiG4kJORjyGXQhB+efzS0Xy2Jb GA/XddoFiskBIV3FUMrDoNweI3P45LTaOWTc7R70Zcwjt1eVW/5Qedtb0m91+7/danPIZ4rGccZp +ZLSM1SPTJJ+EN19ts2Z12OEhAfT3uINPKQs82cfl9+advDBD5c81iTTtXtONvHLOrKJAPhQPUVR 6bVbY9a5h6rREnjx7xLfiz/wy2LCPPv/AJO0/wDMbp3/ACagzM03+LfCX6WjL/e/EPorNA7F85eX nTQvzl5eYKRlby45zS7KGnVxFKSduLFwa9q1zoMvr0/o7g66Hpy7p/8A85CappVxJpFlBIk19D6s kvAhikcgQKGp/OVqPl75R2XCQ4ieTZq5DYJx5hsr+y/ID6tfhluo7e15o/2lVruNkU+HFCBTKsUg dVY5WfuZzBGLdEf84+f8oZe/9tGX/kxBke1P7wf1f0ldJ9PxZB+aXma30HyfeszgXl7G9rZx1+Iv IvFmH+op5V+XjmPo8JnkHcN23NPhi8E8ga03lrzbpeqXiPFZSErI7KQGgl5RNIv8wRt9v5c3mqx+ JjMRzcDFLhkCX0zrMkcvl++kjYPG9pKyOpBUqYyQQR1BzncY9Y97szyeAfkv5b0TX9fvbbV7VbuC K0MkaMzrR/URa/AVPQ5ve0MsoRBia3dfpoCRNvoLR9F0zRrBLDTIBbWkZZkiUswBY8juxY9TmhyZ JTNy5uwjEAUEbkEpB5y8r+X/ADBpqR665js7R/X9QSCIKeJWrMdqUOX4M08Z9PMsMkBIbvAfOWne TH1ODSPJMFxf3TPSS4DtKrsdhHEtPi92+7N7p55OHiyUA6/II3UXtP5VeRpPKmgut3Q6pfsst4FN QgUERxVGx41NT4k9s0+t1Piy2+kObgxcA83kf5l/+Tguf+Yix/5MQ5tdJ/i49x+8uJl/vfk9486I 7+TtdRFLO2nXYVQKkkwPQAZpNP8A3kf6w+9zsn0n3Pnr8s38gC8vk84KphaNDZs/q8QwJ5j9zvUg jrm+1ni0PDdfg4N+J6Hqlp+UWs+XtTi0RIrm+0jSruazVTcqYkj5y8hz4g0mmrvXr4Zr4S1EJji5 SkO78cnJkMZia6B5r5e/8l75v/4y6X/ydlzY5f72H+d+hxYfRL4PXPyD/wCUHl/5jpf+IR5qu0/7 34OZpfoeg3t9ZWNu1ze3Edtbp9qaVgiD6WIGYMYmRobt5Nc3hX5neeZPOl/beW/LUUl3apLyLopr PMAQOINCEQE7n59Bm60em8EGc9j9zg5svGeGKXeWNS1v8r/N0lvrVq31K6UR3PCpV0Bqs0LGgfhU /eRscszQjqcdxO4YwJxS3e+6N5g0XW7UXWlXkV3CRU+m1WX2dT8Sn2IzR5MUoGpCnPjIHkwb/B35 vf8AU4Rf8iR/zRmb4+n/AJjT4eT+cxfz3efml5PtbW4uvM31pbp2jVYokBBUVqeSZk6aODKSBCqa spyQHNMfKlh+a/mPQLXWYPNSwRXXqcYniUsPTkaM1ISnVK5XnlgxzMTDkyxjJIXxMk0Tyv8Ambba ta3GpeZ47uwjcNcWwiCl0/lrwGY+TNhMSIwotkYTB3LL72zs42l1OOxin1KKFhFL6a+s3EErGHoW oTmJGR+m9m4jq88/5WX+Y3/UjXP3y/8AVPM/8ph/1QOP40/5qHuPPXna5nhnuPy9eae3PKCWRHZ4 2HdGMRKn5ZIabGBQyfj5oOWX81jdv5w8r6758tZfMXlt7LVWuIYWuY7iQMk8bBY/VhKpWhAB70Hf Mg4JwxHglca7moZIynuN3tXmPX7Hy/o1xq98Ha1tuHqCIBn/AHjrGKAlf2nHfNPixHJIRHMubOQi LLx38xfzF/L3zPoc8cNjO2sqqiyu5YkQpRwSC6uW48a7bjNtpdJlxy5+nq4eXNCQ82M/lj5i8maD eXd55hsnu7kekdOdI1l9Mrz9Q8WZVr9ihp92ZOsxZJgCBrvasE4xNll/5g/mtpXmPyhf6dpem3zR y+kZb2WNVhjCTowJKs/2ivHem+Yml0UseQGRHu+DdlziUSACnf5ACQ+SL8RMEkOoTBGYcgG+rw0J UFaj2rlPaf8Aej3fpLPS/Sfeuj/KTVNb1r9Led9VGolDSGytQyRcAahakKVXxVRX/KxOujCPDiFe afAMjcjbLPM3kHy35g0mLTbq2ECWy8bKWABHgGwom1OO260pmLh1M8crB5822eKMhRY7oXlDzd5a 0zVLG51iK/8ALsdnP9UhdG9dW4GgBJoi+3Jh4AZflz48hBEanbCGOUQRezBv+cef+Uo1H/mBP/J6 PM3tT6B73H0nMvfc0bnuxVj3mjyJ5d8zy28urRSSNbAhBHI8YIO9GCnfL8Opnjvha54xLmw+883e Sfy+8wjQbLQjCziL1byEqz8ZPFpCZGp4Fsy44MmeHEZNRyRxmgHqOa1yXi3nz8tvOGo/mK+tWFmt zp00lrJ6oliQqIkjRwyyMjV/dk7A7ZuNNq8ccPCTvu4WTDIzscntOadzWAedbjyR5cuLf6z5Ri1G S8DuZLeyt5KFSK8yV6nlmdpxkyA1Oq8y0ZOGP8N/Bjn+PfJSW13b2/kq7tFvYJLW4ktbSGGRoZRR 15pxNDmR+WyWCcgNG+bX4kf5p+TFoNW/LzSdH1HSJdN11bfV/RMjXDW6uptmZkMRCoOr71BzKMMs pCVw9Pv6tVwiCKlu9b/Km38sxeU0Pl2a4msZpnkcXZT1klIUMjcFVduI/rmq1pmcnrq/JysAjw7I vzJ+XXlXzHqEV9qts8s8Sen8Ejxqy1qOQQj78hi1U8YqJZTxRkbKZaJ5Y8v6HGY9JsIbMMKO8a/G w8Gc1dvpOV5M05/UbZRgI8giNT0nTNUtTa6laxXdud/TmQOAfEV6H3GRhOUTYNJMQebHNO/KryTp usQ6tY2TQ3Fu3OJBLI0Ybs3Fmbp1HbMietySjwk7NYwRBsBl2Yra8q/5yAsL660XS2tbeSdYrh/U Mal+PJNq8QetM2fZkgJGz0cXVAkCmSflBDND+XekxTRtFIv1jkjgqwrcykVBzH1xvMa8vubNOKgG Y5iNzsVdirsVfP8A580rUz+couFtJjbyXdg6TCNihVUhVmDAUoGUg5vdNOP5er6H9LgZInxfk+gM 0TnuxV2KsQ/Nu1ubr8vNXhtonmmIgYRxqWYhLiN2NB4KpJzL0MgMsSfxs05xcCkn5B21zb+T7xLi J4XOoSMFkUqaehCK0NPDLu0yDkFfzf0lhpQRH4vSs1zkuxVCaxFJLpF9FGpeSS3lVFHUsyEADJ4z Uh70Hk8V/ILT7+18z6ibm2lgH1MrWRGTf1U2+IDfNv2nMGAo9XC0sSCbe65pXOdirsVeD/nV5Y8x P5wTVrOwmurSaGIJLBG0oV4tirhQaePvm77PzQ8PhJouDqYS4rAZVbfnFq31eP6z5R1L6xxHq+nG /DlTfjyStPnmMdBG9pxbRnP80qv/ACuK9/6lHVP+Rbf80YPyA/nxT4/9EvQrC6N3Y290Y2hM8SSm F9mQuobi3uK0OYEhRIbwbV8il2KsA/O7TJb3yS5gtjczwXETrwTm6KSVYigJA33zO7Onw5NzWzRq Y3FS/Iq2uLfyVJHcRPC/12U8ZFKmnCPehw9pEHJt3I0wqKvq35e+b73U7q7tvOt5ZW88rSRWiRuV iVjUICJ12X5YIarGIgGAP49yZYpE/UhP+VY+eP8Aqfr7/kU//ZRkvzmL/Ux+PgjwZ/znf8qx88f9 T9ff8in/AOyjH85i/wBTH4+C+DP+c7/lWPnj/qfr7/kU/wD2UY/nMX+pj8fBfBn/ADn/2Q== + + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj xref +0 60 +0000000004 65535 f +0000000016 00000 n +0000000076 00000 n +0000000126 00000 n +0000000009 00001 f +0000000270 00000 n +0000000290 00000 n +0000000763 00000 n +0000000834 00000 n +0000000010 00001 f +0000000011 00001 f +0000000012 00001 f +0000000013 00001 f +0000000014 00001 f +0000000015 00001 f +0000000016 00001 f +0000000017 00001 f +0000000018 00001 f +0000000019 00001 f +0000000020 00001 f +0000000021 00001 f +0000000022 00001 f +0000000023 00001 f +0000000024 00001 f +0000000025 00001 f +0000000026 00001 f +0000000027 00001 f +0000000028 00001 f +0000000029 00001 f +0000000030 00001 f +0000000031 00001 f +0000000032 00001 f +0000000033 00001 f +0000000000 00001 f +0000001115 00000 n +0000001136 00000 n +0000002101 00000 n +0000013625 00000 n +0000013647 00000 n +0000026897 00000 n +0000026919 00000 n +0000044841 00000 n +0000044863 00000 n +0000065469 00000 n +0000065491 00000 n +0000085021 00000 n +0000085043 00000 n +0000096385 00000 n +0000096407 00000 n +0000108820 00000 n +0000108842 00000 n +0000131490 00000 n +0000131602 00000 n +0000131624 00000 n +0000146382 00000 n +0000146429 00000 n +0000146449 00000 n +0000146965 00000 n +0000146985 00000 n +0000147332 00000 n +trailer +<<9068343085304a48bb626a918b0d3c0c>]>> +startxref +175003 +%%EOF diff --git a/ci/CI_AI/CI_05.ai b/ci/CI_AI/CI_05.ai new file mode 100644 index 0000000..c451ae3 --- /dev/null +++ b/ci/CI_AI/CI_05.ai @@ -0,0 +1,577 @@ +%PDF-1.4 % +1 0 obj << /Type /Catalog /Pages 2 0 R /Metadata 31 0 R >> endobj 2 0 obj << /Type /Pages /Kids [ 5 0 R ] /Count 1 >> endobj 3 0 obj << /ModDate (D:20061213185935+09'00') /CreationDate (D:20061213185934+09'00') /Producer (Adobe PDF library 5.00) /Creator (Adobe Illustrator 10) >> endobj 5 0 obj << /Type /Page /MediaBox [ 0 0 841.88965 595.27539 ] /Parent 2 0 R /PieceInfo << /Illustrator 7 0 R >> /LastModified (D:20061213185934+09'00') /ArtBox [ 100.17334 147.84277 752.13477 456.10693 ] /Group 26 0 R /Thumb 27 0 R /Contents 29 0 R /Resources << /ColorSpace << /CS0 24 0 R /CS1 25 0 R >> >> >> endobj 7 0 obj << /Private 8 0 R /LastModified (D:20061213185934+09'00') >> endobj 8 0 obj << /CreatorVersion 10 /ContainerVersion 9 /RoundtripVersion 9 /Options 9 0 R /AIMetaData 10 0 R /AIPrivateData1 11 0 R /AIPrivateData2 12 0 R /AIPrivateData3 14 0 R /AIPrivateData4 16 0 R /AIPrivateData5 18 0 R /AIPrivateData6 20 0 R /AIPrivateData7 22 0 R /NumBlock 7 >> endobj 9 0 obj << /OptionSet 1 >> endobj 10 0 obj << /Length 1417 >> stream +%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 9.0 %%AI8_CreatorVersion: 10.0 %%For: (lee) (lee) %%Title: (CI_05) %%CreationDate: 2006.12.13 6:59 PM %%BoundingBox: 100 147 753 457 %%HiResBoundingBox: 100.1733 147.8428 752.1348 456.1069 %%DocumentProcessColors: Cyan Magenta %%DocumentSuppliedResources: procset Adobe_level2_AI5 1.2 0 %%+ procset AGM_Gradient 1.0 0 %%+ procset Adobe_ColorImage_AI6 1.3 0 %%+ procset Adobe_Illustrator_AI5 1.3 0 %%+ procset Adobe_pattern_AI5 1.0 0 %%+ procset Adobe_cshow 2.0 8 %%+ procset Adobe_shading_AI8 1.0 0 %AI5_FileFormat 5.0 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%CMYKProcessColor: 1 1 1 1 ([Registration]) %%AI6_ColorSeparationSet: 1 1 (AI6 Default Color Separation Set) %%+ Options: 1 16 0 1 0 1 0 0 0 0 1 1 1 18 0 0 0 0 0 0 0 0 -1 -1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 2 3 4 %%+ PPD: 1 21 0 0 60 45 2 2 1 0 0 1 0 0 0 0 0 0 0 0 0 0 () %AI3_TemplateBox: 421.5 296.7754 421.5 296.7754 %AI3_TileBox: 23 19 819 577 %AI3_DocumentPreview: None %AI5_ArtSize: 841.8898 595.2756 %AI5_RulerUnits: 6 %AI9_ColorModel: 2 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -58 632.2754 1.5 1586 1118 26 0 1 7 42 0 0 1 1 1 0 %AI5_OpenViewLayers: 7 %%PageOrigin:23 19 %%AI3_PaperRect:-23 578 819 -17 %%AI3_Margin:23 -18 -22 17 %AI7_GridSettings: 100 100 100 100 1 0 0.8196 0.8549 0.8667 0.9098 0.9275 0.9333 %AI9_Flatten: 1 %%EndComments endstream endobj 11 0 obj << /Length 6200 >> stream +%%BoundingBox: 100 147 753 457 %%HiResBoundingBox: 100.1733 147.8428 752.1348 456.1069 %AI7_Thumbnail: 128 64 8 %%BeginData: 5854 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FDFCFFFD8AFFA8A9A8FFFFFFA8FFFFA9A8A9FFFF7EA8FD04FFA8FF %A8A9A8A9A8A9A8FFA8A9A8A9A8FFA8A9A8FD04FFA9A8FD53FFA92F7EFFFF %7E54A8FF5A5454FFA8542F5AFFFF7E7EA97E54542F5A5484AF542F5A5A7E %A8AF2F5453FFFF7E2FFD1BFFA8AFFD04FFA8FFFFAFA8FFFFFFA8FD04FFA9 %FFFFA8A9A8AFA8A9FFFFA9A9A8AFFFFFA8AFFD04FFAFA9FD0DFF7E5453FF %5954A8FFFF5A2F5AFFA92F2F2F7EFF7E53FFFFFF2F547EFFFFA92F2FA8FD %04FFA82F2F5AA82FA8FD1AFFA82F2FA9FFA92F7EFFA92F54FFFF53542FA9 %FFA92FA953542F2F2F542FFF5A542F2F2F5AAF542F54A8FFA8545AFD0DFF %842F7E7E547EFFFFFF53545AFFA8547E542FAF7E5AFFFFFF542F84FFFFA8 %542FA9A8AFFFFFFF7E2F542FA9FD1BFFAF2F54A8FF2F5AFFFFA8542FFFFF %542F5454FFA854A8FFFF842F5AFFFFFF542F7EFD04FFA9542F5AA95A53FD %0EFF7E2F2F302FA8FFFFFF5A295AFFA929FF532F2F7E53FFFFFF2F2F7EFF %FFA929302F2F29FFFFFFA8542F2FA8FD1BFFA8302FA82F5AA9FFFFA92F2F %FFFF2F7E29305AA929A9FFFF7E3053FFFFFF2F2F7EFD05FF7E2F29542FFD %0FFF842F7E54542FFFFFFF54545AFFA854FFFF2F542F7EFFFFFF542FA9FF %FFA8542FFD07FF54542F5AFD1BFFAF2F5453547EFFFFFFA8542FFFFF54A8 %842F547E54A8FFFFA92F7EFFFFFF542F542F547EFFFFFF7E542F84FD0FFF %7E2F53AF2F2F53FFFF5A2F5AFFA92FFFFFA82F5453FFFFFF2F2F7EFFFFA9 %2F54FD06FF53547E2F2F7EFD1AFFA8542F7E2F547EFFFFA92F2FFFFF2FA9 %FF5A2F2F2FA9FFFF7E5453FFFFFF2F2F7EFD06FFA82F542FA9FD0EFF842F %5AFFA92F545AFF5A5453FFA854FFFFFF7E2F7EFFFFFF542F84FFFFA9542F %7E7E847EFF7E54A8FF7E542F84FD19FFA92F54A85A2F54A9FFA8542FFFFF %54A8FFA9542F54A8FFFF842F7EFFFFFF542F84FD06FF2F7E2F542FFD0EFF %7E5A5AFFFFA8535AA87E537EFFA953FFFFFFA97EA8FFFFFF535A7EFFFFFF %7E5A535A537EA85A7EFFFFFF7E5A53FD19FFA82F29A9A853295AA9A92930 %FFFF29A9FFFF7E302FA9FFFF7E2F53FFFFFF2F3053A9A8A9A9FF2F7EFFA9 %293053FD54FF547EA9FFA87E5AA9A87E5AFFFF7EA9FFFFFF7E7EFFFFFFA9 %5484FD04FF5A7E547E5AFF7E7EFFFFFFA95A7EA8FD2AFF7E7EA8A9A87E7E %A97EFFFFFF7EFD73FF7E538453AF2F7E2F5AA8FFA854FD31FFA9FD08FFA8 %A9FD09FF7EFFA9FD2BFF2F54A85AA8307E5A2FA9A85A597EA8FD2DFF7E7E %2F7E537E535A59A87E7E2FA82FA9595A5A5A7E5A535A53FD2BFFAF5AFF7E %FFAF845A7EA9FD32FFA97E84547E845A53A97EA95AA97E5AA8A95A7E5AA9 %7E5A5A7EFD2CFF547E7EA8FFFFFF53A9535A535A7EFD72FFA9AFA8FDFCFF %FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF %FD15FFA8A9FD7DFFA8542FFD7EFF5A7EFD3AFF7E84A9FFFFA97EFFA9847E %FFFFA97EA9FFFFA984A9A97E847E847E84FFFF7E847E84A9FF7E84A8FFFF %FFA884FD17FFA984A8FD13FF7E7E535A53FD21FFA82F29A9FFA829A8FFA9 %2930FFFF2F302FA8FFA92FA9595A2F2F2F5A53FF2F302F5A537EFF5A2930 %84FF7E307EFD0EFFA9FD08FFA92F7EFD06FF5AAFFD0AFFA854537E547EA9 %FD20FFA92F54A8A92FA9FFFFA8542FFFFF542F542FFFA854A8FFFF842F7E %FFFFFF542F84FD05FF5A2F54A85A7EFD0EFF7E542FA8FFFFAFA8FFFFA82F %53FFA85A7EFF842F53FFFFA97E8453542F5A84FF2F84FD06FFA8FFA8FD1B %FFA82F2FA92F7EFFFFFFA92F54FFFF2FA82F2F53A92FA9FFFF7E2F53FFFF %FF2F545AA9A8FFFFFFA8542F2F53FD0FFFA92F547EFFFF5A2FFFFFAF2F5A %FFA92F5AFFA92F84A9542F542F542F542F7EA8542F7E5A7EFFFF7E547E54 %53FD1AFFAF2F542F5454FFFFFFA8542FFFFF54A8A92F545354A8FFFF842F %5AFFFFFF542F5A5A5A7EFFFFFF7E542F7EFD10FF2F297EFF7E2930A8FF7E %302FFF7E302F7E7E307EA929302F2F2930535AA8A92F54535A7EFFFFA92F %2F29A8FD1AFFA8302F7E29305AFFFFA92F2FFFFF2FA9FF7E2F2F29A9FFFF %7E3053FFFFFF2F2F7EFD06FF7E2F302F84FD0FFF2F547EFF535453FFFF7E %2F54FFA92F542F5A2FA9FFA97E7E54542FA9FFFFA8545AFD06FF7E2F5484 %FD1AFFAF2F54A87E2F5484FFA8542FFFFF54A8FFFF5A2F54A8FFFFA92F7E %FFFFFF542FA9FD05FFA92FA95A542FAFFD0DFFA82F2F7EA82F2F7EFFFF5A %5453FF7E547E2F2F54A8FD04FF7E2F54A8FFFFFF2F2F2F5A2F2FA8842F2F %2F547EFD19FFA8542FA9FF5A2F54A8A92F2FFFFF2FA9FFFFA8542FAFFFFF %7E5453FFFFFF532F2F5A535AA8A92F84FFFF2F2F2FFD0EFF2F542F542F54 %53FFFF842F7EFF842FFF54542FFD05FF7E542FAFFFFFFF845A5A537E7EFF %53547EA82F54FD1AFF7E84A9FFFFA97EA9A9847EFFFF84A9FFFFFFA8A9FF %FFFFA97EA9FD04FF7E847E847EFF7EA9FD04FF7E84A8FD0DFF302F2F2930 %2F2F2FFFA8547EFF7E54FFA92F54FD05FFA82F2FA8FD0AFFA87EFFFFA97E %FD54FF2F542F542F542F542FA9FD0EFFAF542FAFFD2CFF7E84FD0EFFAFFF %7EA9FD11FF7EFFFFFF7EFD0EFF542F2F7E842F2F2F547EFD0FFF537EA8FD %05FFA8A9FFFFA8A8FD1FFFA85984535AA8A953FF7EFF7EFF7EFF5AFFFF5A %5A7E537EAFFF59A9FFA9A8A9A8A8A87EA8FF7EA853FF5A8453FD0EFF2F54 %2FA9FFFF7E5A5AFD15FF845A7E847EFD045AFFFF7EFD1CFF2FA97E7E5A84 %2F8454A97E547E5A7E7EFFFF7EAF7E7E53847E7E2FFFA87E2F7E547E5A7E %A9547E7E845A7E7EFD0EFF5A2930A8FD1AFFA95384597E537E2F54FFA953 %7EFD1BFFA9537E7EA97E2FA8547E5359547EFF2FA9FFFD047EA97E5A7EA9 %FFFF537E2F7E537E537E2FFF53A953A953FD0EFF5A7E7EFD1DFF847EA8FF %A87E2FAF5A7E2F54A9FD19FF54A97EA9A8847E545AFF53FF54FF5A7EFFFF %A85A7E84A8A9FF7E7EFFFF7EA87EA8A92F5AAF54FF7EFFA9547EFD2EFF7E %5354A8A97E548484A8FD76FFA97E7EFFFFFF7EFDFCFFFDFCFFFDD9FFFF %%EndData endstream endobj 12 0 obj << /Filter [ /FlateDecode ] /Length 13 0 R >> stream +HWmo6hcd( R6f7]QMԬQ]3CIdVŗ<~\.ʂyw]Z\Oޜ~QBv~jwvsQ^%vݶg/4}^-4RKzyvQfł\h_\_A}ܯ6a,ղP뿬nς[))7Ω Ka\{n׻]z_妸Z>ׇoa_0l24v-nK](p|nyyu9M3_aa@7yۈnCt}N/y֝xDDaݦպb.^P̾?v=`@ ]Wa 8۟oڇm gZ&O}\v-y OC^x_g#6i) vlnhso]uKޔhVthoEsq O_Xoj5P_! 9_VŇUç"9 +BXmuq:Z3Wv ~:{_f| @ +?y"d&EۯaY>}KPGWA^L2j2d*bt>i5!4?=Тo!w r><28fG^O߾mq;JK)5N1T%\$TI$&5MS7 M51nT#SuPWmmj]Z֢uSlbc >h:(3)*J"H.ҋ#H2ҜFl G90u 1MiR iAY1΋ifPn!91MitI4K,$4U2MqPfI3MiLS'b8Spw6mCQ=)B4ff5[<0jf~j njvfffzfjjtsjbfG ƳξcV~##]n3iH*2ߥ+0@Pc]br%a\:hXҬ"IƞD\bt䣧71BADߟE44I"X>%vD$>#xN n?[PȂp )-DX FOS%(J5 X@ A0}&$P#IQح71A$p Au {DHM@^E@"ƈ6NpbAȽ)2dS96Х)e;ҙ ֊Py=TBHEbLsP}%N />շX܆ZZt4g@'9'ətXL:$pPaHҞGadlQ#N;J"-E(OjO5VXxR݉]ˎ^6ˏU jͱ'$Ì[- ƕoU'YŸݸԵ8׏k[U"3Ț+%e_ZOnh k׭vvt%|GkZbL~|c|E/S~SWÝ*/yxʂSLS*3/51 +ì9Φp%߶[ G峕vSpdA tz8ͬ5:P1)Fb>Sy ~)חo#V 5T +M + }w}ĸ%x^-t`n%"[zIV(p%nB:΅|u)Xf"t#葓Nx=j A 8S" 4h3@~RkzA(6OB'rfI@(7TI0 p + CAJY +RCfM {sP=*KN5wLC BC:_ ((H:K젞TPSMUyYpXC BC;w1Vȿn: q@T0 c00 p0 phXCBp9 n*8&XEYXeyXWST,M ,,:x!4#-3=<;T`j`V_91#ǂw_,1qpL<\wc =-Ʊ +;qi$sN6G"Z3gО?3`lVtrgpA_,KR;h,#8[@'nk26܀84Vj*J#QL(EP+G+G+G+G+qsJۉ_q%mF]xAX*32qibZ3Lnf3řΌwzW#:Hܙ<%NF΃ƗA_phfu h$Ou׺+b૰QGugVV +V5+e l`-LW_BX5h `hم"W)Xc ²4wER  nA}!8T"`CsBq!4 $&` IC( B4vеtrb i: ޶dzptSCu BQCc;hmW*2(:資Tw쀶 )9kUP +@[(~]P9数N#3L #~1Uю P'`C3"I9b"A#q 7|.1(ɤFM"9(Ǔ'gS8TYa +܂~ +!H^ + K+X{,TWЭ`ɞGhVV056f)p⍞G mO|t8H!;00,3"v-%H Z)C\D\YFIIKK*ҖBҎNJ&/ՉקZ*(QIҧ +TjƗӡtnp4;0Ñv}3XX]č&7{f131fFff 5jv5o} +M6b.a)`Y l ݡDC4F=a'[I [6} kh^wQ]sUcRcjSϪELW[ZSbm1$‚t" Ȇ(E%C/E jQ#gB0 +K!4<1Ij9儈^En " +!t) "(QIhE"D\$I)bEQ6qg9\%W6hdϚ)DKuh$)Ӵ&aj#܊J)~Ⅸz[ 4b mp϶4&arrds%FqWʳ|W<:3<9BO'>khO5gAJ ɕ┶ÇT=Nv~̧+ +ݲܹR&:=DfݎOջx<_^?}?\}~[0Wl/\ i*vy}HxK clv6( lf 7:* /ؔĖ?wxjɮsN}7iS[oSot@h20>&Ch20>&Mw%w#w!wƛ؛ƛ SȽ0AZ})Bd`} {hCn.x=r6'nG$g p3(fP #384Z5('3H&a:HA:ltFagB^;e.8ooc+xΗ)tT<qpN'VAɜlYZp/Q싟: i?l}^q! ^N$Sh>cF=pB>D^ l-[#%z*+KF#:l!*$0TP%(V̴5JVheQKPP?h +9,EnִI$$$$$m "yW)V.NSr񅵚D*?JuK]$GʾcJfZz1f3M4vر%;^-d&'CLd&s-Ʉd6'ÎLdΛJ?j"6rjQ+%5SS;-SK 5R[95樵Pԡ uPj-ښmKE%k*Pn􂊯l+E'h:b锂蜦jV c^+蹊kQ' ѣ^-ѵnmgOsFjrP/Y@{ /8DNr\CjY2ed⋇֮Cߧ{,' ?{`['{~AV/3ꮻqp%ml% LVho[vކ: i?l{ya c c c c 60Opʅ?H:[ G})|"N ~!3H&a:HA:ltFagBRkzAW~û*@ n YFy4L4b M0Pkla%;e +X\AgZ3[Y讀*tDJgpg O5j nVp>(YC j aػ[`t >-_ +иPHF !( + iTH.A_Rt&GoS?5UV +MhA2"ZU!bM +QӈA2"zE] + +XSmPE:T2C-owE 9g|>/*wQD:]-aU>iڭMwrbt_f`KY[ñQ,g22F\\ ]WP.#RYSG9˗?үчO___KQѫ!J0;B|^THLdߑhb* dr4l05G!<#SBύyԑ׹}y:@`{'y{ P(Ϩ:Hjў`3 ;USKJz. +[e0P +C1\A_ +T.(@ėɅޘQ;+vjGMʅw8rͭO>e{_/RZ`hv ۨ +@ɱf thݰFO| GZ"\ L1$dHɐaRsQrϐs:iHԐ!cCΆJ8y2qhU]uU]uU]uUGߢSgq(U˻5KDTqJD_ge"./o&.<-_xVYg]~/Z?ҏo^/N~>u8|}>^t^%R$ dty$|tro-9\X 18C#tځxNxcS +ul Ѵ0|=t+]jHb3$ge\vǍ(|o"@l,dϩ"%˖nݙܬv[4%N}|Ęm8XPhe$BAX_9V +M褑nODD8Pd9b!~_zw^DPeצlSr teAO8}qfv$6vc?:Q0#یhx~Sk'?ÞҙVwiuL<ɚ3>ȳEfp錮kOA.S=5:ٻUw>?Ͽ4DZܨ]ި(i:(-fН>3azE  Zw4yOܼm:eK -y$1oGJHS*zJcW  bXT7mG)WrWW27ҷY$:v1֝d=UP"~ xNrIaD`^ ŔEtE\E8E EED$Pbq G14f̐Bhb?$Ee 8whj{QH>9l2I'$6 `U).h*J`#*bl0AdE*XX$A$ C`K$f0:3NC ڴE,9-F[k !E;څВY`i +G1 }С! =/-q)a ;#IGX{Thy45Z1'+AWJ8$T`)7eԼ뛀>wʕ8Fq'FԇVFR}}S&Io~.>nr=Hz[ѽG]Ŷ9.ǻ(7nx1WCv7+Td?7moZ^eF {dOkf]e5EP6ةEV`ynR$<ymcdܸкG>7q'^PT0:lVuh{S{G;;jrhs}˽s[{^^,osծ5P v.Bs@)?Xd===<({4]HmـX HS\l| +I-[@GXw +TCtOR% eH6h39$[@E^F +V,;,[ /ɛ5^BcX\"o\b%AܱSc$C #[ + +sg1L1BdƃrVF?79W#cs1>1:F:N~ d+?DYjNt\9Zho~`TL ]:n6â\LHВ + E +F. + k5ynmAc2MuVHŢOwFS&mm__@Kj*;Mq?6y2bxz۳e#Dn8%}Gt䊿I"g tDJst.3ylTS7r_4g"%5&VE}j&qr$CH["IIHs0RqdF/Q̠f H[4k =y$ mZ%@@M5֙v "%Y +7G ЎV (  +G'Ol.cr9(sw"nu.hƒ/{JR³;i-&YG ?B2*wS uȭ@E^ 뫷*73rdUՕ+;JkRnJ:.|<|t>#Dܠ +Pu4v!P F|bf1~BBnnLՕ+xمHV+[30]YD%SS=@265)Mn{G̽u9pbj+UBLzBn+]u0gYt'Zǥ] jEk5KgUD;Pu垘q k=L&ºrr[(b!6N(n_<"eF'3u9֩1Jijv Be зJiE2\qցHv8εAQN O D\~T;64EspN-dќx.g<ˀ.Mu.Zf\ *.T_';e&WAj?rS~#{"1A,%ny +%{ dW,y+!I7Kӵ{J>,OIT&]D|ԫɑCtcڵ@9&Zc&|ҝ5VNXZKt<ς~g}8^<>^\WçI5ׇ6UÝ{1~6~>V=x?]XnƪgJ  +T1\$te&Vtsp;E] (CSNY*v~ yз+ϋ]]AM.i|(3TI<<.wФJUJ2?X':$Uk55֨7Yµ< 9m߁f/pY34L> stream +HWT}~Yiv\+]E Ej` 4ܷ;lXuE vթS჏w֟o?{q=rw.oOvg/ݽ=XֳY~]<pj?@>r$5ʛ>'M)2N {3'T#"?ה!A%80|*vdž/+v}s}9pwص_x)c'̵9<64ĿnTr'.b K~ʉ`afo>8RÎcs+L͙k><ܰ9qg?^࿭~X}w!azn[Ewg?^o^;Y[cOCu$d-y!s? ~rw^7SqEO؝ܬSP"{ڎu66~+{[9n.Y#Mrɠ6W6Vsdwv=7xޞ׋%%&*31S)>Cj{`9d}i˂/ nTE[&ΜBbbͣTz:T |t2֞mbeh<ڡR (K3DmxBav,׾XJS57 W|6!Kc*0 +ͷM[Ͼe=ĕll F +67CLxp~r<`paYV&;/Xp 2vR|^6+5z,QmfR'Gz>.@KΝcofVoV7'%h!MfЛ5Dߡݑss`PX0|?|\,N>P -vF?LPJӒ/ZZnvENyy~ॺ"2?259&f{KXD5S Nwzx~rowf_]]9{| W[+nW׫-ӧ6_] +GkUknS)3 SL16X۶_#3ȓZ!p_60&Ww^MK|0J\GSȶc_A-21LvBUm:7"CC.l#b?aR"x-QgR!D}tsQtZ"9x +#!%?KJ HLNR(R%b>v.\/&w{ʨDH3Rlǡ8бڡk4`3SED7< JlR,єrciҍgip!|n*|2)eQRV=m  BX/݄>-Z&dn`$,N2o ԱS0e4?JN+ +z4 e7U +!gj%`̵QՇ%1j"|,:8FsDc^PR +&A hX%쎍|?[B8'<,۪@krrVeyGwzݹ?\ǚbeoq<޼)iCmZ)oY3σ uIlj2zxݒqsOyݰ@^jĊ|B;icc1=D‡;`^p% +IaQkikuy XⵌCTc뜔4}nE<֕BdH(bp x +u$p1Wq&Ĉ<9[yVlw+z(v51xPd}^_Yk7-4yh\SUzXh\𮽶m kv_6kˮ9D$.oAޡ|T_qIvMWCZkgPxAV΃P GUX?`le)σ=A6Z4s͆j;?YO5_S +I`v@)E:룷IaQyn91I0z#ȕiV2;0Oa+6_*-XX(ubwIIB`[+1? W@^TlESKR+ѺB+ +nH#p#^GIzW(6ɥ 4Z/JU{`V歂G L0VWVg6ՂxAP+;c:z?f2L Ko( +5"i`HOuI%Ξ zFYЛu õ9)Bz! Sn8 t`Q9D|vYF[zHډ:~T;>7>T݋N1:O銥u[]Imߠ}WM]Ķr])4l"{]U%O4r*\W2A׏Ty̤q2D tДGWY^_!p&aM3lFc_w&w8px:)JA=g^ D %L|˥!~q꒲'Un+mus8z ^!/IauA\=^w%g8e|8 &)er(=H :DK̻;["cJȘjߜCAȭD2 Kϫxs_9kB^[xjS·.vz e~P/; A{9y?x(MĿ a>BMhDWk9o ]'64kgg;g)b:,K-(:9aum$Q%PO~]d +P$W +.B@Hr$Rb8j{,?heh {QZ; ' fqV":&؃I݋WJߎ =:>uYtzlLvW496Ȁ8M~0u\ecA륜@N.C*c ywJo~욌isx_P[>|J.ѬrO7ûufĄfu,[hR=$9kɷ>0&;Lr[zOAOU?V7(ീ{??h>%&fޠ +)2$/x:܀?t ۵}j +`zIӆ8'ϵ`)J Aڪմp1T"߿(^sA\>\ t Z*![  tJS]暶|)ԃCH`gK4?qG*ԃW1S}Aʅ:U3}'_ 5oӘC85˔<Q H;A8(lc cA{uYOX=Qȫi1#L`w5( (D Tm`!֬5?a8}ښM6EHVs/0iIh R$mϜ"j1_d5+ʈeb,䁗%V(Hzl^3~{r +UŻV|3_ZYeiYW#}!1m;s;Z!uPE + +S@pjyX^֫-.MU*>z^596Co@lK~=Y*9@` cS|;^ MEId86EPD,w-79sM12;+\7=({~=d#lD~KY n@vB9X2^~HUDOgj9Toidʫ?^Ig7dk^IjR[r #d{(ftҨW9DgrFu2%A X(8|on3r+Bġ`\G!kI8 b# ;?p.#ljJbxܪ+ d ۨ8(FDžڼ, tK/\\SW+v1ksmKVj+Ԉ8` ,x5*kŬ.|kZiu.vCduQE_ZBN-BRq]{ MWl ۋe),^q{'(Q:;(Dm@\5_S(`c3$ VC0hP |Gt8@xS:RNN$^Cqx s Go foDd];*@ K[ eR<l$lR,Z2xBa7U`ۇ+oui]^@hu_xC+G&]*B8+Q# +=IpJԽEpd]VYؙج"4[ʠ6Z(j-j ֖KjZ,Ԛ'1`}$8Xj e}io.#lÓHւJJ{LvX7fʶjmH]չ0#WFуv\LrZ4ba7Zuv g]Eqp x MQ]T2j=.;C ޭ +dc9V|ou;x ;@sP|4vV,]m'(~'\eincRJ̋;ΨJ?FjPNT}jLJ;n Us/?-nι4nfE[[T1 2pWY+k j>IROQ2:E-߽nHHjP6ckTΌrMR_Lsq'C\aCʁ/+cN[Z7;LQOM?ra9o#E9A^R\\:AZLhC7L}fI]O[]6򤋿IX*Bkke[K ]O}7N*k]]̫khw~"-U)P]S|@WD" +*nGPCQ}t3,G°η&Ý8aOӓd$̨"]De@@K2*nnW EDNPgz!P0U5*-e92S;@L?U3.I݂sջbY!̴Քx,s8sؿ4eOYWQVKcH5`i 26[M'-4dR3S({CtRۮQۛ?_?}+o~O?>?o?X)RÛᑤTDV s*kR`7{ߓ.O2CfCĹv/I30K~xԽf%s}")J X[E}W΀e*+ +*]˞O&oċk^Bğy拤cq?_x>9=+`;g“J B6vl3k=Dm&%^0XB? ^LaxUvW@Y{USv T q?E(wٹ u g+(%0* 0'Ҍql\:%hw_0}wUJ8#%0އ  `Ě~ [,48zFx/%]qݩN^B)[kS4E#dȲ˶ wO׀Qʜ^$-awh9~oLs(棂EuRNZbyrl\Ka/sb[{REAYt642j˫8S!.qcT̼CX Z%p,▬ankM[U9_Y%[$|o"tH+;V/,~.#QE)cپf/eEřhpj,Is;#\G#UuVK=ECP(MX&à-k57l)VSy]㹯}\xiGjk6K~1[wyHqPHc(%CplL}434؜3֖f͚0K1ACpj r0zit6gsNtd̑($MR\B_ZhqK,#Voұl:m][؈[E)T򗚇T:';g/PxGkbh0F4buGk΍վjJ:۴!fNXY@1F;8'/)Xtw*V#\>jmWFF/4a#(NQ_hM#kʡ x]S"fsŕn"8 0lmhMy`> >dj4xĎNΒ n8|d˥՟V]*|թFv؞D#/x) XLҪ`ɄXXzWc]50P"Đ?)n鋗_z><$㫇_~{<}ο܍~;/oowhG/w3Ow:?54cQy,Wٌ/\[o $xsp+>+jͧ#:NEXBi+Tk,߿m f ltVRt[Ӹ_luˬ#):izmT-n2ͧl ;}%+hc)[9Y"{sb"}LbʂM(V$ua&~F$1 :p9c0u8ŽN;vpA>ͦ(%ѭBPi40[ ϙEylRo::Kļq+9\?R?TQZsnkߥ{UJ`hTJk*. SCTE ujUԸVs0UgO]TS Ű5l>(Wj_`ܟJ] lTW䊽-"I22${C&KJke9ecܞWv-k%XXv&`=!U.hw#*. S«lukvLUWՐ/Rv͹ SĮOBXN. j]\ڣA,/UG +ǘuBzK~ʠܴF.wtb$lp!6)mЪgSZ 4$]8dqc/[>Z\h_{Le>s Xa.J M$ |9"U4Nv>w)ƀ@3"ד<&NZr=U|3앺uŦy^76I1uKh4dԃ慑urVY +nss*&/n[R.~աCpt$\.F?Ak?cr +QI1TrxtI+¶VHЊi)gˍc=B]ٵo{/s or4 %XB j_ i݂jXȅFL wȽ!)UQgnBܹjcp>m{+sbuXܻ +&cPSC׭ 5G +b5],>1TIQjG4ٌ̀jx˲R +U &$XaV'A^N6l;s8q#sTOZ˕ԋҊ$t (g rt +n.fPL!,8]ŕ@ }&Mޝlb8,)-Z9#j8)ɵcO9~H s[>a 0DpԢ[e|>L$:6^=whHZN%IpFe/ u@6<"5 O|A[,FX!)xA٪1NT:U+k(f6oZa;#s ]2_WXLd} 4TKzآq +K4:^zD(i=װ@BaDn iu3>]$FB6Ev:73rZG=:G}*\i>p)afҞĪ9w &OzE=YTv=(|*JOR"MMOףË;~99q(k i[շ4?+2&v$IS?ɟ?{d^-z fvf Cx(Doy'G%jdQE0Tf`tx_-Xkmzc=ūe"jle\kQn +B!ytcʃe)`*.`CCo;k q1MNm-#nƲ*!\F4*%ip8jv]dдW˜};`ɱCx*h;TzzAGK76BL _v0 +6|JנX/l_Z>pV⧠峣V0P +o ڇga@\½ ,N*nk K{!:"Jlh9]f[Ν*ϝ=io郡$ E*YF pq_9IQA,{M=:-XPyA!'ndf,Ks_fZ)35,↖dܒk+A64`R(aI>(h^q4 q ~J :Y tPP:mO!sݹVEr .0Cnvf%i~4< [p(쀆m~͸[k]xC d8$([4]MŞJzA!Hܒ"Չy!F4ّd~oEc<Ā|z463i}cy|=vݲںN21S2:sj,ɸoY׹*w >> +2Z^6.VgLkEMXrsի5':"gy#5ɳ.H{avc5#ȸF 8PZ<{WPu?zzPƛ@%z%DTɳ{x +zc,sn +c`+4iۙA!p.Qg~kg ă,{'ӡ= {p[/@]ҭּ">c$<'iimKveꜩv"wDZbB!&LYn\k5a@,bn[6*c+*lNTlY +v礒t[,P%MĮ6^4(W6g!ѓ/ء/7}8_!q[JT@Xr+lhMb%A?jD#Bn10vʼn`,QZм jiK <* +A!ǫXwqm(Yڽ1n DA|+=F(hr <3 92H=Q4 $Kz)^*) Qڷ9[geQ_ +  |Wվvk?Т|I_jK(+lkXe+lEiU'whpH y +|\emH"2QijlWG0]d0& +LܡzZ#X +X-K 5{5+& <Ĵ#bj&m(oqti,;?Bח{K#mQUt3[J22{lefwr별?n*>uc(qI;7"ESPqe eZtnhywΩ7U05 y- *G{%vs)k m$['o$KRQ0\e{t/k\6Ǣ熁p7sp-yn`@V 7ufWi:f SKϗkQ7SDS^&o=ZI5qL>5=!CJAT Rt&@ޗ/O킒oovpx2"1HtƊ¯kԵ}6q#ZVC]o(99Z˱=nJ p+4/R}v8Ys%gW:0xv$vHw7FoN?d8΁tXTS1-if!"0n8Cau?2z Xtv0TK I}ܿGL7ܺh@DuPz֜L}-R,97`;SRZc|&S81pz#nDTVajtghݺ%P5=Coӻȹ|ps*KfkEc+.^+k]C|Fs$ ֭YΤ"Pf~> +H\W +ɟBY|&>cWÁ$k]{ۑFw2ۻ{/ݷM:8$t37sXòcggO_,w?;_;~7㇯_|_Ç7e|__gWڋGj_r װ~7 +kCs;o.,ep(WGӧ7O?S=ɩ(DQsjK̋b;2ؼ+L1,,z{|[AS̘-!=SپԘcm=i҇_|({8@mqu{V?\ˍ9cb]tVX!`bCN2q:3nXEC{x(_-(B4QV@f4\a3So|.y^,[ jom/}\OCkIRilfNQExmR,k8(7+ rf4ap &LslDiHHg lFJV7b7.iCL~ ChpN{yPye1$zr2v<%X>g3IaRx2ThXY4vI_6DC9Jzt52)..1ŮҽsfR/<0)Dd2fX$1UmՄW E#X]D)(uzBs}خ$>"#)hX+6ƻ1j~eFd`a0sps0ē.AU5]e0c!)(gL׈r'p}.}@m#g1-xa;^Vc(V*WN$GZ1  +4l6L='NVHV3`ڡ:HW̎LZ0u6S> @bQTN '(ɠckKbΪ΂ܓT(Z29df.z-ي* S4V*oSLLXa8FM=f݊>sn-ۺhuAJ1ΚVԊ_ +*Mv 쌤v pht3C>4y-z#YH?1aȱS+yk.I,fkƭ3Z6mf zY rKQs)ٖH}MNvYx4O[3ɬ.R Pfe0K4DkN@|$Kys/M3F͡QFl`d'I+aJC hkhY2v/`-_F*v$sJLx0xbMh37r`+vD md"\*FȠcrCdS9B5r^:> 2зzl2]f4r+gU[tJcLcn3o1,#* +A#0}g6ͅ[lb$.OΧGS9_nkFu +غŐ7F- mk4SBn]FNex|Cu) 7B8`pU{Qt,ߪ}_(aҧh209F\g7s5v0R)7g'$kM`j#>`<_(Ȼ.uجC(h"R73s5/4mk{C(y(7?xeU0V> !`ཅwT]; VQR_ǧY GŖ^} 蟫__+'~~C>A>&B~_cJ&=:W<+ʟE*gB_ < 3LgtÇ;l񴀐f 1'vþх;c{Q86D&1qIR.#B݅\ֵHK9ԱZ6`Fu37~"`;LH!>#ԃ̍tċYz{&N`7EGWBc ~hdscG m:!495Ve9WEêU:il Cmv?wX Xy$'Ǐj*~!xYn +u^od/c(6[ ܣTȦ + D9@5cu=͛GWsHQ)~Bq M2<'L +7/=Xa,ge\췇Z,ՖxR5jckԲt)v0vbQ嵠Hv$BSSL3~rC.lw]upx {(UGϜt5іJAغ{>wDwтK}I[̥N@kI ZX°~u57ȂhLh;`F ڢ*7m~Qh,眉-OKʼn?k*6g>gėRg%~iJ!y\[gElKUkMQe6.W|ieS&Ekdkc;ŶGW{$9;PuYȎ5ņl{3uS%(@KջOgN+35Ks.V[3/DKq>f/(Ê#uS~i;!:Tm˧:Q Kh<SeSgTia5\i=s r[Q^;c=ɿ)PV߀x,91-nEG[vnY4G6#i9_⭿GV7r$i!^cpً}TM/Tm[-hvv7Ws2хt#S|`~ endstream endobj 15 0 obj 18546 endobj 16 0 obj << /Filter [ /FlateDecode ] /Length 17 0 R >> stream +HlKrW{ +@`wgOo +(]gBmdCCms폄2GO؛Zˌl_0S cs%gjpضf|p7|Xzdݺs۱t?ǘ^p\bxЛ$sllom,ycV0_J&BC-f\̛~:_&-{Ҟ7n_^ѧ\_S=zM{my|MiX'<^/B x'Uۢǚd&MH/]^A4hmsC3"IE} U>cߛay]fk r 6Zt ni]"l*vʅNuN_{U.!=بِږNp\\+s`|J Ȥr/}The@@80$.ŧl֛Gʑ_OPo3wlNYI4;B`שN6lJImh;üXKw"^{fBe6VTXu|^u-碑M/>5| ӽvjem޿R F/;$E:L{ HncctvmN +qC%tFWۯOwo +-^@$Pՙfӗͥ°2`JA.wxi[}6oFVxƛ}H<}RWo%utNw89SkstS+辯9[%"0"TR6`֎'3kuHBQ/݅SO|le${?#oDҩ?tm|+MسFSBjtf#uԹ9Dž}p{8|Z;r,(_,hYREkD|ѣ.!vzp聧* 8& '#",`#1,f1j\*MӋ +/6U\+2d$͞y/*؀8 }!vƫzdVlۄЍg~)E]W=1瑍9o\y@_Y~i:U&Zj?FItYOѪʤmQꀼlH <[90w;TKE;۸fU\L(o8r +݁h.We`tf*6BF~ =?Uš­ٮ/jeB.36W1: 9F}ZDGfEHosYuCLz_=!_vG֪M25ƛ `wЖC1H-ỽ{1PU-1ݡ *pu{׉s]Fung*U,SΊczwBZvܷ++6٪hnM'EMkq;sG9a,1q[ ^3ͨrnO0Zm5D&kF:k+T:^{Je5'=axO.b*de^_Q hr,;^E!%}LollDRQ*8y:24j.S %sZ8FPC3SjU/s~IzJnЫ@vvèyl@aZT2bfV#eüoMXSvo܏sKi֪DvÙF\v Hjf;&Ӭ6נsk2Q?yd~VifLDKǮqF_kf +dt kNYA5t~ken\+u-5CBs7u7L)]lyߟ8DQj)*OV8ʾw1h.ߧ$^CR̕m +v 32+o8Z2Хá19p".˜ vr:֑>W£N%s϶l"H]1Rtk>8S1NVc^e\ԕH2oQA,Tp>y)M + pUW-t:Yb^e@N[^:S&<(C *0.E`@ +1쥲 %+\tt&> 3DZMV#OuO*Pxˉo`{tne!lt4~|hoaMZ!ϋyӐBuHxg|GOV({jE˥?c?U# 2/s_>bU00(YZXp! ̦/|<Ξp +Ry4Yr7$]x k_vG,qp6{#ѭ(u ZcqI49¸BM{[`LN0Bbym^VV!nٱGM&f\8b=;A-4#X775Vk㗸H--N٭`NO{pVxYK6`{MD[t* f閰V8 g6vW @ 2Ykshڌdq2`J!`CmV2`@llZS= +Xl +>*V)ՊFƨ(>zH| MX9G|}^E +#6F^jX0߂[pHEK xOyP se$x--NdĿia%fp;ʀm6w|joEn*o^:w +t-OS^ҥ5S[iѹ1S%+GE)w5ehT"u t`43L +,yF&a0rjxOxm$ `0 BqĵfhKr_|~iҲfDAN +K Ya੄ҩ~~-~9MxҍD,,sgOkknXeR?OˁZ] \('azQ!NP | ߟ?@RdA YAp%I8˰[`ߵ7ugiVr5wUWi1<u̫yߕȮpzCd%&7Nš&u[rܫeD5[Hmxn\xs..츨CR&7">nN,ܼ +"c߄G-0 +}0Pobvlin"`LBψU3vt0XLl FպS=jޛ~ɭsGDw^ͬ9;ZeW^Ea@ϼJ5׀}rieRUh:,0-LH7}uFyk<eFpi(*x62cnR^ Fо`f\;H;v=n8tΌ'>zIӻ 붧[u0'R^kͻeӽSJqL=0qIxܛ:bD+3n'N"2?}w y"@L;O u'Q m<62sW-dsmY'PgI鲅 #4.l{62Ocy0}ej i+ALgWJ_|=cmP2 Z)4md~݂Ƃ&ě]V些ɎNK >zg{zr7sYQӯ8&{%wŧ3y jy!{51~~K -b7aβ._w^ & 3N+R5&ӓ9WBVe9nMhyH09`1\&t $QTK*itS`a;!bT5LIj٤y7*VAZǰaQ4 +5_3.`cz*ǯ ?9XX,7v,mwD2^5$w-6!$.lM*RQ&eg`tL|=JAV9ZdEEIȠ[>(fx2bS.h2SDחLOv+Hq!7_wMUE2>6m( 'ԛ$0'~%q;Ⱥi |L #hPg +)@ɿit>GeXcp PS-sm'RvXCmytD20t7޻:sP `XQ$'s ̈́ǤE{8tPtyn G6IM(2XGPlvv N2XlNj'M k`Te7X$D*15zHg4n21Ɯ@dyVҢYK5HO`fBlqpL +Gf)FB"fpM$zo1q_Yt<">FkFqp<mS1MJ蓼 n7J#9Efswףh2_bm׌.N-hn2DZ6P6sċ&&5޽,ċ!Jh>ԙ!eQ0ђ:=]{'7 =b2n0*i  agh֍l#7<Oʬ͍Mu(cɫ(8f ?xIS8:ىm'dbp b\ +|5(`Æ6~l 22:ۨ6*wȂa:&t0<X\"[LU3Z2|6eJ6 QлO˄u  +יHx[,dDjs{ZjMXgY5E<21I|=tXjc#yo-ǺiMZ `-p59VuL)_l;P%~C*"eH Q#Y()FBSG38暁oE-ߢ겔0fjsnBC p] cn:>YϕufҧjG&P$$`#[PꨛGVߘUFr*^ q` M{ j%#XfvZ+vZs =^BHEzSGz }lR&}cKkB,# ;qyJ1˚npCי<_ɨ*<{.ke +h"HѨY|6^S?fʸ9 +!}P }91^Iᜳ~V'x& +FL!2m>,wor + ޻av@褍@m4;K|U\%a f{]7 Q +e\S~ χ>fo~o?_~~w??}P>yc?п. @?P W$Y:9Fp(o>}x @3|2J-հA0p[H |Jg[~Uj z2ė7L,6(FcE(`"<7G7NI=> p2w$︺ +:M<Ӧlf֩HqnѣGĖ=zoFG[5 =Gyb4#="Vs&L)z˚;4aTj$UysK{\_{ te9ۂS^v^syAȉίz=E +~R"Zۙ^Ml 8RCj#V#<+zZ:ul8 rR3du.n13` QMmz5C=]̓kq^ )3RYպź.c4Wm^fy']Jx'8G]aЩbs_S}48Hqzbɰ]gV5Q?tˡvr"SrUEr0F蔣85kڞR'vIV S*h6{Bza#D.mwJr~ɳb,] Hs]YW,E}~9oLĊjͩ[x:aP:FfWIXfk.yķ^ E=\!` + ꒨dPNO?'b-$ڹ.^#$aaz@"ɋ~0{s}reDsq&sAsJ>TRMцd\f.dT6,2Hxho] u{3z.Y +i]mN!XOETòpv\mC3fI6I4̺МVID' =T`p(c$dQO[貘O:ogR$kqMQ?7UAAyVCw?W;$;,H+ )QGw'*Șm]cZjgcx5y#G'YWX@_ 8zz=u$)s~%>( ,YEm.}5 wa(-TMg$B9e+\V҃@^Y+f%:E]p H*>:&̮]!"(IIN [F/,-)^3J2~lвLpsl [/*G8ZUkW$ d7A.0i/$I(@|x}9 +ٌcR. &{R/1ʝz@PrS1Xt0@c/RҲ?)h2e' +`&5O@cxK57W>H@wx}8̲-yF1MZA+qKNq$]2ĀwBߗ<ԑzxMb# b ["5߹I +1[miֿІ}jTPpvxO}8;ЋĮס>N?,;4jq{~6Gʌnߨaբ +Q~/v_Gc_̆.M峋u60N5 +Ml FQ-+@\ 鏅?RbhKI|oh +@}]_b 3Y:CmSRܗ뾙,gAގ7:>Ce.Jd,@c?b̡1>Y`TBZ]]{h di`F(s{]2 ځOxgmnI /5ewyz sž '$mrrںf!Fe$k,,Yg'Df64>fi4{Q4F$$?kX~T Mr!$8h}|gn{V5zmZ>4-GQ!_lOnrz+VZVJjujC4n,8*^X'Bۘ}u-dxQPtN&30|Y#>>q%.fENֹ2սH{(ղ$9N:,42I>25.YӁឦ&C#=b3JY\`$6Rpx]n8.]ԟ*r|5'R_PA;DAdǸdmz, +|F2/ǤXI5rR>(RZp*J.U)#c^1k"O@~O} ; h׿]IQ_ e_JM@M;D@R +`sYU'Ɵ>YA9L˭Q>ws8R ^@lB 3u!dĮ ;?54aJH'B lՃDʘ0bݰaͶ*lnUg @C*K6sl'bveL=?ۼ]`W]"x9by6Q;X_Lk5l\bI`lֺ*pw#r->Bbl|!/bgޙ3BXN2@^d\U.5 з$%fJdSu>+>a l\!ͯKIڅe%qσgޙ3B8 pLoT p8/Nq˙ K XA*>03`E'@7(_?תv_DS_m+vG ?dbo՝z_Y]]]UVҲ${..yl>ܙlF mED{% 3sotamD:#|q]3!G}˜.?h;|7/j'+ݢ:}zrFt7J[ DY&TԶí8j=_hXE v茜 +Т2W <ֆIHΫdyAVN>V現9\ ^$/wUk5%2 hK VΣY9p8h5l)[k둰ikK ^T.4̮,9@/{4.Ymi7 1؍ՅUO6"-oj1-&;at۪$Bן׏ؽ;#D~k@e `x՟yj-zpx{]g1YihT`$X5H`\[Hۼ}m1um@z|w~/Z:͵P[ݻ;o{~vc ^wCʨ#^Y`r]lMfNз7߂aSFZ" ]9b.뙠W|:v2xehtq7O 4R9\k^^njRKq N2fWli&EYJy3N 뽹b,X;5"rمI]BE0xv0Z5!@NrKIUˣpuKGͶJIV<}xx{gϏ߾zo?ǻ[>9*K|L/埀sl?to! +=%!&@y.[<,³'tr/3>A'c]cdXaB+mP dX)$ |_) 8&z:U88{#]ե@SфL|t}$}vEX]!# 4uss0[Rjb菎*՚-WRu +EepSˁb'逢s' !Kg7Sϕ=AsDpǂpuWVMӃ2Xt e"E#=.ڌqp9@j@A'S{vr!g/\V|gNE-XA1@-zWW3/( ,5>3hv!%b) *|DZ `lX?uf,HUVj[| `Sl1};sYmzodWD6z *@hNS< lgWZLZ"qJy:˂ K17]X,Ipb3qP-qck|dRV`ߋ{6\f\`g վjDe]# +rI-z4 +"Adi9?fU_[,78 aLI\m6ĐܾwNaϱF~.JL  ,#=U3M]u +0=TE'3I-V֑wߎ@@](lpBX֗9OB2/3IVE!M8CM%v5z;ZSe Gn(Q6cT]g{6j!: fQn๲A& ֛hXd:'d"V-k@J9Mw3Г<X; cpss~ +35nuOAA+:D2Q >): .b NTv +H.ySĢCHR' sd*sAe U|rgZ s[UqW] +W?|O?߾[@Ͼ[;9??~O?z?߿Ϗktk+ߠz66FTki-](5!~MK馼:Z{3@Lf)3J'3 =1m8C pjhoBhmW$H"rv6i8% nh9#58ت`C-=h{ ;.v,,4e] -Go$Pk˴h8mD#&X&8k^632/UU^z=P5l!q5+5&*^W&8C >cєV% ⨑RXFʜ`A'pYF\6- +MT.z#sӕ9,$^p-iJCpr$}%s2+vRK6lr  uw#$Id$d#n6tذu:zb%}lϺ Ulo0Y[[|U@7{e*o{nn![(5#֖~O( V7\6:G֘`Ct[z ; 0 si݃XQ=*;t'(v6BLm0]dlO(۵u dlkGhcv >NsApTuVI}ئeO7 k~P*=m>S6#&"s !GPا |%kO+@/`x}O[V 5CŮ/H`S0Xr-bO {ľ_5k~.Ja߾O{_|_?|~x7_|wK[}yo?o/?> +}K`s}283Onۿl)$XF+pi;[j;=>&jsJlff]fd&x&e $ׂAEF_mz1Ұ P5`8U%2a12ZL0Oq. |v7LL|{6- .nl<&cۄfzi6CV`\k1{^&H1d n109ZKa#f۷aOM`N17#4y_ >a&Sϓkv{o{L{*Fgeuϼ1` C|NcmybƥKaI97Fuڨ'E%"OOjmրŔG"|V ۍs +!:E~s{[nٷh}U}^,4zޭwͥ J}>5k$'`^{CfXPj+ 9LMlhbTI2c&Z4x"dx1ÔRݛXce+{ffٙLtS_uIL8YSA%LNU0%E?G=e[< +.ϋ{guBze#,lRqAůqH(Ι"ӟ@zd@m?h>v_~ |Tv)3ϥzЫ+1p1zKХBm(X5~qTA8KlE9gV 焞;Yl}5P“VF g1]bG2R- \"yVyXIUH䰵۟bʵ8mHmF;AL֒2_ޅ7X*ʉCJb+ R#Sz>+r{ߕ\}sY9 +P>1Ș\#CGKWVzq=Z4ݝE$'.EmcZPTWEHCTz̚Pa%TW>yVk^+eAWT/)Ym>%Q` kp^2y}o _^0*Ġ9~im\ ?w\a~4.ͩm优&6؍kFvZzTĵ2|qXQ2^z*##ؖv(*@)uwj}PaSo1~3mb +a2\iKWZJ-YFS : 3kG?ɗ2h~$e$c 4[WMfȮknJ*27]0M,mF_Nr:=yzо<Pm7a&9u(yJ(lK55Q+X~ ֌o{cRl-JG }9$wC0_0-5WuzTVe[ eϢ~ƷT<3m",ʠvRi" >9iNMA(Εܴb +Xi\Y3k +jY q˺N1WQz;[nOxYX2:j59xYků]64qeP3#_iZŏ 1lz[ GOu Rzۧ MhdO [zo2uK3aOIq#`SöˢbbjtBn~FZN"{OiU}k9: k&euyEJ=WP/b53"ae3$KK뀕zeVӓuU* jkwX =,QOPwAmsIjBC/ٖ/2>S"CQuŝ4taS U+cv9Sqów/`L 7HJ V|'0N *Y(ȱOB9d_ cJ/2e4W?:]* +U5B fRX2za[7(q.L!TrC"6DEnw 7_ mSNpeF#DSkrF/q+Gz +0=^nd^ fove2ڏaqI~\G0Yq3k /r = {=8 >z42 c's:䊫r8w #dʘFFNG+Ze8 [{Qq[W6Φ(/LIp!7 ZJ϶)UݢZ ~jt.%<'޶$؀l)X--D(Y?uIJI]A3ߠ@ߚRb̫.)y;TwSDo +bI;dVx$zƒqCN\:&:_XLIbV9;9bE_[,k;ox 4"?C Km9q`ݍ=9x];(p]5)67ZUrf)+*nt<A UHaO Yֶ)3JBki|w۰#cRgW{r74Xپu5l`*V.(;4Fr1*at6{w"-gŶGAOסc,Н<6Lil]"^Tb1īZY]̎.ot~!kD9;ogYu{;/5 +QO籮-)E`<1uKZ]b&%,}ɂxR dTM6n#ƕVV̿Ya=yLoNIXXˉZK'>`:͊R塞( dqjNtaΣP{Iڽͪ|@qכ Q\1"|k ^J;~Ps}Jn +B q,~xx~}Kޘlo2s4.M9=4Ѽv͐_.^u;trw(7DžU4-ve>U uGwcI°01Y?}25RqGAх9?yD)Pbb1[ࠕރ"7Ыr ؖްRSl]ߣT0hNQryPY$ E㻢s2R=f+%^-A\gZ-eD( H[q]DbPrAVc&v<-utUIm/w Sީ@`s14zR0`@Hւ4<.s!в57 +`S{`Q[s_4 :ҘCW8]`haF)o6 O/A? ;v0ǝS26/Es- ;/ xIDsvmM&~aia2wbX21X|_38AdVޅUss*Xz {^d; x788Y-d⋰ߚg>0[!);Ąv endstream endobj 17 0 obj 20825 endobj 18 0 obj << /Filter [ /FlateDecode ] /Length 19 0 R >> stream +H|W;d <ܡlE G2dozsW`tםŇ'Sa{ɩ5u> +ip @-~3!}7D +c[)B5#y|)'g`Xj"m]l$TDdY,zʾ"|Zpn԰ETYlk˺ W)r`qٖ>4-C*ܮkv:c#bHEiL[? U*oY]5n{NxEհ__dN~R7 &7{=ΫVKK]Yx+i @x{i}"Ԁ.r"mlmU-g]7UN[]hبZZ +&R(~=m,ikfW9v9ySmC"Z'Wo}HE@nD?MQSH1ԤdQGT(ljrtۺY?PwpYGIޚ+̾|0H$=XO#_38b)eFIwE_iӗ_(x>Ѝw96x_9&t*]^cs".:t^3S?bqglv 7m["\gdt5w&BxM:]WnneYA6 Ю:vm(1ہ`)C~)';I;[Ts V>,86`qEQHQ}' Rrb,O|y++w$M`&ڥ_N'2ӛ7b!Z Gg{h7 } gzbz^2ڡ269j[ZÍQk&A$8;γY /gWɌ$`뺗  2 CFޭ.2 P7&juWI:쩩- *u+S˯X)9{_/p5WE窧sL2\]9#mƝDݸ:4hp 3Z4E@t* ro //Vj ҵ+j^+@\&}wlVkG-.s\5R-`K)p::(W\&6F+ETZ;CmXTzN=pM끎;$Gպ8O)H飻1S\R#@LdG0LJ{ܰ]+Q$ݙ(FLUo^6u^`kزɞ3&w E$U0oCM66GkkԶ⛫|*R ~-M( g!C>bl$,S8<&ϟIapU];Rh Go=j$ "{.(1A9Uj4O],p|Ѭ6R-<`֭RfT3iMMK ,v\ZKqq>JդLӬ452yO { ,Oo{䀴_ߏb z)cG + b>D322/3__^Fd}le`bldܗL=ԁb|k'F2?#pZ5_Q=[\'hu&'2ė2AF v}|ᅄ!̦;EB=- 4]͝;2P^ȵA}a!s$.v!;NRq@uP<= WU>u{ \Zo0CחW0iH+"ز{f l̨-m >6WQsǗQrZ=ΩpHQ1|vJ&0zaN AFv<OlM1KmX/q$a8LP ^{RqQ/՜ &1mka74.=4&C:ΚkҊP^ Ɖ׏o2_G() +OaqB8Q<:Dy \n|IF)ԛpIA_7`l 6=)/X6ZCyK :d2`qT_}p"\W%vG^op!U=R]5A$A-"< ^jx/NӥR-(V<]s?e5( 8dJT. ef!yiU{յؑ_0,̪ؐTHInȲaaY36'YpƄ9*߮`cQ*L] my, (|_xj,Nuv.Hc.#3Ig8 +*=@1|85tmvXRerԫg\v٭?<$LwaNA64EdRSShx{8uDrtQ|BqJuנl`۳['1t=_wDTp-^2#pNc^HûnB&4G'=Uvv +d9K=r. YX*>\tFi|T\uY96 +֓NKխ@U< +\v[몣_=vz柛}/??{ |OOOo/ߝ%⫻OS8QyW߀wJ~N+fK#EqQp CSWO,pOԅd gq AK@8e@QZb7 ֥e+%+%`k24=RLfT3r;FѼP"ُJtXLٝr<}<(k2a% T=+%%kLDm-A`CܺDH!%$yYJด{H[EQݮ6N{rt֕òXٺFꠔ&j҉OkvÚ@=vց"bVa5V˸?𓮒g_* 0`qLcmk֒V,u烟Z%.'ǫ BV]uĥf[4u𳠑KUPmk._ˢbСpb}^A}T0J* +d؜u"рDz5jZ3`1TvӿҍT_WٔDVJ}98yȘ^[]xtJ1$su]Nm)ep{b`Ms}7c^` ~+AhPDq ,)JP[b^1UKZZl"4G:8uQܕL@!u 1 Y|HH6/NW#47?><>NH<ׯ?]<wy*F7Y^(1#f<+Lx|˸C;lڇ_VFk%pCF^YZ8!'K9'3%8y5lk -OJcTMr0C_PV9&|"w'7*(|p0S'7 `OW<:*ZS[ፇƶ]tHzтf>.|:܎pwru?]#qtP<)/ϤM)Nv :x˄LaFTYrƪ-39kR +.d0/\ jAs6ӹtGY4l9\R$%5o]^Wܮq()L(<6ɢK)fR +q>|uYmaDٍb.\F=K$> ww{x]Ϛ]\7̙M+pL]M!z{+'Q?ԎϺӕi_ [i 8#P)ᴷ}T &xgcNQkpn*|Mx&ӦM߯y,8l T%epe|sN>w1Y|2`\r=qf,,ior-1'WKv-9 +\A+GBT߰л@>(;LJ ?xbv-`e<׷HYj2֥/>eJ6ىk[79CWGXdփm3h`^Jm:x#n"yl,ŃrQ9Բ `44xUy00!m#EARD9y@I 77Ӡ_Y|T10A)Ѓ)d`2V6[`ثo8;^fn)+I]#\uE6#}1bL#E` +͘2b<ĝf|耞> !!OEd,ŴnyӀ7javeYH~}9wFltm#9>4&i증wsx<[bv2'w:9+Mm*#'W{Ng'/j<}5B1߾o0uՅťfDTKmDzwmXI9 mi{coW ] +J$SS\< / +ěpo0?vOlZ69}iesUeR_ס61Y$7|={t*Mtjk;NQvDt$rO0lw{xNtm"~]64r+7vƣ**t4wC'W[#qմ9xϧ[6t]EzqVh3(WC) 6 JKZѻvSF_4kl5Ht~]R,C),ܘ]4rm'ce$ZHطXp}>j]Uxj}+DecwbIh2{K0%Z^1YݬWI =3C|.khv"B4= @Nv@msD\*A=sJ2D0i 9ME&Z bN(hmP^%7{TJ/Ptc[qy'b~/5uпw4E޷AkҞ T(]p-n`X'BuT?1tВ@y~o (k' `tшN[3 -@Lgs}y-Sl\36nb(|޲%P[ +Wy:|ںwX^Y6ýpD!*_}t!h,vۼUkl6b^7pg_WJ~ys'5`*҇ҍkvAxgWnVf, d~f5ezqm4%-0d&5j' p8 w>^} 2T { T\l'G_}1;{%HҖ+Ґp5rI:uB`\ M*|`kЛeEۥ 9-SCE/(a|+lk/jxAK҅/:JsEtffEi0/.l$}o0el@dpMd)+;@hd5N7VJgݢ,, Pj}VDf֍oO6[׿O|߽}ǟß駏?9vw?sg]r>Ot(@oE@O뽽碵_]iաΕJޖy:8P; j(<.!}b"A!hXЦ#DH>ώ-~jɾ,q^ki!˶.~DaJ ׀;$nn@ Ѯ;|_G<o}`N8P +E5*rzM3Gx_nu YD urfsdt;uIW?4\fDۑ$2N;Q& L,ty*YW7UdO9dK!4Wq̹QCΞFsb~,6/K?/wb fOm :uqXza~mmgaQy0 pmoZ3v3v"+كlhCVp8E 3]M8ga9$ZM<Ƹ[~U϶bY 0x[V O벢# a)'ҌjAF̞]v0W s0H$znTz̢$Ko,gP&_Wk=a,p*!ˬAX(/B+y{w)c[hwfNpBHD:9dgju]o?4H@)mؤ*mU6흷n@l"=ux~'=6)H2RZKx^⼖ 28@%Qh0y?X$a-2U3U, i ˢ[L}H nqTR"(Mzi< +n<5Gjp`\P՞] ZGtsx,9ZAJLVɞkH0@ |Ӏ[&nӢ0ZMhM<+*֦D.uh잆!w(MB)I!Wdž(=UA\-Vj#}y5!{\& 1m?+5im몮 \*W"M + 6W"=Tº0o;F̵?yͷ燛˳׀Us֞lvs}~a{?}}mfaFeU7i^nqp)rX宛CnDk;lDW" k>/pm?^ON_7|?>_ރwyyvf𓓰|¿q gG@le-ׯvυ JgG_C[4Qr_]w69AԏG8 GqO%Iܹ:fG_nq{@_<8M +D'i2; $9Z8!$ֻN ~<Ѫ kk ]lFZ0%A;@q,%0k Ha؆%Neh}g5t[cIوַ4F3hP786)NFԍOG- '2'>*X-L@|DU*y=E_ū8dL/P̳(}hd}1fQS{!D%/PDņ6U"k^ aXE3W]WŝWb״ ֬"͞뒋rBx@*8JPEGlO=UUN欶.HG@@xao`쥔WVeFh(2}~ 5kaX+6l*ıo`Obްjcu +-EaA4G_;ʁV{h_ ;Ȋ/-$bigzmU}[Q-ORQu6m<Ա kk{Q!kk-/s'nz,jDV(nVL7lԶb] 4 Kl+%g][fL;D߇ȝ.WƦ[_w0r0=cӉ_x bBgGX)({ M(XޗӺ"0ݦvQSf4)|rS07 J%ihQ)Z1 Q%s@kM"[ӒTD"Ze;tDˡJtg  a%7f3M6#Vے9e2pd\8d{h팇DUIƕ -<־l\N%ڜe*fEJCذu#DYNZo,95Wc].틃>MoOøh\(Xe zsB,8`i_$*Kc|4AT0r0c2Uee#N|i8`^/$&0`H gʜ7yX0+VN+0tm%*Ԇ1<ћg7ߞnί.Ϯ?o_ +۳71lW.n?pvqݓ+| 6BΎ7ܜ\Q@!6OsڵZڵ֬vEHS*bVkm h #%v))jkUa7S.U*JUk40b)if1" 48(rwfJ1.fXIw͖Y MMkNP:g55 zUSsn U4DϐXVqP6;4*es͸p%31fX:r?d"MuOrXU`m`Rb֝}|P]dTR(#+p"Q127A^ 9A-Z)kb}K}IXra4dQD@@NSk=i/v0p +>XXqSSyf Ikه[oB-.d`5f4mq=DlI49F-[Saa 5 &ֻαT_LOr#r+ZxQ&:_KrjeاC3nB֬ä݄Dp+άXv4Ia~g)pW&!|L؀.dc-y"^!"k;l8a*jC$bBNRw%(Pq58;aCK/f~~)Nhz6\3̀k{d~^(|KıZۦ53{ +. t hMx`TwkY;7&waܷc#V:?񸫫ɓ.$oAL[ьl"gխw3G!ᣥյVhCđ-2<\il(e9CG}l#UU,D>g˿Cۇy 5~l&3WGti=B)u ))GWEwX\e2p1Jyσ3 gOzx䓨Oh8 n~'1㒆!.Pu'E4괅jQ'HCa{/iMkT+ƶ>Ex|Nu2BU`4bzaFwYYVqJh@댭U%Iڔe|tkS^J6"1cn ?6H`đUq:)-gDߖ386A%(HsC {u$u(v(z$x]2BsePLW#.2uBj {+մE:[CVe.{Bٯo* FMƘ\Oj8\FCi繁&МGfEfm-7p?PMK=M\͢왩|+J} L v5֮}Wg<[اc ܏?=Ե~' x(ȶjޢXռa{5oVuj>2<#ۮxƧ>69$}N/~6Gֆꃄ#| Nߝ\BXt~=1 +e4d'Y*GYd|޸$!T9л3g!-{||m"Z%k9Jb +/[T@tzucki괉6i?ɻnKWl V c !0, F d u6%Mr)*1 @J (Rd!PϗFY%+4tS`6N` +ƭͫcbOV4VC'{E3:n=kL&,"aǣ[(PV;= Ӫ#:Te;0Ex{bpDD:o< +b hyn]'>Y:{<>i)1ZځShR]_6d:ɬL7e/EE?8{M쁰BS CL^z+NϔlKX鶁;qd$)@װVqLLHp*uv6=~Z|lpW7/Ap˶]1BJIHĘ$jGY SWtPt&i+ec4#SN7TgԴ[yBَ̜&FF>7ů_o~|g ӷ?|݇O^|qF>?+? +KH]ֽw*= ,w#g*A GӂaQ&f þTknFjMMӦ$O:[MC9[s~ 'DQdMU(@g0rJ k0-΅#_ZH&xi0ֳi'IՌ BNܾ1$mlUERR+^pJ=Jq-9y5Pkeϱ Aաm.ѕ\x(/|e ޅƊap)d ͒`L `-X5#P\/l$dzG3R·[1xAev٢?tRSciC-y ?[u78Kl +.oB7\lzi]$ +yshI8ڪq&x|÷cԝchNM /ZQ><~y7M0&\7:{ {xL0{B-*OVZ!8RD(;IkZUf#Lj\Wʒ5̐[1USQ<{*D]\vp|<;wO~ DzE֑qɉXBR.&55)I24 b+7L܆ZȗVftq|5ݠְ;?Ф$ѫq_\O;BW[VgqWB\ ѣcȚ rX@꤇ fnP@LVF#u wX}[/l5#~엡 $QHp01dD>{XP[]]9 F!j&;N:OB@d we#Pnw64,0Ԛʬ뛥:j(~.:O:FG0E!Z>Q›#X%&_[Á\'qBQLXe!=:h5Qr#;cpImES=߆$1^(̄}חJo +vGC&Jmx!XXi"l#P +F]6{ a]c +vdĒMFWLXiـDl$Ȃ +#'(rJ jl<ҙvI/ UNKEwum5n+xwLP$6 S>fB1ΦBХqpA8)+UtKEנmYضk}GQ̛ݵ]hg2[WzvlI^ˏ9c BZ+v50mZB9ydH'8A2`^TYs)hW]T]7,*QF[48.l! +㽺ہ }_z%(t)eUɛJDI|USkw;olLU&%w}i,g,H]Ƣ'\-3!W۲.n*,fFz=˓mR ,a*1%ErY F1xgLNt2L/J@P`L%kGB&hNj}vY`kf<]g쁦xR3h`Yk3w呝vz8*..#KdI``Scѫ}kA`0 pzn 7DgwF|9J2ghмC)hPMk-J8 ·%y0B'YBQ:Fn>Xu&U"$cHTR\U X/3%-Fo1ſbCN:DӻTӣGH˜B$?D&Ϋnm h2k QB A^Q서hdkQwI0v0;+j_ i*>t +=I9vy]7nx䔲{Rr;7)yDZq6DK2 K,q5MJWP( Y:w -eB-gYߐ x?b'fEVEp=AqD+PvMVf>U%-PNQn @ 7(U~xAs#AO~x)V<)tRnPΤkJ.-h9}\OUtQe&M<,/B[y) +LX)kMF)DnFp=e=:4:+PU1mAQ8[c +!kR3rcg +0ZìwYVP==i}uyW//^^^>xq>u2G7?M_0˯n:QX*WfbnnR{*77w'}\ +&I&t,ݦ!<8XztKjق˛Vy+}9{}U3(&X5~3.p`C@X _h]xҡ9s۸:s7M2]P@ [j̗fVakC)lK3F*.0wh]Ex+&g0,- ֨֫>ir[Y}&9eGAoZҬn{lvѵ~_9o%;g0; %(@V}m M;h_C f+<F d0Aj \72{Q[n`4ׯ3|،LyrL7 +|DDo9L-5I@1bۖƙ;9Dӡ!i9D3Kr=Dj{0G=KHnb}\LEw2+}uϺB}%t>pVj̭Vi|J~_CV/7p 7+߫Vn h0ҵؿ 6Q endstream endobj 19 0 obj 18827 endobj 20 0 obj << /Filter [ /FlateDecode ] /Length 21 0 R >> stream +HWˎ\1 -|g\Jvư@0Ķ4 Y)V&`kt*Թ%ݕ*1Tݝ.@vͩmϞooO_O>|~𖟟?ז﷟q=з9.jSCi{b$-ܺFƭ3>I ~#'I܂ܝFl@\p '’kd̬ys{}2R#bLh<[SnHհS/ "лqIO}]eov3WvEs`}`)TMqc|aN{CFa µ9!ު$ _~*ʜ\k~AM)ZBU^x5^L;(x_w7m3foc?O{sBii'_AZ 彸/|6y'c-#o0ѭe$g^XK/\kNI`mY0jU h`7W^dL^MV1@ΩF^^c`TE]l͓jAem +D?"TFe_)q$/M2Rb̀^M6$5 +f5{tE5[ [[+J="gS+lwQ]֣J̰ V%bxsC}`vJ3[< + p}x|>]^O]05'QP^V@(  +h|)k]d/YVCD:`Aϻx> +Woc3PxkBhx›@O5@D`Ti# Z Qu+hԅ V 87}i,|sd|qTDD;͡9(yB7 +%RPez% )!:^s2(N +4&6V L(,%j4,d-W2?-^C[,tg6<-#Z d\ctu&acP0@uЎS3ƛ7{lYirw1f;hcJ;ĺJ(.atsc` +;/Tpе/эIĨ{lɴeID6J6Hr8ZD1eݼd,BZ8*Y.nsT ͋9ٛLI؏ށ,$$6ͻ)(4w'ֻ3e<>@I".A2Ѷf1שp껖ݯrg]u".}8 +6vLETI"X;#!rVv_(wFXzsmc* NAB׳/܏0*W-?Zcͫ5a1[̽>*J'Ur=Uћ6os'J)/8{mH:lµR뢭Kk!J.!ρZS4tl a -`a7sUְy}[m 4TkM B9M_9߿|dz/~|9==osPҋgaZ;_[ھ~m@߾>.@p v,y:*?y?t>hGm`(WzG;[<$:qz+? 2)ԲCӆ TDn W8smSli&%N#:^ fE~mD0CNԠ3G}LbKhp)KRmS"hɰ8ݩCJ<'6 + 0Xp _0i@SjqI;EhU6kJbD4j2)H4Zuⴿ(N^Хm \Fev +r$$D VVִ-#5-`"OpљmqlҺ mP~ ?(05$@0KKe=-3?f1ap:$[N8l ._Q6Z  42ؖ.!*>iQTGh QbG#jCTʣgd[IKM '_i ̧iܖ42%Gq׌j5*LQ ɷ6*ǧP-@g$qF9HSyjCsb|Ěj0*ת!V#'?8w΅_7ݘ9й-fR=;`k֤pRo*2r`" 3,@&TcՕ-f-!hK؋чK #唧K|e,{Ns8P cN) BsR6 CB=֪r r8.6HQfRPw0\|݉q<0pqV2ӸH/ jպ60NA=:''[9J4ŲDI^Oό.#096POL_.-I)`A0ר] +|vT+u +ŋ@VR%spvY/o=UN\(Qg=7UtԪUVC#Ɔ?2dWFSO6ę +╙TyH$09:)QV\̍f931ɸK9%! 9J!U@EJFp,E-*z Tynulj 9ff(cNs *䊴3ɉ?GY쇩{=Ipy g +>ltyєe#'98Xݾ?r2΋( +HGMU vQ#-mXz~67T%3$%Y`Ð0zYǛM0E)>qkSjϹN;4 8 +/}ۡ8*98 + g~g$mEYDa=]yl+ 45YWMcN "ߜ%g%TՃ=M^1 w!K ~?u$g:s_`RKZƤ)?@]M5OD9ԶK(W +8x u@ 2@SM3[ŵ|\ځ- 9v{mLM:"dDre0'AElÄ9b,::uuX-M;c42D.<ڵZTjV= tD?,-XL 9W6 ܇!\ϹT,R҄. ֶPLCfb;iKi?ҘnfUKkZXp۳1 t$U5LiJ) +#kz"@@.x]#U1έ*'&2HS17ifQ"pA3 3uh01 ʵ(I e{S<~- oe"xp Do Gvd:%="!z{g4Nk,_LEA-*;qާd+ՙdcSàTsU<SFQAݬӲcsLiѱ'aA,2 V }$Ǐ]xwp` J v'/z> Sd]>X#A=ME]n98>gL6hԋnd@mXeY_ipohA3a$_DϮJaq б LЫ0LU,4-'y4HB ~cI{Lx:Q,N6,Zcu\/in!>dgѨ=I}kbNX?V LPu);"ҴmaZPX?_1Bv7f14LXh(?S]ߩHQ;/Qp]"B>|@&+OAt55Ӆ/i'6妾nVrWFBs~X H4 0C^0-Jvg 4(gϡa9po? +e- OLHᾡa2aMpQYſi9j}ՠ9cĸ8͎q߯_'^-?."cbMT'.iBg8ky~ly&k,*GLjkt璾iL/IDwЉ vutܴcCdmi"mCdNgJy8=pS00~???~;)Ͽ`Ξq+DO~ 4'4E"e^so{k.eλ>7 t"E c9ҍ ~kz$UO\CuOnUpqo?~<?Vqe5tZ[+^xk!wq|5ԏǖ7h?O׋ErgmԖ'm~ɬ +:^dyy03k~J`yOԲ}{:|8.DgY9Sdy JY8P\`g)m^ii(d^B~[Vo+ ( ?J0UW4X'!S:LԼ;5LBp`xݜEa2XyGA|D#f ‰LX?L@u]6@~c/oWA 6,oL尫MEv"WDtD(g||*MͱXHy?*%q+ōY-b?'-m[.a3l_ +bR4E︥xDå)]f` Ѻ PN➕L\QV-]Dy&"1њ)(f%So&fεSJ:9"_⍼VT`3ݼ b8UjӞ +tnDZ +?u,f+[D d%Њ,UAX2ED-6vPmY}KJnʮBIW, /QnoCH؆p4;Y_TsP #剎FK3 :TiA5ъ_ "8٫Q9Du[M(R5?ܩV6R{JX9p &hZD0&>`b v"b4B0eG-#ŏ(٢ RY\E;?^mE}A*܌<'s'|]toA}<5SBs|J!$jԸ9CG|=9;]ӄ4))<#?Ð7 f8~ +w~U0o{ysnGMWt>8O., Ƿ|F9t=,<јI'.|BmB)V{n6]r[TYع `dYYG^Mw!#Tqa4*_YeF~>]8]7R+г3 㤱 +zKdY0$pZ^!^'pxm45Sͱj۶,B > ^dsaBKKx|\xO zZ$v*xpAnJrҐ,ȿ9ʂҎGp*$1 \xIihI.9-^uaQ`sX%TnQ{'@kݖ/ iyC"+Tdva}-+3sHƌ47R< "r5ߏNB*m a{@#IYRh1ܪ ?٨sI~+ doc +hdg</.'#&&*2=^2qM?io7ne;<>= V<Aζ&T+ߔ/hM21ɄfXzqd6t2AX8CUĸ DLtWi50; SIcHw,`[Clp(H&ĵfv4# ;&ǯ' ]u4d`xf鶋h.oTè# +.*>S{z^-<BG-̽ebK`@acC GI9 4M< aϒB@ 0J"r=WZ'ylCc'|p5XX\O"dz Ӱ8O3H?a._XKX.^ 5ڏ8ߙFMF.Ҫ^Lc脄JH ]NǗգTLb16_%5k  qoaUdbWfhT j|eyR*ζd7fZ3(Iތ3ʎlɣ9W=;EZǣԜi-%@ȡ [Vmr鈤sW:Jʏ8h5^utR$n^S؞=k1H3R\7Q5 MиPl[@1pr8&sb.=u9ke $gLad8 Y =|{scM2\-iDk䜝>[ѯFuI)Y2jj A8e)VK *?4ĦBFSOIhk.7Q^G_f"{i^Ȯڑ$7>E?rfF_aєR PQD`!cd/%k!A"9BPٮj"i8kdz@ʵIˬ=oz| +ۍV;2omY}0hc{F?'P:"ux#eƸ>!mpu +w|F:0}D7CCk2l?z? 46K>4}ɍ.kRYq>vDC dZ7b_>j ']C:@/L?oR5ӳoW +v%E&I(y~2G&6&{IN2WV>q1ӌ:Ța75;8| "& *A +v*uخKP@)RhWQzlP`CUk#ԱBQzq Yj8ȪZ} +:3 %Zw!3!k6#pY9 ȏc#epi&Jq %3r[VmCeM!ћ}bvA#R;a~`ȷUCZS*NPٰԧ+tQZ0Mt9! Gڡ^Y)O'?Hu{F>Bb(1_1:[5) +P]JA"/Wdp6sQsڰI_HUӷsb#qEZ違09+`!c;bk <UvaufߘOQF~16jVP$gb$vFNX9r`F4X' S}&&2JSkcfp تa]2U @P=N D4١ǩeF}T5}`uĒ)"6C8!lt +b`UGZdXcekP-a-t#mQUu(vr +D>b6tNA +"l,A1{״c:4(*bZ^TJ"ԭ'&x$ @4>`2ST7ُoaej?T!O*NJ2݂TԸ.Hљ|f~ ǩ!ٟ#Z}qR8M#enD1;#?/Bt2&W̧YK%*a/&"ERuSVKqL7%GփHD5KﵬuIuL>J]b$cy"Vf5n +x^/nqgY\y~q#-!\Zv)Ϧt! U s;mď1&㴑>ׯ򏺮$+[ BDjŖI +³AfI2H*i'v}_z-f5ħ$?jO^iI,~V&]Ƽ6 J Qx@OFS}uBȵ9&}CD-4/a !3Cex0K]VRXT3MRs/lNm#,a-{ 3F$?BrCe$y[Cr2Shu/LT{!Pژ\jwu \&}HWv>2:,Z<ɉDv9κzsk=81cZy=ΚݚJuyMʿssHgu@(>e|.砏wI2ʦ&L?8f$n,.Bh9eLiȷ㨦~N˺. 2UYR9`p|Ȕ,1&"BQs ~b@ q+泣56IHCV"7+T "pz= B"Czˢ_@N#1H6H8 ĠZ~[&;AK6ym֖ ]=V +0 d$!^T~烱NDPKJs@l /NADP㵫9\s4њ]#=M?Tz`}YXDF?#nW t^~"8tA<]ǣv$ @ˢGORDqϤߘj+DF},hju0Tw龱Hָz2e* ' ȧNgIݭ N' +J$OHN/OĖֹ +qciм;XzdxyDV0:!7CK:+e'/1Z0 ˵qD4O|,}8(uv$<ԓFϖfnijхľ.u;XNmxEQ{ZWbknyYHRSl;9^)v)x@'{+!BGʑSfy.7荒#,#$ÈRT#]N7[oʿwT,Am9K>θJZ?F KUӜNOBm!#͋q)G>V3z}R]Z #9>1B!7ͫ1݄ t0 T7ޏkPcpۑ27sY@ԫdyT@yD$[@0C= JT[= &uS_H(J'RȵX+=uyVy^uwY?0ikvI2 +n{Hۤ mɂ| )e)(݃?klNіzi&KBUqtvZH?fD ݳ t*AMN yY8b*yc!o Z2\ Cq\}ӐCfyaTͣ޷4i1U6~k)+s"5#S#q_H;+߮4pɶد~DđK^aij>;HN<$;ebŢOg%@``4n2a̲cu#Xz&^"<'YhHcqY%9A H޽ը RβUa>:k"aATq:4UA8/}H %fIJh9MZe'k ++ƫ"q53!׎"NǾj&٦!Jc0~u[ަ<^0A1̞g3{q +6$:Z4f̃[)h ( 8(>_zi);^97 cSTXLfY&#fLHqj#uz=6{>PmuUiR3@R 85@r)n9+i8DjMP*ηPV˭*8IXkڛ(0B CU; +0ֶ,r`&MamE4K73[q&a@^xV"i(^O5et5UMpi|OJum b1bQ #?Yu3'*Wt0+aIfcgSQA(YL4Svg),-9ht1k sA08lvRsGrIȊDM~aKm6gNܑ/d)nָ_d3`+l$g(;Z`4u >&<>>[.J +2"/Q 㯐Y/z)ME$tWs" ="&>Z98}%V@ټ;hf@Am: .+G\x_a'^z #_j$1aF5v95R:܁XL ~*"8{\קP2RjPb0[!b'I1BZ2̢GhUxW3To|uM$mʧV`oZ3;Xƙ8]tI~X3y)vc U&ܦYal{l|$}|V'I95-Z(O|Dc"% +X?զUa$VTt qEt8͒BJ]"\_ endstream endobj 21 0 obj 16664 endobj 22 0 obj << /Filter [ /FlateDecode ] /Length 23 0 R >> stream +H\W͎|y3dO@Hn !ojڇ԰fT_n=w3ǽǼXkN2ˍX7!>W2f^{.q~lUyhH\%gֱho-OD,s"e((a-\3U:^vW'd`c6,{JR!@ͬA\}q <*cdٵ9dWk er i9);jmc9{ջIzQiM*~i^܄LԘWVi3 }H;v9ÅUW D6#*wLYcڻ-Q5˧ A 2ەR)@,o7cvBhu6:ш>~p^H*yzۇovf} +7џo4殦UAi]J5)JLs" [eۮQ2a`WK1ZCg._ VZ' [(56_ D9`!|R \DFx$#{-!ꍿ݈1K,º:ɑO9:FEf "@;zt9i(/T[r|XdyLC)Hw"}fp7Jzzl-. ;``* $V11G3-Vy YDpJAQON +b|KHBlXg/ Hq2.oU`TXȒ:72p؏YND Oy;aAb.;<Ɂczi2@0joԚ `~<X:@eeYy+3,@LVj"aX&KvVu ZvGWwMHo{N9LCmġneFgK;#D$PƧNl3UB ( !2YNLO;A3fed"*>~pݐ@~<:fk6(6"$!d$;sa$s[Q{qjngsg: ⢚ lB*8:wo(,%!Gf>|9&?¢d0&V1T ɨ,&ۡy4 tTfiD "BH{bz& +G?~^16yЇuZ ~/7+2E֨Vԑ!ts) fP C%QYQm cZr~ndMgIAݺl {>y.D]i.`\"]^lBHdRJV,;? cdZZؠ6 qߠECy7͌8C ЇgluEV@gArY08&~2Dgc8쑡5Y ́@Y׋q0"=ɱh3Y6\w3"KKܹiixb"#vg>ު-lNyglNUFU=)eGR_sspR쬧]oRׅwx]̹&2K:5"vƟnay];_rgks CoĆN+^"64uAO3>.X) G-kv4u1]@hj8)̀DtH?sDV-/(Թ1xI+4JhJ2^hݛ4kA$bKtnE Bc[ؒ>hi0Յ AfߢH5Q!eGjU}lIe(1i6"gl#\k8d-SjM*GQ1(+zy`ބ9 CɘjS[梻FACڄt3gd./M;N"z܇̺6큦@mzMKceli֐ +;^`Ms5E+Ky&fNemI`!2r{K*zFt ^P>#kXئ +)=m`y`RԽxaVf/ІOWWxx7Lc䗗L?'0 +Yzt<(4-D8Vfh|Ak3&Ȯ6q_0!;P b,um/( .a=g"m| l<2.S{JCu}gZzͻ%'[al1 kF [ +瘾ˈ,Խ囵K">);B{:~ZG*%>M#1{j1&U{OÕ1?L5ư12E$IҞmrD4bW&HdTa15M"_ s~X:>s-%mDc;XK%IR9iM +mC mQI:Pͧ:H67ۦfB +:'|^)+1?u[=Nq Tθ5PȰaڦtt'm^<6.곫qbKm)5ےUVa065* ` +V$g?1>ӡ^)WnPW,"'t%p:"9|KۃP7^6$8DZLB`G+)%xSr>%'$kް7}?ˏw@_ :=ׯ~~??/_'/~Ѷww"so4MS/"! +)S7g>L—і5gu4r}˼F.w`[_56O .|IhI\ֻ־$7hc(f"G>{%+׵@[} JDJݦ6n+ +9ָFp|$Y{ӦZ i3S;G s ndSt7 Â:E䈰"ŚrQgOrOzjy(gh=]bO ka_t =v{jw >X y܅䦈w`;J :w) K(Evv)jM3@9<2rDC /(Fp$n].뭳@cˉjo6݌z3c,g;qUZh{$ NQu-`a/^g߄a\kʹJ""e+.suvD=fg:߱7aSq&.($6' +2iч0 ?Neo{Od&Wm.I〘% Z NJyGu' YYQIƋئdj'TVry S]&Tqnu,r29}\V6ׄoO.A7a}BF$ 7HZ"F O,{ZRr(ICai 2(j96=P! 0Q}:٨J]9:R0LBP|`C&46ѬoE?aR&y 9 W}(F=/0ŸBʞdЙ˘HBZل=ݐm8gH6o<ժ?Va2A!di ,CJ{Dvhn#;k@MTuӟ6NԾ(7 l9X̦FUI KJ{Y@z?fybb>;sC*ݒ'О~8կw=#} 47SLHr[Aqr#9Oru)S +!& ~t+ 1o4;6]hx^T7 {h& Vi,|V()ltY U_IΜߴ څExWv](X5r{W*4ZiA'qC"]aSb ݘ^H~vtz1Ʃ5ŽF*{y:ӓz5>ǙRJ[y"k,vO 9!YKb\j}Ge>[Ǘ_^~Z^߾_zϿϯ_x@( >͈<0(2aoyX-jiTpjШ##&bmQD])wy7hZIrM@zm ooN~XmBAw%"ps*$UiBXaca^(l!FvM=05GgϟKC>( _bN#nש7cb#b&Ybu\G-UFBP+qwȁ s|p1qBHx7Zy'tj&T5]}$̕D0Gq^0S!lIDkvGSc86V/>"p &xr@ +`F!O#nkY4DI,YDiH]k€-4OdW(B%B>-Y#g1>A sz$Ejr[Պ<ڶCr4j۶A (Y܇i#gH"e>Lu+,as5[stmyYH!w;MT-TAa ED}ӹ!hME;b V){Kn\ s /Hs$b# >EG}Ɩ@ba`u r$ɱcLnz5&= _=W%C#WpPcCfc{A Uj= y |>FO/-tt>J9dȓ(Krq7V EOBp& RN^pUqttS#O_T&Y +V8q̮ +!dӕz_dOhe* +Dʡ6:}ˍy%"ྫZ`:qD,^%GFpIĢH$R)DҦ>V:*+!BڒPlxFQ٦"vaxaxe|^g`Q5)J.؄=i*21p <կT{#")!D};MFF^tg!Y:-0tD`aUayUD^dM?=4K=ޗKW+TĖ{K]5ib[&H@or u[x(F;/ Ɇ,3Ph"Ybd؝9$Q-4U +Rdr~ J"?wy{4)F[9<>~'o~Fnw5O?O_߿c/̴jGL4lH;xu*BE ~V,BmԤlDH2+c#QlR&#HX/DJ8Hsh-"J5ՎJ +P's ɤp9W.P!3A5\ aAzշ}Jhu.?͚ JT<9=M'!lOZs)xA)׌r(L D9B1ӏ73j-K`'ǒ(+ d͉~θwQTOUV2 %'AwhP<0Vn֎ +|fy8ݣbQ(p9FEC*-US`8b%*Rft|l0Ξ0:2rlnp&Ț6" +Yd`5`sr%(&=doN-ɚ{{NpO:E'˘H![\Kg\,~jXXLQqHM 5v捓wv)6QpA0p̸cZuІ3NUPYd1d1=`V,d띉 + BT"RUgPRU5oZsP?G}(baY!ߥQJ+C0?ɡk1zb-! z;^9db)9& ޸W6T{Fcfݹ݃P盘 +7؅R<˞U\α$x7o wtx,z# Q+Ų>jLM+s~UkC7BӨY#'6{–/# Lu_jz,l@BkLHQ{XSD&w`û>_tY[H$^:(wFM^&˞:d)nF';Y/,9(t+nQy +*xM)CȪ-Sp1CArOt'2 :c +~Z69k"4$|jN͛qdOƓc:LG$ )} rY嶯N"E!8,c!YW|DAJ& ++0Ҭv`V^ %>B8?8`Rt:lF=4:LAz1vQ~8 +Li Gc@ݣ2dE!o32}8d g߽hCze)vєSˢ,Bɖz'.%ݜti dc9M,l\@C'؍JsJm7 Ctc^SR|8zX;:P/Ҷ<G r=F!$WF6i;I2m'| F/ = v42] V|fp\A7F:0,O8!IX rM AAXp_nf3P'XmVcXVNyr:Y+Usal#$˝.^`a\=S>8jT,S}ѠyW5<[+cV FaU< V`\vrl*' JSףv)fbT}cNn3wp̔_+r ł"OWr(wյIJQ+<vBYHS8r"Ȟbi<3o h0Qp>5X939w %Us=alizW\Xl  ޘD&xG6kfbϽc纟ycL? \iC[wV{rG[j#k3O1"4`C@]lON^Yhꍷ|NfEٓm!a#QLF`sŘFV[AzB?_7eSoSxgL*ޕDa\H T7j*DjY8_p7KVJ6 +3⎄!{Ed#ncNUeeeFFW0T-#4/nsT7C2[ *;eJw C}?տ4Mf0NC)< p81(E+,pI~+ ?bGKW"+/Ac9RI?1ÞJЮMiJG.h1CX"5Ж׍GzxLH%Q-0J?nZ օ1Yq~_&+'6@iy+J~([E2vey֮1B3_zu +MYԛ86+$ø/-C~dog)ج΀#f8b8(3\auPvdP<|"T^8jѳ Ƈ!F![87/1Td'!/Q{?MɑC~3ԢU~Jب0,M݇CB`DnYKn=g s,G+|L5$ɞh("1z/5(y/];3n%* 2dݼ| h< + + upGLJ1)6Peb66;#ӊOAvIo7P,‰N_P&d^LD^..DwO4Ngdg6^h`Vb&\fA4B"c"՘ µh,t piōwQ8fJMZI0-u}"cvŌ#J 903KhgO,q +VoDjn\T {e!+u(m#K:3Q .@ +)5}>U𬻄j]Kdbr>r`dǺ4$)Uv@tum qC@I"4bLc@&-v22kD;yیj{*5Rg-!-ډ5ډi'ҋpGxެǼBg<HΛ`C-7d>KBCodKIQa ! ]OkjVݥh?pi_S3=juPMbx,13 C_aJ<\lzg]e\؀%rvX} yC$Hut]K:^zܢF)d: yA. r{ %7 \0O;GШsRiCO:$6 sDj:+lNӃ0SaaRP3QkȒXr0 +F^T_YNVsar +JZhGmW@,Hi{Ҵ)KUCeDz$Pvs2 + +.2S@b2T 0C>L 1}6zH:O@'j 5,HO͛tk +uWYIĢ* lie +d7MA年D0m,BXa7(EÅAjCL!X)a=WgοMMhSX(+u,51fI[_B) B/!&F}d7F$K"/]BߦaI5nFd6Phq5 5T\7 rȹn>C w8ke#66tyA=qn/rqw 0Zs7-(I@#AjC &cTeӂ kPb*0T&V@.5 Q[;[~@RTD/jm:7_% WdǸrC:=2rۊF)S'!tJ EFzK]a0I 8m1,Z^uWxxW9hC(P1 $H/E% +u|:ܱ#b5iCD52K +Ac4V:;9^P o>/@O>߾{S w_?}?ӯ׏g+MRp92zU|*3 h'M'XBK-%C716-;1^1TbcYmJ߳q^TgV/4]Cl 6'S:؅w.OyٰQGrY^ĒҢ6"Q] kry"XKQ+<ٶP`F)_-%_HAb_X`)֦dP0S> +_t]0Fbrro)wK#yH6{3.sFMrv;厘F"ivDGJ!d|FSHFX15*LIQhq9I +|~`-PR\st6OS\<\@.0Ww}d_8o +H 0},Up) A+Xi ;F:]42 +N(~JEOv^ܦ P7wYf(bZ0>52i7J<2 ~Bu#\eU4GQ=(MdM%xƀOBnȋC82 cBbP>P ZpbTd81@s#XTK4Eꓭ~_֗L i^ʩi YKbEi( 6d̟ @L ϨAg) nr2P낔s:9Q~8!P3dgC4AY< KUk6f4V"{CR tZ T_H6I9;|ޥ<LH(t@c@kGъ7[8e|l!;:C`xxP#:q[(m=Y (Vk:h%n7,R +U +P`|+^Js.Jh3AsE0E1#Dft;L\)r,Iz+(V1I}о+ zbmo (xh51'ȥ^ _WY{o= { -LcZ$S0jDXC7E\ͼڧ1E:;FNyLO$LuĶV\-eբ^#2&)[1@z0KmzzC^s5d}o^Id[9d5^I Aq9;r]~U^^#i3klaǝZcŦtuk/rCi7;9\0 +W<.cÒr +UӸp 51/p1=Ɯ?yK¹<=LڪE}Sy=(9O'Gs?Xr#Ko?gv'+SXLcZ(- lڒk*'=UlwOD*l#Pc@.CԚMo,4V RC Eȍ!Ld]cFW6T2Mo/I`M3ciqL^U$)zX9ΩM*H"eO2]ۢ_M +;uNL6ёؒ*GaS6$6 Nؘٶ46"QLc|O2ª>9uuCK_.#@MZ}Owng=FUo*~{c +_q.,_B:VuDV3"Kp.#cZ w߆J;Rb8.CvRHn<:n!yXs$\)J0iLYm3 x^(K}!Yh*=V@ݿ9Ui5q_CtDi99IVMJi ā=]^QVrf&mzOG5Xܝ0Id ^em1֘H ,$IIFȒNa=b r\0uxlƆgdލbaSHg6tu+;bHh +̼cvg{QF٪Cx0/H>"7vͮ+yCWj6C rd"=ؐ2&Bۓibq{X,E.Σ$"g +]4c0P BC!!StW|yNޢّ =[׾T{ܿ=_py>w_2wW>^qXGBc0:0ø(磖+T2xBjo3 +r,'U &˳)XI +5vmhl +$F@{6h OQQ q&l%lAx;#ɮjsnG[geCT:@ag11ND1;6j1e/>u|d)m!׶;4KC%Uca cÒȔ6ҼkڡKPAL>dAݑXfKF-:tÆ_uUH<[ۚtc.( -٢]G0r,e qX`qXR + v̞lb0 $tʞ ji# FI"3#V4ع\*bd '{SVEksx= +Ue :D1yQbPB0s୐e, +MUebc +S$-I͉1ŝct9>"K"A!"WXԹӗkڱd[Y^GK=)5:#ihEf'ZKPP&X9P1ܙ9쿬"#Մ^ش?;hDh>OlBC~b0#!=c'p1ey~_"mMpD"g nRh|i<5TKK*PkdEXbYJf[u-娐C3;/Vҗp1g?ĺ CH:+]"D +kxbǑ;߮[R^ kVզ)엧z13x6ٽJ Bó.p:}`][]~7M!Tnu;:hLCX-d[M~ʾ/5};>_omy?o>~ow鲻_yr:q$d\~xe><ih+a gVݝ^N3 _wCg>oٿOrs&2 endstream endobj 23 0 obj 19328 endobj 24 0 obj /DeviceGray endobj 25 0 obj /DeviceCMYK endobj 26 0 obj << /Type /Group /S /Transparency /I false /K false /CS /DeviceCMYK >> endobj 27 0 obj << /Height 64 /Width 128 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Filter /FlateDecode /Length 28 0 R >> stream +HkUG8ܸq',- EKP*BB%)666RC$VjC|y_sߏl3_a:|̙s]˕ǛOןO:i(q!Cۯ^3')Z]uq{?ϳ&,Ѧ 1Z3)Oܠ=uiJ=Dq]z q({F'T430?l6m@ohN}z^M\2ǗEqĘ47dR۝ba:ciۋ#L?u*ņLO4 3r?C"?-S[I'$Xm5M&= 6$?+aοs2NC0O7~ WḻX!yxpN ׇ3=*j%7FZJG-U8g~ ''){j8j=ը8D)D>Ӊûv*:)"!&<ɢ/u|a;[{#m(A2F"gzd_\x'lʾA2ѓ"S +՜b)WB'3m_4Y a ~dX!IF<-v VoRFZ痦.-Zzy*z<3|5J=i0at7!Hz i +g(4*2iSW| }Qj m5z5)τSbE: '2sbGp..h9Ha +aoLW[€(59m۠iC#PGCCZ%4Y *if]aAp7$3a?9R89hGO-M6̰gb()hKq59( 3)ÿ͉AƛL|sS㵒P*8r(L>>)|򣄇2qdȆL&XG`GgO΁X*z'XB.EoVb큆'ֿkU_3_N8"d/O- 8;_ (PXc6\' ZuPjDUɹRЪ5Ҷ'_)498'dB$r6Aw(%s&&K~U}͆1bϡ-ݙlKCYsiL]`ID+yDK-p"Vk> ?M#$JK5=̔ +J$3#mem`HF)sKn+`FB T: ~ \H%F7УC' +P%ЖL,_89Ȅa*+PXcFe`V;Ɍv6.G'S7:뀜 Q)k&oא +h+'߶$W[d2&rBj UGHf^6Q\k/g><qvnhU*((((((;WeU endstream endobj 28 0 obj 1859 endobj 29 0 obj << /Filter /FlateDecode /Length 30 0 R >> stream +Hlˎc Ad.,`"pl/:c1=y9ґl C_֍O_>>W_=Ο~u/ۡ7O{Fߝ.ß7_y{r:<z|_緗ϟN?ԯǗ/W1CvP.8ҏܮ/?>}ػwy'|{?^秾Q8ИG<v^v`esVc_?OqZЎ#}jXX.9/HK:Z~]-,9q@VqUli-%m>HFk#2I\gCCiάLYm6t&]m dsjϭJmmvz̝.`񝛎d\ =4?XlQ`ڸɣ]~K=oL瘒/cך/y`ѸcxKB<Z֓$?d5v 1>.l1^^ChtI~\%1LW~YyFf+#%cꞥ賕5Źx8Ylc=כf@u_Bk/ g!䡨GD>Y=2&#Ҭٙ!zE1Du6\H&-wȓ+&yq+:tI\=SLp{t17MA6uSCػ\uҚarMzy!e剑mU뀄B">hD-[N\Zʎbzi{{kD-Tͫ3W$5*G9C9d#/s*z`>à4G0To,3{kxb;GT7iL ^h+z1 p8=>00WLš/,#BNc1NeA)s6j;!cr*WSC2 M/5{A)[&mDb_낾~Q<۪~mڽ\Lto(8>(l@}'g SjY jIֵck>Dd{_; 4pv/9DARs,.Si{Ice⋹pKގB8{z]hK&5'pKvG$^ѡO/N\ +9eV u0x~kW;ާnA4fvwſ# ԷFAXH,("f,|^_"A%9ҮִG1ƾ0W7˖1e%;S"dI&B҈sAo物? 箱O|`{O>mGn}AV\3B u. T `U*}^' }0|!>?S:vP4!H 컆dҀιکCP7$`4p&&wu^0b#<.i]0xxb3?46Sꮂzm@hC3-E*geEJz/ex$z[4F CS e8xs5\R%W^4hbkV#ͮkϔ^ZC~- Ck4 +|(t <)c%~\7pF1;(3fՊw鄸vh6rv85+ӬdS)Ob>K`_|]vS},fa9`4@I#GNI gV%>@S+̙}F +sc6QCz, +Js<964Mۤc{ +j}$ +Q3zsi潜V)yߘy>,! X,BĿ>] 9{XZ!=&AZSpNOPby2,C0QwH4w +19]R>ۯx`(C 3ʨuyX +YX{(8#զ &Α^W^Tm5_S=Ds[hLq&PL`82& +U.TB3eaʋ!\_'\"ib+~фW{]>RymǗ0^:Xhf5siͺSEuxݫM5yA:P~eEO_hS9Lh姁aox"ngy/ VLOԮ^$La}ٻ3;!Be*CHi +. r+d:5]pߤwgi:53=򊫋 6Z](ZF,MBc3Dfi[ۓG>%&{ٻ.RkTj ):Nt^qR39Q^]Bm(vĆwOc3r* ǹڿሼ6{CvQAw ^k& ϧгY<&"wIj?~|mKt q:N!Dkܿ@=V7bX pĪ_^4nK,/]R˟/ϖo%Tk?nNWU G8^'DCODcS>t#wbָsj wD[8~Eulu* +%uFսLO֨~[V.;/TE22Ӓ%%}tM Axy*;BJjYً&9P@/DŽ2DB)Oy9ÝRxwj4iryGƃ]Sk4bY{FTu'$6җEv0Y>/F6s I-/[P^Ô80YIEt6;E%[0 o4QxS=J4wץ7+51sTc6]hJ. :Ҳd-;Yu/d,[~Bk|rn; `Ke4ٚj#s=4ٍ{_1dCfA] PZ[)-^ +v2ИuqrAbsܫHJZi_98xdw1s~$%rea[!?%vSx{k݊i "w<r%A& N-xcT8(dczabQ x/ԎlEw!7L&c1ibgLv+Pĝ9Fd' $ІtΑf=ax^IcvklqVhxQ0y(sH%ɫzbf)"Ca3+jU4}T Gߤ|[&R^iQJ] +GpZ@z_ÎG^^-)Yf]Xͩ,4~E)T C9'rlS$}]*`2Oi~%4iK$Me bۅ钪Yf{>Z_Zm_ ]aKѐYY5$L@Sé3Ascru!Nu"m(͖ ^*b&pz6pe+˦%t"=pZe8Lѳqu@i%̦ n=1f1z!=1XtCɢr_Ђ֪1t<&l}+h *Zl١le N;mR2bnǺ ??yʑiT"5pO +.l t v͓g\[ՃVhFm\8ɄpSb|JlyxRj] n9S/=䂦4$d9bG8!:оC, 2 07:)jv6k>b04vvk{h9jFdC+K֕jd}Ӛ lߎV'XĈ oFgg" YR&kF%bd2twvߺs/dVuB9F5 a΁C4(;5,JftlxX{EC}뮽Plg=?Z,p8N4yvhXk02I*9lj({ ~ӎVۿ\v <,"YIWhXlȐ>bңC4ʮ 5~f앩i2ii`xB-@fzRtҾ3,K)0PχlaEGԢ,F4闢ɓ9)1'1o/A`}xK J~KSH4^4E#ӷ߲VtY$5w,m '?J]K,޾Фe]c@aE̩U<| +voEDr]l(pKMKo%玖KV~/=֯nsf['`](m9fƳYDK͘vEEz T,閙T][ LCcXn+'(0k!h}ʸsegGB(|uFԚtAjЙӌ~ŁTl2ӕLF{1g/E_`濢vHrA Y0ͤ==v*dw>(xeƁ'ƿܳ$bj`# a^;eZll#K`,bBA,ƈ.'v+h~`Omo69G䎒!UkWoS@~4ʠl-eNpL"hW.qEZ#>%|‹aD-l9Mqg׿_|HrkZ ǨB5˷,*0$T!fe'QX)0/zV=%4Xp%^͑0:n|>v̘0b5'HhΨQ6nАQrQzVڳ07&mAa0t/l9\zq-f1]yk){q EE)a _`ep5~ Ծ5w;M[ٔ:z}TL\͍o^h sơ=7>%{ !_'@F9mǂZAݪ?JrܐyܨA9@NS>0Jّأ1&ӣ +.m{юkl]Nn&xboTƦ{. n3Ngl]b)!zoO|5cztmVVd@DFƙ{{أ8uW{dRUdG-6U[je;j XȚ:S.{C +chCUP65+EP|ݲCNLa$˰ +:/;^X&nh^a*eMh؈l^.66h*1|&~ +ҍL~S]+g XA3Պٖ[w@ȉZ;Rmt%;6Jr4`;Fpbw_F( ӊre"ۼ^嫸G1 Xۊn4joI8Yp';~.ը1&SU]awH@v[-W_l7 ڠS_$9XjAugbnbڬ 8@,|uR?tQغCr |?(g1ns8Sɠך.. ;(0E0y=ՎW?7ZҐسutgQA*~gc?̫`b +XD't0j!F4mi΄X{z~#J +=8 M1NB #HN0l#~#`[@xp_gqb^. t ^⁙8%N _Y^)TY'V[tEPc>M|mjԟ_{cք%@_Q`R%0RA<® CݲӞn$5PgٻsP + ׅT"ά(yDj: DDަxJ(l`6эUdhc%2i9ڢBb9Ok Ҭ%5Di4uU`tˬbI^qN0@gQ5;5"EI^趃gf +0_d `%$ eU5&es,%1t>G&J,]{bhf{ ڷ>cƓ8l 1{țqbS iA}g(_9E̹+Թܽ1`9`X$VNgs]XLlnpn4M@'IYd]?kBL}=hZ ZV SOˮV6)STP$.JTr;NvžTaMbsݵ[W'U3OIkFZbEO~ر" nXez@0=]# Vx: ifbJB&p+1'kCnri9O8MMzv&In$A/hpq:lȌ[!Ŗ>2cL\fÉcrvŴe/nX̀רRƕf H S1!npza)xtt:ڔ1Z}J\Qb3E~7,1+x(nGl[,"$vA.٬$ Y KB .f:">̉%i5|5X-EUYWl`Zq("%:8vq7лRE`(O afϪ&,[L0rv =eB7'sI(hG+6,N,'m'3 BZ0>[K-~*2pT 5WIM!.+ܓinQF/Ǐ։"5rJe}'JSյJ4Xܕ3l/k@L[6? ̱{iiEW+ PE[M;KO`*dDz>}7be.p+ؒvse\hv[(P[+B'*sµ`"qan8xxb xE0@K\umHµ({;H;I6nrz68m 0*fjPybۤSZS݊\ގ5"%"Gbנlxf(.=esaX,@3$b=޷MwFĢ`5fh6DXnzJ؂t%yUV ^N=+G[)]t `펿2@1q}b`NEzo(Jo3󊧁M*Cl|U%;;)J%P\Qnnv*4ːޗg rӘ[0P9+'pd@JJ#f`ۤi<5z/,c6zyj2\- ?+gFM)֦(7'iB]-:$G'(ÕfƇGBFR&>YĥL{hb+RXU6]ԬEv9&cBc~5x$uv<{vx.?Lؘ zJ>DrQ>:1SFF.Ȑ!UVh60.kW.{IorTãդſŽz5%$O#l[v)}{ *<O'\_ eّcXԇS_ÿ*byi%7 9_5h[Iyo4ڲ'0Z +E#,2椼W^?C XNv451t<>EaM~GHB"W u 4!y DŸ䳶?jGXӔ +w뉈6ǴZհÿZUU1UXaNܷNQ42}սRAݛFӅQ'ؼ-|]F(PpjŸۤwkƯqa#Ȣ)!p+!MxcrM(MFԤUЛX{N"c}*4T"j+EYmDt'2ji~Z o7[ub#f1QbgO6 +N[7VG~Aza<`t6E>X̷`pI(pʶZr](PT);AHzG?&b *Q&F#>V~ZoyOc2JfVuz ˁ1ߍr*NBX t]qZDR ƿm525Il4|`gʡ(>ti?Z ֧]at+v +}q7,ek ux-KNyi,`hi?#zmJdra9ّ%zCkrIB%vy%:D{R7"30ܴΖ.J?Ghj`R6jyhPsw4*M l㗒Zi"7}ZoM'%j,~-n;6AcFo <@ KqTU7|M[d?PwI-)|Y[XrZBX}Q{-Hlzfk+i/E{Z8/,oWFMms[\hUbodg2p a' N#'Ue-4LV窘h?s@9qQ5f%@_*Iq +_}-4WIC[kkb]2,P4Bh4ʌ2K2PQ3 uHҢ<`h35 k,.H|>JlCeBg&;rg707F=LN (,kedϗlz' *? +NȢJc]hKΎ#ʠYZ!]y5@J"Un2{vqlZ+88tl>Vu<:}b#:'Y2LӅOCvf o?$exRgx[/?j>D^>i?Q6q)aoErZcUNoSg+` {mAha8o1Ea#FrMV%)H7s)^X%fKapiWuz]e/%Ax|<+TŭyХS@G@rԓTF.cAYCB9* ?5v腑u:=ztExg?^#?%G '; S>pgT"[H9u)j` LJ|tkJN9"[;h4Mw{]ny^=)Έ]L6ʴaJv$>L )s.Y +ذ~ʪ~~WAS14=/f-uאaG2}܄O7Ӗ]$,DO¿Mng%!'aGR +ö\X P`',׬ugnuZ43}îJ}nO2&lYr58:pԠ(,jBQ{Kq`UJ2ĚRL)e?3赭aKtd=(r+rJ(AkPG +Met,XVrE=Xx~yuӗ#͉@FkruF@RB}m u0yL fl|<2V$w봧UfunaÝe@ASKcUʗ 5Z.;l<0Ďz@+.x*Za~kkrܠM&J~#j'm8B΁V"ly;Ԕ}`|/%gE&bBL@*6@@H6?98m +UW(p1S<`e0%\ޕ +*]+Y?PJ;>`>h niwo.NaɢiP:zĿF*%dѩAMvd j#8E?iCPV^To<1l%iGNY=-sc +>K֡Yr!jH+P'&gť{媅#\-fO0v4ǝj 3 BTiuh7hd5%2/[~z(ǓЁQcWwȮ#&Ԣ +;d6G e?~UמZ@1Ai(] NbItعy\w-?Sp'q/ <_8HNe#+p,"ڜpC@Q3EeˑZZ/Ydj*XiȊ6m}j|%.tlf4D}giȼ*_`I7!|'xoex|^47{*'aO.a4;ޝ]"끉N{383hNK}euY$w5I2OGZYާÜ +Y9=7鷼_@2D#[ SڙOX%eqoȢI| դtm/[L`p;|m:"VKo7N=Mb͍9iȫGc/o'X3˦?MtzGt4hpXX}BÍ$zw4`ped[',8*Ghf}  ^,r,;}ddD5+U^Sk15&~4 j+RmdĪ|v-WڼH1&6v^1N*PŀHT,#{ˣ!ZAv-2Gl]ėՒJ ti0c|6-K#,WĦ>qzB"W\R|^{9Ƹ eq3 5Sf8%?=A5MU5IX@'4̿24Iuۆw/jjlq~̵6J +%wڥ7u+3n!v5~ +6tXt|d0%{pJĀK[Kte  +ٰ;N o y&_$W w IdO |NO-"kq5}ȍ`g3`,o8SG/lҺb::40Qbdkvdtb/Kr7":L!$%b-Di]Q]QO/"uNrD0<2,Qs7QfH1g`>϶3A`t>6=nPte +Ya4U)U1꽣hPSh+ܚVa;s6bhDm6U/sx6olts/Z՘>]WhWKA[" d7Vs%{@cS6G;; : +vdWae:Y$6SB*j+,p4yD凥Z͉ HVǗ/?sI>t "zX +dJO47.{+1DRʌؠ@BT78!,nئ[Z|Q"ڒ.K5 Q/ QM%O;!˄ X,M6j ]qPt +N^5p 9S9dvk Xp.}asjw8l`ø I1Q}%Ķ,6~U[^!ȸVibP= +Sij) f爞@`tPСNfu^ +mA<!!;2ISLh5 + w*׾bîZ!41 NoC2 d B5n9𻄚 PS#r-&)¾&1ȍn^ }zֆGX#@_D$SQ;tC{yd$2'1bl%C<20txfEwɀ/ OVM*DFR!՜:rVM\C +@VFoM<ajFa٧A 8/0oM19D߬5am`̳jnw;vT2{@+zp8v!*TJ"XWA>Cai`{NW#{TȕM6GEOYBbhXeb<&+GC׻:ZأBi +)Հ0@st}Z3NF[.%[}yc,~rVㆾF6&sN'>ю/1!5v;O\ONg90acXG{gꍁAh17 {j\5ܡQ4ebkaC$GOym`vX,z~E]ؐjq\R"7A7x{-:+L/ي+iOc0,kRGK +H h3bqImjOL ,q;fi0 >ЪRg@@aMQ:,Ы6D +K80Prm~䙧miYVn +@" UCyO39KcWB&OȞY[`Q 5TUK-3?D"`R/ {6Hxi,R?%lzrw?UQa +d>xUP%EXh[>:m+p5rGAiPYic7=~P"4C'+^Wv'p6*-[3D9޽3PE &)ЗİB!"iYXȬrB/snI DZ-&y-݈7oŗ¶[+x'Q@M)XB12M +-g)({ʇj,w$HgVUսh?eV dzϡ/9oY2 tG ktM$Xd~~ri}S.ÊQ4,46شG+ FØn_JR7[Bz8W;bRm'(t)s igiB2òEHUv*y扅.d`}e̘HMXgN} Ǽ-E:[2 +>%Ð +l &t WNN%T3BٚF; \H>iSh00b%ȥëw߭luۣJ _7R7 +/YMU cM4vdS~y=Ntb!PGdi +G 4vgp>yQC]d6;<|ct@"% 3 Kp<1F,VSˠ%;x_ûypxEF/Wfz{v2![h]!QmdNq@q0 T v &dbV"$k9mrJJ ӥ]aP _c6jBiz2[CGY(4G2ܗ1HOYb}%-Lt9oV:lu '7s [dH;Yq<4͟Q ѦȁV,q}70S`l,zHP[.F%.qi *!v@MC#: CJ~ h޲)M_dvRb|1HZ}Yrݘ+Ƨ +t紡`t7ѝ'RD cRO_{9h"LdܱC JBLQ-ZaG9 UXIIY0R3zC9.u)ዪ܎xG~ Uz u73<'ayTͲJ"HHVPJ4`qJmV( ZBp-!ÚQTwK$A.%x2ŘYƛG4 *y ˜Ryo *p +"aeLdWfuſBrBlR62CcCt+G}1?oد?^q\XZ.z }mh_x +}_uyإgp$Lge˽nA o}׿P)#{Rh{@g@/#QNprÅ}+ +n(Tf%tiD;RLx(G%~B#SNϢ`٬:|@mq!qҹaEI@/ŘmAґ/Fe} Ug`དߵ,wc8"){y툣\Nk+gx=ő [d4Hm#7;njh\3:#>yϻ_j 2SǾ5**(E4rTRrn&Odcls;]Ayq[?( LǗ^uՊ@@v,KUU.atoX}uoml?UNx:|N*߱(ҝ3ȉ-ZUvVZvݢrxcxE8V˜U'6/qOhg1!9|1JƣoYV EZQz`y i h&aҕ}{9B]2(JNxk鬢v~*Y˕$C! +boۖ.D '0'.9<(nT\$^Pn)yfLM~%VB r6AtC5r3A]&W)ლ1^'H>KlQiH3)rY[C9ԏ] tf, ^F#*jb}QkbQ2jTn=: 7K ό-1 9#PRU!W-Pw ,CMς]><' (v% X Z1XcBhh-Q-{J^Kqsf@)]Q1ݰ!1yɮ#F&eΉJDg ixX^+wMyѬOsƊh +ob6om !bJ !p1H:ʮs;̦fJÆThf@"ڌ +j*oEr>и92B"֓t0 .N+xн`& Iu٘髈`2(^FwE[5:-&]u9kM.#(- +ԪY/SY#íDTı~x*KQ(%aPr7ᳫzȼ`|M@DOCzZwoŞc(D,$9aLa /gqP+TFr$.'SQ?"E;f?~^C1wAOf7(!|+fZFx%'NK+0TZhU9!F$#./-.BvE%b5V6֧qhd 5 +*ɨƸ1imR0fۣ R(6lЖ\@ idv +fqhx00{ب{>e(PwCQLnv(U=rVQ~5^F?ih; iLF5 +jNZ@jpc; y ʍMLsC.z h Y$n!bfnU _'{!*C?u|KvUblHg^j +Y}m5|d."֢Em'HJnW,r1=404|y()İCoEUt3Ug"`v]V({5#Ld>sھ$a_V/)]GF?B5^+Q?_~}ߒ +m84XzMrߥǀ!Br""m~%Udn!^)%_JR$U.;Y S[)a6@iaWFP[V4`瘨W}LF!pp*sB JVAc =+@ъ㕗#+7FJ?X7Qi Y ,)W=@$g,j gƁtuxƸ(xclȄT-5KEh2(#`\cD cɭ۶@{# @m{sbG(Zct^ mB!O4h( ILU'ha1YDY P AoqE@=e_;QL{J?Q;jT1OnɈyIcHy |$NBJ`9rlPbHBF `UZRW{CZ*.{1@(WCb zٗ !wpB1Alb|?b!|Q1N# +$|u,`8ZAbP,a~ EATR?CH)1f1 +&xba(3A"ZknOgCre2,5=y4P 1:Yg8>2uUB,]RKbi xL׌%3^*LpthcE3vA߱LųSIӨb=Htf֗&цpᮠZ{kBÆ2@NR11'0!ZazPtOby Yh%Ær3Tvۇ "ܿJ)/Y#QD8_%iBZXbETeT}0Ÿj˲;'ES8)Bӆ>CBN/RGGmmB=hb RLA8Tڲb1\!,ٜ R2})LVK;Xt+,]ʩu %oXwj*ZI!s9B⋱![IB/>nCcwb3ܗD3}8TŨń[Zzj%1}2J8d3S-RZx :`aJAN`[KZT*_g;0jmc+gQ}D'QƜ 2WPNI( Tcvճ#Q=]+ɵ4*g1]#ڴ=ճQ=]+"(S2y$feaJ$=~iTR>W.%Q`8:UJU\OR ~9v4MMFkKbW,ّNS7ϙwMU{;\X4$Ŝ%dKPPv U'O]!6q#gfM1*[ɀVN@IneU/4z%Gm|N1B^m +^)ЮQk*ۊ'mҾ% DEk]a QWsG-us☑IE}s,N}ɑc|w= S7̟Lz 9z䈡z*;*8-wB|L$LsW:\ϡ~aMm{dgߖL_|ش</ԟj5gyxQȱ<Җж<^GLMOŬ (ev6˹Vj(UPsHa[c$zbSvM-[DoENpQVW#cdK?mm'Y<MӬ=}"#tӧ riMT$a=ObhͱX7IH|,f:;y[}5S4%΄b=ubd %uuRKfדaJVSv~LKB< +y()g9(O硨<θe9Xcrrn߭ 0-2Q2$[I3L/9DP[Y QD@mi}"Jy=6\BuK>8Q)#20=h0zCqcCDҷ]C"/BbѬN Nyy(s=o#W.$xkdyT.HӾ:-Rs0|ng/uZSux@Ls]"s߱27˷6KVڛ{hKuN--\F; oWc(b>A^/v-ȍGٖL4rѡ˲pAThYdC f g=;(4e E#/+[̈́n.1ح6gwfQi-2,W)sWyņ` 񜒂X[ˎ1'yCb3Z|z1zIXֈEqб/G -Y#'<)M|1GU^*g-㍕2T9 +i#.dZ kgZ8)ԏ`HJun$UԔO8jygce jj6HІdf!\5zeG+85 8}բٔ(e:xB=)3cڂSF;Ju4'+Pc{SJ9.!隞~yvK3ڃPLy46ͰffgڪM_bpPLS_<$elTfGO59WML´kGfAb[KmGz _"`"14 @Aq}_Zں֠UV3"rPi4j}o+{搯8<++4s%çF(?ź!hd g=:C[jZ@ m ϴ.PN|@B? Dd.ܠ }H# *5HIIMf`{ݐ:ܔ_fY7PdA2`(ǰfLGw$ @OfD $ٳ.F:5~)VFO9@B[뗋5QEhB/ޒu%] +EBktL/~y'T]Eˆu JJ6H +>lmapc׬~b~=v_!,HY9/@Y~ +N7lzJ"9 /mCgEdwB.#{% w` 1̀AY`mYcc5BC"= 6kn(!qp2U,|mY=1rZbbX5) Fńꀡ-I44Yk{y['d:u@1BXϱj+Wj"i3fbZy \PˎkU-42ZDB l]gTS"xCg?cBWlc\_ZBo97u#owbI^b-7l!BN|BUgG;|l\5OЫcЕ(u¸,W +n`쏗![r`ZaGp,4js] C qd`.ֹ1yh(BG@ˈ3[ Ўui/PTYYhE9A9=2JO>=q'pQH?}`RK>xOم'<< +;UTCلczX +'\wjA1B&;X5R M;iűN ʳ歓_YuYnxIIgyFqN"Fo'1D/?߿ 0?q endstream endobj 30 0 obj 29559 endobj 31 0 obj << /Type /Metadata /Subtype /XML /Length 932 >> stream + + + + + + 2006-12-13T18:59:34+09:00 + 2006-12-13T18:59:35+09:00 + Adobe Illustrator 10 + Adobe PDF library 5.00 + + + + 2006-12-13T18:59:34+09:00 + 2006-12-13T18:59:35+09:00 + Adobe Illustrator 10 + 2006-12-13T18:59:35+09:00 + + + + endstream endobj xref 0 32 0000000004 65535 f 0000000016 00000 n 0000000087 00000 n 0000000151 00000 n 0000000006 00001 f 0000000316 00000 n 0000000000 00001 f 0000000644 00000 n 0000000723 00000 n 0000001025 00000 n 0000001062 00000 n 0000002533 00000 n 0000008787 00000 n 0000022148 00000 n 0000022171 00000 n 0000040799 00000 n 0000040822 00000 n 0000061729 00000 n 0000061752 00000 n 0000080661 00000 n 0000080684 00000 n 0000097430 00000 n 0000097453 00000 n 0000116863 00000 n 0000116886 00000 n 0000116915 00000 n 0000116944 00000 n 0000117037 00000 n 0000119040 00000 n 0000119062 00000 n 0000148699 00000 n 0000148722 00000 n trailer << /Size 32 /Info 3 0 R /Root 1 0 R /ID[<33bab6c74eaec9684401d967289e06dc>] >> startxref 149738 %%EOF \ No newline at end of file diff --git a/ci/CI_JPG/CI_01.jpg b/ci/CI_JPG/CI_01.jpg new file mode 100644 index 0000000..a9ef041 Binary files /dev/null and b/ci/CI_JPG/CI_01.jpg differ diff --git a/ci/CI_JPG/CI_02.jpg b/ci/CI_JPG/CI_02.jpg new file mode 100644 index 0000000..97e4dac Binary files /dev/null and b/ci/CI_JPG/CI_02.jpg differ diff --git a/ci/CI_JPG/CI_03.jpg b/ci/CI_JPG/CI_03.jpg new file mode 100644 index 0000000..4b0937c Binary files /dev/null and b/ci/CI_JPG/CI_03.jpg differ diff --git a/ci/CI_JPG/CI_04.jpg b/ci/CI_JPG/CI_04.jpg new file mode 100644 index 0000000..3879234 Binary files /dev/null and b/ci/CI_JPG/CI_04.jpg differ diff --git a/ci/CI_JPG/CI_05.jpg b/ci/CI_JPG/CI_05.jpg new file mode 100644 index 0000000..7f034e3 Binary files /dev/null and b/ci/CI_JPG/CI_05.jpg differ diff --git a/docs/IMPLEMENTATION_BACKLOG.md b/docs/IMPLEMENTATION_BACKLOG.md index d70952b..876b7e9 100644 --- a/docs/IMPLEMENTATION_BACKLOG.md +++ b/docs/IMPLEMENTATION_BACKLOG.md @@ -60,7 +60,41 @@ | E-M14 | 현장운영(혼잡·안전·주차·에너지) | BE·FE | 대시보드·경보 | | E-DEP | 역할별 프론트 번들 + Spring jar + 워커 서비스 빌드·Gitea CI/CD·systemd·env | DEV | (G2 후)push→배포 | +## Phase F — 벤치마킹 유래(티켓·관람객) 추가 항목 +> 근거: `docs/analysis/ticketing-app-benchmark.md`(2026-07-11, COEX·NOL/멜론/티켓링크·킨텍스 앱·Whova/Eventbrite 벤치마킹). ⓑ=P1 보강(기존 화면 스펙 수정 후보), ⓒ=P2 신규(신규 화면/기능). **PLANNING.md·design.md 직접 수정 금지 — planner/designer 경유 반영.** 관련 화면 표기는 현행 design.md v2.1 기준. + +### ⓑ 보강 (P1) — 기존 화면 스펙 수정 후보 +| ID | 작업 | 대상 모듈 | 관련 화면 | 출처 | 담당 | 완료 기준 | +|---|---|---|---|---|---|---| +| F-B1 | 스마트티켓 부정입장 방지 — 디바이스 바인딩/회전(애니메이션) QR + 본인인증(캡처본 무효·양도 방지, 현행 정적 QR 대체) | M10 | SCR-M15·31 | 벤치 §2②·§3 B1 | VIS·BE·kintex-mobile-dev | 회전 토큰·기기 인식 체크인 왕복 | +| F-B2 | 취소·환불 규정 정교화 — 3구간(100/50/0%)→다구간 취소수수료 + 예매수수료 별도 환불 규정, 서버 권위 | M9·M10 | SCR-P8 | 벤치 §2④·§3 B2 | VIS·BE | 다구간 예상환불액 서버 산출·표시 | +| F-B3 | 티켓·배지·관람객 앱 다국어(한/영/중/일) — 공개사이트(M12) 외 티켓·배지·지갑 화면 확장 | M10·M12 | SCR-P7·M5·M15 | 벤치 §2⑧·§3 B3 | VIS·CMS·FE | 4개 언어 티켓·배지 렌더 | +| F-B4 | 세션 정원 기반 사전등록(RSVP) — 즐겨찾기에 정원·마감·대기 상태 추가 | M11 | SCR-M9 | 벤치 §2①·§3 B4 | VIS·AI·BE | 정원 소진·마감 상태 처리 | +| F-B5 | 결제수단 사전등록·간편결제 빠른결제 | M9 | SCR-P7·M14 | 벤치 §2⑤·§3 B5 | VIS·BE | 저장 결제수단 빠른결제 왕복 | +| F-B6 | 행사 라이브 공지 피드/현장 푸시 — 알림센터에 더해 행사별 라이브 공지(긴급·프로그램 변경) | §5B notification·M12 | SCR-M11·M5 | 벤치 §2⑤·§3 B6 | VIS·COM | 라이브 공지 발행·푸시 수신 | + +### ⓒ 신규 (P2) — 신규 화면/기능 후보 +| ID | 작업 | 대상 모듈 | 관련 화면 | 출처 | 담당 | 완료 기준 | +|---|---|---|---|---|---|---| +| F-C1 | 티켓 오픈 알림·관심 행사 구독 — 오픈 예정 행사 구독→오픈 시 푸시/EDM | M10·M12 | 신규(공개사이트·앱) | 벤치 §2⑤·§3 C1 | VIS·CMS | 구독→오픈 알림 발송 | +| F-C2 | 관람객 앱 주차 연계 — iparking 실시간 현황·주차비 결제·사전 주차권 | M14·M10 | 신규 SCR-M(주차) | 벤치 §2⑥·§3 C2 | VIS·kintex-mobile-dev·BE | 주차 현황 조회·결제 연계 | +| F-C3 | 관람객 대면 혼잡/대기 안내 — 입장·주차·인기 세션 실시간 혼잡도(현행 M14는 홀매니저용) | M14 | 신규 위젯(SCR-M5) | 벤치 §2⑥·§3 C3 | VIS·BE | 혼잡도 위젯 표시 | +| F-C4 | 관람객↔관람객 QR 명함 교환·컨택트 지갑 — 리드캡처(업체→관람객) 외 관람객 상호 교환 | M11 | SCR-M8 확장 | 벤치 §2②·§3 C4 | VIS | QR 교환·컨택트 저장 | +| F-C5 | 인기 세션/바이어 미팅 예매 대기열·취소표 알림 — 정원 초과 대기 우선권 | M11 | SCR-M9·M8 | 벤치 §2⑤·§3 C5 | VIS·AI | 대기 등록·취소표 알림 | +| F-C6 | 관람객 멤버십/등급 — 일반/바이어/VIP·재방문·바이어 인증 등급별 혜택·매칭 우선순위 | M10 | 신규 마이 | 벤치 §2⑦·§3 C6 | VIS·ADM | 등급 산정·혜택 차등 | +| F-C7 | 최근 본 행사·부스 개인화 홈 피드 — 재방문 전환 | M10·M12 | SCR-M5 확장 | 벤치 §2⑤·§3 C7 | VIS | 최근 본 이력·피드 | +| F-C8 | 셀프 체크인 키오스크·즉석 배지 인쇄 화면 — PLANNING M10 즉석배지 화면화 | M10 | 신규 SCR | 벤치 §2③·§3 C8 | VIS·FE | 키오스크 체크인·배지 인쇄 | + ## 진행 규칙 - **Phase B(공통 레이어)는 전 도메인 선행 기반** — C/D 착수 전 인증·시스템관리·공통기능 정착. - 각 모듈 완성 직후 QA 점진 검증. 보안 불변(자격증명·PII·스택트레이스 미노출·AI 워터마크·등록업체 응찰·admin env) 반려 사유. - 기획=planner, 디자인=designer 경유. 아키텍처 표준(Phase A) 위반은 시정. +- **Phase F는 벤치마킹 유래 추가 백로그**(P1 보강 6·P2 신규 8) — 기존 SCR-P7/P8·M14/M15 등 화면 반영은 designer 경유, 모듈 반영은 planner 경유. 착수 우선순위는 벤치 §3 톱5(F-B1·F-B2·F-C1·F-C2·F-B3). + +--- + +## 변경 이력 +| 날짜 | 변경 내용 | +|---|---| +| 2026-07-11 | 백로그 v2.0 최초 재작성 | +| 2026-07-11 | Phase F 신설 — 티켓·관람객 벤치마킹 유래 항목 14개(ⓑ 보강 6 F-B1~B6 · ⓒ 신규 8 F-C1~C8) 추가. 근거 `docs/analysis/ticketing-app-benchmark.md`. 기존 항목 무수정 | diff --git a/docs/PLANNING.md b/docs/PLANNING.md index da27384..f7edbdb 100644 --- a/docs/PLANNING.md +++ b/docs/PLANNING.md @@ -1,6 +1,6 @@ # 킨텍스 자동전시시스템 (Exhibition Automation Platform) 기획서 -> 작성: 기획 에이전트(planner) · 작성일: 2026-07-11 · 버전: **v3.0** +> 작성: 기획 에이전트(planner) · 작성일: 2026-07-11 · 버전: **v3.1** > 근거 문서: `docs/analysis/kintex-website.md` (킨텍스 웹사이트 25개 페이지 + 전시주최자매뉴얼 PDF + 참가업체 매뉴얼 PDF 전수 분석, 2026-07-11) > 근거 문서: `docs/analysis/reroomai-source.md` (ReRoomAI 이미지 파이프라인 소스 분석, 2026-07-11) — 6장 나노바나나 파이프라인 제어 파라미터 확정 근거 > 근거(글로벌 벤치마크, 2026-07-11 크롤링): Eventleaf·VenueSight·Whova·ExpoPlatform(전시관리·부스판매·리드캡처), Eventbase·Pointr·Swapcard·Brella·Grip(wayfinding·비즈매칭), RainFocus·Bizzabo(등록·리벤뉴), ExhibitForce·Ways·FinancialModelsLab·Digitevent(전시 BI/KPI), FindRFP·Procore·4castplus(RFQ/Tender/역경매). 각 §5 모듈에 매핑. @@ -142,20 +142,44 @@ ### 2-1. 역할별 웹/모바일 포털 분리 (IA·채널 매트릭스) -각 역할은 목적이 다르므로 **별도 프론트 앱(도메인/서브패스 분리)**으로 제공하되, 공유 Spring Boot 백엔드 + SSO + 역할 RBAC 위에 얹는다. 데스크톱(설계·에디터·대시보드)과 모바일(현장·조회·승인)의 용도를 명확히 나눈다. +각 역할은 목적이 다르므로 **별도 프론트 앱(도메인/서브패스 분리)**으로 제공하되, 공유 Spring Boot 백엔드 + SSO + 역할 RBAC 위에 얹는다. 데스크톱(설계·에디터·대시보드)과 모바일(현장·조회·승인)의 용도를 명확히 나눈다. **계정 체계·모바일 앱 채널은 §2-1-1(소유자 확정 2026-07-11)** 을 따른다. -| 역할 | 웹 포털 (데스크톱 주력) | 모바일 (현장/조회 주력) | 주 사용 모듈 | 인증/권한 | -|---|---|---|---|---| -| **주최자** | 주최자 콘솔 `organizer.` — 홀 배정·배치·행사 대시보드·옥션 발주·BI | 조회·승인·현장 상황판 | M1·M2·M6·M15·M16·M12 | Event owner | -| **참가업체** | 참가업체 포털 `exhibitor.` — 부스 신청·설계·유틸리티·옥션 발주 | 현장 체크인·**리드캡처(배지 스캔)**·승인 | M3·M4·M5·M10·M11·M15·M9 | Event member(부스 단위) | -| **장치/공사업체** | 업체 포털 `contractor.` — 도면 제출·**AI 설계자료 열람·옥션 응찰(견적서 제출)** | 현장 시공·반입 통행증·안전 체크 | M3·M4·M15·M8·M7 | 등록업체 검증 계정 | -| **킨텍스 직원(홀매니저·운영)** | 운영 대시보드 `ops.` — 검수·규정 플래그·홀 부하·물류·현장운영 | 현장 검수·안전·혼잡 모니터 | M2·M6·M8·M14·M16 | 내부 계정(행사 전체 열람·승인) | -| **테넌트 관리자** | 백오피스 `admin.`(테넌트 컨텍스트 고정) — 자기 전시관 사용자·RBAC·감사로그·마스터데이터·룰셋·시스템설정 | (없음, 웹 전용) | **M18** | 테넌트(전시관) 관리자(단일 테넌트 스코프) | -| **플랫폼 슈퍼관리자** | 백오피스 `admin.`(테넌트 스위처) — 테넌트 마스터·온보딩·전역 코드·크로스-테넌트 감사·통합 BI | (없음, 웹 전용) | **M18 + 테넌트 관리** | 플랫폼 슈퍼관리자(크로스-테넌트, §1A-4) | -| **관람객/일반 대중** | 공개 홍보 사이트 `www/expo.` (SEO·다국어) + 관람객 사전등록 | 관람객 앱 — 배지/QR·티켓·wayfinding·비즈매칭·플로어플랜 | M12·M10·M11·M13·M17 | 공개/셀프서비스(쓰기 제한) | +| 역할 | 웹 포털 (데스크톱 주력) | 모바일 채널 (앱 타깃) | 가입 트랙 | 주 사용 모듈 | 인증/권한 | +|---|---|---|---|---|---| +| **주최자** | 주최자 콘솔 `organizer.` — 홀 배정·배치·행사 대시보드·옥션 발주·BI | **운영 앱(B2B)** — 조회·승인·현장 상황판 | 승인/초대 + **2FA(OTP) 필수** | M1·M2·M6·M15·M16·M12 | Event owner | +| **참가업체** | 참가업체 포털 `exhibitor.` — 부스 신청·설계·유틸리티·옥션 발주 | **운영 앱(B2B)** — 현장 체크인·**리드캡처(배지 스캔)**·승인 | 승인/초대 + **2FA(OTP) 필수** | M3·M4·M5·M10·M11·M15·M9 | Event member(부스 단위) | +| **장치/공사업체** | 업체 포털 `contractor.` — 도면 제출·**AI 설계자료 열람·옥션 응찰(견적서 제출)** | **운영 앱(B2B)** — 현장 시공·반입 통행증·안전 체크 | 승인/초대(등록업체 검증) + **2FA(OTP) 필수** | M3·M4·M15·M8·M7 | 등록업체 검증 계정 | +| **킨텍스 직원(홀매니저·운영)** | 운영 대시보드 `ops.` — 검수·규정 플래그·홀 부하·물류·현장운영 | **운영 앱(B2B)** — 현장 검수·안전·혼잡 모니터 | 내부 계정 발급 + **2FA(OTP) 필수** | M2·M6·M8·M14·M16 | 내부 계정(행사 전체 열람·승인) | +| **테넌트 관리자** | 백오피스 `admin.`(테넌트 컨텍스트 고정) — 자기 전시관 사용자·RBAC·감사로그·마스터데이터·룰셋·시스템설정 | (없음, 웹 전용) | 내부 계정 발급 + **2FA(OTP) 필수** | **M18** | 테넌트(전시관) 관리자(단일 테넌트 스코프) | +| **플랫폼 슈퍼관리자** | 백오피스 `admin.`(테넌트 스위처) — 테넌트 마스터·온보딩·전역 코드·크로스-테넌트 감사·통합 BI | (없음, 웹 전용) | 내부 계정 발급 + **2FA(OTP) 필수** | **M18 + 테넌트 관리** | 플랫폼 슈퍼관리자(크로스-테넌트, §1A-4) | +| **관람객/일반 대중** | 공개 홍보 사이트 `www/expo.` (SEO·다국어) + 관람객 사전등록·**게스트 예매(가입 없이 티켓 구매)** | **관람객 앱(B2C, 스토어 공개)** — 배지/QR·티켓 지갑·wayfinding·비즈매칭·플로어플랜 | **간편가입(이메일/소셜) + 게스트 예매 허용 · 2FA 미강제** | M12·M10·M11·M13·M17 | 공개/셀프서비스(쓰기 제한) | - **분리 원칙**: 6개 프론트(organizer·exhibitor·contractor·ops·admin·public+visitor)는 **역할별 번들 분리**로 최소권한·공격면 축소. 공유 디자인 시스템(design.md)·공유 컴포넌트 라이브러리·공유 API 계약을 상속한다. (UI 상세·화면 목록은 designer 후속 반영 필요.) -- **모바일**: 참가업체 리드캡처, 홀매니저 현장 검수, 업체 반입 통행증, 관람객 배지/wayfinding은 모바일 전용 최적화. 단일 앱 내 역할 스위칭 또는 역할별 별도 배포는 designer/개발 트랙에서 결정(백오피스는 모바일 미제공). + +#### 2-1-1. 계정 체계 및 모바일 앱 채널 전략 (소유자 확정 2026-07-11) + +> **개정 표기**: 본 소절은 v3.1에서 소유자 확정안을 신설한 것이며, 기존 §2 권한 모델·§5B-3 인증·M10을 삭제하지 않고 정밀화한다(상충 시 본 확정안 우선). 근거: `docs/analysis/ticketing-app-benchmark.md`(게스트 예매·간편가입은 티켓팅 업계 표준). + +**(A) 계정 체계 — 단일 통합 + 가입 트랙 분리.** 계정 테이블/RBAC는 **하나(단일 통합)** 로 두고, 그 위에서 **가입 트랙(등급)만 분리**한다. + +| 가입 트랙 | 대상 | 가입 방식 | 2FA(OTP) | 예매/조회 | +|---|---|---|---|---| +| **업무 트랙(B2B)** | 주최자·참가업체·공사업체·킨텍스 직원·관리자 | **승인/초대 기반 가입(SCR-49)** | **필수** | 역할 RBAC에 따름 | +| **관람객 트랙(B2C)** | 일반 관람객·바이어 | **간편가입(이메일 또는 소셜 로그인)** | 미강제(선택) | **게스트 예매 허용**(가입 없이 티켓 구매, SCR-P7 기반영) | + +- **계정 승격 연속성**: 관람객 → 바이어/참가업체 담당자로 승격 시 **단일 계정 위 등급 상향**으로 처리하여 **데이터 연속성(리드·비즈매칭·재방문 이력)** 을 유지한다(계정 재생성 없음). 승격 시 업무 트랙 요건(승인·2FA)이 추가 적용된다. +- 게스트 예매자는 사후 간편가입 시 예매 이력을 계정에 병합(티켓 지갑 연속). + +**(B) 모바일 앱 — 코드베이스 1개(Expo `mobile/`), 배포 타깃 2개.** + +| 앱 타깃 | 대상 | 배포 채널 | 핵심 화면 | +|---|---|---|---| +| **① 운영 앱(B2B)** | 업무 사용자 전용 | **스토어 미공개** — 사내 QR/APK 배포 | 현장 체크리스트·검수·승인 | +| **② 관람객 앱(B2C)** | 일반 관람객·바이어 | **스토어 공개 배포** | 간편가입·티켓 지갑·배지/QR·wayfinding·비즈매칭 (SCR-M5~M9·M14/M15 계열) | + +- 두 타깃은 **단일 Expo 코드베이스(`mobile/`)** 에서 빌드 프로파일/엔트리로 분기한다(중복 구현 회피). 배포 채널·스토어 정책만 다르다. +- **관람객 1차 접점은 공개 웹(SCR-P7/P8)** 이며, 관람객 앱은 **리텐션 채널**(재방문·티켓 지갑·현장 wayfinding)로 위치한다. 앱 미설치 관람객도 웹 게스트 예매로 전 여정 완결 가능. +- 백오피스(관리자)는 모바일 미제공(웹 전용). 업무 사용자 모바일은 운영 앱으로만 제공. --- @@ -449,9 +473,11 @@ sequenceDiagram ### M10. 관람객 등록·티켓·배지·현장체크인·리드캡처 — **P1** - **현재**: 킨텍스 공식은 행사일정 검색 수준. 관람객 등록·배지·리드캡처는 행사별 주최자 사무국에 파편화(공통 플랫폼 부재). -- **AI 자동화 후**: 온라인 사전등록(관람객/바이어 유형별 폼) → **모바일 배지/QR 발급** → 현장 QR 체크인(즉석 배지 인쇄·오프라인 대비) → 실시간 입장 집계. 참가업체 **리드캡처**: 모바일 앱으로 관람객 배지 QR 스캔 → 연락처·관심도 평점·메모 저장 → 팔로업 EDM(M12) 연계. 사전등록·체크인 데이터는 M16 BI로 흐른다. -- **기대 효과**: 현장 등록 대기 제거, 참가업체 ROI 측정(리드 수·품질), 관람객 흐름 데이터 확보. -- **한계**: 배지 QR·리드캡처는 개인정보(§10 R10) — 동의·보존정책 필수. 오프라인 체크인 폴백 필요(현장 네트워크 불안정). +- **AI 자동화 후**: **간편가입(이메일 또는 소셜 로그인) 또는 게스트 예매(가입 없이 티켓 구매, SCR-P7 기반영)** → 온라인 사전등록(관람객/바이어 유형별 폼) → **모바일 배지/QR 발급** → 현장 QR 체크인(즉석 배지 인쇄·오프라인 대비) → 실시간 입장 집계. 참가업체 **리드캡처**: 운영 앱(B2B)으로 관람객 배지 QR 스캔 → 연락처·관심도 평점·메모 저장 → 팔로업 EDM(M12) 연계. 사전등록·체크인 데이터는 M16 BI로 흐른다. +- **계정 트랙·승격 연속성(소유자 확정 2026-07-11, §2-1-1)**: 관람객은 **간편가입 트랙(2FA 미강제)** 이며 게스트 예매를 허용한다. 관람객 → **바이어/참가업체 담당자 승격** 시 **단일 계정 위 등급 상향**으로 처리해 **리드·비즈매칭·재방문 이력의 데이터 연속성**을 유지한다(계정 재생성 없음, 승격 시 업무 트랙 요건 2FA 추가). 게스트 예매 후 간편가입 시 예매 이력을 티켓 지갑에 병합. +- **채널**: 관람객 1차 접점은 공개 웹(SCR-P7/P8), 리텐션은 **관람객 앱(B2C, 스토어 공개)** — 티켓 지갑·배지/QR·wayfinding·비즈매칭(§2-1-1 B). +- **기대 효과**: 현장 등록 대기 제거, 게스트 예매로 가입 마찰 최소화, 참가업체 ROI 측정(리드 수·품질), 관람객 흐름 데이터 확보. +- **한계**: 배지 QR·리드캡처는 개인정보(§10 R10) — 동의·보존정책 필수. 오프라인 체크인 폴백 필요(현장 네트워크 불안정). 게스트 예매는 최소 개인정보 수집·사후 계정 병합 정책 명시 필요. - **필요 데이터**: 관람객 폼 스키마, 배지 템플릿(M17 CMS), 부스-참가업체 매핑(M2). - **연동**: M11 비즈매칭, M12 마케팅, M13 wayfinding, M16 BI. - **근거**: Eventleaf·VenueSight·Whova·WebMobi(자가등록·QR 체크인·즉석 배지·리드 리트리벌 앱·관심도 평점·메모). @@ -587,7 +613,7 @@ UIWS 표준 공통 모듈을 이식해 전 포털이 공유. 킨텍스 도메인 kintex 인증은 §2 행사 단위 RBAC를 **UIWS 표준 인증 위에** 구현한다(재설계 금지). - **JWT + RBAC**: 발급·역할 게이트. §8-1 SSO(플랫폼+행사 이중 권한)와 결합. -- **2차 인증 OTP(TOTP RFC6238)**: SHA1·30s·6자리·±1 윈도우. `TotpService` 이식 — 로그인 2단계(비번→OTP), 최초 QR 등록, 마이페이지 재설정/해제, 관리자 OTP 초기화(`otp_secret=NULL`). 대상: 관리자·홀매니저·주최자·업체(내부/발주 권한 계정) 필수, 관람객 셀프서비스는 선택. +- **2차 인증 OTP(TOTP RFC6238)**: SHA1·30s·6자리·±1 윈도우. `TotpService` 이식 — 로그인 2단계(비번→OTP), 최초 QR 등록, 마이페이지 재설정/해제, 관리자 OTP 초기화(`otp_secret=NULL`). **대상(소유자 확정 2026-07-11, §2-1-1 정합)**: **업무 사용자(주최자·참가업체·공사업체·킨텍스 직원·플랫폼/테넌트 관리자) = 필수**, **일반 관람객 셀프서비스 = 미강제(선택)**. 관람객이 바이어/참가업체 담당자로 승격(§2-1-1 A)하면 그 시점부터 업무 트랙 요건으로 2FA가 필수 전환된다. - **로그인 실패 잠금** + 관리자 해제. - **admin 비밀번호**: env `ADMIN_PASSWORD_ENC`(AES-256-GCM) + `ADMIN_KEY_FILE`(별도 키파일 root 600) 복호 → 기동 시 BCrypt 재시드. **하드코딩 `admin123` 시드 금지**(§10 보안). - **스키마**: UIWS `TB_USER`·OTP 시크릿 컬럼·`TB_AUDIT_LOG` 이식(멱등 DDL, sql.init `mode=always`+continue-on-error). @@ -991,3 +1017,4 @@ graph TB | v1.2 | 2026-07-11 | planner | **기술 스택 확정 — React + Spring Boot 3.x(Java 17) + MyBatis + PostgreSQL(PostGIS), 나노바나나 Python 워커 사이드카. GUARDiA 표준 프레임워크 정렬(사용자 지정).** §8 아키텍처 전면 정합화: 백엔드 FastAPI→Spring Boot 3.x + MyBatis(REST + WebSocket/STOMP, 룰·배치/배선 엔진=서비스 계층 + PostGIS 공간 SQL), 프론트 Next.js→React 18/19(Vite·TS, SVG/WebGL 캔버스), 비동기 큐 Redis 유지 + 이미지 생성은 별도 **Python 워커 사이드카(`tools/nanobanana`, google-genai Python SDK)** 로 분리(Spring이 RenderJob 큐잉→Python 워커 소비·생성→오브젝트 스토리지 적재→WebSocket 완료 푸시, 서류·알림도 동일 큐). Python 워커 유지 근거 명시(ReRoomAI 검증 client.py·Python SDK — Java 재구현 회피). 인증(행사 단위 RBAC+JWT)·오브젝트 스토리지 유지. §8 mermaid 갱신. **기능 범위(M1~M9)·우선순위·나노바나나 파이프라인 로직 불변** — 스택 표기 정합화만 수행 | | **v2.0** | 2026-07-11 | planner | **정체성 확장 — 킨텍스 자동전시시스템(Exhibition Automation Platform).** 글로벌 전시테크 크롤링(Eventleaf·VenueSight·Whova·ExpoPlatform·Eventbase·Pointr·Swapcard·Brella·Grip·RainFocus·ExhibitForce·FindRFP·Procore·4castplus) 근거로 확장. ①§1 비전 재정의(생애주기 폐루프, M15 옥션이 코어를 발주로 연결) ②§2 역할·포털 매트릭스 신설 — 6역할(주최자·참가·업체·홀매니저·관리자·관람객/대중) 웹/모바일 분리(organizer·exhibitor·contractor·ops·admin·public+visitor), 관리자·일반대중 페르소나 추가 ③§4 모듈맵 mermaid 확장(M10~M18) + 우선순위 총괄 ④§5A 신규 모듈 상세: **M15 공사/장치 옥션(P1·핵심, 역경매·견적서(Quotation)=응찰·실시간 순위·종합평가 낙찰·등록업체만 응찰·Auction 1─N Quotation ─ Award·PDF/버전, 입찰 플로우 시퀀스 다이어그램)**, M10 관람객 등록·배지·리드캡처(P1), M12 마케팅·EDM·공개 홍보 사이트(P1·SEO/다국어), M16 경영분석 BI(P2→**P1 승격**, design.md 반영은 designer 후속), M17 CMS(P1), M18 관리자 백오피스(P1), M11 비즈매칭·M13 wayfinding·M14 현장운영(P2) ⑤**M2·M3 3안 생성→선택/병합 UX 구체화**(1·2·3안 다양화, 구역/블록 병합, 병합 후 규정 재검증, 버전 기록) ⑥§7-3 엔티티 확장(Auction/Quotation/Award·Visitor/Badge/Lead·KPI/Content/User) ⑦§8-1 아키텍처 보강(역할별 분리 프론트+SSO/RBAC+공개사이트 SEO/다국어+백오피스+옥션 엔진) ⑧**§5B 공통/시스템관리 레이어(UIWS 표준 이식) 신설** — kintex 스택=UIWS(GUARDiA 표준 프레임워크) 동일 → 시스템관리(사용자·RBAC·공통코드·메뉴·감사로그·시스템설정)와 공통 업무기능(worklog·schedule·message·stats·notice·opinion·search·meeting·report·notification·audit) + 인증(JWT + **2차 인증 OTP TOTP RFC6238** + 로그인 실패 잠금 + admin 비번 env `ADMIN_PASSWORD_ENC` 주입)을 `workspace/uiws` 레퍼런스로 이식, 도메인 모듈은 그 위에 적재. **M18 관리자 시스템을 UIWS 시스템관리와 통합(중복 제거)**, BI/CMS/알림 경계 규칙 정의, Phase 1 선행 기반으로 로드맵 반영. ⑨**§5A M16-1 운영사(킨텍스·venue operator) 관점 수익성/ROI 소절 신설** — 참가업체 관점 ROI(리드 기반)와 명시 구분하고, 킨텍스 관점 7지표(홀·기간별 가동률/매출구성/행사별 P&L·마진/전시장별 ROI·RevPAD·㎡당 수익/참가사 리텐션·LTV/수요예측·수율·가격 최적화/경영진 KPI) + 데이터원(M1·M4·M9·M10·M15) + **BI 데이터마트(스타 스키마 Fact/Dim, DA 후속 트랙)** 정의. **기존 M2~M5 P0 코어·§6 나노바나나 로직·§8 확정 스택 보존.** design.md/타 문서는 미수정 — designer/planner/DA 후속 반영 필요로 표기 | | **v3.0** | 2026-07-11 | planner | **멀티테넌시(다중 전시관 SaaS) 확장 — 킨텍스 전용 → 다중 전시관, KINTEX=기준 테넌트 #1.** 기존 스코프(부스 코어 M2~M5·도메인 M10~M18·§5B 공통 레이어·§6 나노바나나·§8 확정 스택) **전부 보존**, 격리 레이어만 순증. ①헤더 v3.0 정체성 확장(다중 전시관 SaaS) ②**§1A 멀티테넌시 절 신설** — 테넌트=전시관(Venue) 모델(tenant_code·명칭·branding·domain/subdomain·active·locale, 홀·요율·규정 룰셋·부스표준을 테넌트 소유로 재정의)·`tenant_id` 전파 및 격리(전 도메인 엔티티 격리 vs 전역/테넌트 참조 구분)·테넌트 컨텍스트 해소(서브도메인 kintex/coex.wise.ai.kr 우선 + 사용자 소속 보조, fail-closed 주입)·역할 계층(플랫폼 슈퍼관리자 vs 테넌트 관리자 2계층, 행사 RBAC는 테넌트 내부 스코프)·마이그레이션(기존 데이터 tenant_id=1 백필, DDL은 db-engineer/DA 후속) ③**§2 역할 갱신** — 페르소나 관리자 행을 테넌트 관리자로 재정의 + 플랫폼 슈퍼관리자 신규, 권한 모델을 "테넌트 격리 ⊃ 행사 RBAC" 3중 구조로, §2-1 포털 매트릭스 admin 2계층 분리 ④**§7 데이터모델 갱신** — §7-1 마스터데이터 테넌트 스코프 재정의(킨텍스 실측=테넌트#1 시드), §7-3 Tenant/Venue 엔티티·tenant_id 전파·참조 구분·역할 계층·백필 추가 ⑤**§8-2 멀티테넌트 격리 전략 신설** — 공유 스키마 + tenant_id 컬럼(단일 kintex_db 유지·DB/스키마 분리 미채택, 비교표)·fail-closed 다층 강제(필터→서비스가드→MyBatis 공통 인터셉터→PostGIS)·성능(`(tenant_id,…)` 선두 인덱스)·보안(교차 차단·감사·스토리지/쿼터 격리)·온보딩 6단(코드 배포 없이 데이터 온보딩) ⑥§1 Non-Goal에 온보딩 범위 한정 추가. **design.md·architecture/*.md·src 미수정** — db-engineer/DA/designer 후속 반영 필요로 표기. 근거 없는 코엑스 실측 추정 배제(온보딩 시 입력) | +| **v3.1** | 2026-07-11 | planner | **계정 통합 + 가입 트랙 분리 · 모바일 2타깃 확정(소유자 확정 2026-07-11).** 기존 스코프 전부 보존, 계정·앱 채널 전략만 정밀화(상충 시 확정안 우선·삭제 없이 개정 표기). ①**§2-1 채널 매트릭스 개정** — 모바일 열을 운영 앱(B2B)/관람객 앱(B2C)으로 분리, 가입 트랙 열 신설(업무=승인·초대+2FA 필수 vs 관람객=간편가입/게스트 예매+2FA 미강제) ②**§2-1-1 신설** — (A)계정 체계 단일 통합+가입 트랙 분리(승격 시 단일 계정 등급 상향으로 리드·비즈매칭·재방문 이력 연속성 유지), (B)모바일 코드베이스 1개(Expo `mobile/`)·배포 타깃 2개(운영 앱=스토어 미공개 사내 QR/APK, 관람객 앱=스토어 공개), 관람객 1차 접점=공개 웹(SCR-P7/P8)·앱=리텐션 채널 ③**§5B-3 인증 정밀화** — 2FA 대상을 "업무 사용자 필수·관람객 미강제"로, 승격 시 2FA 필수 전환 명시 ④**M10 갱신** — 간편가입/게스트 예매(SCR-P7)·계정 승격 연속성·게스트 예매 개인정보/병합 정책 반영. 근거: `docs/analysis/ticketing-app-benchmark.md`(게스트 예매·간편가입 업계 표준). design.md·타 문서 미수정(designer 후속 반영 필요), 시크릿 미기재 | diff --git a/docs/WORK_STATUS.md b/docs/WORK_STATUS.md index 46fdfd2..ef96118 100644 --- a/docs/WORK_STATUS.md +++ b/docs/WORK_STATUS.md @@ -85,3 +85,4 @@ |---|---| | 2026-07-11 | 최초 작성 — 요구사항 27건·현황·남은 큐·함정 | | 2026-07-11 | 신규 5화면(SCR-13~17) 구현 완료 반영 + 미개발 통합 백로그 `UNDEVELOPED_BACKLOG.md` 신설·연결 | +| 2026-07-11 | 모바일 트랙 하네스 신설(kintex-mobile-dev + kintex-mobile-orchestrator, WISE 모바일 레퍼런스·Stitch 디자인 경유·G3 EAS 게이트) + 웹 WISE 기능 레퍼런스 강화(kintex-frontend-dev·impl-orchestrator) | diff --git a/docs/analysis/ticketing-app-benchmark.md b/docs/analysis/ticketing-app-benchmark.md new file mode 100644 index 0000000..5c92074 --- /dev/null +++ b/docs/analysis/ticketing-app-benchmark.md @@ -0,0 +1,141 @@ +# 관람객·입장권 예매·모바일 앱 벤치마킹 — 코엑스 및 공연/티켓 앱 + +> 작성일: 2026-07-11 · 대상 트랙: 킨텍스 자동전시시스템 관람객(M10)·티켓·모바일 앱 +> 목적: COEX·공연/티켓 빅3·글로벌 이벤트 앱을 조사하여 현행 `design.md` v2.1 티켓·관람객 화면(SCR-P7/P8·M14/M15·M5~M9)을 대조·보강. +> 원칙: 확인된 기능만 사실로 기술하고 미확인 항목은 "추정" 표기. 본 문서는 **권고만** 기록하며 PLANNING.md·design.md를 직접 수정하지 않는다. + +--- + +## 1. 벤치마킹 대상별 요약 + +| 앱/서비스 | 분류 | 강점 | 약점/한계 | 특징 기능 | +|---|---|---|---|---| +| **COEX**(코엑스 공식 웹) | 전시장 운영사 | PC·모바일 실내 길찾기(PC 지도→모바일 QR 이전), 주차 혼잡 예측(KakaoT 데이터), 관심분야 행사 알림 | 전용 네이티브 앱보다 웹 중심(추정), 예매/티켓 기능은 각 주최자 사이트에 파편화 | 실내 내비게이션, 주차 혼잡 예보, 관심 기반 행사 알림 구독 | +| **인터파크 NOL 티켓** | 공연/티켓 빅3 | 성숙한 예매 퍼널·개인화(관심인물/공연/장르), 오픈 알림(티켓캐스트 알리미), 예매대기·취소표 우선권, 결제수단 사전등록 빠른결제 | 콘서트 특화(좌석 선점·대기열)로 무료 전시엔 과한 요소, 우편배송 등 물리 티켓 잔재 | 티켓오픈 알림, 관심 구독, 예매대기 서비스, 단계별 취소수수료 규정 | +| **멜론티켓** | 공연/티켓 빅3 | 좌석 5분 선점 UX, 수령방식 선택(배송/현장수령/모바일 발권), 당일 모바일티켓 제시 입장, 강한 매크로 방지(CAPTCHA) | 좌석제 공연 중심, 입장 UX 상세는 비공개(추정) | 모바일 발권, 좌석 선점 타이머, 봇 방지 | +| **티켓링크** | 공연/티켓 빅3 | 스마트티켓(별도 발권 없이 QR/바코드 입장), 스마트스탬프(휴대폰 고유값 인식 → 캡처 티켓 무효·부정입장 방지) | 좌석/경기 특화, SMS URL 등록 절차 존재 | 스마트티켓, 스마트스탬프(디바이스 바인딩), QR/바코드 입장 | +| **킨텍스 공식 앱**(com.kintex) | 전시 특화(자사) | 무료 사전등록, 실시간 주차현황·주차비 결제, 이벤트/할인권(맛집·호텔), 지역별 등록 분포 | 전시별 통합 예매·티켓 지갑·비즈매칭은 미약(추정), 부스 wayfinding 약함 | 사전등록, 주차 연계, 편의(할인권), 등록 분포 데이터 | +| **Whova**(글로벌 이벤트 앱) | 이벤트·컨퍼런스 | 참가자별 고유 QR(배지+앱), 리드 리트리벌, 멀티트랙 개인 아젠다+세션 정원 등록, 인앱 메시징·매치메이킹·밋업 스케줄·QR 명함교환, 셀프 체크인 키오스크, 온디맨드 배지 생성, 라이브 공지/폴 | 유료 SaaS, 국내 결제/PG·다국어 로컬라이즈는 별도 | 개인 아젠다(정원), QR 명함교환, 셀프 체크인 키오스크, 라이브 인게이지먼트 | +| **Eventbrite**(글로벌) | 티켓·발견 | 오거나이저 앱 QR 스캔·이름/게스트리스트 체크인, 마켓플레이스 발견성 | 컨퍼런스 당일 인게이지먼트는 약함 | 발견(마켓플레이스), 간편 체크인, 디지털 게스트 리스트 | + +--- + +## 2. 조사 항목 ①~⑧ 베스트 프랙티스 비교 + +### ① 예매 퍼널 (단계 수·게스트 예매) +- **NOL/멜론/티켓링크**: 회차/좌석 → 권종/수량 → 예매자 → 결제 → 완료. 좌석제는 선점 타이머(멜론 5분). 콘서트는 대기열/대기번호. 회원 로그인 유도. +- **Whova/Eventbrite**: 무료·유료 티켓 유형별 수량 선택 → 등록폼 → (유료 시)결제 → QR 발급. 게스트 등록 허용, 셀프 등록 중심. +- **킨텍스 앱**: 무료 사전등록 폼 중심(결제 최소). +- **베스트**: 전시(대체로 무료·정원제)는 **좌석 선택 대신 권종·정원 기반 짧은 퍼널**이 적합. 게스트 예매 허용 + 무료 권종 결제 스킵 + 회원 저장 유도가 표준. + +### ② 티켓 수령 방식 (QR·스마트티켓·본인인증) +- **티켓링크 스마트티켓**: 별도 발권 없이 앱/URL 등록 → QR·바코드. **스마트스탬프 = 휴대폰 고유값 인식**으로 캡처본 무효·부정입장 방지. +- **멜론**: 모바일 발권/현장수령/배송 선택, 당일 모바일티켓 제시. +- **Whova**: 참가자별 **고유 QR**(배지·앱 동시), 리드 리트리벌 연계. +- **베스트**: **모바일 QR 지갑 기본 + 디바이스 바인딩/본인인증으로 위·변조·양도 방지**. 캡처만으로 입장 불가(회전 토큰·기기 인식)가 정품 티켓 신뢰의 핵심. + +### ③ 입장/체크인 UX (밝기 부스트·오프라인 QR) +- **공통(티켓링크 안내)**: QR 인식률 위해 **화면 밝기 상향** 권장. +- **Whova/Eventbrite**: 셀프 체크인 키오스크·오거나이저 앱 스캔, 이름/게스트리스트 폴백. +- **베스트**: **QR 풀스크린 + 자동 밝기 부스트 + 오프라인 캐시 표시**(현장 네트워크 불안정 대비) + 셀프 체크인 키오스크/즉석 배지 인쇄 폴백. + +### ④ 취소·환불 플로우 +- **NOL 티켓(공개 규정)**: 예매수수료는 예매 당일 자정까지만 환불. 취소수수료 = 관람일 D-10까지 정액(뮤지컬/콘서트 4,000원 등, 최대 티켓가 10%), D-9~D-7 10%, D-6~D-3 20%, D-2~D-1 30%, **당일 90%**. 웹 상단 '예매 취소' 셀프 메뉴. +- **베스트**: **구간별 취소수수료 규정(서버 권위) + 예상 환불액 실시간 표시 + 셀프 취소 + 원결제수단 자동 환불(PG 위임)**. 무료 전시는 노쇼 관리(취소 유도)가 관건. + +### ⑤ 개인화 (관심 행사·오픈 알림·최근 본) +- **NOL**: 관심인물/관심공연/관심장르 → 맞춤 추천 + **티켓오픈일 알림(SMS·이메일)**, 예매대기·취소표 우선권. +- **COEX**: 관심분야 선택 → 관련 행사 알림. +- **Whova**: 개인 아젠다·AI 추천 세션·매치메이킹. +- **베스트**: **오픈 알림/관심 행사 구독 + 관심분야 기반 추천 + 최근 본 행사·부스**로 재방문·전환 견인. + +### ⑥ 현장 편의 (주차·길찾기·혼잡도) +- **COEX**: 실내 내비(PC→모바일 QR), 주차 혼잡 예보(KakaoT). +- **킨텍스 앱**: 실시간 주차현황·주차비 결제, 할인권. +- **베스트**: **관람객 앱에 wayfinding + 실시간 주차(현황·결제·사전권) + 혼잡/대기 안내를 한 화면 동선**으로 통합. 킨텍스는 GTX-A 킨텍스역·iparking 연계가 차별점. + +### ⑦ 멤버십/등급 +- **NOL/멜론**: 회원 등급·포인트·간편결제 저장. +- **Whova**: 유형별(참가자/바이어/연사) 배지·권한 차등. +- **베스트**: **관람객 유형(일반/바이어/VIP)·재방문·바이어 인증 등급**으로 혜택·알림·비즈매칭 우선순위 차등. (전시 우선순위는 낮음) + +### ⑧ 접근성·다국어 +- **COEX/킨텍스**: 다국어 안내·수어 통역(COEX). +- **Whova**: 글로벌 대상 로컬라이즈. +- **베스트**: **티켓·배지·앱 다국어(한/영/중/일) + WCAG AA + QR/텍스트 대체수단**. 국제전시장 특성상 외국인 바이어 대응이 필수. + +--- + +## 3. 킨텍스 자동전시시스템 반영 시사점 (ⓐⓑⓒ 3분류) + +> 대조 기준: `design.md` v2.1 — SCR-P7(예매)·SCR-P8(확인·취소)·SCR-M14(모바일 예매)·SCR-M15(티켓 지갑)·SCR-M5(관람객 홈·배지)·SCR-M6(wayfinding)·SCR-M7(플로어플랜)·SCR-M8(비즈매칭)·SCR-M9(세션 아젠다) / `PLANNING.md` M10·M13. + +### ⓐ 이미 반영됨 (현행 스펙이 베스트 프랙티스 충족) +| # | 항목 | 현행 근거 | +|---|---|---| +| A1 | 짧은 예매 퍼널(권종→예매자→결제→완료), 게스트+회원, 무료 권종 결제 스킵 | SCR-P7·M14 | +| A2 | PG 위임 결제(카드정보 미저장 안내·카드입력 UI 없음) | SCR-P7 3단계·M14 | +| A3 | 모바일 QR 티켓 지갑 + **자동 밝기 부스트** + 오프라인 캐시 표시 | SCR-M15 | +| A4 | 셀프 예매 확인·취소(게스트 예매번호+이메일 조회), PII 마스킹 | SCR-P8 | +| A5 | 구간별 환불 규정 표(D-7/D-3/당일) + 예상 환불액 + 원결제수단(PG 위임) | SCR-P8 | +| A6 | 티켓→체크인→모바일 배지 상태 승격 흐름 | SCR-M15→M5 | +| A7 | 관심분야 기반 AI 추천 세션·부스, 즐겨찾기 | SCR-M5·M9 | +| A8 | wayfinding(블루닷·존레벨 폴백)·인터랙티브 플로어플랜·비즈매칭 | SCR-M6·M7·M8 | +| A9 | 단체 예매(대표자+동반자 명단 CSV) | SCR-P7 2단계 | +| A10 | WCAG AA·라이트/다크·비시각 대안 목록 | 전 화면 공통 | + +### ⓑ 보강 권고 (기존 화면 스펙 수정 후보 · P1) +| # | 권고 | 대상 화면/모듈 | 근거(§2) | +|---|---|---|---| +| B1 | **스마트티켓 부정입장 방지 강화** — 디바이스 바인딩/회전(애니메이션) QR + 본인인증으로 캡처본 무효·양도 방지(현행은 정적 QR) | SCR-M15·31 / M10 | ② 티켓링크 스마트스탬프 | +| B2 | **취소·환불 규정 정교화** — 단순 3구간(100/50/0%)을 인터파크식 다구간 수수료 + 예매수수료 별도 환불 규정으로 확장, 서버 권위 명시 | SCR-P8 / M9·M10 | ④ NOL 취소수수료 | +| B3 | **티켓·배지·관람객 앱 다국어(한/영/중/일)** — 현행 다국어는 공개사이트(M12)만 명시, 티켓·배지·지갑 화면에 다국어 확장 | SCR-P7·M5·M15 / M10·M12 | ⑧ 국제전시장 | +| B4 | **세션 정원 기반 사전등록(RSVP)** — 현행 즐겨찾기는 있으나 정원·마감·대기 미반영. 정원 소진·마감 상태 추가 | SCR-M9 / M11 | ① Whova 개인 아젠다 정원 | +| B5 | **결제수단 사전등록·간편결제 빠른결제** — 재방문·유료 권종 전환율 향상 | SCR-P7·M14 / M9 | ⑤ NOL 빠른결제 | +| B6 | **행사 라이브 공지 피드/현장 푸시** — 알림센터(M11)에 더해 행사별 라이브 공지 피드(긴급 안내·프로그램 변경) | SCR-M11·M5 / §5B notification·M12 | Whova 라이브 공지 | + +### ⓒ 신규 화면/기능 후보 (P2 백로그) +| # | 후보 | 대상 화면/모듈 | 근거(§2) | +|---|---|---|---| +| C1 | **티켓 오픈 알림·관심 행사 구독** — 공개사이트+앱에서 오픈 예정 행사 구독→오픈 시 푸시/EDM | 신규(공개사이트·앱) / M10·M12 | ⑤ NOL 오픈알림·COEX 관심알림 | +| C2 | **관람객 앱 주차 연계** — iparking 실시간 현황·주차비 결제·사전 주차권 | 신규 SCR-M(주차) / M14·M10 | ⑥ 킨텍스 앱·COEX 주차예보 | +| C3 | **관람객 대면 혼잡/대기 안내** — 입장·주차·인기 세션 실시간 혼잡도(현행 M14는 홀매니저용) | 신규 위젯(SCR-M5) / M14 | ⑥ COEX 혼잡 예보 | +| C4 | **관람객↔관람객 QR 명함 교환·컨택트 지갑** — 현행 리드캡처는 참가업체→관람객만 | SCR-M8 확장 / M11 | ② Whova QR 명함교환 | +| C5 | **인기 세션/바이어 미팅 예매 대기열·취소표 알림** — 정원 초과 세션·미팅 슬롯 대기 우선권 | SCR-M9·M8 / M11 | ⑤ NOL 예매대기 | +| C6 | **관람객 멤버십/등급** — 일반/바이어/VIP·재방문·바이어 인증 등급별 혜택·매칭 우선순위 | 신규 마이 / M10 | ⑦ 등급 차등 | +| C7 | **최근 본 행사·부스 개인화 홈 피드** — 재방문 전환 | SCR-M5 확장 / M10·M12 | ⑤ 최근 본 | +| C8 | **셀프 체크인 키오스크·즉석 배지 인쇄 화면** — PLANNING M10 즉석배지 언급 있으나 화면 미정의 | 신규 SCR / M10 | ③ Whova 셀프 체크인 키오스크 | + +### 톱 5 보강 권고 (우선 반영) +1. **B1 스마트티켓 부정입장 방지**(디바이스 바인딩·회전 QR·본인인증) — 정품 티켓 신뢰의 핵심. +2. **B2 취소·환불 규정 정교화**(다구간 수수료·예매수수료 분리·서버 권위) — 정산 정확성·분쟁 예방. +3. **C1 티켓 오픈 알림·관심 행사 구독** — 개인화·재방문·전환의 최대 레버. +4. **C2 관람객 앱 주차 연계**(iparking 실시간·결제·사전권) — 킨텍스 현장 편의 차별점. +5. **B3 티켓·배지·앱 다국어(한/영/중/일)** — 국제전시장 외국인 바이어 대응 필수. + +--- + +## 4. 출처 URL + +- COEX VISITOR(실내 길찾기·주차): https://www.coex.co.kr/ , https://www.coex.co.kr/guide/indoor-navigation/ , https://www.coex.co.kr/guide/parking-information/infomation/ +- COEX 행사 일정·관심 알림: https://www.coex.co.kr/event/full-schedules/ +- 인터파크 NOL 티켓 취소/환불: https://ticket.interpark.com/TiKi/Info/BookingGuide.asp?Url=guide_05.html +- 인터파크 NOL 티켓 수수료: https://ticket.interpark.com/TiKi/Info/BookingGuide.asp?Url=guide_11.html +- 인터파크 티켓캐스트(오픈 알림): https://ticket.interpark.com/TiKi/Info/BookingGuide.asp?Url=guide_10.html +- 인터파크 예매대기 서비스: https://ticket.interpark.com/TiKi/Info/BookingGuide.asp?Url=guide_13.html +- NOL 인터파크(개인화/오픈예정): https://nol.interpark.com/ , https://tickets.interpark.com/contents/notice +- 멜론티켓 이용안내: https://ticket.melon.com/customerservice/howto.htm +- 티켓링크 스마트티켓: https://www.ticketlink.co.kr/help/guide/popup/smart-ticket +- 티켓링크 앱: https://play.google.com/store/apps/details?id=kr.co.ticketlink.cne +- 멜론티켓 앱: https://play.google.com/store/apps/details?id=com.iloen.melonticket +- 킨텍스 주차/앱 사전등록: https://www.kintex.com/web/ko/service/parking_user.do , https://www.data.go.kr/data/15119880/fileData.do +- Whova 이벤트 앱 기능: https://whova.com/whova-event-app/ , https://whova.com/blog/best-event-conference-apps/ +- Eventbrite vs Whova 비교: https://boompop.com/blog/whova-vs-eventbrite-comparison +- 이벤트 체크인 앱 비교: https://qrsage.com/blogs/best-event-check-in-app + +--- + +## 변경 이력 +| 날짜 | 변경 내용 | +|---|---| +| 2026-07-11 | 최초 작성 — COEX·NOL/멜론/티켓링크·킨텍스 앱·Whova/Eventbrite 7종 벤치마킹, ⓐ10·ⓑ6·ⓒ8 시사점, 톱5 보강 권고 | diff --git a/docs/deliverables/DA산출물/_gen/gen_da_deliverables.py b/docs/deliverables/DA산출물/_gen/gen_da_deliverables.py new file mode 100644 index 0000000..50e1a25 --- /dev/null +++ b/docs/deliverables/DA산출물/_gen/gen_da_deliverables.py @@ -0,0 +1,854 @@ +# -*- 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 new file mode 100644 index 0000000..03a1944 Binary files /dev/null and b/docs/deliverables/DA산출물/데이터베이스설계서.pptx differ diff --git a/docs/deliverables/DA산출물/테이블정의서.xlsx b/docs/deliverables/DA산출물/테이블정의서.xlsx new file mode 100644 index 0000000..073baeb Binary files /dev/null and b/docs/deliverables/DA산출물/테이블정의서.xlsx differ diff --git a/docs/deliverables/_gen/_docxutil.py b/docs/deliverables/_gen/_docxutil.py new file mode 100644 index 0000000..47c7c80 --- /dev/null +++ b/docs/deliverables/_gen/_docxutil.py @@ -0,0 +1,70 @@ +# -*- 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 new file mode 100644 index 0000000..dfe99e4 --- /dev/null +++ b/docs/deliverables/_gen/gen_dev_plan_pptx.py @@ -0,0 +1,785 @@ +# -*- 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 new file mode 100644 index 0000000..36e4842 --- /dev/null +++ b/docs/deliverables/_gen/gen_docx.py @@ -0,0 +1,337 @@ +# -*- 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 new file mode 100644 index 0000000..e69de29 diff --git a/docs/deliverables/_gen/sources.py b/docs/deliverables/_gen/sources.py new file mode 100644 index 0000000..7145c7b --- /dev/null +++ b/docs/deliverables/_gen/sources.py @@ -0,0 +1,264 @@ +# -*- 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 new file mode 100644 index 0000000..882d1fc Binary files /dev/null and b/docs/deliverables/개발계획서.pptx differ diff --git a/docs/deliverables/사업수행계획서.docx b/docs/deliverables/사업수행계획서.docx new file mode 100644 index 0000000..7a9eaad Binary files /dev/null and b/docs/deliverables/사업수행계획서.docx differ diff --git a/docs/deliverables/지침서/_gen/gen_guides_pptx.py b/docs/deliverables/지침서/_gen/gen_guides_pptx.py new file mode 100644 index 0000000..f5fecfa --- /dev/null +++ b/docs/deliverables/지침서/_gen/gen_guides_pptx.py @@ -0,0 +1,1025 @@ +# -*- 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 new file mode 100644 index 0000000..4a4135a Binary files /dev/null and b/docs/deliverables/지침서/개발자지침서.pptx differ diff --git a/docs/deliverables/지침서/사용자지침서.pptx b/docs/deliverables/지침서/사용자지침서.pptx new file mode 100644 index 0000000..ed2d04c Binary files /dev/null and b/docs/deliverables/지침서/사용자지침서.pptx differ diff --git a/docs/deliverables/지침서/운영자지침서.pptx b/docs/deliverables/지침서/운영자지침서.pptx new file mode 100644 index 0000000..099623e Binary files /dev/null and b/docs/deliverables/지침서/운영자지침서.pptx differ diff --git a/docs/deliverables/프로그램사양서/_gen/gen_program_spec_pptx.py b/docs/deliverables/프로그램사양서/_gen/gen_program_spec_pptx.py new file mode 100644 index 0000000..8eb02b9 --- /dev/null +++ b/docs/deliverables/프로그램사양서/_gen/gen_program_spec_pptx.py @@ -0,0 +1,1115 @@ +# -*- 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 new file mode 100644 index 0000000..e4601a9 Binary files /dev/null and b/docs/deliverables/프로그램사양서/프로그램사양서.pptx differ diff --git a/docs/design.md b/docs/design.md index 3a95cf2..9aa805f 100644 --- a/docs/design.md +++ b/docs/design.md @@ -1,9 +1,11 @@ # 킨텍스 AI 전시관리 시스템 — UI 디자인 스펙 (design.md) -> 작성: 디자인 에이전트(designer) · 작성일: 2026-07-11 · 버전: v1.3 -> 근거 문서: `docs/PLANNING.md` v1.0 (모듈 맵 M1~M9, P0 기능, 나노바나나 표준 샷 세트 S1~S7) · Stitch 산출물 `stitch_kintex_ai_system_architect/`(18화면 + DESIGN.md 3변형) 학습·정합 +> 작성: 디자인 에이전트(designer) · 작성일: 2026-07-11 · 버전: **v2.1** +> 근거 문서: `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 프롬프트만 영어. +> +> **v2.0 범위 확장**: v1.3까지는 P0 코어 + SCR-13~17 중심 19화면이었다. v2.0은 **PLANNING v3.0의 전 모듈(M1~M18)·공통레이어(§5B)·6+2 역할(전시업체·공사/장치업체·주최자·킨텍스 직원(운영·관리)·시스템 관리자(테넌트/플랫폼)·일반 대중 공개사이트) × 웹(MDI)·모바일**을 빠짐없이 커버하는 **전 화면 인벤토리**로 확장한다. 기존 SCR-01~17·SCR-M1/M2 번호·토큰은 **재번호화 없이 유지**하고, 신규 화면만 이어서 부여한다 — 업무 웹 `SCR-18+`, 관리자 백오피스 `SCR-A*`, 공개사이트 `SCR-P*`, 모바일 `SCR-M3+`. 전 화면 요약 매트릭스는 **§2.8**, 화면 상세는 §3(코어·도메인 웹)·§3A(관리자)·§3B(공개사이트)·§4(모바일)에 정의한다. --- @@ -285,9 +287,150 @@ An enterprise MDI (multiple document interface) application shell for "KINTEX AI --- +## 2.8 전 화면 인벤토리 매트릭스 (v2.0) + +> **완결성 원칙**: PLANNING v3.0의 어떤 모듈/역할에서도 "design.md에 화면이 없어 디자인 불가"인 페이지가 0개가 되도록 아래 인벤토리로 전수 커버한다. 상태 컬럼 — **✅Stitch**(`stitch_kintex_ai_system_architect/`에서 이미 Stitch 생성) · **✅구현**(현행 프론트 `src/frontend`/`mobile` 구현 존재) · **🔲미생성**(신규 정의, Stitch·구현 착수 전). + +### 2.8-1. 모듈 × 역할 × 채널 요약 매트릭스 + +역할 약칭: **주**(주최자) · **참**(참가업체) · **장**(장치·공사업체) · **홀**(킨텍스 직원 — 홀매니저·운영) · **관**(시스템 관리자 — 테넌트/플랫폼) · **대**(일반 대중·관람객). 셀 = 대표 SCR(웹) / (모바일은 M접두). 다중 역할 공유 화면은 대표 역할에 표기. + +| 모듈 | 주최자 | 참가업체 | 장치업체 | 킨텍스직원 | 관리자 | 대중/관람객 | +|---|---|---|---|---|---|---| +| **공통 인증/셸** | SCR-01·49·50·51 · MDI셸 §2.7 | SCR-01 | SCR-01 | SCR-01 | SCR-01 | SCR-P4 | +| **M1 홀배정·견적** | SCR-15·18·19 | SCR-20 | — | SCR-15 | SCR-A7 | — | +| **M2 배치** | SCR-03·04·19 | — | SCR-03(RO) | SCR-03(RO) | — | SCR-P3 | +| **M3 부스설계** | — | SCR-06 | SCR-06·09·38 / M1 | — | — | — | +| **M4 유틸리티** | — | SCR-07·08 | SCR-07(검토) | SCR-10(부하) | — | — | +| **M5 나노바나나** | SCR-04 | SCR-06·12 | SCR-12 | SCR-11(참고) | — | — | +| **M6 서류·마일스톤** | SCR-22·23 | SCR-05 | — | SCR-24·10 | — | — | +| **M7 등록업체 매칭** | SCR-24 | SCR-24 | SCR-38 | — | SCR-A7 | — | +| **M8 반입/반출** | SCR-25 | SCR-25 | SCR-25 / M10 | SCR-25 | — | — | +| **M9 정산·결제** | SCR-21 | SCR-21 | — | — | SCR-21 | SCR-P7(티켓결제 연계) | +| **M10 관람·배지·리드·티켓** | SCR-30 | SCR-32 / M4 | — | SCR-31 | — | SCR-P4·P7·P8 / M5·M14·M15 | +| **M11 비즈매칭** | SCR-30 | — / M8 | — | — | — | / M8 | +| **M12 마케팅·공개사이트** | SCR-33·34 | SCR-36 | — | — | — | SCR-P1·P2·P3·P5 | +| **M13 wayfinding** | — | — | — | POI=SCR-A7/SCR-35 | — | / M6·M7 | +| **M14 현장운영** | — | — | — | SCR-14 | — | — | +| **M15 공사/장치 옥션** | SCR-26·29 | SCR-26·29 | SCR-27·28 / M13 | — | — | — | +| **M16 경영분석 BI** | SCR-13 | SCR-13(참ROI) | — | SCR-13 | SCR-13 | — | +| **M17 CMS** | SCR-35 | SCR-36 | — | — | SCR-35·37 | SCR-P5 | +| **M18 관리자·시스템관리** | — | — | — | — | SCR-16·A1~A10 | — | +| **§5B 공통 업무기능** | SCR-39~48 | SCR-39~48 | SCR-38·39~48 | SCR-39~48 | SCR-39~48 | — | + +### 2.8-2. 전 화면 SCR 인덱스 (상태 포함) + +**웹 — 코어·도메인 (§3)** + +| SCR | 화면명 | 모듈 | 역할 | P | 상태 | +|---|---|---|---|---|---| +| SCR-01 | 로그인 & 행사 워크스페이스 선택 | 공통 | 전 | P0 | ✅Stitch ✅구현 | +| SCR-02 | 주최자 대시보드 | M1·M6 | 주 | P0 | ✅Stitch ✅구현 | +| SCR-03 | 부스 배치 에디터 | M2 | 주·홀 | P0 | ✅Stitch ✅구현 | +| SCR-04 | 배치안 비교(S7) | M2·M5 | 주 | P0 | ✅Stitch ✅구현 | +| SCR-05 | 참가업체 부스 홈 | M3·M6 | 참 | P0 | ✅Stitch ✅구현 | +| SCR-06 | 부스 설계 스튜디오 | M3·M5 | 참·장 | P0 | ✅Stitch ✅구현 | +| SCR-07 | 유틸리티 배선 뷰 | M4 | 참·장 | P0 | ✅Stitch ✅구현 | +| SCR-08 | 유틸리티 신청 요약·위치표시도 | M4b | 참 | P0 | ✅Stitch ✅구현 | +| SCR-09 | 규정 검증 리포트 | M3 | 장 | P0 | ✅Stitch ✅구현 | +| SCR-10 | 홀매니저 승인 큐 | M2·M3·M6 | 홀 | P0 | ✅Stitch ✅구현 | +| SCR-11 | 홀매니저 검수 상세 | M2·M3 | 홀 | P0 | ✅Stitch ✅구현 | +| SCR-12 | 시각화 갤러리 | M5 | 전 | P0 | ✅Stitch ✅구현 | +| SCR-13 | 경영분석 대시보드 | M16 | 주·홀·관 | P1 | ✅Stitch ✅구현 | +| SCR-14 | 홀 현장운영 대시보드 | M14 | 홀 | P2 | ✅Stitch ✅구현 | +| SCR-15 | 전시 일정 관리 | M1·M6 | 주·홀 | P1 | ✅Stitch ✅구현 | +| SCR-16 | 관리자 대시보드(랜딩) | M18 | 관 | P1 | ✅Stitch ✅구현 | +| SCR-17 | 컴포넌트 가이드(스타일가이드) | §1 | 내부 | — | ✅Stitch ✅구현 | +| SCR-18 | 홀 배정 신청 & 자동 견적 | M1 | 주 | P1 | 🔲미생성 | +| SCR-19 | 부스 판매 인벤토리 | M1·M2 | 주 | P1 | 🔲미생성 | +| SCR-20 | 온라인 부스 셀프 선택·판매 | M2 | 참 | P1 | 🔲미생성 | +| SCR-21 | 정산·결제 대시보드 | M9 | 주·참·관 | P1 | 🔲미생성 | +| SCR-22 | 서류·마일스톤 워크플로 | M6 | 주 | P1 | 🔲미생성 | +| SCR-23 | 신고서류 작성(웹폼→HWP/PDF) | M6 | 주 | P1 | 🔲미생성 | +| SCR-24 | 등록업체 매칭·RFQ | M7 | 참·주 | P1 | 🔲미생성 | +| SCR-25 | 반입/반출 슬롯 예약 | M8 | 참·장·홀 | P2 | 🔲미생성 | +| SCR-26 | 옥션 목록·개설 | M15 | 참·주 | P1 | 🔲미생성 | +| SCR-27 | 옥션 상세·실시간 순위·응찰 | M15 | 장 | P1 | 🔲미생성 | +| SCR-28 | 견적서 작성·제출 | M15 | 장 | P1 | 🔲미생성 | +| SCR-29 | 낙찰 비교·선정(Award) | M15 | 주·참 | P1 | 🔲미생성 | +| SCR-30 | 관람객 등록 관리 대시보드 | M10·M11 | 주 | P1 | 🔲미생성 | +| SCR-31 | 현장 체크인 데스크/키오스크 | M10 | 홀 | P1 | 🔲미생성 | +| SCR-32 | 리드 관리·AI 스코어링 | M10 | 참 | P1 | 🔲미생성 | +| SCR-33 | EDM·캠페인 관리 | M12 | 주 | P1 | 🔲미생성 | +| SCR-34 | 스폰서십 패키지·판매 관리 | M12 | 주 | P1 | 🔲미생성 | +| SCR-35 | CMS 콘텐츠·게시 워크플로 | M17 | 주·관 | P1 | 🔲미생성 | +| SCR-36 | 참가업체 마이크로사이트 편집기 | M17 | 참 | P1 | 🔲미생성 | +| SCR-37 | 다국어 콘텐츠 관리 | M17 | 관 | P1 | 🔲미생성 | +| SCR-38 | 업체 수주 부스 대시보드 | 업체포털 | 장 | P1 | 🔲미생성 | +| SCR-39 | 업무일지(worklog) | §5B | 전 | P1 | 🔲미생성 | +| SCR-40 | 통합 일정·캘린더(schedule) | §5B | 전 | P1 | 🔲미생성 | +| SCR-41 | 쪽지(message) | §5B | 전 | P1 | 🔲미생성 | +| SCR-42 | 공지(notice) | §5B | 전 | P1 | 🔲미생성 | +| SCR-43 | 의견접수(opinion) | §5B | 전 | P2 | 🔲미생성 | +| SCR-44 | 통합검색(search) | §5B | 전 | P1 | 🔲미생성 | +| SCR-45 | 회의록(meeting·STT→Jasper) | §5B | 주·홀 | P1 | 🔲미생성 | +| SCR-46 | 업무보고·통계(report/stats) | §5B | 주·홀 | P1 | 🔲미생성 | +| SCR-47 | 알림센터(notification) | §5B | 전 | P1 | 🔲미생성 | +| SCR-48 | 마이페이지·환경설정 | §5B | 전 | P1 | 🔲미생성 | +| SCR-49 | 회원가입(웹) | §5B-3 | 전 | P1 | 🔲미생성 | +| SCR-50 | 비밀번호 찾기·재설정 | §5B-3 | 전 | P1 | 🔲미생성 | +| SCR-51 | 2차 인증(OTP) 설정 | §5B-3 | 전 | P1 | 🔲미생성 | + +**웹 — 관리자 백오피스 (§3A · SCR-A*)** + +| SCR | 화면명 | 모듈 | 역할 | 상태 | +|---|---|---|---|---| +| SCR-A1 | 사용자 관리 | M18·§5B-1 | 관 | 🔲미생성 | +| SCR-A2 | 역할·권한(RBAC) 관리 | M18·§5B-1 | 관 | 🔲미생성 | +| SCR-A3 | 공통코드 관리 | M18·§5B-1 | 관 | 🔲미생성 | +| SCR-A4 | 메뉴 관리 | M18·§5B-1 | 관 | 🔲미생성 | +| SCR-A5 | 감사로그 | M18·§5B-1 | 관 | 🔲미생성 | +| SCR-A6 | 시스템설정 | M18·§5B-1 | 관 | 🔲미생성 | +| SCR-A7 | 마스터데이터 관리(홀·요율·요금·부스표준·등록업체) | M18 | 관 | 🔲미생성 | +| SCR-A8 | 규정 룰셋 버전 관리 | M18 | 관 | 🔲미생성 | +| SCR-A9 | 테넌트(전시관) 관리·온보딩 | §1A·M18 | 플랫폼관 | 🔲미생성 | +| SCR-A10 | AI 플랫폼 설정(AiConfig) | §6·F100 | 관 | 🔲미생성 | + +**웹 — 공개 홍보 사이트 (§3B · SCR-P* · MDI 비적용·SEO/다국어)** + +| SCR | 화면명 | 모듈 | 역할 | 상태 | +|---|---|---|---|---| +| SCR-P1 | 공개 홍보 사이트 홈 | M12 | 대 | 🔲미생성 | +| SCR-P2 | 행사 상세(소개·일정·교통·연사) | M12 | 대 | 🔲미생성 | +| SCR-P3 | 공개 인터랙티브 플로어플랜 | M12·M2 | 대 | 🔲미생성 | +| SCR-P4 | 관람객 사전등록 폼 | M10 | 대 | 🔲미생성 | +| SCR-P5 | 참가업체 마이크로사이트 공개 뷰 | M17 | 대 | 🔲미생성 | +| SCR-P6 | 참가/부스 신청 문의 | M12 | 대 | 🔲미생성 | +| SCR-P7 | 입장권 예매(티켓팅) | M10·M9 | 대 | 🔲미생성 | +| SCR-P8 | 예매 확인·취소 | M10·M9 | 대 | 🔲미생성 | + +**모바일 (§4 · SCR-M*)** + +| SCR | 화면명 | 모듈 | 역할 | 상태 | +|---|---|---|---|---| +| SCR-M1 | 시공업체 현장 체크리스트 | M3 | 장 | ✅Stitch ✅구현 | +| SCR-M2 | 홀매니저 현장 검수 | M2·M3 | 홀 | ✅Stitch ✅구현 | +| SCR-M3 | 모바일 홈·대시보드(역할별) | 공통 | 전 | ✅구현(부분) | +| SCR-M4 | 참가업체 리드캡처(배지 스캔) | M10 | 참 | 🔲미생성 | +| SCR-M5 | 관람객 앱 홈·모바일 배지/QR | M10 | 대 | 🔲미생성 | +| SCR-M6 | 관람객 wayfinding·실내 지도 | M13 | 대 | 🔲미생성 | +| SCR-M7 | 관람객 플로어플랜·부스 검색 | M12·M2 | 대 | 🔲미생성 | +| SCR-M8 | 비즈매칭·미팅 | M11 | 대·참 | 🔲미생성 | +| SCR-M9 | 세션·아젠다·개인 일정 | M11 | 대 | 🔲미생성 | +| SCR-M10 | 업체 반입 통행증·안전 체크 | M8 | 장 | 🔲미생성 | +| SCR-M11 | 모바일 알림센터 | §5B | 전 | 🔲미생성 | +| SCR-M12 | 모바일 서류·승인 조회 | M6 | 주·홀 | 🔲미생성 | +| SCR-M13 | 모바일 옥션 순위 조회 | M15 | 장·참 | 🔲미생성 | +| SCR-M14 | 모바일 티켓 예매 | M10·M9 | 대 | 🔲미생성 | +| SCR-M15 | 내 티켓 지갑 | M10·M9 | 대 | 🔲미생성 | + +**집계**: 웹 코어·도메인 51 (기존 17 + 신규 34) · 관리자 10 (신규) · 공개사이트 8 (기존 6 + 신규 2 티켓) · 모바일 15 (기존 3 + 신규 12) = **총 84화면**(기존 유지 20 + 신규 64). 상태: ✅Stitch 18 · ✅구현 20 · 🔲미생성 64. + +--- + ## 3. 화면별 스펙 -> P0 모듈(M2·M3·M4·M5) 중심 총 14개 화면(웹 12 + 모바일 2) + **v2.0 확장 SCR-13~17(경영분석 M16·홀운영 M14·전시일정 M1·M6·관리자 M18·컴포넌트 가이드)**. 각 Stitch 프롬프트는 화면 1개를 1회 생성하는 기준으로 작성했다. +> P0 모듈(M2·M3·M4·M5) 중심 총 14개 화면(웹 12 + 모바일 2) + **v2.0 확장 SCR-13~17(경영분석 M16·홀운영 M14·전시일정 M1·M6·관리자 M18·컴포넌트 가이드)** + **v2.0 도메인 확장 SCR-18~51(M1·M6·M7·M8·M9·M15·M10·M12·M17·§5B 공통레이어)** + 관리자 §3A·공개사이트 §3B·모바일 §4. 각 Stitch 프롬프트는 화면 1개를 1회 생성하는 기준으로 작성했다. **디자인 토큰은 전부 §1을 따르고 신규 토큰은 추가하지 않는다.** --- @@ -607,6 +750,656 @@ A design-system style guide / component reference page for "KINTEX AI 전시관 --- +### SCR-18 홀 배정 신청 & 자동 견적 [M1] + +- **대상**: 주최자 · **모듈/P**: M1 · P1 · **진입 경로**: 콘솔 사이드바 "홀 배정" / 전시 일정(SCR-15)에서 "이 기간 배정 신청" +- **레이아웃 (위→아래)**: + 1. 좌측 2/3 — **가용성 캘린더**: 홀×날짜 그리드(홀1~10 행, 월 컬럼), 셀 상태색(예약=파랑·가배정=주황·공실=회색). 홀/반홀(1A·1B) 토글, 기간 드래그 선택 + 2. 우측 1/3 — **배정 신청 폼**: 홀·반홀 선택, 기간(장치·본행사·철거 구분), 목표 부스 수, 용도. 입력 즉시 하단 **자동 견적 카드** — `2,250원/㎡ × 면적 × 일수 × 성수기(+10%)/1전시장(+10%)` 라인별 산식 노출 + 관리비 예치금(15~20%) + 총액. AiLabel 없음(규칙 엔진). "가견적 PDF" · "배정 신청서(HWP) 생성" 버튼 + 3. 하단 고지: "최종 배정 확정은 킨텍스 내부 심사 — 본 견적은 공시요율 가견적" +- **상태/컴포넌트**: 캘린더 그리드·견적 라인 테이블(tabular numerals)·StatusBadge(신청/심사중/확정). 로딩 — 캘린더 스켈레톤 · 빈 — "선택한 기간에 배정 신청서를 작성하세요" · 에러 — 요율 마스터 로드 실패 배너. **데이터 원천**: 요율 마스터(확보)·행사일정(부분 실/샘플), 가용성은 킨텍스 내부 데이터 필요 → 부분 샘플. +- **Stitch 프롬프트**: + +``` +A hall booking and instant-quote screen for an exhibition organizer, Korean B2B web app "KINTEX AI 전시관리", desktop 1440px, light theme, primary blue #0066B3, backgrounds #F9FAFB, 1px borders #E4E7EC, Korean sans-serif (Pretendard). All UI text in Korean. Left sidebar 240px ("홀 배정" highlighted). Main split: left 2/3 an availability calendar — a grid with hall rows labeled "홀1, 홀1A, 홀1B, 홀2 … 홀10" and month/day columns, cells color-coded (blue "예약", amber "가배정", light gray "공실"), a selected date range highlighted in light blue #D9EAF7, and a half-hall toggle. Right 1/3 a booking form card titled "배정 신청": selects "홀/반홀", date range fields "장치 · 본행사 · 철거", a number input "목표 부스 수 510", a "용도" select; below it an auto-quote card "자동 견적(가견적)" showing itemized rows with mono numbers — "임대료 2,250원/㎡ × 4,941㎡ × 4일 = ₩44,469,000", "성수기 +10%", "제1전시장 +10%", "관리비 예치금(15%)", divider, bold total "합계 ₩58,700,000", fine print "공시요율 기준 가견적 — 최종 배정 확정은 킨텍스 심사", and two buttons "가견적 PDF" (outlined) and primary "배정 신청서(HWP) 생성". Data-dense scheduling + commerce quote aesthetic. +``` + +- **라우트**: `/o/:eventId/hall-booking`. MDI 적용(주최자). + +--- + +### SCR-19 부스 판매 인벤토리 [M1·M2 / F005] + +- **대상**: 주최자 · **모듈/P**: M1·M2 · P1 · **진입 경로**: 사이드바 "부스 판매" / 배치 에디터에서 "판매 현황" +- **레이아웃**: + 1. 상단 KPI 밴드: 총 부스 510 · 판매 128 · 홀드 12 · 예약 22 · 잔여 348 · 판매율 25% + 2. 좌측 2/3 — **판매 현황 뷰 토글**: [맵] 홀 도면 위 부스별 판매상태 히트(판매=녹·홀드=주황·예약=파랑·공실=회색, 클릭 시 우측 상세) / [테이블] 부스번호·유형·면적·가격·상태 배지·업체 + 3. 우측 1/3 — **부스 상세/상태 변경 패널**: 선택 부스 정보 + 상태 드롭다운(공실→홀드→예약→판매), 배정 업체 검색, 단가·프리미엄 계수, 메모 + 4. 상단 우측: 일괄 상태 변경·CSV 내보내기 +- **상태/컴포넌트**: 판매 히트 맵(도면 오버레이)·데이터 테이블(지브라·sticky)·StatusBadge. 로딩 — 스켈레톤 · 빈 — "확정 배치가 없습니다 — 배치 에디터에서 부스를 확정하세요" · 에러 재시도. **데이터 원천**: M2 확정 배치(부스 좌표) 실 가능, 판매 상태는 신규 인벤토리 API → 부분 샘플. +- **Stitch 프롬프트**: + +``` +A booth sales inventory management screen for an exhibition organizer, Korean B2B web app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Sidebar 240px ("부스 판매" highlighted). Top KPI band: stat cards "총 부스 510", "판매 128", "홀드 12", "예약 22", "잔여 348", and "판매율 25%" with a thin progress bar. Main split: left 2/3 with a view toggle "[맵] / [테이블]" (맵 active) showing a top-down hall floor plan where each small booth rectangle is filled by sales status color — green "판매", amber "홀드", blue "예약", light gray "공실" — with booth numbers; a small legend. Right 1/3 a detail panel for the selected booth "A-102 · 독립부스 · 6×3m": a status dropdown "공실 → 홀드 → 예약 → 판매" set to "판매", an exhibitor search field showing "(주)한빛로보틱스", price fields "단가 ₩13,500,000 · 프리미엄 계수 1.2", and a memo box; top-right buttons "일괄 상태 변경" and "CSV 내보내기". Dense inventory-management aesthetic with a colorful status map. +``` + +- **라우트**: `/o/:eventId/inventory`. MDI 적용(주최자). + +--- + +### SCR-20 온라인 부스 셀프 선택·판매 [M2 / F006] + +- **대상**: 참가업체 · **모듈/P**: M2 · P1 · **진입 경로**: 참가업체 포털 "부스 신청" / 공개 인터랙티브 플로어플랜(SCR-P3)에서 "부스 선택" +- **레이아웃**: + 1. 좌측 히어로 — **인터랙티브 플로어플랜**: 판매 가능 부스는 초록 테두리+가격 라벨, 매진은 회색 잠금. 호버 툴팁(부스번호·면적·가격·유형), 필터(가격대·유형·존) + 2. 우측 — **선택 요약·결제 카드**: 선택 부스 리스트(부스번호·유형·면적·가격), 옵션(코너/아일랜드 프리미엄), 합계 + "장바구니 담기/부스 예약", 결제(PG) 진입 버튼 + 3. 상단 진행 스텝: 부스 선택 → 정보 입력 → 결제 → 확정 +- **상태/컴포넌트**: 인터랙티브 SVG 맵(비시각 대안=부스 목록 테이블 — N1 접근성)·요약 카드·스테퍼. 로딩 — 맵 스켈레톤 · 빈 — "현재 판매 중인 행사가 없습니다" · 동시성 — 방금 타인이 선택한 부스 잠금 토스트. **데이터 원천**: M2 부스+SCR-19 인벤토리 상태 → 부분 샘플. +- **Stitch 프롬프트**: + +``` +An online self-service booth selection and purchase screen for an exhibitor, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, green #0E8A5F for available booths, Korean sans-serif. All UI text in Korean. Top progress stepper "부스 선택 → 정보 입력 → 결제 → 확정" (step 1 active). Left hero (2/3): an interactive top-down exhibition floor plan where available booths are outlined in green with small price labels like "₩13.5M", sold booths are gray with a lock icon; a hover tooltip on one booth "A-102 · 6×3m · 독립 · ₩13,500,000"; a filter bar "가격대 ▾ · 유형 ▾ · 존 ▾". Right panel (1/3): a "선택한 부스" summary card listing "A-102 · 독립 6×3m · ₩13,500,000", a "코너 프리미엄 +10%" option checkbox, a divider, bold total "합계 ₩14,850,000", and buttons "부스 예약" (outlined) and primary "결제하기". Friendly e-commerce-meets-floorplan aesthetic, clear availability affordances. +``` + +- **라우트**: `/e/:eventId/booth-select`. MDI 적용(참가업체). + +--- + +### SCR-21 정산·결제 대시보드 [M9] + +- **대상**: 주최자(납부 스케줄)·참가업체(유틸리티·부대 결제)·관리자(정산) · **모듈/P**: M9 · P1 · **진입 경로**: 사이드바 "정산" +- **레이아웃**: + 1. 상단 KPI: 청구 총액 · 납부 완료 · 미납 · 다음 마감 D-데이 + 2. 좌측 2/3 — **납부 스케줄 타임라인/테이블**: 계약금 20%→중도금 30%+20%→잔금 30%+예치금 15~20% 행(금액·기한·상태 배지·[결제]/[영수증]) + 유틸리티/부대/옥션 수수료 정산 라인. 지브라·sticky + 3. 우측 1/3 — **결제 카드**: 선택 항목 PG 결제(카드/계좌이체/세금계산서), 예치금 대비 실사용 정산 요약(검침 기반), 세금계산서 발행 버튼 + 4. 하단: 행사 종료 후 정산 내역(전기사용료·폐기물 처리비) 투명화 표 +- **상태/컴포넌트**: 스케줄 테이블·PG 결제 모달·StatusBadge(미납 error·완료 success)·DdayChip. 로딩 스켈레톤 · 빈 — "청구 내역이 없습니다" · 에러 재시도. **데이터 원천**: 요율·납부 규칙(확보)·PG(연동 전 샘플) → 혼합. +- **Stitch 프롬프트**: + +``` +A settlement and payment dashboard for exhibition billing, Korean B2B web app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3, success green #0E8A5F, error red #D92D20, Korean sans-serif. All UI text in Korean. Sidebar 240px ("정산" highlighted). Top KPI cards: "청구 총액 ₩64,500,000", "납부 완료 ₩38,700,000", "미납 ₩25,800,000" (red), "다음 마감 D-9" (amber D-day chip). Main split: left 2/3 a payment schedule table with columns 항목, 금액, 기한, 상태, 액션 — rows "계약금 20% ₩12,900,000 · 2026.06.01 · 완료(green pill) · [영수증]", "중도금 30% ₩19,350,000 · 07.15 · 미납(red pill) · [결제]", "잔금 30% + 예치금 15%", plus utility/booth-service/auction-fee settlement lines, zebra striped, sticky header. Right 1/3 a payment card "결제" with method segmented control "카드 / 계좌이체 / 세금계산서", a deposit-vs-actual settlement summary "예치금 ₩9.6M / 실사용 ₩7.1M / 환급 ₩2.5M", and a button "세금계산서 발행". Bottom: a small post-event settlement table "전기사용료 · 폐기물 처리비". Financial, trustworthy, number-forward layout. +``` + +- **라우트**: `/o/:eventId/settlement` · 참가 `/e/:eventId/booth/:boothId/payment`. MDI 적용. + +--- + +### SCR-22 서류·마일스톤 워크플로 [M6] + +- **대상**: 주최자 · **모듈/P**: M6 · P1 · **진입 경로**: 대시보드 마일스톤 노드 클릭 / 사이드바 "서류·마일스톤" +- **레이아웃**: + 1. 상단 — **마일스톤 진행 바**: D-150/D-30/D-25/D-7/D-0 (SCR-02 히어로 재사용, 컴팩트) + 2. 좌측 — **신고서류 체크리스트(7종+)**: 행사운영계획서·부스배치도·재해대처계획서·방화관리 책임서약서·주차관리 신청서·보안요원 배치계획·위험물 반입신고서·리깅 구조계산서 — 각 행 상태 배지(작성전/작성중/제출됨/검수중/승인/반려)·마감 D-데이·[작성]/[업로드]/[HWP 생성] + 3. 우측 — **AI 서류 검수 요약 카드**: 누락 항목·불일치("배치도 부스 수 486 ≠ 운영계획서 510") AiLabel + kxwp 릴레이 안내(제출 파일 생성→수동 업로드) +- **상태/컴포넌트**: 체크리스트·StatusBadge·DdayChip·AiLabel. 로딩 스켈레톤 · 빈 — "행사를 생성하면 마일스톤·서류가 자동 구성됩니다" · 에러 재시도. **데이터 원천**: 서식 템플릿(있음)·AI 검수(Claude 라우터) → 부분 실. +- **Stitch 프롬프트**: + +``` +A documents and milestone workflow screen for an exhibition organizer, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI, Korean sans-serif. All UI text in Korean. Sidebar 240px ("서류·마일스톤" highlighted). Top: a compact horizontal milestone bar "D-150 배정 → D-30 사전협의 → D-25 유틸리티 → D-7 신고서류 → D-0 개장" with the D-7 node highlighted amber. Main split: left 2/3 a required-documents checklist with rows, each showing a document name, a status pill and a D-day chip and action buttons — "행사운영계획서 · 승인(green) · [보기]", "부스배치도 · 제출됨(blue) · [HWP 생성]", "재해대처계획서 · 작성중(gray) · D-7 · [작성]", "리깅 구조계산서 · 반려(red) · [수정]", plus 방화관리 책임서약서, 주차관리 신청서, 보안요원 배치계획, 위험물 반입신고서. Right 1/3 an "AI 서류 검수" card with a purple accent left border listing issues: "부스배치도 부스 수 486 ≠ 운영계획서 510 (불일치)", "재해대처계획서 필수요소 누락 2건", and a note "kxwp 제출: 파일 자동 생성 후 업로드 안내". Workflow-tracker aesthetic, clear status affordances. +``` + +- **라우트**: `/o/:eventId/documents`. MDI 적용(주최자). + +--- + +### SCR-23 신고서류 작성 (웹폼 → HWP/PDF) [M6 / F022] + +- **대상**: 주최자 · **모듈/P**: M6 · P1 · **진입 경로**: SCR-22에서 "[작성]" +- **레이아웃**: + 1. 좌측 — **웹폼(섹션 아코디언)**: 서류 유형별 필드(행사 개요·안전 담당·비상 연락·위험물 목록 등), AI 자동 채움 버튼("행사 데이터로 자동 채움" AiLabel), 필수 필드 검증 + 2. 우측 — **실시간 문서 미리보기**(A4 프레임, 킨텍스 제출 서식 레이아웃) — 폼 입력이 즉시 반영 + 3. 하단 고정: 저장(임시)·"HWP 생성"·"PDF 생성"·"제출(kxwp 릴레이 안내)" +- **상태/컴포넌트**: 폼·문서 미리보기·AiLabel. 로딩 — 미리보기 스켈레톤 · 빈 — 신규 폼 · 에러 — 필수 미입력 인라인 · 생성 실패 재시도. **데이터 원천**: 서식 템플릿·AI 자동채움(Claude) → 부분 실. +- **Stitch 프롬프트**: + +``` +A form-to-document authoring screen where an organizer fills a web form and sees a live official document preview, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI. All UI text in Korean. Split layout: left half a form titled "재해대처계획서 작성" with accordion sections "행사 개요", "안전 관리 담당", "비상 연락 체계", "위험물 목록", each with Korean labeled inputs, and a purple button "행사 데이터로 자동 채움". Right half a document preview panel showing an A4 page with an official Korean government-style form layout, header "재해대처계획서", filled fields, a table, and a stamp box — updating live from the form. Sticky bottom bar: "임시 저장", "HWP 생성", "PDF 생성", and a primary "제출". A small note "제출은 kxwp 업로드용 파일 생성 방식입니다". Clean document-authoring split view. +``` + +- **라우트**: `/o/:eventId/documents/:docType/edit`. MDI 적용. + +--- + +### SCR-24 등록업체 매칭·RFQ [M7] + +- **대상**: 참가업체·주최자 · **모듈/P**: M7 · P1 · **진입 경로**: 설계 스튜디오 "업체 찾기" / 사이드바 "등록업체" +- **레이아웃**: + 1. 상단 필터: 공종 14분류(전시디자인설치·리깅·전기시설…), 지역, 규모, 예산 + 2. 좌측 — **업체 카드 리스트**: 업체명·분류·지역·규정 준수 이력·평점·(AI 추천 배지 "부스 규모·업종 적합" AiLabel). 미등록 업체는 노출 안 됨(게이트) + 3. 우측 — **RFQ 작성 패널**: 선택 업체 다중, M3 설계안·물량서 첨부, 요청 항목·마감, "견적 요청 발송". 발송 후 견적 회신 비교 진입(옥션 SCR-26으로 승격 가능) +- **상태/컴포넌트**: 업체 카드·AiLabel·첨부. 로딩 스켈레톤 · 빈 — "조건에 맞는 등록업체가 없습니다 — 필터를 조정하세요" · 에러 재시도. **데이터 원천**: 등록업체 DB(739, 수집 가능)·AI 매칭(Claude) → 부분 실. +- **Stitch 프롬프트**: + +``` +A registered-contractor matching and RFQ screen for exhibition booth construction, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI recommendations. All UI text in Korean. Top filter bar: category chips "전시디자인설치 · 리깅 · 전기시설 · 카펫 · 급배수" (14 categories), and selects "지역 ▾", "규모 ▾", "예산 ▾". Left 2/3 a list of contractor cards, each with company name "(주)공간디자인", category tag, region "경기 고양", a compliance record "규정 준수 98% · 시공 42건", a star rating, and some cards a purple "AI 추천 · 부스 규모·업종 적합" chip; a small verified badge "킨텍스 등록업체". Right 1/3 an "견적 요청(RFQ)" panel: selected contractors chips, an attachment list "부스 설계안 v2, 물량서(BOQ)", request-item fields, a deadline date, and a primary button "견적 요청 발송" with a note "미등록 업체는 응찰할 수 없습니다". Directory + procurement aesthetic. +``` + +- **라우트**: `/e/:eventId/contractors` · 주최자 `/o/:eventId/contractors`. MDI 적용. + +--- + +### SCR-25 반입/반출 슬롯 예약 [M8] + +- **대상**: 참가업체·장치업체·홀매니저(운영) · **모듈/P**: M8 · P2 · **진입 경로**: 통합 일정 / 사이드바 "반입·반출" +- **레이아웃**: + 1. 좌측 2/3 — **하역장·화물출입구 슬롯 그리드**: 게이트(도크1~6) 행 × 시간대 컬럼, 예약 셀(업체·차량·중량물 우선 표시), 드래그로 슬롯 선택. 중량물(5t↑) 우선 배치 하이라이트 + 2. 우측 1/3 — **예약 폼**: 차량정보·중량·품목, 지게차 신청(유료 옵션), 통행증 발급(QR) → 모바일(SCR-M10) 연동. 철거일 피크 대기열 예측 미니 차트 + 3. 상단: 행사·홀 필터, 반입/반출 전환 +- **상태/컴포넌트**: 슬롯 그리드·예약 폼·QR 발급·예측 미니 차트. 로딩 스켈레톤 · 빈 — "예약 가능한 슬롯이 없습니다" · 충돌 — 동시 예약 잠금 토스트. **데이터 원천**: 하역장 배치(운영팀 협업 필요) → 샘플 중심. +- **Stitch 프롬프트**: + +``` +A move-in/move-out dock slot reservation screen for an exhibition, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Top: filters "행사 ▾ · 홀7 ▾" and a toggle "[반입] / [반출]". Left 2/3 a scheduling grid with dock rows "도크1 … 도크6" and time-slot columns (08:00–20:00), reserved cells filled blue showing "(주)공간디자인 · 5t 트럭", one cell highlighted amber "중량물 우선 5t↑", empty cells selectable. Right 1/3 a reservation form "슬롯 예약": vehicle info fields "차량번호 · 중량 · 품목", a checkbox "지게차 신청(유료)", and a "통행증 발급(QR)" button showing a small QR preview, plus a small teardown-day queue prediction mini bar chart "철거일 대기열 예측". Logistics-scheduling aesthetic with a clear time grid. +``` + +- **라우트**: `/e/:eventId/logistics` · 운영 `/ops/:eventId/logistics`. MDI 적용. + +--- + +### SCR-26 공사/장치 옥션 목록·개설 [M15] + +- **대상**: 참가업체·주최자(발주) · **모듈/P**: M15 · P1(핵심) · **진입 경로**: 사이드바 "옥션" / RFQ(SCR-24)에서 "역경매로 전환" +- **레이아웃**: + 1. 상단: 탭 [진행중] [마감] [내가 개설] + "옥션 개설" 버튼 + 2. 좌측 — **옥션 카드/테이블**: 옥션명·공종·유형(역경매/RFQ)·응찰 수·현재 최저가·마감 D-데이·상태 배지. AI 자료 첨부 아이콘(배치·설계·물량·이미지) + 3. **옥션 개설 모달/우측 패널**: 공종 선택, 낙찰 기준(최저가/종합평가+가중치 슬라이더: 가격·평판·납기), 라운드·마감, AI 자료 첨부(M2~M5), 초대 업체(등록업체 게이트) +- **상태/컴포넌트**: 옥션 카드·StatusBadge·DdayChip·가중치 슬라이더. 로딩 스켈레톤 · 빈 — "진행 중인 옥션이 없습니다 — 옥션을 개설하세요" · 에러 재시도. **데이터 원천**: M15 신규 엔진(미구현) → 샘플. +- **Stitch 프롬프트**: + +``` +A construction auction (reverse auction) list and creation screen for an exhibition platform, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI attachments, Korean sans-serif. All UI text in Korean. Top: tabs "진행중 | 마감 | 내가 개설" and a primary button "옥션 개설". Left 2/3 a list of auction cards, each with auction title "A-102 독립부스 시공", a category tag "전시디자인설치", a type chip "역경매", "응찰 5개", "현재 최저가 ₩8,400,000", a D-day chip "마감 D-2", a status pill "진행중", and small purple attachment icons "배치 · 설계 · 물량서 · 예상이미지". Right 1/3 an "옥션 개설" panel: category select, an award-criteria toggle "최저가 / 종합평가", weight sliders "가격 60% · 평판 25% · 납기 15%", round/deadline fields, an AI attachment picker "M2~M5 자료 첨부", and an invited-contractor list with a note "등록업체만 초대 가능". Procurement/marketplace aesthetic. +``` + +- **라우트**: `/e/:eventId/auctions` · `/o/:eventId/auctions` · 개설 `/auctions/new`. MDI 적용. + +--- + +### SCR-27 옥션 상세·실시간 순위·응찰 [M15] + +- **대상**: 장치업체(응찰) · 발주자(주최자/참가) 뷰 병행 · **모듈/P**: M15 · P1 · **진입 경로**: 옥션 목록 → 옥션 클릭 +- **레이아웃**: + 1. 상단: 옥션 컨텍스트(공종·유형·라운드·마감 카운트다운) + 실시간 배지 + 2. 좌측 2/3 — **AI 자료 열람**: 탭[배치도][부스 설계안][배선/물량서(BOQ)][나노바나나 예상 이미지] — 응찰 근거 자료(워터마크 상시) + 3. 우측 1/3 — **실시간 순위·응찰 패널**: 현재 순위 리스트(익명 옵션: "업체 A"), 내 위치 강조, 최저가/내 견적, 라운드 종료 타이머(Redis 실시간·WebSocket), "견적서 작성·재응찰"(SCR-28) CTA. 발주자 뷰에서는 "견적서 비교표 보기"(SCR-29) +- **상태/컴포넌트**: 자료 뷰어 탭·실시간 순위 리스트·카운트다운·AiLabel(예상이미지). 로딩 — 순위 스켈레톤 · 마감됨 — 응찰 비활성+"마감된 옥션" · 에러 — WebSocket 재연결 배지. **데이터 원천**: M15 신규 → 샘플. +- **Stitch 프롬프트**: + +``` +An auction detail screen with AI reference materials and a live ranking panel for exhibition construction bidding, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI, Korean sans-serif. All UI text in Korean. Top: context "A-102 독립부스 시공 · 역경매 · 라운드 2 · 마감 01:24:36" with a live red dot badge "실시간". Left 2/3 an AI reference viewer with tabs "배치도 | 부스 설계안 | 물량서(BOQ) | 예상 이미지" showing a photorealistic booth render with a faint watermark "AI 생성 예상 이미지" under the 예상 이미지 tab. Right 1/3 a live ranking panel "실시간 순위": ranked rows "1위 업체 A ₩8,400,000", "2위 나의 응찰 ₩8,700,000" (highlighted), "3위 업체 C ₩8,900,000", an anonymize toggle "익명 순위", a countdown timer "라운드 종료 01:24:36", and a primary button "견적서 작성·재응찰". Competitive, real-time marketplace aesthetic. +``` + +- **라우트**: `/auctions/:auctionId`. MDI 적용. + +--- + +### SCR-28 견적서 작성·제출 [M15] + +- **대상**: 장치업체 · **모듈/P**: M15 · P1 · **진입 경로**: SCR-27 → "견적서 작성·재응찰" +- **레이아웃**: + 1. 좌측 — **견적서 라인 에디터**: 공종·자재·수량·단가·금액 행 추가(물량서 BOQ에서 자동 프리필 AiLabel), 소계·부가세·총액 자동 계산, 납기·유효기간·조건 + 2. 우측 — **견적서 PDF 미리보기**(A4, 업체 로고·총액·서명란) + 버전 이력(라운드 내 재응찰 v1·v2) + 3. 하단: 임시저장·"견적서 제출(응찰)" — 제출 시 순위 즉시 갱신 +- **상태/컴포넌트**: 라인 테이블(tabular)·PDF 미리보기·버전 이력·AiLabel(BOQ 프리필). 로딩 미리보기 스켈레톤 · 빈 — 신규 견적서 · 에러 — 필수 미입력 인라인. **데이터 원천**: M15·BOQ(부분) → 샘플. +- **Stitch 프롬프트**: + +``` +A quotation (bid) authoring and submission screen for a contractor in an exhibition reverse auction, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI prefill. All UI text in Korean. Split: left half a line-item quotation editor titled "견적서 작성" with a table (공종, 자재, 수량, 단가, 금액) rows like "골조 · 알루미늄 프로파일 · 48 · ₩45,000 · ₩2,160,000", a purple note "물량서(BOQ)에서 자동 채움", and computed rows "소계 ₩7,636,000", "부가세 ₩763,600", bold "총액 ₩8,399,600", plus fields "납기 · 유효기간 · 조건". Right half a quotation PDF preview (A4) with a contractor logo, itemized table, bold total, and a signature box, plus a version history list "v1 (라운드1) · v2 (현재)". Sticky bottom: "임시 저장" and primary "견적서 제출(응찰)". Precise document + spreadsheet aesthetic. +``` + +- **라우트**: `/auctions/:auctionId/quote`. MDI 적용. + +--- + +### SCR-29 낙찰 비교·선정 (Award) [M15] + +- **대상**: 발주자(주최자·참가업체) · **모듈/P**: M15 · P1 · **진입 경로**: 옥션 마감 → "견적서 비교" +- **레이아웃**: + 1. 상단: 옥션 요약(공종·응찰 수·낙찰 기준) + 2. **비교 테이블(전폭)**: 열=업체, 행=총액·항목별 단가·납기·평판·종합점수(가중 스코어 산식 노출 AiLabel). 최적값 셀 하이라이트, 종합점수 내림차순 + 3. 우측/하단 — **선정 패널**: 추천 업체(종합점수 1위) + 선정 사유 입력, "낙찰(Award)" → 계약/발주 전환(M6·M9) 링크, 낙찰 통지 미리보기 +- **상태/컴포넌트**: 비교 테이블(sticky 열·하이라이트)·스코어 게이지·AiLabel. 로딩 스켈레톤 · 빈 — "응찰이 없습니다" · 에러 재시도. **데이터 원천**: M15 → 샘플. +- **Stitch 프롬프트**: + +``` +An award decision / quotation comparison screen for a buyer in an exhibition reverse auction, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI score, green #0E8A5F highlights. All UI text in Korean. Top: auction summary "A-102 독립부스 시공 · 응찰 5 · 낙찰기준 종합평가". A wide comparison table with contractor columns "업체 A · 업체 B · 업체 C" and metric rows "총액 ₩8.4M / ₩8.7M / ₩8.9M", "골조 단가", "전기 단가", "납기 12일 / 10일 / 14일", "평판 4.8 / 4.6 / 4.2", and a "종합점수" row "92.4 / 89.1 / 84.7" with a small purple chip "AI 가중 스코어"; the best cells are highlighted green, columns sorted by score. Right panel "낙찰 선정": a recommended card "업체 A · 종합 1위", a "선정 사유" textarea, a primary green button "낙찰(Award)", and a note "낙찰 견적서는 계약·발주 문서로 전환됩니다". Decision-support, comparison-heavy aesthetic. +``` + +- **라우트**: `/auctions/:auctionId/award`. MDI 적용. + +--- + +### SCR-30 관람객 등록 관리 대시보드 [M10·M11] + +- **대상**: 주최자 · **모듈/P**: M10 · P1 · **진입 경로**: 사이드바 "관람객" +- **레이아웃**: + 1. 상단 KPI: 사전등록 12,480 · 체크인 8,210 · 노쇼율 · 바이어 비중 · 리드 생성 수 + 2. 좌측 2/3 — **등록 추이 차트**(Recharts Area, 일자별 사전등록/체크인) + 등록 유형 분포(관람객/바이어/VIP) + 3. 우측 1/3 — **등록 폼 관리**: 활성 폼 리스트, AI 폼빌더 진입(F046 AiLabel), 배지 템플릿(M17 연계) + 4. 하단 — **등록자 테이블**: 이름(마스킹)·유형·소속·등록일·체크인 상태·배지 발급 — 개인정보 마스킹·감사 표기 +- **상태/컴포넌트**: KPI·Recharts·등록자 테이블(PII 마스킹)·AiLabel. 로딩 스켈레톤 · 빈 — "사전등록이 아직 없습니다" · 에러 재시도. **데이터 원천**: M10 신규 → 샘플. **PII 준수**(N2). +- **Stitch 프롬프트**: + +``` +A visitor registration management dashboard for an exhibition organizer, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI, Korean sans-serif. All UI text in Korean. Sidebar 240px ("관람객" highlighted). Top KPI cards: "사전등록 12,480", "체크인 8,210", "노쇼율 12%", "바이어 비중 34%", "리드 생성 2,140". Main: left 2/3 a chart card "등록·체크인 추이" with a multi-series area chart (blue = 사전등록, slate = 체크인) over dates, and a small donut "등록 유형 (관람객 62% · 바이어 34% · VIP 4%)". Right 1/3 a "등록 폼 관리" card with a form list and a purple button "AI 폼빌더", plus a "배지 템플릿" thumbnail. Bottom: a registrants data table with columns 이름, 유형, 소속, 등록일, 체크인, 배지 — names partially masked like "김**", status pills, zebra striped. Data-rich CRM-style aesthetic with privacy masking. +``` + +- **라우트**: `/o/:eventId/visitors`. MDI 적용. + +--- + +### SCR-31 현장 체크인 데스크/키오스크 [M10] + +- **대상**: 홀매니저·현장 운영 · **모듈/P**: M10 · P1 · **진입 경로**: 운영 대시보드 "체크인" / 키오스크 모드 +- **레이아웃**: + 1. 중앙 — **QR 스캔 존**(대형 카메라 프레임) + 수동 검색(이름·연락처·등록번호) + 2. 스캔 성공 시 — **관람객 카드**: 이름·유형·소속·사진, "즉석 배지 인쇄" 버튼, 재발급 + 3. 우측 — **실시간 입장 집계**: 시간대별 체크인 수, 현재 장내 추정 인원, 오프라인 모드 배지("네트워크 불안정 — 로컬 저장 후 동기화") + 4. 키오스크 모드 토글(셀프 체크인 대형 UI) +- **상태/컴포넌트**: 스캔 프레임·관람객 카드·집계 위젯·오프라인 폴백 배지. 로딩 — 스캔 대기 애니메이션 · 실패 — "등록 정보를 찾을 수 없습니다 — 현장 등록" · 에러 재시도. **데이터 원천**: M10 신규 → 샘플. **오프라인 폴백 필수**(F049). +- **Stitch 프롬프트**: + +``` +An on-site check-in desk / kiosk screen for exhibition staff, Korean B2B tool, desktop 1440px (kiosk-friendly), light theme, blue #0066B3, success green #0E8A5F. All UI text in Korean. Center: a large QR scan area with a camera viewfinder frame and text "배지 QR을 스캔하세요", and below it a manual search "이름 · 연락처 · 등록번호로 검색". On a successful scan, a visitor card appears: photo, name "정관람", type chip "바이어", company "(주)미래기술", and buttons "즉석 배지 인쇄" (primary) and "재발급". Right column: a real-time entry widget "실시간 입장" with a small bar chart by hour, a big number "현재 장내 8,210명", and an offline status badge "오프라인 — 로컬 저장 후 동기화". A top-right toggle "키오스크 모드". Fast, high-contrast, glanceable check-in aesthetic. +``` + +- **라우트**: `/ops/:eventId/checkin`. MDI 적용(운영). + +--- + +### SCR-32 리드 관리·AI 스코어링 [M10] + +- **대상**: 참가업체 · **모듈/P**: M10 · P1 · **진입 경로**: 참가업체 포털 "리드" (모바일 리드캡처 SCR-M4의 웹 관리) +- **레이아웃**: + 1. 상단 KPI: 총 리드 · 핫리드(AI 스코어 상위) · 팔로업 대기 · 전환 추정 + 2. 좌측 2/3 — **리드 테이블**: 이름(마스킹)·소속·관심 제품·관심도(별점)·**AI 스코어(0~100 게이지, AiLabel)**·메모·수집 시각. 필터(스코어·태그) + 3. 우측 1/3 — **리드 상세·팔로업**: 선택 리드 프로필, AI 추천 팔로업 문구(EDM 초안 AiLabel), "EDM 발송"(M12 연계)·CSV 내보내기 +- **상태/컴포넌트**: 리드 테이블(PII 마스킹)·AI 스코어 게이지·AiLabel·EDM 초안. 로딩 스켈레톤 · 빈 — "수집된 리드가 없습니다 — 모바일 앱에서 배지를 스캔하세요" · 에러 재시도. **데이터 원천**: M10 신규 → 샘플. **PII 동의·보존**(R10). +- **Stitch 프롬프트**: + +``` +A lead management and AI scoring screen for an exhibitor, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI score. All UI text in Korean. Sidebar 240px ("리드" highlighted). Top KPI cards: "총 리드 342", "핫리드 68", "팔로업 대기 120", "전환 추정 24". Main: left 2/3 a leads data table with columns 이름, 소속, 관심 제품, 관심도, AI 스코어, 수집시각 — rows with masked names "김**", companies, interest tags, 5-star interest, and an AI score shown as a small horizontal gauge with number "87" and a purple chip; filter chips "스코어 80+ · 태그". Right 1/3 a lead detail panel: profile, a purple "AI 추천 팔로업" card with a draft message, and buttons "EDM 발송" and "CSV 내보내기". Sales-CRM aesthetic with prominent AI scoring and privacy masking. +``` + +- **라우트**: `/e/:eventId/booth/:boothId/leads`. MDI 적용. + +--- + +### SCR-33 EDM·캠페인 관리 [M12] + +- **대상**: 주최자 · **모듈/P**: M12 · P1 · **진입 경로**: 사이드바 "마케팅" +- **레이아웃**: + 1. 상단: 캠페인 KPI(발송·오픈율·클릭율·등록 전환) + "새 캠페인" + 2. 좌측 2/3 — **캠페인 리스트**(상태 배지: 초안/예약/발송중/완료) + 성과 미니 지표 + 3. 우측 1/3 — **캠페인 빌더**: 세그먼트(사전등록자·과거 관람객·바이어) 선택, AI 카피 초안(AiLabel)·나노바나나 이미지 삽입, 발송 시점(즉시/예약), 미리보기(수신동의·수신거부 준수 표기) +- **상태/컴포넌트**: 캠페인 리스트·StatusBadge·세그먼트 셀렉터·AiLabel. 로딩 스켈레톤 · 빈 — "캠페인이 없습니다" · 에러 재시도. **데이터 원천**: M12·M10 세그먼트 → 샘플. **정보통신망법 수신동의 준수** 표기. +- **Stitch 프롬프트**: + +``` +An EDM / marketing campaign management screen for an exhibition organizer, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI. All UI text in Korean. Sidebar 240px ("마케팅" highlighted). Top: KPI cards "발송 24,000 · 오픈율 38% · 클릭율 9% · 등록전환 1,240" and a primary button "새 캠페인". Left 2/3 a campaign list with rows showing campaign name, a status pill ("초안" gray, "예약" blue, "발송중" amber, "완료" green), audience size, and small open/click metrics. Right 1/3 a "캠페인 빌더" panel: audience segment checkboxes "사전등록자 · 과거 관람객 · 바이어", a purple button "AI 카피 초안", an image slot "나노바나나 예상 이미지 삽입", a send-time toggle "즉시 / 예약", and an email preview with a footer "수신거부 · 정보통신망법 수신동의 준수". Marketing-automation aesthetic. +``` + +- **라우트**: `/o/:eventId/campaigns`. MDI 적용. + +--- + +### SCR-34 스폰서십 패키지·판매 관리 [M12 / F070] + +- **대상**: 주최자 · **모듈/P**: M12 · P1 · **진입 경로**: 사이드바 "스폰서십" +- **레이아웃**: + 1. 상단 KPI: 스폰서 수 · 판매액 · 잔여 패키지 · 이행물 진행률 + 2. 좌측 — **패키지 티어 카드**(다이아몬드/골드/실버: 혜택·가격·잔여 수량·판매 상태) + 3. 우측 — **스폰서·이행물 관리**: 스폰서 리스트(계약·결제 상태), 이행물 체크리스트(로고 노출·부스·연사 세션), 노출·리드·ROI 대시보드 링크(F071) +- **상태/컴포넌트**: 티어 카드·이행물 체크리스트·StatusBadge. 로딩 스켈레톤 · 빈 — "등록된 스폰서십 패키지가 없습니다" · 에러 재시도. **데이터 원천**: 신규 → 샘플. +- **Stitch 프롬프트**: + +``` +A sponsorship package and sales management screen for an exhibition organizer, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Sidebar 240px ("스폰서십" highlighted). Top KPI cards: "스폰서 18", "판매액 ₩420,000,000", "잔여 패키지 6", "이행물 진행 72%". Left 2/3 sponsorship tier cards in a row — "다이아몬드" (gold accent, benefits list, "₩50,000,000", "잔여 1"), "골드", "실버" — each with a benefits bullet list and a "판매" status. Right 1/3 a "스폰서 관리" panel: a sponsor list with contract/payment status pills, a fulfillment checklist "로고 노출 · 부스 배정 · 연사 세션", and a link "노출·리드·ROI 대시보드". B2B sales/CRM aesthetic with clear tier hierarchy. +``` + +- **라우트**: `/o/:eventId/sponsorship`. MDI 적용. + +--- + +### SCR-35 CMS 콘텐츠·게시 워크플로 [M17] + +- **대상**: 주최자·관리자 · **모듈/P**: M17 · P1 · **진입 경로**: 사이드바 "콘텐츠(CMS)" +- **레이아웃**: + 1. 좌측 — **콘텐츠 트리/목록**: 페이지·공지·행사 소개·연사·FAQ. 상태 배지(초안→검수→승인→게시), 언어 탭(한/영/중/일) + 2. 중앙 — **콘텐츠 에디터**(블록/리치텍스트, 미디어 라이브러리, 예약 게시), sanitize 표기(XSS N2) + 3. 우측 — **게시 워크플로 패널**: 버전 이력, 검수 요청·승인, 예약 게시 일정, 사이니지 배포 토글(M13 연계) +- **상태/컴포넌트**: 콘텐츠 트리·리치 에디터·StatusBadge·언어 탭·버전 이력. 로딩 스켈레톤 · 빈 — "콘텐츠가 없습니다 — 새 페이지" · 에러 재시도. **데이터 원천**: M17 신규 → 샘플. +- **Stitch 프롬프트**: + +``` +A headless CMS content editor with publishing workflow for an exhibition platform, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Three-column: left a content tree/list "페이지 · 공지 · 행사 소개 · 연사 · FAQ" with status pills ("초안" gray, "검수" amber, "게시" green) and language tabs "한 · 영 · 중 · 일". Center a block/rich-text editor with a toolbar, a media library strip, a title "행사 소개", body content blocks, and a "예약 게시" datetime field. Right a publishing panel "게시 워크플로": a version history list "v3 (현재) · v2 · v1", a "검수 요청" button, an "승인" button, a schedule "2026.08.01 09:00 예약", and a toggle "사이니지 배포". Clean content-management aesthetic, multilingual affordances. +``` + +- **라우트**: `/o/:eventId/cms` · 관리자 `/admin/cms`. MDI 적용. + +--- + +### SCR-36 참가업체 마이크로사이트 편집기 [M17] + +- **대상**: 참가업체 · **모듈/P**: M17 · P1 · **진입 경로**: 참가업체 포털 "마이크로사이트" +- **레이아웃**: + 1. 좌측 — **섹션 편집 패널**: 부스 소개·제품·나노바나나 예상샷 갤러리·연락처·미팅 예약 링크(M11), 테마 선택 + 2. 중앙 — **라이브 미리보기**(반응형: 데스크톱/모바일 토글) — 공개 뷰(SCR-P5)와 동일 렌더 + 3. 우측 — 게시 상태·URL(서브도메인/슬러그)·SEO 메타·다국어 토글 +- **상태/컴포넌트**: 섹션 편집·라이브 프리뷰·반응형 토글·AiLabel(예상샷). 로딩 스켈레톤 · 빈 — 템플릿 선택 안내 · 에러 재시도. **데이터 원천**: M17·M5 이미지 → 부분(이미지 실 가능). +- **Stitch 프롬프트**: + +``` +An exhibitor microsite builder screen, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI images. All UI text in Korean. Left panel: section editor list "부스 소개 · 제품 · 예상샷 갤러리 · 연락처 · 미팅 예약" with edit controls and a theme picker. Center: a live preview of a company microsite with a hero banner, company name "(주)한빛로보틱스", a product grid, and an AI-render booth photo gallery with faint watermark "AI 생성 예상 이미지"; a device toggle "데스크톱 / 모바일" above the preview. Right panel: publish status "게시됨", a URL field "hanbit.expo.kintex.kr", SEO meta fields, and language toggle "한 · 영 · 중 · 일". Modern site-builder aesthetic. +``` + +- **라우트**: `/e/:eventId/microsite`. MDI 적용. + +--- + +### SCR-37 다국어 콘텐츠 관리 [M17 / F069] + +- **대상**: 관리자·콘텐츠 담당 · **모듈/P**: M17 · P1 · **진입 경로**: CMS(SCR-35) "다국어" / 관리자 사이드바 +- **레이아웃**: + 1. 상단: 언어 커버리지 요약(한 100% · 영 82% · 중 60% · 일 45%), 대상 언어 선택 + 2. **번역 매트릭스 테이블**: 콘텐츠 키 × 언어 셀(원문·번역·상태: 미번역/AI 초벌/검수완료). AI 자동 번역 버튼(AiLabel) + 사람 검수 워크플로 + 3. 우측 — 선택 항목 병렬 편집(원문↔번역), 용어집·톤 가이드 +- **상태/컴포넌트**: 번역 매트릭스·커버리지 바·AiLabel·병렬 에디터. 로딩 스켈레톤 · 빈 — "번역 대상 콘텐츠가 없습니다" · 에러 재시도. **데이터 원천**: M17·AI 번역(Claude) → 부분. +- **Stitch 프롬프트**: + +``` +A multilingual content management (translation matrix) screen, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI translation. All UI text in Korean. Top: language coverage summary with progress bars "한 100% · 영 82% · 중 60% · 일 45%" and a target-language selector. Center: a translation matrix table with rows = content keys ("행사명", "홀 안내", "참가 신청") and columns = languages (KO source, EN, ZH, JA), each cell showing translated text and a status chip ("미번역" gray, "AI 초벌" purple, "검수완료" green); a purple button "AI 자동 번역". Right panel: a side-by-side editor "원문(한) ↔ 번역(영)" with a glossary and tone guide. Localization-tool aesthetic, dense grid. +``` + +- **라우트**: `/admin/i18n` · `/o/:eventId/cms/i18n`. MDI 적용. + +--- + +### SCR-38 업체 수주 부스 대시보드 [업체 포털] + +- **대상**: 장치·공사업체 · **모듈/P**: 업체 포털(M3·M15·M8 진입 허브) · P1 · **진입 경로**: 업체 포털 로그인 기본 홈 +- **레이아웃**: + 1. 상단 KPI: 수주 부스 · 진행 옥션 · 응찰 대기 · 시공 진행 · 마감 임박 + 2. 좌측 2/3 — **수주 부스 리스트**: 행사·부스·발주자·설계 상태·시공 단계 스테퍼(설계→규정검증→반입→시공→검수), [설계 스튜디오]·[규정 리포트]·[반입 예약] + 3. 우측 1/3 — **옥션/견적 현황**: 참여 옥션·내 순위·마감 D-데이, 알림 피드 +- **상태/컴포넌트**: KPI·수주 리스트·시공 스테퍼·StatusBadge·DdayChip. 로딩 스켈레톤 · 빈 — "수주한 부스가 없습니다 — 옥션에 참여하세요" CTA · 에러 재시도. **데이터 원천**: M3·M15 → 부분 샘플. +- **Stitch 프롬프트**: + +``` +A contractor's awarded-booths dashboard (home) for exhibition construction, Korean B2B web app, desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Sidebar 240px (업체 포털 menu, "수주 부스" highlighted). Top KPI cards: "수주 부스 8", "진행 옥션 3", "응찰 대기 2", "시공 진행 5", "마감 임박 1". Left 2/3 a list of awarded booth cards, each with event + booth "스마트팩토리 · A-102", client "(주)한빛로보틱스", and a construction stage stepper "설계 → 규정검증 → 반입 → 시공 → 검수" with the current stage highlighted, plus buttons "설계 스튜디오", "규정 리포트", "반입 예약". Right 1/3 an "옥션 현황" panel with participating auctions, my rank "2위", D-day chips, and a notification feed. Operational contractor-portal aesthetic. +``` + +- **라우트**: `/c/dashboard`. MDI 적용(업체). + +--- + +### SCR-39 ~ SCR-48 공통 업무 기능 (§5B UIWS 표준 이식) + +> **UIWS(`workspace/uiws/frontend/src/pages`) 표준을 kintex §1 토큰으로 정합.** 전 포털이 공유하는 공통 업무 레이어(§5B-2). 각 화면은 UIWS 대응 페이지를 레퍼런스로 하되, **레이아웃 언어는 §1(1px 테두리·지브라 테이블·44px 행·StatusBadge)**, MDI 셸(§2.7) 문서 탭으로 열린다. 목록/상세/폼 3형태는 대표 화면 1개 프롬프트로 생성 후 파생. + +#### SCR-39 업무일지 (worklog) — 전 역할 · P1 +- **레이아웃**: 좌측 필터(기간·작성자·유형)+목록 테이블(날짜·제목·유형·진행상태·시간) / 우측 상세·작성 폼(공수·이슈·댓글). 킨텍스 접합: 홀매니저 현장 일지·업체 시공 일지. 상태: 로딩 스켈레톤·빈("작성된 업무일지가 없습니다")·에러 재시도. 참조: UIWS `worklog/WorklogCalendarPage`·`RelatedDocsPanel`. +- **Stitch 프롬프트**: +``` +A daily worklog screen in a Korean B2B enterprise app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3, 1px borders #E4E7EC, Korean sans-serif (Pretendard). All UI text in Korean. Left sidebar 240px ("업무일지" highlighted). Main split: left 2/3 a filter bar (기간, 작성자, 유형) above a data table with columns 날짜, 제목, 유형, 진행상태, 공수 — zebra striped rows with status pills "진행중 / 완료", 44px row height, sticky header. Right 1/3 a worklog detail/compose panel: date, title, a work-type select, hours input, an issue textarea, and a comment thread with an input. Dense enterprise productivity aesthetic. +``` + +#### SCR-40 통합 일정·캘린더 (schedule) — 전 역할 · P1 +- **레이아웃**: 월/주 캘린더 + 우측 일정 리스트/작성. 킨텍스 접합: 행사 마일스톤(M6)·옥션 마감·반입 슬롯(M8) 통합 뷰(색상 범례). SCR-15(운영 전 홀 일정)와 구분 — 본 화면은 개인/부서 업무 일정. 상태: 로딩·빈·에러. 참조: UIWS `schedule/PersonalSchedulePage`·`ScheduleCalendar`. +- **Stitch 프롬프트**: +``` +A unified personal/team schedule calendar in a Korean B2B app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Sidebar 240px ("일정" highlighted). Main: a large month calendar grid "2026년 8월" with color-coded events — blue "행사 마일스톤", purple "옥션 마감", green "반입 슬롯", plus a legend and view toggle "월 / 주 / 일". Right 1/3 an agenda list for the selected day with event cards and a "일정 추가" button. Clean calendar/scheduling aesthetic. +``` + +#### SCR-41 쪽지 (message) — 전 역할 · P1 +- **레이아웃**: 3-패널(받은/보낸/작성) — 목록·상세·수신자 선택(부서 트리). 킨텍스 접합: 주최자↔참가↔업체↔홀매니저 행사 내 커뮤니케이션. 상태: 빈("받은 쪽지가 없습니다")·읽음 표시. 참조: UIWS `message/*`. +- **Stitch 프롬프트**: +``` +An internal messaging (쪽지) screen with a 3-pane layout in a Korean B2B app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Sidebar 240px ("쪽지" highlighted). Layout: a left folder rail "받은 쪽지 · 보낸 쪽지 · 임시보관", a middle message list with sender, subject, time and unread bold rows, and a right reading pane with message body, sender info, and reply box; a "쪽지 쓰기" button opens a recipient picker with a department tree. Familiar mail-client aesthetic. +``` + +#### SCR-42 공지 (notice) — 전 역할 · P1 +- **레이아웃**: 공지 목록(중요 상단 고정·팝업 설정) + 상세. 킨텍스 접합: 킨텍스 공지(852건 도메인)·행사 공지 → M17 CMS 채널 정합(경계: 발행성=CMS, 알림성=공지). 상태: 빈·로딩. 참조: UIWS `notice/*`. +- **Stitch 프롬프트**: +``` +A notice board (공지사항) screen in a Korean B2B app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Sidebar 240px ("공지" highlighted). Main: a pinned "중요" notice at top with a red badge, then a notice list table (제목, 작성자, 게시일, 조회수) with zebra rows, a search bar, and a "공지 작성" button; clicking opens a detail view with title, meta, body, and attachments. Clean announcement aesthetic. +``` + +#### SCR-43 의견접수 (opinion) — 전 역할 · P2 +- **레이아웃**: 의견/건의 접수 폼 + 처리 상태 목록(접수→검토→답변). 킨텍스 접합: 고객의소리·규정 문의. 상태: 빈·로딩. 참조: UIWS `opinion/*`. +- **Stitch 프롬프트**: +``` +A feedback/suggestion (의견접수) screen in a Korean B2B app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Sidebar 240px ("의견접수" highlighted). Main: a submission form with a category select "규정 문의 / 개선 제안 / 불편 신고", subject, body, attachment, and a "제출" button; below, a list of my submissions with status pills "접수 / 검토중 / 답변완료" and reply preview. Simple support-form aesthetic. +``` + +#### SCR-44 통합검색 (search) — 전 역할 · P1 +- **레이아웃**: 상단 대형 검색창 + 결과 탭(행사·부스·업체·문서·콘텐츠) + 필터. 킨텍스 접합: 크로스 모듈 검색. AI 자연어 검색 옵션(AiLabel). 상태: 빈("검색어를 입력하세요")·결과 없음. 참조: UIWS `SearchResultsPage`·`NlSearchPage`. +- **Stitch 프롬프트**: +``` +A unified/global search results screen in a Korean B2B app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI. All UI text in Korean. A large centered search bar with a purple "AI 자연어 검색" toggle. Below, result tabs "전체 · 행사 · 부스 · 업체 · 문서 · 콘텐츠" and a left facet filter, with grouped result cards each showing an icon, title, snippet with highlighted query terms, and module tag. Fast, clean search aesthetic. +``` + +#### SCR-45 회의록 (meeting · STT→Jasper PDF) — 주최자·홀매니저 · P1 +- **레이아웃**: 회의 목록 + 상세(녹음 재생·STT 전사·요약 AiLabel·액션아이템→일정/서류). 킨텍스 접합: 사전업무협의(D-30) 회의록 자동화. Jasper PDF 출력. 상태: 전사 중 진행 배지·빈·에러. 참조: UIWS `meeting/MeetingDetailPage`. +- **Stitch 프롬프트**: +``` +A meeting minutes screen with STT transcription in a Korean B2B app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI. All UI text in Korean. Sidebar 240px ("회의록" highlighted). Main: left a meeting list, right a detail view with an audio player, a transcript panel "STT 전사" with speaker labels, a purple "AI 요약" card, an action-items table linking to 일정/서류, and a "회의록 PDF" button. Note "사전업무협의(D-30)". Productivity + AI aesthetic. +``` + +#### SCR-46 업무보고·통계 (report/stats · Jasper PDF) — 주최자·홀매니저 · P1 +- **레이아웃**: 기간 탭(일/주/월/분기/연) + 요약 지표 + 차트(Recharts) + 작성자/유형별 표. 경계: 경영지표=M16(SCR-13), 여기는 일상 업무보고·통계. Jasper PDF. 상태: 로딩·빈·에러. 참조: UIWS `report/*`·`stats/*`. +- **Stitch 프롬프트**: +``` +A work report / statistics screen with period tabs in a Korean B2B app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Sidebar 240px ("업무보고" highlighted). Top: period tabs "일간 · 주간 · 월간 · 분기 · 연간" and a "보고서 PDF" button. Main: summary KPI cards, a bar/line chart (Recharts style) of work volume over time, and a breakdown table by writer and work type with zebra rows and tabular numbers. Reporting aesthetic distinct from executive BI. +``` + +#### SCR-47 알림센터 (notification) — 전 역할 · P1 +- **레이아웃**: 알림 목록(카테고리: 마감 D-데이·낙찰·승인·결제·시스템)·읽음/안읽음·딥링크(문서 탭 열기)·규칙 설정 링크. WebSocket 실시간. 상태: 빈("새 알림이 없습니다")·로딩. 참조: UIWS `NotificationsPage`·`settings/NotificationRulesPage`. +- **Stitch 프롬프트**: +``` +A notification center screen in a Korean B2B app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Sidebar 240px ("알림" highlighted). Main: category filter chips "전체 · 마감 · 낙찰 · 승인 · 결제 · 시스템", a chronological notification list with icon, title, description, timestamp, unread blue dot, and per-item deep-link action; a top-right "모두 읽음" and "알림 규칙 설정" link. Clean notification-inbox aesthetic. +``` + +#### SCR-48 마이페이지·환경설정 (profile/preference) — 전 역할 · P1 +- **레이아웃**: 좌측 탭(프로필·보안(2FA)·알림 설정·테마/언어·즐겨찾기) + 우측 설정 폼. 킨텍스 접합: 역할별 대시보드 개인화·최근방문. 상태: 저장 토스트·검증. 참조: UIWS `auth/MyProfilePage`. +- **Stitch 프롬프트**: +``` +A my-page / settings screen in a Korean B2B app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Sidebar 240px. Layout: a left settings tab rail "프로필 · 보안(2차 인증) · 알림 설정 · 테마·언어 · 즐겨찾기", and a right form panel showing the 프로필 tab: avatar, name, email, department, role badge, and a save button; the 테마·언어 tab shows a light/dark theme toggle and a language select "한 · 영 · 중 · 일". Clean account-settings aesthetic. +``` + +--- + +### SCR-49 회원가입 (웹) [§5B-3] + +- **대상**: 참가업체·업체·관람객 셀프 · **P1** · **진입 경로**: 로그인(SCR-01) "회원가입" / 초대 링크 +- **레이아웃**: 중앙 카드(가입 유형 선택: 참가업체/등록업체/관람객), 약관·개인정보 동의, 이메일 인증, 등록업체는 사업자 검증(등록업체 DB 게이트). 스텝(유형→정보→인증→완료). 상태: 검증 인라인·중복 이메일·미등록 업체 안내. +- **Stitch 프롬프트**: +``` +A sign-up screen for "KINTEX AI 전시관리", Korean B2B web app, desktop 1440px, light theme, blue #0066B3, on #F9FAFB. All UI text in Korean. Center card with a step indicator "유형 → 정보 → 인증 → 완료". Step 1 shows account-type cards "참가업체 · 등록(장치)업체 · 관람객". The form: name, email with "인증코드 발송" button, password, company/business-number fields (for contractor, with a note "킨텍스 등록업체만 가입 가능"), consent checkboxes "이용약관 · 개인정보 처리방침 동의", and a primary "가입하기" button. Trustworthy onboarding aesthetic. +``` + +- **라우트**: `/signup`. MDI 비적용(인증 전). + +--- + +### SCR-50 비밀번호 찾기·재설정 [§5B-3] + +- **대상**: 전 역할 · **P1** · **진입 경로**: 로그인 "비밀번호 찾기" +- **레이아웃**: 이메일 입력→인증코드→새 비밀번호(강도 표시). 로그인 실패 잠금 안내. 상태: 코드 만료·불일치·성공 토스트. 참조: UIWS `auth/FindIdPage`·`ResetPasswordPage`. +- **Stitch 프롬프트**: +``` +A password reset screen for "KINTEX AI 전시관리", Korean B2B web app, desktop 1440px, light theme, blue #0066B3, on #F9FAFB. All UI text in Korean. Center card with a 3-step flow "이메일 확인 → 인증코드 → 새 비밀번호". Current step shows an email input and "인증코드 발송", then a 6-digit code field with a resend timer, then a new-password field with a strength meter and confirm field, and a primary "비밀번호 재설정" button. A small note about account lockout after failed attempts. Minimal secure-auth aesthetic. +``` + +- **라우트**: `/forgot-password`. MDI 비적용. + +--- + +### SCR-51 2차 인증(OTP) 설정 [§5B-3] + +- **대상**: 관리자·홀매니저·주최자·업체(필수)·관람객(선택) · **P1** · **진입 경로**: 최초 로그인 강제 / 마이페이지 보안 탭 +- **레이아웃**: QR(authenticator 등록)·시크릿 키·6자리 코드 검증. 재설정·해제(관리자는 사용자 OTP 초기화). TOTP RFC6238 설명. 상태: 코드 불일치·성공. 참조: UIWS `auth/OtpSetupPage`. +- **Stitch 프롬프트**: +``` +A two-factor authentication (OTP) setup screen for "KINTEX AI 전시관리", Korean B2B web app, desktop 1440px, light theme, blue #0066B3, on #F9FAFB. All UI text in Korean. Center card titled "2차 인증(OTP) 등록": step 1 "인증 앱으로 QR을 스캔하세요" with a QR code image and a secret key text with copy button, step 2 "6자리 코드 입력" with a 6-digit OTP input, and a primary "등록 완료" button; a helper "Google Authenticator 등 TOTP 앱 지원". Security-focused, calm aesthetic. +``` + +- **라우트**: `/otp-setup`. MDI 비적용(로그인 게이트). + +--- + +## 3A. 관리자 백오피스 화면 (SCR-A*) — M18 · §5B-1 + +> **admin 백오피스(`admin.`, 웹 전용).** SCR-16(랜딩 대시보드)의 하위 M18 본체 화면. UIWS `system/*` 표준을 §1 토큰으로 정합. 플랫폼 슈퍼관리자는 상단 **테넌트 스위처**, 테넌트 관리자는 자기 전시관 고정(§1A-4). 공통 레이아웃: 좌측 admin 사이드바 + 상단 유틸리티(테넌트 컨텍스트·검색) + 목록/폼 2단. 전 화면 **감사로그 기록**(N2). MDI 적용. + +### SCR-A1 사용자 관리 +- **레이아웃**: 사용자 목록(이름·이메일·역할·소속·상태·2FA·최근접속) + 필터/검색 + 사용자 폼(역할 배정·상태·OTP 초기화). 6역할+등록업체+관람객 계정. PII 마스킹·감사. 상태: 로딩·빈·에러. +- **Stitch 프롬프트**: +``` +A user management admin screen for "KINTEX AI 전시관리" back-office, Korean B2B tool, desktop 1440px, light theme, blue #0066B3, 1px borders #E4E7EC, Korean sans-serif. All UI text in Korean. Left admin sidebar 240px ("사용자 관리" highlighted). Top utility bar with a tenant switcher "KINTEX ▾" and a search box. Main: a users data table with columns 이름, 이메일, 역할, 소속, 상태, 2FA, 최근접속 — role badges (주최자/참가/장치/홀매니저/관리자), status pills (활성/잠금/비활성), a "2FA 설정됨" check, zebra rows, sticky header, and a right-side user detail/edit panel with role assignment, status toggle, and an "OTP 초기화" button. Enterprise admin aesthetic with masked emails. +``` + +### SCR-A2 역할·권한(RBAC) 관리 +- **레이아웃**: 좌측 역할 트리(플랫폼/테넌트/행사 계층) + 우측 권한 매트릭스(메뉴·기능 × 허용 체크). 플랫폼 vs 테넌트 계층 구분. 상태: 저장·검증. +- **Stitch 프롬프트**: +``` +A role & permission (RBAC) management admin screen for "KINTEX AI 전시관리", Korean B2B back-office, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Left admin sidebar ("역할/권한" highlighted). Layout: a left role tree grouped by tier "플랫폼 · 테넌트 · 행사" with roles like "플랫폼 슈퍼관리자, 테넌트 관리자, 주최자, 참가업체, 장치업체, 홀매니저", and a right permission matrix table with menu/feature rows and allow checkboxes (조회/생성/수정/삭제/승인 columns), a save button. Structured access-control aesthetic. +``` + +### SCR-A3 공통코드 관리 +- **레이아웃**: 코드 그룹 목록 + 코드 상세(홀·부스유형·공종 14분류·유틸리티 요금코드). 전역 vs 테넌트 오버라이드 표기(§1A-2). 상태: 로딩·빈. +- **Stitch 프롬프트**: +``` +A common-code management admin screen for "KINTEX AI 전시관리", Korean B2B back-office, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Left admin sidebar ("공통코드" highlighted). Split: left a code-group list "홀 · 부스유형 · 공종(14분류) · 유틸리티 요금코드 · 상태", right a code-detail table (코드, 명칭, 정렬, 사용여부) with add/edit rows and a badge distinguishing "전역" vs "테넌트 재정의". Master-data admin aesthetic. +``` + +### SCR-A4 메뉴 관리 +- **레이아웃**: 메뉴 트리(드래그 재정렬) + 메뉴 속성(역할별 포털 IA §2-1 매핑·권한). 상태: 저장. +- **Stitch 프롬프트**: +``` +A menu management admin screen for "KINTEX AI 전시관리", Korean B2B back-office, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Left admin sidebar ("메뉴 관리" highlighted). Layout: a left draggable menu tree (nested items with drag handles and show/hide toggles), a right menu-property form (메뉴명, 경로, 아이콘, 역할별 노출 checkboxes for 주최자/참가/장치/홀매니저/관리자, 정렬). Tree-editor admin aesthetic. +``` + +### SCR-A5 감사로그 +- **레이아웃**: 필터(사용자·액션·기간·테넌트) + 로그 테이블(시각·사용자·액션·대상·IP·결과). 승인·낙찰·설계변경·룰셋개정·리드 접근 전수. 내보내기. 상태: 로딩·빈. +- **Stitch 프롬프트**: +``` +An audit log admin screen for "KINTEX AI 전시관리", Korean B2B back-office, desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Left admin sidebar ("감사로그" highlighted). Top: filters (사용자, 액션 유형, 기간, 테넌트) and an "CSV 내보내기" button. Main: a dense audit table with columns 시각, 사용자, 액션, 대상, IP, 결과 — rows like "2026-08-01 14:22 · 최매니저 · 낙찰(Award) · 옥션 #A-102 · 성공", monospace timestamps, zebra rows, sticky header. Forensic/compliance aesthetic, high density. +``` + +### SCR-A6 시스템설정 +- **레이아웃**: 설정 그룹(요율/규정 룰셋 버전·AI 게이트·마감 정책 D-25/D-7·알림·보안) 폼. 상태: 저장·검증. +- **Stitch 프롬프트**: +``` +A system settings admin screen for "KINTEX AI 전시관리", Korean B2B back-office, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Left admin sidebar ("시스템설정" highlighted). Main: grouped setting cards — "마감 정책" (유틸리티 D-25, 신고서류 D-7 numeric fields), "AI 플랫폼 게이트" (모델 라우터 상태 toggles), "알림" (이메일/WebSocket toggles), "보안" (로그인 실패 잠금 횟수, 세션 만료). Each group has a save button. Clean settings-form aesthetic. +``` + +### SCR-A7 마스터데이터 관리 (홀·요율·요금·부스표준·등록업체) +- **레이아웃**: 좌측 데이터셋 탭(홀 마스터·요율표·유틸리티 요금·부스 표준·등록업체 DB) + 우측 CRUD 테이블/폼. 킨텍스 실측=테넌트#1 시드. 트렌치 좌표·CAD 업로드(미확보 표기). 상태: 로딩·빈·에러. +- **Stitch 프롬프트**: +``` +A master-data management admin screen for "KINTEX AI 전시관리", Korean B2B back-office, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Left admin sidebar ("마스터데이터" highlighted). Split: left dataset tabs "홀 마스터 · 요율표 · 유틸리티 요금 · 부스 표준 · 등록업체 DB", right a CRUD table for the 홀 마스터 tab with columns 홀, 면적(㎡), 규격, 바닥하중, 부스수 — rows "홀7 · 11,290㎡ · 126×90×12m · 5t/㎡ · 510" with edit buttons, plus a "트렌치 좌표 CAD 업로드" area labeled "미확보 · 킨텍스 협의". Structured data-admin aesthetic with tabular numbers. +``` + +### SCR-A8 규정 룰셋 버전 관리 +- **레이아웃**: 룰셋 버전 이력(v1.0·v1.1…) + 룰 항목(높이 5m·리깅 6.5~8.5m·복층·방염·소음·이격) 편집 + diff 뷰. 발효일·승인. 상태: 저장·버전 비교. +- **Stitch 프롬프트**: +``` +A regulation ruleset version management admin screen for "KINTEX AI 전시관리", Korean B2B back-office, desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Left admin sidebar ("규정 룰셋" highlighted). Split: left a version history list "v1.2 (발효 2026-01-01) · v1.1 · v1.0", right a rules editor listing rule rows "장치물 높이 ≤ 5m", "리깅 6.5–8.5m + 구조계산서 D-7", "복층 ≤ 1/2", "방염 필수", "소음 70–75dB", "이격 벽 30cm·천장 60cm" with editable values and severity (차단/경고), plus a "버전 비교(diff)" button showing changed rules highlighted. Governance/versioning aesthetic. +``` + +### SCR-A9 테넌트(전시관) 관리·온보딩 [§1A · 플랫폼 슈퍼관리자 전용] +- **레이아웃**: 테넌트 목록(KINTEX·COEX…: 코드·도메인·활성·행사수·사용자수) + 온보딩 위저드(코드·서브도메인·브랜딩·마스터데이터·관리자 계정·격리 스모크→active). 크로스-테넌트 접근 감사. 상태: 온보딩 단계·검증. +- **Stitch 프롬프트**: +``` +A tenant (venue) management and onboarding admin screen for a multi-tenant exhibition SaaS "KINTEX AI 전시관리", Korean B2B back-office, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Left admin sidebar ("테넌트 관리" highlighted, super-admin only). Main: a tenants table with columns 전시관, 코드, 도메인, 상태, 행사수, 사용자수 — rows "킨텍스 · KINTEX · kintex.wise.ai.kr · 활성 · 24 · 1,280", "코엑스 · COEX · coex.wise.ai.kr · 온보딩중"; a right "전시관 온보딩" wizard panel with steps "테넌트 생성 → 서브도메인 → 브랜딩 → 마스터데이터 입력 → 관리자 계정 → 격리 스모크 → 활성화", each step with a checkmark or current highlight, and a note "코드 배포 없이 데이터 온보딩". Platform-admin, multi-tenant aesthetic. +``` + +### SCR-A10 AI 플랫폼 설정 (AiConfig) [§6 · F100] +- **레이아웃**: 모델 라우터 설정(텍스트: Claude 기본→Ollama 폴백 / 이미지: 나노바나나 Gemini), 온도·top_k·RAG 토글, 나노바나나 쿼터·비용 상한(테넌트별), 상태 헬스(모델 가용). 시크릿은 env only(미표시). 상태: 저장·헬스체크. 참조: UIWS `system/aiConfig/AiConfigPage`. +- **Stitch 프롬프트**: +``` +An AI platform configuration admin screen (AiConfig) for "KINTEX AI 전시관리", Korean B2B back-office, desktop 1440px, light theme, blue #0066B3, purple #6D4AFF for AI. All UI text in Korean. Left admin sidebar ("AI 플랫폼 설정" highlighted). Main: a "텍스트/추론" card with a model router "기본: Claude → 폴백: Ollama" with health dots, sliders "temperature · top_k", and a "근거 검증(RAG)" toggle; an "이미지(시공 예상)" card showing "나노바나나 (Gemini gemini-3.1-flash-image-preview)" with a per-tenant "생성 쿼터" and "비용 상한" field; a note "API 키는 서버 env로만 관리(표시 안 함)". No secret values shown. Config-panel aesthetic with health indicators. +``` + +--- + +## 3B. 공개 홍보 사이트 화면 (SCR-P*) — M12 · M17 · MDI 비적용 + +> **공개 사이트 `www/expo.`** — SEO·다국어(한/영/중/일)·딥링크·공유 목적. **MDI 비적용, 전통 페이지 내비(헤더 GNB)**, SSR/정적 생성·CDN. 인증 앱과 별도 렌더 경로. 브랜딩은 테넌트 서브도메인 분기(§1A-3). 디자인은 §1 톤을 공개용으로 확장(넓은 히어로·마케팅 밀도), 라이트 테마. + +### SCR-P1 공개 홍보 사이트 홈 +- **레이아웃**: 헤더 GNB(행사·참가안내·관람안내·교통·언어) + 히어로(나노바나나 전경 이미지·AI 고지·사전등록 CTA) + 진행/예정 행사 카드 그리드 + 킨텍스 소개·교통(GTX-A 킨텍스역) + 푸터. 상태: 로딩·빈. +- **Stitch 프롬프트**: +``` +A public marketing website home page for "KINTEX 전시" exhibition center, Korean, desktop 1440px, light theme, primary blue #0066B3, clean modern marketing design (not MDI). All UI text in Korean. Top header GNB "행사 · 참가안내 · 관람안내 · 교통 · 🌐 언어" with a KINTEX logo. Full-width hero with a photorealistic exhibition hall render (faint watermark "AI 생성 예상 이미지"), a large headline "세계로 통하는 전시, 킨텍스", and a primary CTA "관람 사전등록". Below: a grid of event cards (poster, title, dates, hall, "사전등록" button) for "진행중 · 예정" events, a "오시는 길 · GTX-A 킨텍스역 도보 3분" transport section with a map, and a footer. SEO-friendly, spacious, multilingual public site aesthetic. +``` + +### SCR-P2 행사 상세 (소개·일정·교통·연사) +- **레이아웃**: 행사 히어로(배너·기간·홀) + 탭(소개·프로그램/세션·연사·참가업체·교통·FAQ) + 사전등록 고정 CTA. 다국어·공유. 상태: 로딩·빈. +- **Stitch 프롬프트**: +``` +A public event detail page for an exhibition, Korean marketing website, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. A hero banner with event title "스마트팩토리 코리아 2026", dates "2026.09.05–09.08", venue "킨텍스 제2전시장 홀7", and a sticky "사전등록" CTA. Below, a tab bar "소개 · 프로그램 · 연사 · 참가업체 · 교통 · FAQ" with the 소개 tab showing rich content sections, a session agenda timeline, and speaker cards with photos. Share buttons and language selector. Attractive public event-page aesthetic. +``` + +### SCR-P3 공개 인터랙티브 플로어플랜 +- **레이아웃**: 대형 인터랙티브 홀 맵(부스 클릭→업체 정보 팝오버·마이크로사이트 링크), 검색·필터(업종·존), 범례. 관람객 부스 탐색·부스 셀프선택(SCR-20) 진입. 비시각 대안=부스 목록(N1). 상태: 로딩·빈. +- **Stitch 프롬프트**: +``` +A public interactive floor plan page for exhibition visitors, Korean marketing website, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. A large interactive top-down hall map with clickable booth rectangles; clicking a booth shows a popover with company name, category, and a "부스 상세" link. A left search/filter panel "업종 · 존 · 키워드" and a booth results list (accessible non-visual alternative), plus a legend "판매 · 예약 · 공용부". Modern, explorable venue-map aesthetic. +``` + +### SCR-P4 관람객 사전등록 폼 +- **레이아웃**: 다단계 폼(유형 관람객/바이어·정보·관심분야·약관/개인정보 동의·완료→모바일 배지 안내). AI 폼(주최자 구성). 상태: 검증·중복·완료. 개인정보 동의 필수. +- **Stitch 프롬프트**: +``` +A public visitor pre-registration form page for an exhibition, Korean marketing website, desktop 1440px, light theme, blue #0066B3, on white. All UI text in Korean. A centered multi-step form with a progress indicator "유형 → 정보 → 관심분야 → 완료". Step shows visitor-type cards "관람객 / 바이어 / VIP", then fields 이름, 이메일, 연락처, 소속, 관심 분야 chips, and consent checkboxes "개인정보 수집·이용 동의(필수) · 마케팅 수신 동의(선택)", and a primary "등록하기" button; completion screen shows a QR badge preview and "모바일 배지가 발급되었습니다". Friendly, trustworthy registration aesthetic. +``` + +### SCR-P5 참가업체 마이크로사이트 공개 뷰 +- **레이아웃**: 업체 브랜드 히어로 + 제품 + 나노바나나 예상샷 갤러리(워터마크) + 부스 위치(플로어플랜 딥링크) + 미팅 예약 CTA(M11). SEO·다국어. 상태: 로딩·빈. +- **Stitch 프롬프트**: +``` +A public exhibitor microsite page, Korean marketing website, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. A branded hero with company name "(주)한빛로보틱스" and tagline, a product showcase grid, an AI-render booth photo gallery with faint watermark "AI 생성 예상 이미지", a "부스 위치 · A-102 홀7" card linking to the floor plan, and a "미팅 예약" CTA. Clean company-microsite aesthetic, multilingual. +``` + +### SCR-P6 참가/부스 신청 문의 +- **레이아웃**: 참가 안내(부스 유형·요금 개요) + 문의 폼(회사·담당·규모·희망 홀·메시지) + 담당 연락처. 셀프서비스→로그인 유도. 상태: 검증·전송 완료. +- **Stitch 프롬프트**: +``` +A public "exhibit with us" inquiry page for an exhibition center, Korean marketing website, desktop 1440px, light theme, blue #0066B3. All UI text in Korean. Top: a "참가 안내" section with booth-type cards (조립부스 / 독립부스 / 프리미엄) and a rate overview. Below: an inquiry form with fields 회사명, 담당자, 연락처, 예상 규모, 희망 홀, 문의 내용, and a "문의 보내기" button, plus a contact info block and a link "이미 계정이 있으신가요? 로그인". Lead-generation marketing aesthetic. +``` + +### SCR-P7 입장권 예매 (티켓팅) +- **대상**: 일반 대중·관람객(비로그인 게스트 + 회원) · **모듈**: M10(티켓) · 결제=M9 연계 · **라우트**: `/tickets/:eventId/purchase` (공개, MDI 비적용·SEO) +- **레이아웃**(위→아래, 중앙 정렬 스텝 폼 720px): + 1. 상단: 행사 요약 배너(포스터·행사명·기간·홀) + 스텝 진행 인디케이터 "행사·권종 → 예매자 → 결제 → 완료" (StatusBadge 진행 톤 재사용, pill 스텝) + 2. **1단계 권종 선택**: 티켓 종류 카드 리스트 — 일반(₩0 사전등록/현장 유료) · 바이어(무료·자격심사) · 단체(10매↑ 할인) · VIP. 각 카드 = 권종명·설명·가격(tabular numerals)·수량 스테퍼(−/+). 소계·할인·합계 실시간 요약(우측 sticky 요약 카드). 회원이면 로그인 배너 "로그인하면 예매 내역이 자동 저장됩니다" + 3. **2단계 예매자 정보**: 게스트=이름·이메일·연락처(마스킹 저장), 회원=자동 채움. 단체는 대표자+동반자 명단(CSV 업로드 옵션). 관심분야 chips(선택). 개인정보 수집·이용 동의(필수)·마케팅 수신(선택) 체크 + 4. **3단계 결제**: 결제수단 선택(카드·간편결제·계좌이체) — **PG(결제대행) 위임 안내 문구 상시 노출**("결제는 PG사 보안 페이지에서 진행되며, 카드정보는 본 사이트에 저장되지 않습니다"). **카드번호 입력 UI 없음**(PG 리다이렉트/모듈 자리표시 카드). 무료 권종은 결제 단계 자동 스킵 + 5. **4단계 완료**: 예매번호(굵게)·발급 티켓 QR 미리보기·"이메일로 티켓을 보냈습니다" + CTA "모바일 티켓 지갑 열기"(SCR-M15 딥링크)·"예매 확인/취소"(SCR-P8) +- **상태**: 로딩(권종·재고 조회 스켈레톤) · 빈(판매 티켓 없음 "현재 예매 가능한 입장권이 없습니다") · 에러(재고 소진 시 해당 권종 "매진" 배지+비활성, 결제 실패 인라인 재시도) · degraded(PG 어댑터 미연결 시 "결제 준비 중 — 잠시 후 다시 시도" 안내, 무료 권종은 정상). 정원/재고는 서버 권위. +- **데이터 원천**: M10 티켓·권종·재고 + M9 결제 어댑터(PG) → 신규·샘플 시연. 개인정보 마스킹·WCAG AA·라이트/다크 토큰 준수, 신규 토큰 없음. +- **Stitch 프롬프트**: +``` +A public ticket purchase (ticketing) page for an exhibition, Korean marketing website, desktop 1440px, light theme, primary blue #0066B3, on white, form container ~720px centered. All UI text in Korean. Top: an event summary banner (poster thumbnail, title "스마트팩토리 코리아 2026", dates "2026.09.05–09.08", venue "제2전시장 홀7") and a pill step indicator "행사·권종 → 예매자 → 결제 → 완료" with step 1 active. Left main column shows ticket-type selection cards — "일반권 ₩0(사전등록)", "바이어권 무료·자격심사", "단체권 10매↑ 10% 할인 ₩9,000", "VIP권 ₩50,000" — each card with name, short description, price in tabular numerals, and a quantity stepper (− / value / +); one type shows a red "매진" badge and disabled state. A right sticky summary card lists selected tickets, 소계, 할인, and a bold 합계 with a primary button "다음 단계". Include a subtle blue login banner "로그인하면 예매 내역이 자동 저장됩니다". Below reference the later steps: a payment step must show method chips "카드 · 간편결제 · 계좌이체" and a persistent gray notice "결제는 PG사 보안 페이지에서 진행되며, 카드정보는 본 사이트에 저장되지 않습니다" (NO card number input fields). Trustworthy, conversion-focused e-commerce checkout aesthetic; rounded 8px cards, 4px inputs, WCAG AA contrast. +``` + +### SCR-P8 예매 확인·취소 +- **대상**: 일반 대중·관람객(비로그인 게스트 조회 + 회원) · **모듈**: M10(티켓) · 환불=M9 연계 · **라우트**: `/tickets/lookup` · `/tickets/:orderNo` (공개) +- **레이아웃**(위→아래): + 1. 상단: "예매 확인·취소" 타이틀 + 조회 폼 — 예매번호 + 이메일(게스트) 또는 "로그인하여 내 예매 보기"(회원). 인증 후 조회 + 2. **예매 상세 카드**: 행사·예매번호·예매일·결제금액(tabular)·상태 배지(예매완료/사용됨/취소/환불완료). 티켓 리스트(권종·수량·매수별 QR 썸네일→탭 시 확대). 예매자 정보(이메일·연락처 **마스킹** 표시 jo***@ex***.com, 010-****-1234) + 3. **취소·환불 정책 패널**: 환불 규정 표(D-7 100% · D-3 50% · 당일 0% 등, 서버 권위) + 현재 예상 환불액 하이라이트 + 안내 문구. CTA "예매 취소" (사용됨/기간 경과 시 비활성) + 4. 취소 확인 모달: 환불 정책 재고지·환불 수단(원결제수단 자동)·"취소하기"(error 톤) — PG 환불도 어댑터 위임 명시 +- **상태**: 로딩(조회 스켈레톤) · 빈/미일치("일치하는 예매를 찾을 수 없습니다 — 예매번호·이메일 확인") · 에러(취소 마감·이미 사용·PG 환불 실패 인라인) · degraded(PG 환불 어댑터 미연결 시 "환불 접수됨 — 처리까지 영업일 소요" 안내). 취소 가능 여부·환불액은 서버 권위. +- **데이터 원천**: M10 예매·티켓 + M9 환불 어댑터 → 신규·샘플. PII 마스킹 필수·WCAG AA·라이트/다크 준수, 신규 토큰 없음. +- **Stitch 프롬프트**: +``` +A public "check & cancel my ticket booking" page for an exhibition, Korean marketing website, desktop 1440px, light theme, primary blue #0066B3, error red #D92D20, on white. All UI text in Korean. Top: title "예매 확인 · 취소" with a lookup form — inputs "예매번호" and "이메일", a primary "조회하기" button, and a secondary link "로그인하여 내 예매 보기". Below (results state): a booking detail card showing event title, 예매번호 "KTX-2026-018245", 예매일, 결제금액 in tabular numerals, and a green status badge "예매완료"; a ticket list with rows (권종, 수량) each showing a small QR thumbnail; a booker info block with MASKED contact "jo***@ex***.com · 010-****-1234". A right/lower panel "취소·환불 정책" shows a refund table (D-7 100%, D-3 50%, 당일 0%) with the current 예상 환불액 highlighted, and a red outline button "예매 취소" that opens a confirmation modal restating the policy and refund method. Note "환불은 PG사를 통해 원결제수단으로 처리됩니다". Clean, reassuring self-service booking-management aesthetic; 8px cards, WCAG AA. +``` + +--- + ## 4. 모바일 대응 (390px) 모바일은 **조회·승인·현장** 시나리오 전용(PLANNING.md §8). 캔버스 편집(SCR-03, 07)과 스펙 편집(SCR-06 좌패널)은 모바일 미제공 — 뷰어+승인 액션만. 아래 두 화면은 **모바일 전용 신규 화면**, 나머지는 반응형 축소. @@ -654,9 +1447,166 @@ A mobile field inspection screen for a KINTEX hall manager verifying booth const --- +### SCR-M3 [모바일] 홈·대시보드 (역할별 진입) + +- **대상**: 전 역할(로그인 후 역할 분기) · **P1** · **진입 경로**: 모바일 로그인 → 하단 탭 "홈" · **상태**: ✅구현(부분, `mobile/app/(tabs)/index.tsx`) +- **레이아웃**: 상단 행사 스위처 + 역할 배지 · 오늘 할 일/마감 D-데이 카드 · 역할별 바로가기(참가=내 부스·유틸리티 / 장치=현장 체크리스트·옥션 / 홀매니저=승인 큐·검수 / 관람객=배지·플로어플랜) · 알림 요약 · 하단 탭 4개(홈/현장·업무/알림/더보기). 상태: 로딩 스켈레톤·오프라인 배지·빈. +- **Stitch 프롬프트**: +``` +A mobile home dashboard for "KINTEX AI 전시관리", 390px width, light theme, blue #0066B3, large touch targets, Korean sans-serif. All UI text in Korean. Top: an event switcher "스마트팩토리 코리아 ▾" and a role badge "홀매니저". Body: a "오늘 할 일" section with D-day task cards, a role-specific shortcut grid of large tiles ("승인 큐 · 현장 검수 · 홀 부하 · 알림"), and a compact notification summary list. Bottom navigation bar with 4 tabs "홈 · 현장 · 알림 · 더보기". Clean, glanceable mobile dashboard with big tap areas. +``` + +--- + +### SCR-M4 [모바일] 참가업체 리드캡처 (배지 스캔) + +- **대상**: 참가업체 현장 인력 · **모듈/P**: M10 · P1 · **진입 경로**: 모바일 "현장" → "리드 스캔" +- **레이아웃**: 대형 QR 스캔 카메라 프레임 + 스캔 성공 시 관람객 카드(이름·소속·유형) · 관심도 별점·태그·메모(음성 입력) · 저장→리드 목록. 오프라인 저장·동기화. 개인정보 동의 표기. 상태: 스캔 실패·중복·오프라인. +- **Stitch 프롬프트**: +``` +A mobile lead capture (badge scan) screen for an exhibitor, 390px width, light theme, blue #0066B3, large touch targets. All UI text in Korean. Top: a full-width QR scanner camera frame with guide "관람객 배지 QR을 스캔하세요". On scan, a visitor card slides up: name "정관람", company "(주)미래기술", type chip "바이어", a 5-star "관심도" rating, interest tag chips, and a memo field with a mic icon "음성 메모". A primary "리드 저장" button, an offline badge "오프라인 저장 · 동기화 대기", and a small consent note "개인정보 수집 동의 확인". Fast field-sales mobile aesthetic. +``` + +--- + +### SCR-M5 [모바일] 관람객 앱 홈 · 모바일 배지/QR + +- **대상**: 관람객 · **모듈/P**: M10 · P1 · **진입 경로**: 관람객 앱 로그인/사전등록 완료 +- **레이아웃**: 상단 내 모바일 배지(QR·이름·유형, 밝기 최대 안내) · 오늘 일정·추천 세션(M11) · 부스 즐겨찾기 · 하단 탭(홈/지도/매칭/마이). 오프라인 배지 캐시. 상태: 미등록·로딩. +- **Stitch 프롬프트**: +``` +A visitor app home screen with a mobile badge/QR for an exhibition, 390px width, light theme, blue #0066B3, friendly consumer tone. All UI text in Korean. Top: a personal mobile badge card showing a large QR code, name "정관람", type "바이어", and a hint "체크인 시 화면을 보여주세요". Below: "오늘 일정" cards, an "추천 세션·부스" carousel with a small purple "AI 추천" chip, and a "즐겨찾기 부스" list. Bottom tab bar "홈 · 지도 · 매칭 · 마이". Clean consumer event-app aesthetic. +``` + +--- + +### SCR-M6 [모바일] 관람객 wayfinding · 실내 지도 + +- **대상**: 관람객 · **모듈/P**: M13 · P2 · **진입 경로**: 관람객 앱 "지도" +- **레이아웃**: 전체 화면 실내 지도(블루닷 현재위치·경로 라인) + 상단 검색(부스·화장실·비상구·시설) + 하단 시트(목적지 정보·경로 시작). 존레벨 폴백(하드웨어 무의존). 상태: 측위 불가 시 존레벨 안내·오프라인. +- **Stitch 프롬프트**: +``` +A mobile indoor wayfinding / map screen for exhibition visitors, 390px width, light theme, blue #0066B3. All UI text in Korean. Full-screen indoor map of an exhibition hall with a blue-dot current-location marker and a highlighted route line to a destination; a top search bar "부스 · 화장실 · 비상구 · 시설 검색"; a bottom sheet showing destination info "A-102 · 한빛로보틱스 · 도보 3분" and a "경로 시작" button; a small note "존 레벨 안내(정밀 측위 미지원 시)". Clear navigation aesthetic. +``` + +--- + +### SCR-M7 [모바일] 관람객 플로어플랜 · 부스 검색 + +- **대상**: 관람객 · **모듈/P**: M12·M2 · P1 · **진입 경로**: 관람객 앱 "지도" 탭 내 플로어플랜 / 공개사이트 SCR-P3 모바일 +- **레이아웃**: 인터랙티브 홀 맵(부스 탭→하단 시트 업체정보·마이크로사이트·길안내) + 검색·필터(업종·존) + 즐겨찾기 추가. 비시각 대안 목록. 상태: 로딩·빈. +- **Stitch 프롬프트**: +``` +A mobile interactive floor plan / booth search screen for exhibition visitors, 390px width, light theme, blue #0066B3. All UI text in Korean. An interactive hall map filling the screen with tappable booth cells; a top search bar and filter chips "업종 · 존"; tapping a booth opens a bottom sheet with company name, category, a "마이크로사이트" link, a "길안내" button, and an "즐겨찾기 ★" toggle. Below the map a toggle to a list view (accessible alternative). Explorable consumer map aesthetic. +``` + +--- + +### SCR-M8 [모바일] 비즈매칭 · 미팅 + +- **대상**: 관람객·참가업체(바이어 매칭) · **모듈/P**: M11 · P2 · **진입 경로**: 관람객 앱 "매칭" +- **레이아웃**: AI 추천 매치 카드 스택(프로필·관심·의향, 관심/패스 액션) + 미팅 요청·슬롯 예약 캘린더 + 내 미팅 목록(부스 위치·길안내 연계). AiLabel. 상태: 추천 없음·로딩. +- **Stitch 프롬프트**: +``` +A mobile business matchmaking / meeting screen for an exhibition, 390px width, light theme, blue #0066B3, purple #6D4AFF for AI. All UI text in Korean. Top: a stack of AI-recommended match cards, each showing a person/company profile, interests, a small purple "AI 매칭 92%" chip, and "관심 / 패스" buttons. Below: a "미팅 예약" time-slot calendar and a "내 미팅" list with time, counterpart, booth location and a "길안내" link. Networking-app aesthetic with AI matching. +``` + +--- + +### SCR-M9 [모바일] 세션·아젠다 · 개인 일정 + +- **대상**: 관람객 · **모듈/P**: M11(이벤트앱) · P2 · **진입 경로**: 관람객 앱 "일정" +- **레이아웃**: 세션 아젠다(트랙·시간·연사, 즐겨찾기→내 일정) + AI 세션 추천(AiLabel) + 내 개인 일정 타임라인 + 리마인더. 상태: 빈·로딩. +- **Stitch 프롬프트**: +``` +A mobile session agenda / personal schedule screen for an exhibition app, 390px width, light theme, blue #0066B3, purple #6D4AFF for AI. All UI text in Korean. Top: a track/time agenda list with session cards (시간, 세션명, 연사, 트랙 tag) and a "★" add-to-schedule toggle; a small purple "AI 추천 세션" section. A tab toggle "전체 아젠다 / 내 일정" where 내 일정 shows a personal timeline with reminders. Clean event-agenda aesthetic. +``` + +--- + +### SCR-M10 [모바일] 업체 반입 통행증 · 안전 체크 + +- **대상**: 장치·공사업체 현장 인력 · **모듈/P**: M8 · P2 · **진입 경로**: 업체 모바일 "반입" (SCR-25 예약 후) +- **레이아웃**: 통행증 QR(차량·슬롯·유효시간) 전체화면 표시 + 반입 안전 체크리스트(안전모·중량물 표기·금지작업) + 슬롯 시간 안내·현장 지도 링크. 오프라인 캐시. 상태: 슬롯 임박 알림·만료. +- **Stitch 프롬프트**: +``` +A mobile move-in gate pass and safety check screen for a construction crew, 390px width, light theme, blue #0066B3, large touch targets. All UI text in Korean. Top: a large gate-pass card with a QR code, vehicle number "12가3456", assigned dock "도크3", and validity "08:00–09:00 · D-2 장치". Below: a safety checklist with big checkboxes "안전모 착용 · 중량물 5t↑ 신고 · 금지작업 확인(전기톱·용접·페인트)", a "슬롯 시간 D-0 09:00" reminder, and a "현장 지도" link. Rugged field-ops mobile aesthetic. +``` + +--- + +### SCR-M11 [모바일] 알림센터 + +- **대상**: 전 역할 · **모듈/P**: §5B notification · P1 · **진입 경로**: 하단 탭 "알림" +- **레이아웃**: 알림 리스트(마감·낙찰·승인·결제·시스템, 아이콘·시간·안읽음) · 탭 시 해당 화면 딥링크 · 상단 필터·모두 읽음. WebSocket. 상태: 빈("새 알림 없음")·로딩·오프라인. +- **Stitch 프롬프트**: +``` +A mobile notification center for "KINTEX AI 전시관리", 390px width, light theme, blue #0066B3, large touch targets. All UI text in Korean. Top: filter chips "전체 · 마감 · 낙찰 · 승인 · 결제" and a "모두 읽음" action. Body: a chronological notification list, each row with a category icon, title, short description, timestamp, and an unread blue dot; tapping opens the related screen. Empty state "새 알림이 없습니다". Clean mobile inbox aesthetic. +``` + +--- + +### SCR-M12 [모바일] 서류·승인 조회 + +- **대상**: 주최자·홀매니저(조회·승인) · **모듈/P**: M6 · P1 · **진입 경로**: 모바일 "업무" → "서류/승인" +- **레이아웃**: 서류·승인 항목 카드 리스트(유형·제출자·상태 배지·마감 D-데이) + 상세(문서 뷰어·AI 검수 요약) + 승인/반려 액션(오조작 방지: 상세 진입 필수). 상태: 빈·로딩·오프라인 읽기전용. +- **Stitch 프롬프트**: +``` +A mobile document/approval review screen for an organizer or hall manager, 390px width, light theme, blue #0066B3, purple #6D4AFF for AI. All UI text in Korean. Top: a card list of documents/approvals, each with type "신고서류 · 부스배치도", submitter, a status pill, and a D-day chip. Tapping opens a detail view with a document viewer, a purple "AI 검수 요약" card listing issues, and bottom action buttons "승인" (green) / "반려" (red). No swipe-approve (must open detail). Careful, review-focused mobile aesthetic. +``` + +--- + +### SCR-M13 [모바일] 옥션 순위 조회 + +- **대상**: 장치업체·참가업체(발주자) · **모듈/P**: M15 · P1 · **진입 경로**: 모바일 "업무" → "옥션" +- **레이아웃**: 참여 옥션 카드(공종·마감 카운트다운·내 순위·최저가) + 실시간 순위 리스트(WebSocket) + 알림. 응찰(견적서 작성)은 웹(SCR-28) 유도 — 모바일은 조회·순위·마감 알림 중심. 상태: 마감됨·오프라인·로딩. +- **Stitch 프롬프트**: +``` +A mobile auction ranking monitor for a contractor, 390px width, light theme, blue #0066B3, purple #6D4AFF. All UI text in Korean. Top: participating auction cards with category, a countdown "마감 01:24:36", my rank "2위", and current lowest "₩8.4M". Below: a live ranking list "1위 업체 A · 2위 나 · 3위 업체 C" with the user's row highlighted, and a note "견적서 작성·재응찰은 웹에서 진행하세요". Real-time monitoring mobile aesthetic. +``` + +--- + +### SCR-M14 [모바일] 티켓 예매 + +- **대상**: 일반 대중·관람객(게스트+회원) · **모듈/P**: M10(티켓)·결제 M9 연계 · P1 · **진입 경로**: 관람객 앱/공개사이트 모바일 "입장권 예매" · SCR-P7의 모바일 최적화 +- **레이아웃**(1열 스택, 48px 터치·하단 고정 CTA): + 1. 상단: 행사 요약 칩 + 스텝 인디케이터(권종→예매자→결제→완료) 축약(단계 점) + 2. 권종 선택: 티켓 종류 카드 세로 스택(권종명·가격·수량 스테퍼 48px). 하단 고정 요약 바 "합계 ₩9,000 · 다음" + 3. 예매자 정보: 큰 입력 필드(게스트 이름·이메일·연락처 / 회원 자동채움), 동의 체크(필수·선택) + 4. 결제: 결제수단 시트(카드·간편결제) + **PG 위임 안내 상시**("카드정보는 저장되지 않습니다"), 카드번호 입력 UI 없음. 무료 권종은 스킵 + 5. 완료: 예매번호·QR·"티켓 지갑에 저장됨" + CTA "내 티켓 지갑"(SCR-M15) +- **상태**: 로딩 스켈레톤 · 매진 권종 비활성+"매진" · 결제 실패 인라인 재시도 · degraded(PG 미연결 안내, 무료 권종 정상) · 오프라인 배지. 재고/정원 서버 권위. +- **데이터 원천**: M10 티켓·재고 + M9 결제 어댑터 → 신규·샘플. PII 마스킹·WCAG AA·라이트/다크, 신규 토큰 없음. +- **Stitch 프롬프트**: +``` +A mobile ticket purchase flow screen for an exhibition, 390px width, light theme, primary blue #0066B3, large 48px touch targets, Korean consumer tone. All UI text in Korean. Top: an event summary chip "스마트팩토리 코리아 2026 · 홀7" and a compact dot step indicator "권종 · 예매자 · 결제 · 완료". Body: a vertical stack of ticket-type cards — "일반권 ₩0", "바이어권 무료", "단체권 ₩9,000", "VIP권 ₩50,000" — each with name, price in tabular numerals, and a big quantity stepper (− / 1 / +); one card shows a "매진" badge disabled. A sticky bottom summary bar shows bold "합계 ₩9,000" and a full-width primary button "다음". Include a later payment step reference: method chips "카드 · 간편결제" and a persistent notice "결제는 PG사 보안 페이지에서 진행되며 카드정보는 저장되지 않습니다" with NO card number fields. Clean mobile checkout aesthetic, 8px cards, WCAG AA. +``` + +--- + +### SCR-M15 [모바일] 내 티켓 지갑 + +- **대상**: 일반 대중·관람객 · **모듈/P**: M10(티켓)·환불 M9 연계 · P1 · **진입 경로**: 관람객 앱 "마이" → "내 티켓" / 예매 완료(SCR-P7·M14) 딥링크 +- **레이아웃**(1열 스택): + 1. 상단: "내 티켓" 타이틀 + 행사 필터 탭(진행중·예정·지난) + 2. **티켓 카드 리스트**: 각 카드 = 행사명·권종·매수·상태 배지(사용가능/사용됨/취소) + 미니 QR + "입장 QR 보기" 버튼 + 3. **QR 풀스크린 뷰어**(카드 탭): 대형 QR 중앙 · 이름·유형 · **화면 밝기 부스트 안내("입장 시 화면을 최대 밝기로")** + 자동 밝기 상승 · 하단 스와이프로 다른 티켓 전환 · 예매번호(마스킹 부분 노출) + 4. 각 티켓 하단: "예매 상세·취소"(SCR-P8 진입) · 취소/환불 정책 링크 + 5. **배지 전환 안내 배너**: "티켓 QR로 현장 체크인하면 모바일 배지로 전환됩니다" — 체크인(SCR-31) 후 SCR-M5 관람객 배지/QR로 상태 승격(티켓→체크인→배지 흐름 1:1 명시) +- **상태**: 로딩 스켈레톤 · 빈("보유한 티켓이 없습니다 — 입장권 예매") · **오프라인 표시 가능**(QR·티켓 로컬 캐시, "오프라인 — 저장된 티켓 표시 중" 배지) · 사용됨/취소 티켓 흐리게(dim). 상태 전이는 서버 권위(오프라인은 캐시 표시). +- **데이터 원천**: M10 예매·티켓·체크인 상태 → 신규·샘플. SCR-M5(배지)와 상태 연동. PII 마스킹·WCAG AA·라이트/다크, 신규 토큰 없음. +- **Stitch 프롬프트**: +``` +A mobile "my ticket wallet" screen for an exhibition visitor, 390px width, light theme, primary blue #0066B3, success green #0E8A5F, friendly consumer tone. All UI text in Korean. Top: title "내 티켓" with filter tabs "진행중 · 예정 · 지난". Body: a vertical list of ticket cards, each showing event title "스마트팩토리 코리아 2026", 권종 "바이어권", 매수 "1매", a green status badge "사용가능", a small QR thumbnail, and a button "입장 QR 보기"; used/canceled tickets appear dimmed. Include a full-screen QR viewer reference: a large centered QR code, name "정관람", type "바이어", a hint "입장 시 화면을 최대 밝기로 유지하세요" with a brightness-boost note, swipe dots to switch tickets, and a masked 예매번호 "KTX-****-8245". Add an info banner "티켓 QR로 현장 체크인하면 모바일 배지로 전환됩니다" and a small offline badge "오프라인 — 저장된 티켓 표시 중". Each card has a link "예매 상세·취소". Clean consumer wallet aesthetic, 8px cards, WCAG AA, high-contrast QR. +``` + +--- + ## 5. Stitch 사용 가이드 (요약) -1. stitch.withgoogle.com에서 새 프로젝트 생성 → 플랫폼은 웹 화면(SCR-01~12)은 Desktop, SCR-M1/M2는 Mobile 선택. +1. stitch.withgoogle.com에서 새 프로젝트 생성 → 플랫폼은 웹 화면(SCR-01~51·SCR-A*·SCR-P*)은 Desktop, 모바일(SCR-M1~M15)은 Mobile 선택. 공개사이트(SCR-P*)는 마케팅 톤·넓은 히어로로, 관리자(SCR-A*)는 밀도 높은 백오피스 톤으로 생성한다. 2. 화면당 위 코드블록 프롬프트 1개를 그대로 붙여넣어 생성 — 프롬프트에 색상·레이아웃·예시 데이터가 포함되어 있어 추가 지시 없이 1회 생성이 기준. 3. 결과 보정은 짧은 추가 지시로: 예) "Make the right quote panel 340px and sticky". 한국어 텍스트가 깨지면 "Keep all labels in Korean exactly as given" 재지시. 4. 생성 화면은 Figma로 내보내 디자인 시스템 토큰(§1)과 대조 후 조정한다. @@ -667,6 +1617,8 @@ A mobile field inspection screen for a KINTEX hall manager verifying booth const | 버전 | 일자 | 작성자 | 내용 | |---|---|---|---| +| **v2.1** | 2026-07-11 | designer | **입장권 예매 4화면 추가(M10 티켓 기획 대비 화면 갭 해소).** PLANNING v3.0 M10이 티켓을 기획 범위에 포함하나 design.md v2.0 인벤토리엔 예매 흐름 화면이 없던 갭을 메움 — 기존 번호 체계 이어서 순증(재번호화·기존 스펙 수정 없음). ①**§2.8-1 매트릭스**: M10 행에 SCR-P7·P8/M14·M15, M9 행에 SCR-P7(티켓결제 연계) 반영. ②**§2.8-2 인덱스**: 공개사이트에 SCR-P7(입장권 예매)·SCR-P8(예매 확인·취소), 모바일에 SCR-M14(모바일 티켓 예매)·SCR-M15(내 티켓 지갑) 추가, 집계 80→**84화면**(공개 6→8·모바일 13→15, 미생성 60→64). ③**§3B**: SCR-P7(행사·권종→예매자→결제→완료 다단계, 게스트+회원, 결제 PG 어댑터 위임·카드번호 입력 UI 없음)·SCR-P8(예매번호+이메일 조회·QR 티켓 상세·환불정책·취소) 상세+영어 Stitch 프롬프트. ④**§4**: SCR-M14(SCR-P7 모바일 최적화 390px·48px 터치)·SCR-M15(보유 티켓·QR 풀스크린 밝기부스트·오프라인 표시·취소 진입, 티켓→체크인→배지(SCR-M5) 전환 명시) 상세+프롬프트. §5 가이드 모바일 범위 M1~M13→M1~M15. **보안·불변 준수**: 결제 화면 카드번호 저장 UI 금지(PG 위임 명시)·PII 마스킹·WCAG AA·i18n·라이트/다크. §1 토큰 권위 유지, 신규 토큰 0. PLANNING.md·기존 화면 스펙·src 무수정 | +| **v2.0** | 2026-07-11 | designer | **전 화면 인벤토리 확장 — PLANNING v3.0 전 모듈·전 역할 커버.** 기존 SCR-01~17·SCR-M1/M2(재번호화 없이 보존)에 **신규 60화면 순증**: ①**§2.8 전 화면 인벤토리 매트릭스 신설**(모듈×역할×채널 요약표 + 전 SCR 인덱스 + 기생성/기구현/미생성 상태 컬럼, 총 80화면). ②**웹 코어·도메인 SCR-18~51**(M1 배정·견적/부스판매/셀프선택, M9 정산, M6 서류·마일스톤·서류작성, M7 매칭·RFQ, M8 물류, **M15 옥션 4종**(목록·상세순위·견적서·낙찰), M10 관람객관리·체크인·리드, M12 EDM·스폰서십, M17 CMS·마이크로사이트·다국어, 업체 대시보드, **§5B 공통 업무기능 SCR-39~48**(worklog·schedule·message·notice·opinion·search·meeting·report·notification·mypage, UIWS 표준 정합), 인증 SCR-49~51(회원가입·비번재설정·OTP)). ③**§3A 관리자 백오피스 SCR-A1~A10**(사용자·RBAC·공통코드·메뉴·감사로그·시스템설정·마스터데이터·룰셋버전·**테넌트 온보딩§1A**·AiConfig). ④**§3B 공개 홍보 사이트 SCR-P1~P6**(홈·행사상세·인터랙티브 플로어플랜·사전등록·마이크로사이트 공개뷰·참가문의, MDI 비적용·SEO/다국어). ⑤**모바일 SCR-M3~M13**(역할별 홈·리드캡처·관람객 배지/QR·wayfinding·플로어플랜·비즈매칭·세션아젠다·업체 통행증·알림·서류승인·옥션순위). 각 화면 SCR ID·화면명·역할·모듈·레이아웃·상태(로딩/빈/에러/degraded)·데이터 원천(실/샘플 경계)·라우트·**영어 Stitch 프롬프트** 포함. §1 토큰·§2.7 MDI 셸 보존, **신규 토큰 0**. AI 산출물 워터마크·AiLabel·PII 마스킹·WCAG AA·i18n·라이트/다크 토큰 불변 규칙 반영. PLANNING.md·src 무수정 | | v1.0 | 2026-07-11 | designer | 최초 작성 — PLANNING.md v1.0 기반. 디자인 시스템·IA·P0 중심 14화면(웹 12 + 모바일 2) Stitch 프롬프트 포함. 다크모드는 Phase 1 미지원(캔버스 다크 서피스만) 결정 | | v1.0.1 | 2026-07-11 | reviewer 반영 | 화면 수 표기 정정(13→14). 잔여 지적사항은 docs/BACKLOG.md 참조 | | v1.1 | 2026-07-11 | designer | **Stitch 산출물 학습·정합화.** `stitch_kintex_ai_system_architect/` 18화면 code.html + DESIGN.md 3변형(Intelligence·Nexus·Precision) 학습. ①§1-2 컬러: Precision Enterprise AI 변형이 우리 토큰(#0066B3·#1C2536·#6D4AFF·#D92D20·#F9FAFB·#101828·StatusBadge 진행) 실증 확인 — 우리 팔레트를 단일 출처로 유지, #0059A6·#00427D는 짙은 대체 톤으로만 허용, ai-surface #F5F3FF AI 카드 배경 채택. ②§1-3 타이포: Pretendard 권위 유지, Inter를 라틴/tabular 보조로 허용(Stitch가 Hanken Grotesk/Inter 채택), 행 높이 44/48px 권위값 유지. ③§1-4 라운드(4/8px·pill)·엘리베이션(톤레이어+1px 테두리+AI 좌측 보라 액센트)·그리드(12컬럼·1440px·4px 베이스)·지브라 테이블 토큰 신설(이전 누락분). ④§2.5 Stitch 화면↔SCR 매핑표 신설(14화면). ⑤§2.6 범위 게이트 — proposal.md 및 범위 밖 4화면(business_intelligence·hall_operations·exhibition_schedule·admin_dashboard)을 P2 후보로 분류(PLANNING이 범위 권위, designer 임의 승격 금지). PLANNING.md·타 문서 무수정 | diff --git a/mobile/.gitignore b/mobile/.gitignore new file mode 100644 index 0000000..5cf5f2d --- /dev/null +++ b/mobile/.gitignore @@ -0,0 +1,23 @@ +# Expo / RN +node_modules/ +.expo/ +dist/ +web-build/ +*.log + +# Native +ios/ +android/ +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Env / secrets +.env +.env.* + +# Misc +.DS_Store diff --git a/mobile/README.md b/mobile/README.md new file mode 100644 index 0000000..1ed133c --- /dev/null +++ b/mobile/README.md @@ -0,0 +1,43 @@ +# KINTEX AI 전시관리 — 모바일 앱 (mobile/) + +React Native + Expo(expo-router, TypeScript) 모바일 앱. 조회·승인·현장 시나리오 전용 +(design.md §4). 백엔드는 공유 API `https://kintex.zioinfo.co.kr`. + +## 스택 +- Expo SDK 51 · expo-router ~3.5 · React Native 0.74 · TypeScript +- JWT는 `expo-secure-store`(네이티브 keychain/keystore)에 저장, 웹은 AsyncStorage 폴백 +- 디자인 토큰: `theme/`(docs/design.md §1 팔레트·타이포). 신규 토큰 없음. + +## 실행 +```bash +cd mobile +npm install +npm run typecheck # tsc --noEmit +npm start # Expo 개발 서버 (Expo Go / 에뮬레이터) +``` + +API 베이스는 `app.json > extra.apiBase` 또는 `EXPO_PUBLIC_API_BASE` 환경변수로 재정의. +시크릿(키 등)은 하드코딩하지 않는다. + +## 화면 +| 경로 | 화면 | 근거 | +|---|---|---| +| `app/login.tsx` | 로그인(2FA/OTP·아이디 기억·비번찾기/회원가입) | SCR-01 | +| `app/register.tsx` | 회원가입 | 04_backend_public_auth §1 | +| `app/forgot-password.tsx` | 비밀번호 찾기/초기화 | 04 §2·§3 | +| `app/(tabs)/index.tsx` | 홈/대시보드(역할별 진입) | design §2-2 | +| `app/(tabs)/field.tsx` | 현장 허브(역할 분기) | — | +| `app/(tabs)/gallery.tsx` | 시각화 갤러리(워터마크 상시) | SCR-12 | +| `app/(tabs)/more.tsx` | 계정·서버 상태·로그아웃 | — | +| `app/checklist.tsx` | 현장 체크리스트(48px 터치·오프라인·사진) | SCR-M1 | +| `app/inspection.tsx` | 현장 검수(승인/반려·AI 플래그) | SCR-M2 | + +## 계약·보안 준수 +- 응답 오류 봉투 `{ success, data, error }` 처리(`lib/api.ts`), degraded 폴백(`isDegraded`). +- AI 생성 이미지는 `components/AiImage.tsx`가 워터마크 + "AI 생성 예상 이미지" 고지를 **항상** 표시(제거 불가). +- 비밀번호는 저장하지 않음. "아이디 기억"은 이메일만 AsyncStorage 보관. + +## 남은 작업 +- 실 화면 데이터 API 배선 확대(부스 목록·검수 요청 제출 — 백엔드 M6/C-4 확장 대기). +- 앱 아이콘/스플래시 에셋(`assets/`), i18n, 푸시 알림. +- EAS 실빌드(eas.json 프로파일) — 본 스캐폴드 범위 밖. diff --git a/mobile/app.json b/mobile/app.json new file mode 100644 index 0000000..8590e05 --- /dev/null +++ b/mobile/app.json @@ -0,0 +1,40 @@ +{ + "expo": { + "name": "KINTEX AI 전시관리", + "slug": "kintex", + "scheme": "kintex", + "version": "0.1.0", + "orientation": "portrait", + "userInterfaceStyle": "light", + "newArchEnabled": false, + "splash": { + "resizeMode": "contain", + "backgroundColor": "#004C86" + }, + "assetBundlePatterns": ["**/*"], + "ios": { + "supportsTablet": true, + "bundleIdentifier": "kr.co.zioinfo.kintex" + }, + "android": { + "package": "kr.co.zioinfo.kintex", + "adaptiveIcon": { + "backgroundColor": "#0066B3" + } + }, + "web": { + "bundler": "metro", + "output": "single" + }, + "plugins": [ + "expo-router", + "expo-secure-store" + ], + "experiments": { + "typedRoutes": false + }, + "extra": { + "apiBase": "https://kintex.zioinfo.co.kr" + } + } +} diff --git a/mobile/app/(tabs)/_layout.tsx b/mobile/app/(tabs)/_layout.tsx new file mode 100644 index 0000000..fccc948 --- /dev/null +++ b/mobile/app/(tabs)/_layout.tsx @@ -0,0 +1,59 @@ +/* + * 하단 탭 바 4개 — 홈 / 현장 / 갤러리 / 더보기 (design.md §2-2 모바일). + * 현장 탭은 역할에 따라 체크리스트(장치업체)·검수(홀매니저)로 진입. + */ +import { Ionicons } from '@expo/vector-icons'; +import { Redirect, Tabs } from 'expo-router'; +import React from 'react'; +import { useAuth } from '../../context/AuthContext'; +import { colors } from '../../theme'; + +export default function TabsLayout() { + const { ready, token } = useAuth(); + if (ready && !token) return ; + + return ( + + , + }} + /> + ( + + ), + }} + /> + , + }} + /> + ( + + ), + }} + /> + + ); +} diff --git a/mobile/app/(tabs)/field.tsx b/mobile/app/(tabs)/field.tsx new file mode 100644 index 0000000..b3c1912 --- /dev/null +++ b/mobile/app/(tabs)/field.tsx @@ -0,0 +1,99 @@ +/* + * 현장 탭 — 역할별 현장 기능 허브. + * 장치업체 → 현장 체크리스트(SCR-M1), 홀매니저 → 현장 검수(SCR-M2). + */ +import { Ionicons } from '@expo/vector-icons'; +import { router } from 'expo-router'; +import React from 'react'; +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 { activeWorkspace, user } = useAuth(); + const role = activeWorkspace?.myRole; + const isHallManager = user?.hallManager || role === 'HALL_MANAGER'; + const isContractor = role === 'CONTRACTOR'; + + return ( + + 현장 업무 + + {!isContractor && !isHallManager ? ( + + 현장 기능은 장치·시공업체(체크리스트)와 홀매니저(검수) 역할에서 제공됩니다. + + ) : null} + + {isContractor ? ( + router.push('/checklist')} + /> + ) : null} + + {isHallManager ? ( + router.push('/inspection')} + /> + ) : null} + + ); +} + +function Tile({ + icon, + title, + desc, + onPress, +}: { + icon: keyof typeof Ionicons.glyphMap; + title: string; + desc: string; + onPress: () => void; +}) { + return ( + + + + + + {title} + {desc} + + + + ); +} + +const styles = StyleSheet.create({ + scroll: { padding: spacing.md, gap: spacing.md }, + title: { fontSize: type.h2.fontSize, fontWeight: '700', color: colors.neutral900 }, + tile: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + backgroundColor: colors.white, + borderWidth: 1, + borderColor: colors.neutral200, + borderRadius: radius.md, + padding: spacing.md, + }, + tileIcon: { + width: 44, + height: 44, + borderRadius: radius.sm, + backgroundColor: colors.primary050, + alignItems: 'center', + justifyContent: 'center', + }, + tileBody: { flex: 1, gap: 2 }, + tileTitle: { fontSize: type.h3.fontSize, fontWeight: '600', color: colors.neutral900 }, + tileDesc: { fontSize: type.caption.fontSize, color: colors.neutral500 }, +}); diff --git a/mobile/app/(tabs)/gallery.tsx b/mobile/app/(tabs)/gallery.tsx new file mode 100644 index 0000000..3826b97 --- /dev/null +++ b/mobile/app/(tabs)/gallery.tsx @@ -0,0 +1,151 @@ +/* + * SCR-12 시각화 갤러리 (모바일 2열) — 워터마크 상시 + 상단 고정 고지 바. + * 계약: GET /api/events/{eventId}/booths/{boothId}/render-jobs?shot={S1} (스켈레톤 501 → degraded 폴백). + * 실 API 호출 구조를 유지하되, 서버 미구현/오프라인 시 degraded 샘플로 폴백한다. + */ +import { router } from 'expo-router'; +import React, { useCallback, useEffect, useState } from 'react'; +import { RefreshControl, ScrollView, StyleSheet, Text, View } from 'react-native'; +import { AiImage } from '../../components/AiImage'; +import { Banner } from '../../components/Banner'; +import { Button } from '../../components/Button'; +import { useAuth } from '../../context/AuthContext'; +import { api, isDegraded } from '../../lib/api'; +import { AI_IMAGE_NOTICE } from '../../lib/config'; +import type { RenderJobDto } from '../../lib/types'; +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 mkJob(jobId: string, shot: string, status: RenderJobDto['status']): RenderJobDto { + return { + jobId, + boothId: DEMO_BOOTH, + shotPreset: shot, + status, + imageUrl: null, + schemaHash: null, + modelVersion: null, + watermarkRequired: true, + watermarkText: AI_IMAGE_NOTICE, + notice: 'AI 생성 이미지는 계약·심사 서류에 사용할 수 없습니다 (시공 기준은 도면)', + }; +} + +const SHOT_LABEL: Record = { + S1: 'S1 정면', + S2: 'S2 야간', + S3: 'S3 통로', + S4: 'S4 내부', + S5: 'S5 비교', + S6: 'S6 배선', + S7: 'S7 홀 전경', +}; + +export default function GalleryScreen() { + const { activeWorkspace, token } = useAuth(); + const [jobs, setJobs] = useState([]); + const [degraded, setDegraded] = useState(false); + const [loading, setLoading] = useState(true); + + const load = useCallback(async () => { + setLoading(true); + if (!activeWorkspace || !token) { + setJobs(FALLBACK); + setDegraded(true); + setLoading(false); + return; + } + try { + const res = await api.get( + `/api/events/${activeWorkspace.eventId}/booths/${DEMO_BOOTH}/render-jobs`, + ); + setJobs(res ?? []); + setDegraded(false); + } catch (e) { + if (isDegraded(e)) { + setJobs(FALLBACK); + setDegraded(true); + } else { + setJobs([]); + setDegraded(false); + } + } finally { + setLoading(false); + } + }, [activeWorkspace, token]); + + useEffect(() => { + load(); + }, [load]); + + return ( + + {/* 상단 고정 고지 바 (제거 불가) */} + + + AI 생성 이미지는 계약·심사 서류에 사용할 수 없습니다 — 시공 기준은 도면입니다 + + + + } + > + {degraded ? ( + + 서버 이미지 이력에 연결할 수 없어 예시를 표시합니다 (degraded). + + ) : null} + + {jobs.length === 0 && !loading ? ( + + 아직 생성된 이미지가 없습니다 — 설계 스튜디오에서 예상 사진을 생성하세요. + + ) : null} + + + {jobs.map((j) => ( + + + {j.status === 'FAILED' ? ( + diff --git a/src/frontend/src/components/layout/app-shell.css b/src/frontend/src/components/layout/app-shell.css index 6b4db3b..fd62d66 100644 --- a/src/frontend/src/components/layout/app-shell.css +++ b/src/frontend/src/components/layout/app-shell.css @@ -97,7 +97,9 @@ } .kx-shell__nav-icon { width: 20px; - text-align: center; + display: inline-flex; + align-items: center; + justify-content: center; } .kx-shell__user { @@ -174,6 +176,9 @@ border: none; background: transparent; color: var(--color-neutral-500); + display: inline-flex; + align-items: center; + justify-content: center; } .kx-shell__icon-btn:hover { background: var(--color-neutral-100); diff --git a/src/frontend/src/components/ui/AiImage.tsx b/src/frontend/src/components/ui/AiImage.tsx index 0b2b786..e4d38d0 100644 --- a/src/frontend/src/components/ui/AiImage.tsx +++ b/src/frontend/src/components/ui/AiImage.tsx @@ -1,5 +1,6 @@ import type { RenderJobDto } from '../../api/types'; import { AiLabel } from './Badge'; +import { IconSpark } from './icons'; import './ai-image.css'; /* @@ -68,7 +69,7 @@ export function AiImage({ {sample && onGenerate && ( )} diff --git a/src/frontend/src/components/ui/Badge.tsx b/src/frontend/src/components/ui/Badge.tsx index 15384e8..1ff947a 100644 --- a/src/frontend/src/components/ui/Badge.tsx +++ b/src/frontend/src/components/ui/Badge.tsx @@ -1,4 +1,5 @@ import type { ReactNode } from 'react'; +import { IconSpark } from './icons'; import './badge.css'; // ── StatusBadge (design.md §1-4) ── @@ -54,7 +55,7 @@ export function DdayChip({ dday }: { dday: number }) { export function AiLabel({ children = 'AI 초안' }: { children?: ReactNode }) { return ( - + {children} ); diff --git a/src/frontend/src/components/ui/CompareSlider.tsx b/src/frontend/src/components/ui/CompareSlider.tsx index b21a7de..c2d3b54 100644 --- a/src/frontend/src/components/ui/CompareSlider.tsx +++ b/src/frontend/src/components/ui/CompareSlider.tsx @@ -1,5 +1,6 @@ import { useCallback, useRef, useState } from 'react'; import type { KeyboardEvent, PointerEvent } from 'react'; +import { IconCompareArrows } from './icons'; import './compare-slider.css'; /* @@ -125,7 +126,7 @@ export function CompareSlider({ onKeyDown={onKeyDown} > diff --git a/src/frontend/src/components/ui/icons.tsx b/src/frontend/src/components/ui/icons.tsx new file mode 100644 index 0000000..c15b4e0 --- /dev/null +++ b/src/frontend/src/components/ui/icons.tsx @@ -0,0 +1,446 @@ +/** + * 시스템 아이콘 라이브러리 (design.md §3 SCR-17 항목6). + * + * ── 표준 ── + * - Stitch _icons 세트(24×24 viewBox · stroke=currentColor · stroke-width 2 · fill none)를 표준으로 삼는다. + * - 7종(Dashboard·Calendar·Operations·Analytics·Search·Menu·AiBot)은 Stitch 원본 이식, + * 나머지는 동일 스타일로 자체 제작. + * - currentColor 유지 → 테마·상태색을 CSS `color`로 상속(다크/라이트·상태배지 대응). + * - 기본 장식용(aria-hidden). 의미 전달이 필요하면 `title` prop으로 삽입(role=img 자동). + * + * 사용: <IconDashboard /> · <IconCheck size={16} /> · <IconWarning title="경고" /> + */ +import type { ReactNode, SVGProps } from 'react'; + +export interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'width' | 'height'> { + /** 한 변 픽셀 크기(정사각). 기본 20. */ + size?: number; + /** 접근성 라벨 — 지정 시 role=img + <title> 삽입, 미지정 시 aria-hidden 장식. */ + title?: string; +} + +/** 공통 SVG 래퍼 — 표준 속성(viewBox 24·fill none·currentColor)과 접근성 처리. */ +function Svg({ size = 20, title, children, ...rest }: IconProps & { children: ReactNode }) { + const a11y = title + ? { role: 'img' as const, 'aria-label': title } + : { 'aria-hidden': true as const, focusable: false as const }; + return ( + <svg + width={size} + height={size} + viewBox="0 0 24 24" + fill="none" + xmlns="http://www.w3.org/2000/svg" + {...a11y} + {...rest} + > + {title ? <title>{title} : null} + {children} + + ); +} + +const S = { stroke: 'currentColor', strokeWidth: 2 } as const; +const SR = { ...S, strokeLinecap: 'round' as const }; +const SRJ = { ...SR, strokeLinejoin: 'round' as const }; + +/* ───────────────────────────────────────────────────────── + * Stitch 원본 7종 (_icons) + * ───────────────────────────────────────────────────────── */ + +/** ▦ 대시보드 (Stitch icon_dashboard). */ +export function IconDashboard(p: IconProps) { + return ( + + + + + + ); +} + +/** ▤ 캘린더/일정 (Stitch icon_calendar). */ +export function IconCalendar(p: IconProps) { + return ( + + + + + + + ); +} + +/** ◐ 현장운영 (Stitch icon_operations — 라디오 웨이브 + 핀). */ +export function IconOperations(p: IconProps) { + return ( + + + + + + + ); +} + +/** ▨ 경영분석 (Stitch icon_analytics — 막대 차트). */ +export function IconAnalytics(p: IconProps) { + return ( + + + + + + ); +} + +/** ⌕ 검색 (Stitch icon_search). */ +export function IconSearch(p: IconProps) { + return ( + + + + + ); +} + +/** ☰ 메뉴 (Stitch icon_menu). */ +export function IconMenu(p: IconProps) { + return ( + + + + + + ); +} + +/** AI 봇 (Stitch icon_ai_bot — currentColor 정규화). */ +export function IconAiBot(p: IconProps) { + return ( + + + + + + + + + + + ); +} + +/* ───────────────────────────────────────────────────────── + * 자체 제작 (동일 스타일) + * ───────────────────────────────────────────────────────── */ + +/** ◱ 플로어플랜 스튜디오 (부스 그리드 + 강조 셀). */ +export function IconFloorplan(p: IconProps) { + return ( + + + + + + + ); +} + +/** ☷ 참가업체 관리 (사람 그룹). */ +export function IconExhibitors(p: IconProps) { + return ( + + + + + + + ); +} + +/** ❒ 서류·문서. */ +export function IconDocument(p: IconProps) { + return ( + + + + + + + ); +} + +/** ₩ 정산 (원화 원). */ +export function IconSettlement(p: IconProps) { + return ( + + + + + + + ); +} + +/** ⚙ 관리자·설정 (기어). */ +export function IconSettings(p: IconProps) { + return ( + + + + + ); +} + +/** ◔ 알림 벨. */ +export function IconBell(p: IconProps) { + return ( + + + + + ); +} + +/** ✦ AI 스파크(4각별) — AI 액션·라벨 액센트. */ +export function IconSpark(p: IconProps) { + return ( + + + + ); +} + +/** ⚠ △ 경고 (삼각형 + 느낌표). */ +export function IconWarning(p: IconProps) { + return ( + + + + + + ); +} + +/** ✓ 체크. */ +export function IconCheck(p: IconProps) { + return ( + + + + ); +} + +/** ✓ 원형 체크(성공 배지). */ +export function IconCheckCircle(p: IconProps) { + return ( + + + + + ); +} + +/** ✕ 닫기. */ +export function IconClose(p: IconProps) { + return ( + + + + + ); +} + +/** ⛔ 차단(원 + 사선). */ +export function IconBlock(p: IconProps) { + return ( + + + + + ); +} + +/** › 오른쪽 셰브런. */ +export function IconChevronRight(p: IconProps) { + return ( + + + + ); +} + +/** ‹ 왼쪽 셰브런. */ +export function IconChevronLeft(p: IconProps) { + return ( + + + + ); +} + +/** ▾ 아래쪽 셰브런(아코디언 열림). */ +export function IconChevronDown(p: IconProps) { + return ( + + + + ); +} + +/** ‹› 좌우 화살촉(비교 슬라이더 그립). */ +export function IconCompareArrows(p: IconProps) { + return ( + + + + + ); +} + +/** ← 왼쪽 화살표(뒤로). */ +export function IconArrowLeft(p: IconProps) { + return ( + + + + + ); +} + +/** → 오른쪽 화살표(진행·CTA). */ +export function IconArrowRight(p: IconProps) { + return ( + + + + + ); +} + +/** ▲ 위 삼각(증가 델타). */ +export function IconTrendUp(p: IconProps) { + return ( + + + + + + ); +} + +/** ▼ 아래 삼각(감소 델타). */ +export function IconTrendDown(p: IconProps) { + return ( + + + + + + ); +} + +/** ⭱ 업로드. */ +export function IconUpload(p: IconProps) { + return ( + + + + + + ); +} + +/** ⭳ 다운로드. */ +export function IconDownload(p: IconProps) { + return ( + + + + + + ); +} + +/** 🖼 이미지. */ +export function IconImage(p: IconProps) { + return ( + + + + + + ); +} + +/** ⤢ 화면맞춤(확장). */ +export function IconExpand(p: IconProps) { + return ( + + + + + + + ); +} + +/** + 확대(플러스). */ +export function IconPlus(p: IconProps) { + return ( + + + + + ); +} + +/** − 축소(마이너스). */ +export function IconMinus(p: IconProps) { + return ( + + + + ); +} + +/** ◍ 인원(사람 + 카운트). */ +export function IconUsers(p: IconProps) { + return ( + + + + + ); +} + +/** ▦ 그리드(홀 배정). */ +export function IconGrid(p: IconProps) { + return ( + + + + + + + ); +} + +/** ● 오프라인(연결 끊김). */ +export function IconWifiOff(p: IconProps) { + return ( + + + + + + + + + ); +} diff --git a/src/frontend/src/screens/approval/ManagerApprovalQueuePage.tsx b/src/frontend/src/screens/approval/ManagerApprovalQueuePage.tsx index d1ec0c5..d6acea7 100644 --- a/src/frontend/src/screens/approval/ManagerApprovalQueuePage.tsx +++ b/src/frontend/src/screens/approval/ManagerApprovalQueuePage.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { Button } from '../../components/ui/Button'; import { DdayChip } from '../../components/ui/Badge'; +import { IconCheck, IconSearch, IconSpark, IconWarning } from '../../components/ui/icons'; import './approval-queue.css'; /* @@ -58,7 +59,7 @@ export function ManagerApprovalQueuePage() {
- +
@@ -72,7 +73,7 @@ export function ManagerApprovalQueuePage() { 5
- ⚠ AI 차단 플래그 + AI 차단 플래그 4
@@ -83,7 +84,7 @@ export function ManagerApprovalQueuePage() {

제출 서류 목록 ({hall})

- +
@@ -114,9 +115,9 @@ export function ManagerApprovalQueuePage() { @@ -169,7 +170,7 @@ export function ManagerApprovalQueuePage() {
- +

AI 최적화 분석

diff --git a/src/frontend/src/screens/approval/ReviewDetailsPage.tsx b/src/frontend/src/screens/approval/ReviewDetailsPage.tsx index 3d0172a..6c7dd95 100644 --- a/src/frontend/src/screens/approval/ReviewDetailsPage.tsx +++ b/src/frontend/src/screens/approval/ReviewDetailsPage.tsx @@ -2,6 +2,14 @@ import { useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { Button } from '../../components/ui/Button'; import { AiLabel } from '../../components/ui/Badge'; +import { + IconChevronRight, + IconDownload, + IconExpand, + IconMinus, + IconPlus, + IconSpark, +} from '../../components/ui/icons'; import './review-details.css'; /* @@ -48,9 +56,9 @@ export function ReviewDetailsPage() {

@@ -60,7 +68,7 @@ export function ReviewDetailsPage() { 일자: 07.10
- @@ -87,7 +95,7 @@ export function ReviewDetailsPage() { onClick={() => setOverlay((o) => !o)} aria-pressed={overlay} > - AI 위반 오버레이 + AI 위반 오버레이
- - - + + +
diff --git a/src/frontend/src/screens/compliance/ComplianceReportPage.tsx b/src/frontend/src/screens/compliance/ComplianceReportPage.tsx index d163fe2..68aa27e 100644 --- a/src/frontend/src/screens/compliance/ComplianceReportPage.tsx +++ b/src/frontend/src/screens/compliance/ComplianceReportPage.tsx @@ -1,6 +1,7 @@ import { useParams } from 'react-router-dom'; import { Button } from '../../components/ui/Button'; import { AiLabel } from '../../components/ui/Badge'; +import { IconBlock, IconCheck, IconUpload, IconWarning } from '../../components/ui/icons'; import type { ComplianceReport, Violation } from '../../api/types'; import './compliance-report.css'; @@ -55,7 +56,7 @@ export function ComplianceReportPage() { role="status" > 차단 {report.blockCount}건 · 경고 {report.warnCount}건 · 통과 {report.passCount}건 @@ -65,7 +66,7 @@ export function ComplianceReportPage() { {/* PDF 업로드 존 */}
- +

기존 도면 업로드 (PDF/이미지)

드래그 앤 드롭 또는 클릭해 파일 선택 AI 자동 검증 @@ -105,7 +106,7 @@ export function ComplianceReportPage() { ))} {PASS_GROUPS.map((p) => (
  • - +
    {p.label} @@ -125,7 +126,7 @@ export function ComplianceReportPage() {
    상세 도면 (입면) 검토
    diff --git a/src/frontend/src/screens/schedule/ExhibitionSchedulePage.tsx b/src/frontend/src/screens/schedule/ExhibitionSchedulePage.tsx index d300caf..08a28bd 100644 --- a/src/frontend/src/screens/schedule/ExhibitionSchedulePage.tsx +++ b/src/frontend/src/screens/schedule/ExhibitionSchedulePage.tsx @@ -4,11 +4,20 @@ import { StatusBadge } from '../../components/ui/Badge'; import { Button } from '../../components/ui/Button'; import { EmptyState } from '../../components/ui/States'; import { + IconCalendar, + IconChevronLeft, + IconChevronRight, + IconGrid, + IconUsers, +} from '../../components/ui/icons'; +import { + ALL_EVENTS, CATEGORY_LABEL, - EVENT_DAYS, - HALL_UTILIZATION, - SAMPLE_EVENTS, + EVENT_YEARS, STATUS_LABEL, + eventDaysFor, + eventsByDateFor, + hallUtilizationFor, type EventStatus, type ScheduleEvent, } from './sampleSchedule'; @@ -18,61 +27,87 @@ const MOCK = `${import.meta.env.BASE_URL}mock/organizer_dashboard`; const THUMBS = [`${MOCK}/img1.jpg`, `${MOCK}/img2.jpg`, `${MOCK}/img3.jpg`]; const TODAY = new Date(2026, 6, 11); // 2026-07-11 (currentDate) -/** 홀 필터 매칭 — halls 문자열("홀 4, 5"·"제2전시장 홀7")에서 "홀" 뒤 번호만 추출해 전시장 범위("1-5"/"6-10")와 대조. "제N전시장"의 N 오매칭 방지. */ +/** 홀 필터 매칭 — halls 문자열("홀 4, 5A"·"제2전시장 홀7")에서 홀 번호만 추출해 전시장 범위("1-5"/"6-10")와 대조. "제N전시장"의 N·"7A-1"의 -1 오매칭 방지. */ function matchesHall(halls: string, range: string): boolean { if (range === 'all') return true; const [lo, hi] = range.split('-').map(Number); - const m = halls.match(/홀\s*([\d,\s~-]+)/); - const nums = m ? (m[1].match(/\d+/g)?.map(Number) ?? []) : []; + const cleaned = halls + .replace(/제\s*\d+\s*전시장/g, '') + .replace(/(\d+[A-C]?)-\d+/g, '$1'); + const nums = (cleaned.match(/\d+/g) ?? []).map(Number).filter((n) => n >= 1 && n <= 10); return nums.some((n) => n >= lo && n <= hi); } /* * SCR-15 전시 일정 관리 (M1·M6). Stitch exhibition_schedule 이식. - * - 좌 2/3 행사 리스트 + 우 1/3 미니 캘린더 · 홀 가동률. - * ★ 경계면: 행사 목록은 인증 workspaces(실 원천) 우선, 없으면 샘플. 홀 가동률·캘린더 점유는 샘플. + * - 좌 2/3 행사 리스트(또는 WISE 스타일 월 캘린더) + 우 1/3 미니 캘린더 · 홀 가동률. + * - 폴백 데이터: kintex.com 크롤링 10년치(2016~2026, 1,098건) — sampleSchedule.ALL_EVENTS. + * - 캘린더 뷰는 WISE(UIWS) ScheduleCalendar 월 그리드 패턴 참조(외부 라이브러리 미사용, + * 이전/다음/오늘 툴바·요일 헤더·당일 강조·일자별 이벤트 칩). + * ★ 경계면: 행사 목록은 인증 workspaces(실 원천) 우선, 없으면 크롤링 데이터 폴백. */ export function ExhibitionSchedulePage() { const workspaces = useAuthStore((s) => s.workspaces); const [view, setView] = useState<'list' | 'calendar'>('list'); + const [year, setYear] = useState('2026'); const [hall, setHall] = useState('all'); const [category, setCategory] = useState('all'); const [status, setStatus] = useState('all'); + // 우측 미니 캘린더·홀 가동률 공유 월 커서 + const [asideCursor, setAsideCursor] = useState(new Date(2026, 6, 1)); // 2026년 7월 - // 실 원천: 인증 workspaces → ScheduleEvent 매핑. 없으면 샘플 폴백. + // 실 원천: 인증 workspaces → ScheduleEvent 매핑. 없으면 크롤링 10년치 폴백. const usingReal = workspaces.length > 0; const events: ScheduleEvent[] = useMemo(() => { - if (!usingReal) return SAMPLE_EVENTS; + if (!usingReal) return ALL_EVENTS; return workspaces.map((w, i) => ({ id: w.eventId, title: w.eventName, // ★ category/estVisitors 는 WorkspaceDto 미포함 → 샘플/기본값 - category: 'exhibition', + category: 'exhibition' as const, status: (w.dday > 0 ? 'upcoming' : 'ongoing') as EventStatus, period: `${w.startDate} – ${w.endDate}`, + start: w.startDate, + end: w.endDate, halls: w.hallLabel, estVisitors: '집계 대기', thumbIdx: i % THUMBS.length, })); }, [usingReal, workspaces]); - const filtered = events.filter( - (e) => - matchesHall(e.halls, hall) && - (category === 'all' || e.category === category) && - (status === 'all' || e.status === status), + const filtered = useMemo( + () => + events.filter( + (e) => + (year === 'all' || e.start.slice(0, 4) === year || e.end.slice(0, 4) === year) && + matchesHall(e.halls, hall) && + (category === 'all' || e.category === category) && + (status === 'all' || e.status === status), + ), + [events, year, hall, category, status], ); + // 목록은 최신 시작일 우선 정렬 + const listed = useMemo(() => [...filtered].sort((a, b) => (a.start < b.start ? 1 : -1)), [filtered]); + return (

    전시 일정

    -

    킨텍스 전 홀 행사 일정 관리·모니터링

    +

    + 킨텍스 전 홀 행사 일정 관리·모니터링 — 10년치 {ALL_EVENTS.length.toLocaleString()}건 (kintex.com 수집) +

    +
    - {!usingReal && 행사 API 대기 (샘플)} + {!usingReal && 행사 API 대기 (크롤링 데이터)}
    @@ -99,67 +134,49 @@ export function ExhibitionSchedulePage() {
    -
    - {filtered.length === 0 ? ( -
    - 새 행사 생성} - /> -
    - ) : ( - filtered.map((e) => ( -
    -
    + )) + )} +
    + ) : ( + + )}
    @@ -167,13 +184,84 @@ export function ExhibitionSchedulePage() { } const WD = ['일', '월', '화', '수', '목', '금', '토']; +const MAX_CHIPS = 3; // 월 그리드 셀당 표시 칩 수 (초과분 +N) -function MiniCalendar() { - const [cursor, setCursor] = useState(new Date(2026, 7, 1)); // 2026년 8월 +/** + * WISE(UIWS ScheduleCalendar) 참조 월 캘린더 — 이전/다음/오늘 툴바, 요일 헤더, + * 당일 강조, 일자 셀에 행사 칩(카테고리 색), 초과분 +N. 외부 라이브러리 미사용. + */ +function MonthCalendar({ events }: { events: ScheduleEvent[] }) { + const [cursor, setCursor] = useState(new Date(2026, 6, 1)); // 2026년 7월 + const year = cursor.getFullYear(); + const month = cursor.getMonth(); + const byDate = useMemo(() => eventsByDateFor(year, month, events), [year, month, events]); + + const firstDow = new Date(year, month, 1).getDay(); + const daysInMonth = new Date(year, month + 1, 0).getDate(); + const cells: (number | null)[] = []; + for (let i = 0; i < firstDow; i++) cells.push(null); + for (let d = 1; d <= daysInMonth; d++) cells.push(d); + while (cells.length % 7 !== 0) cells.push(null); + + const todayKey = + TODAY.getFullYear() === year && TODAY.getMonth() === month ? TODAY.getDate() : -1; + const mm = String(month + 1).padStart(2, '0'); + + return ( +
    +
    + + {year}년 {month + 1}월 + + +
    +
    + {WD.map((w, i) => ( +
    {w}
    + ))} + {cells.map((d, i) => { + if (d === null) return
    ; + const key = `${year}-${mm}-${String(d).padStart(2, '0')}`; + const dayEvents = byDate.get(key) ?? []; + const dow = i % 7; + const cls = [ + 'kx-mcal__cell', + d === todayKey ? 'is-today' : '', + dow === 0 ? 'is-sun' : dow === 6 ? 'is-sat' : '', + ] + .filter(Boolean) + .join(' '); + return ( +
    + {d} + {dayEvents.slice(0, MAX_CHIPS).map((e) => ( + + {e.title} + + ))} + {dayEvents.length > MAX_CHIPS && ( + +{dayEvents.length - MAX_CHIPS} + )} +
    + ); + })} +
    +
    + ); +} + +function MiniCalendar({ cursor, onCursor }: { cursor: Date; onCursor: (d: Date) => void }) { const year = cursor.getFullYear(); const month = cursor.getMonth(); const firstDay = new Date(year, month, 1).getDay(); const daysInMonth = new Date(year, month + 1, 0).getDate(); + const eventDays = useMemo(() => eventDaysFor(year, month), [year, month]); const cells: (number | null)[] = []; for (let i = 0; i < firstDay; i++) cells.push(null); @@ -185,9 +273,9 @@ function MiniCalendar() { return (
    - + {year}년 {month + 1}월 - +
    {WD.map((w) => ( @@ -195,7 +283,7 @@ function MiniCalendar() { ))} {cells.map((d, i) => { if (d === null) return ; - const hasEvent = EVENT_DAYS.has(`${year}-${month + 1}-${d}`); + const hasEvent = eventDays.has(`${year}-${month + 1}-${d}`); return ( ); } + +function HallUtilization({ cursor }: { cursor: Date }) { + const year = cursor.getFullYear(); + const month = cursor.getMonth(); + const rows = useMemo(() => hallUtilizationFor(year, month), [year, month]); + return ( +
    +
    +

    홀 가동률 ({year}년 {month + 1}월)

    + 크롤링 데이터 기반 +
    +
      + {rows.map((h) => ( +
    • +
      + {h.label} + {h.pct}% +
      +
      + +
      +
    • + ))} +
    +
    + ); +} diff --git a/src/frontend/src/screens/schedule/kintexEvents.ts b/src/frontend/src/screens/schedule/kintexEvents.ts new file mode 100644 index 0000000..1fa0010 --- /dev/null +++ b/src/frontend/src/screens/schedule/kintexEvents.ts @@ -0,0 +1,1112 @@ +/* + * 킨텍스 실제 전시회 10년치 데이터 (2016~2026, 1098건). + * 출처: kintex.com 행사안내(list.do) 크롤링 — scratchpad/crawl_kintex.py + gen_seed.py 로 생성. + * 백엔드 시드와 동일 원천: db/migration/V10__seed_exhibitions_10yr.sql + * ★ estVisitors 는 홀 수·기간 기반 결정적 추정치(샘플)이며 실 집계가 아니다. + * 카테고리는 행사명 키워드 추론(샘플)이다. 이 파일은 생성물 — 직접 수정하지 말 것. + */ +export type KxCategory = 'exhibition' | 'culture' | 'trade' | 'tech'; + +/** [id, title, start(YYYY-MM-DD), end, hallsLabel, category, estVisitors] */ +export type KxEventRow = [string, string, string, string, string, KxCategory, number]; + +export const KINTEX_EVENT_ROWS: KxEventRow[] = [ + ["kx2016-001", "제21회 맘앤베이비엑스포 (국제임신출산_유아교육박람회)", "2016-01-28", "2016-01-31", "홀 3, 4", "culture", 45200], + ["kx2016-002", "2016 서울 국제 안경산업 박람회", "2016-02-18", "2016-02-20", "홀 3A", "exhibition", 12400], + ["kx2016-003", "2016 동아 선물용품(기프트쇼) 및 차・공예박람회", "2016-02-18", "2016-02-20", "홀 4A", "culture", 18200], + ["kx2016-004", "제2회 서울 상조・장례산업 박람회", "2016-02-18", "2016-02-20", "홀 5A", "exhibition", 17000], + ["kx2016-005", "2016 경향하우징페어/리빙앤라이프스타일", "2016-02-24", "2016-02-28", "홀 3, 4, 5, 1, 2", "culture", 91300], + ["kx2016-006", "2016 자동차 경량화 기술 산업전", "2016-03-09", "2016-03-11", "홀 7B", "trade", 19300], + ["kx2016-007", "ADVANCED TECH KOREA 2016", "2016-03-09", "2016-03-11", "홀 8", "exhibition", 18100], + ["kx2016-008", "2016 SBS골프 대한민국 골프대전", "2016-03-17", "2016-03-20", "홀 1, 2, 3", "exhibition", 61300], + ["kx2016-009", "코베-베이비&에듀페어", "2016-03-24", "2016-03-27", "홀 3, 4", "culture", 46500], + ["kx2016-010", "2016 OMC 헤어월드 & 인터뷰티페어 코리아", "2016-03-27", "2016-03-29", "홀 7, 8", "culture", 32500], + ["kx2016-011", "2016 국방부 전역예정장병 취업박람회", "2016-03-30", "2016-03-31", "홀 10A", "trade", 8300], + ["kx2016-012", "2016 코리아 나라장터 엑스포", "2016-03-30", "2016-04-01", "홀 4, 5", "exhibition", 39800], + ["kx2016-013", "2016 매경 창업&프랜차이즈쇼", "2016-03-31", "2016-04-03", "홀 1", "exhibition", 18700], + ["kx2016-014", "2016 스위트 코리아 (디저트&팬시푸드 페스티벌)", "2016-03-31", "2016-04-03", "홀 미정", "culture", 19600], + ["kx2016-015", "2016 스위트코리아", "2016-03-31", "2016-04-03", "홀 2", "exhibition", 29300], + ["kx2016-016", "test", "2016-04-01", "2016-04-01", "홀 미정", "exhibition", 4800], + ["kx2016-017", "2016 플레이쇼 키즈 & 키덜트", "2016-05-05", "2016-05-08", "홀 9A", "culture", 22100], + ["kx2016-018", "2016 서울국제식품산업대전", "2016-05-10", "2016-05-13", "홀 1, 2, 3, 4, 5, 7, 8, 6B, 6C", "trade", 134700], + ["kx2016-019", "제41회 MBC건축박람회/2016 서울 디지털프린팅・사인엑스포/2016 동아 조명박람회/2016 동아 공공시설 및 조경박람회", "2016-05-19", "2016-05-22", "홀 9, 10", "tech", 39200], + ["kx2016-020", "제11회 동아 홈&리빙 페어, 제8회 동아 판촉 및 선물용품(기프트쇼)박람회/제6회 동아 차・공예박람회/ 2016 동아 가구엑스포", "2016-05-19", "2016-05-22", "홀 7", "culture", 14600], + ["kx2016-021", "제7회 동아 스포츠・레저산업 박람회", "2016-05-19", "2016-05-22", "홀 8, 2전시장 로비 9-1", "culture", 24100], + ["kx2016-022", "플레이엑스포", "2016-05-19", "2016-05-22", "홀 1, 2", "exhibition", 34700], + ["kx2016-023", "2016 국제로타리 세계대회", "2016-05-28", "2016-06-01", "홀 1, 2, 6, 3, 4, 5, 7, 8, 9, 10", "exhibition", 244600], + ["kx2016-024", "제22회 맘앤베이비엑스포 (국제임신출산_유아교육박람회)", "2016-06-09", "2016-06-12", "홀 4, 5", "culture", 44800], + ["kx2016-025", "2016 하나투어 여행박람회", "2016-06-09", "2016-06-12", "홀 7, 8, 10", "culture", 51600], + ["kx2016-026", "시티스케이프 코리아 2016", "2016-06-10", "2016-06-12", "홀 2A", "exhibition", 11900], + ["kx2016-027", "2016 대한민국 명품특산물 페스티벌", "2016-06-16", "2016-06-19", "홀 10", "culture", 20200], + ["kx2016-028", "제4회 국제 아웃도어 캠핑페스티벌", "2016-06-17", "2016-06-19", "홀 9", "culture", 13700], + ["kx2016-029", "로보유니버스 & VR 서밋", "2016-06-22", "2016-06-24", "홀 7", "tech", 14900], + ["kx2016-030", "2016 노인 일자리 한마당", "2016-06-30", "2016-06-30", "홀 4A", "exhibition", 6300], + ["kx2016-031", "K-Farm 2016 귀농귀촌박람회/친환경에코쇼", "2016-07-07", "2016-07-09", "홀 7A", "trade", 12500], + ["kx2016-032", "국제 나노기술 심포지엄 및 나노융합대전", "2016-07-13", "2016-07-15", "홀 7, 8A", "exhibition", 36000], + ["kx2016-033", "코리아 룩 페스티벌 2016", "2016-07-14", "2016-07-17", "홀 5", "culture", 23700], + ["kx2016-034", "제1회 피트니스 · 보디빌딩 · 스포츠 아카데미 박람회", "2016-07-15", "2016-07-17", "홀 4A", "exhibition", 14300], + ["kx2016-035", "제6회 국제어린이교육 및 용품박람회", "2016-08-04", "2016-08-07", "홀 7A", "exhibition", 22500], + ["kx2016-036", "2016 미베 베이비엑스포 / 미래 유아교육박람회", "2016-08-12", "2016-08-15", "홀 3, 4", "culture", 47700], + ["kx2016-037", "2016 대한민국 국제 관광박람회", "2016-08-18", "2016-08-21", "홀 7", "exhibition", 21200], + ["kx2016-038", "2016 한국국제가구 및 인테리어산업대전", "2016-08-18", "2016-08-21", "홀 3, 4", "trade", 42600], + ["kx2016-039", "2016 국제접착코팅필름산업전시회", "2016-08-24", "2016-08-26", "홀 7A", "trade", 18200], + ["kx2016-040", "제42회 MBC건축박람회/2016 동아 냉・난방 및 건축설비박람회", "2016-08-25", "2016-08-28", "홀 3, 4", "exhibition", 44000], + ["kx2016-041", "제12회 동아 홈&리빙 페어/제9회 동아 판촉 및 선물용품(기프트쇼) 박람회/제7회 동아 차・공예박람회", "2016-08-25", "2016-08-28", "홀 5", "culture", 17000], + ["kx2016-042", "제8회 동아 스포츠・레저산업 박람회", "2016-08-25", "2016-08-28", "홀 2", "culture", 20600], + ["kx2016-043", "2016 경기도 무형문화재 대축제", "2016-08-26", "2016-08-28", "홀 1A, 1B", "culture", 17800], + ["kx2016-044", "2016 경기도 미래첨단 교통안전 엑스포", "2016-09-01", "2016-09-02", "홀 4", "trade", 11200], + ["kx2016-045", "2016 오토모티브위크", "2016-09-02", "2016-09-04", "홀 1, 2, 3", "exhibition", 52200], + ["kx2016-046", "스마트 시티 이노베이션 서밋 아시아 2016", "2016-09-20", "2016-09-22", "홀 3A", "tech", 17500], + ["kx2016-047", "2016 글로벌 소재부품 산업대전", "2016-09-20", "2016-09-22", "홀 3B", "trade", 16200], + ["kx2016-048", "2016 지역희망박람회", "2016-09-28", "2016-10-01", "홀 5", "exhibition", 18000], + ["kx2016-049", "2016 카페&베이커리 페어", "2016-10-06", "2016-10-09", "홀 1", "culture", 21300], + ["kx2016-050", "2016 호텔&레스토랑 산업전", "2016-10-06", "2016-10-09", "홀 2", "trade", 24400], + ["kx2016-051", "제14회 코베 베이비페어", "2016-10-06", "2016-10-09", "홀 4, 5", "culture", 44700], + ["kx2016-052", "2016 코리아 홈앤리빙페어", "2016-10-06", "2016-10-09", "홀 3", "culture", 14100], + ["kx2016-053", "2016 기상기후산업박람회", "2016-10-12", "2016-10-14", "홀 10A", "exhibition", 11000], + ["kx2016-054", "2016 로보월드", "2016-10-12", "2016-10-16", "홀 4, 5", "exhibition", 59300], + ["kx2016-055", "2016 말산업박람회", "2016-10-13", "2016-10-16", "홀 미정", "exhibition", 17100], + ["kx2016-056", "2016 말문화 박람회", "2016-10-13", "2016-10-16", "홀 9B", "culture", 15400], + ["kx2016-057", "2016 대한민국행복교육박람회", "2016-10-20", "2016-10-22", "홀 7B, 8, 9, 10", "exhibition", 50000], + ["kx2016-058", "대한민국우수상품전시회", "2016-10-26", "2016-10-29", "홀 4, 5, 2, 3", "exhibition", 60400], + ["kx2016-059", "2016 소프트웨어 교육 페스티벌", "2016-10-28", "2016-10-29", "홀 10", "tech", 9900], + ["kx2016-060", "한국 폴리텍 EXPO", "2016-11-02", "2016-11-03", "홀 5", "exhibition", 7100], + ["kx2016-061", "SENDEX 2016", "2016-11-03", "2016-11-05", "홀 2, 3", "exhibition", 36900], + ["kx2016-062", "2016 대한민국 에너지대전", "2016-11-08", "2016-11-11", "홀 2, 3", "trade", 50200], + ["kx2016-063", "디자인코리아", "2016-11-09", "2016-11-13", "홀 4, 5", "exhibition", 40200], + ["kx2016-064", "2016 공학교육페스티벌", "2016-11-10", "2016-11-11", "홀 7", "culture", 8400], + ["kx2016-065", "2016 대한민국 무형문화재대전 제41회 대한민국전승공예대전, 제46회 대한민국공예품대전", "2016-11-11", "2016-11-13", "홀 1", "culture", 14600], + ["kx2016-066", "2016 국민대학교 캡스톤 디자인 경진대회", "2016-11-14", "2016-11-14", "홀 10A", "exhibition", 3900], + ["kx2016-067", "2016 SBS골프 한국골프용품전", "2016-11-17", "2016-11-20", "홀 10B", "exhibition", 22100], + ["kx2016-068", "마이스타일트렌드페어 2016", "2016-11-18", "2016-11-20", "홀 9", "culture", 14200], + ["kx2016-069", "2016 대한민국 과일산업대전", "2016-11-18", "2016-11-20", "홀 10A", "trade", 16500], + ["kx2016-070", "제23회 맘앤베이비엑스포 / 국제유아교육박람회", "2016-11-24", "2016-11-27", "홀 3, 4", "culture", 34400], + ["kx2016-071", "제8회 대한민국 펫산업 박람회 : 2016 케이펫페어 \"일산\"", "2016-11-25", "2016-11-27", "홀 7B, 8", "culture", 23000], + ["kx2016-072", "킨텍스 스푼 아트쇼 2016", "2016-11-25", "2016-12-04", "홀 2", "culture", 30100], + ["kx2016-073", "2016 붐업코리아 수출상담회", "2016-11-30", "2016-11-30", "홀 4, 5", "trade", 10400], + ["kx2016-074", "2016 대한민국 유망프랜차이즈대전", "2016-12-02", "2016-12-04", "홀 8A", "exhibition", 17500], + ["kx2016-075", "인사이드 핀테크 컨퍼런스 & 엑스포", "2016-12-08", "2016-12-09", "홀 6A, 6B", "tech", 8100], + ["kx2016-076", "2016 다문화 너나들이 축제", "2016-12-09", "2016-12-10", "홀 3", "culture", 7100], + ["kx2016-077", "코리아 크리스마스 페어", "2016-12-09", "2016-12-18", "홀 7, 8A", "culture", 53700], + ["kx2017-001", "제24회 맘앤베이비엑스포", "2017-02-09", "2017-02-12", "홀 4, 5", "culture", 33700], + ["kx2017-002", "2017 Advanced Tech Korea", "2017-02-15", "2017-02-17", "홀 7B", "exhibition", 17800], + ["kx2017-003", "2017 경향하우징페어", "2017-02-22", "2017-02-26", "홀 2, 3, 4, 5A", "culture", 85600], + ["kx2017-004", "2017 대한민국 건축기계설비종합전시회", "2017-02-22", "2017-02-26", "홀 5B", "trade", 22600], + ["kx2017-005", "2017 리빙앤라이프스타일", "2017-02-22", "2017-02-26", "홀 1", "culture", 18700], + ["kx2017-006", "2017 스위트 코리아(디저트&카페 페스티벌)", "2017-02-23", "2017-02-26", "홀 7B", "culture", 15400], + ["kx2017-007", "2017 매경 창업 & 프랜차이즈쇼", "2017-02-23", "2017-02-26", "홀 7A", "exhibition", 15900], + ["kx2017-008", "Automotive Testing Expo 2017", "2017-03-06", "2017-03-08", "홀 10", "exhibition", 13600], + ["kx2017-009", "제 14회 한국국제냉난방공조전", "2017-03-07", "2017-03-10", "홀 7, 8", "trade", 36800], + ["kx2017-010", "2017 국제금형 및 관련기기전", "2017-03-07", "2017-03-11", "홀 1, 2, 3", "trade", 75200], + ["kx2017-011", "제24회 국제플라스틱 고무산업전시회", "2017-03-07", "2017-03-11", "홀 4, 5", "trade", 40100], + ["kx2017-012", "2017 한국국제낚시박람회", "2017-03-10", "2017-03-12", "홀 9", "exhibition", 17500], + ["kx2017-013", "세계보안엑스포 2017", "2017-03-15", "2017-03-17", "홀 1, 2", "exhibition", 31400], + ["kx2017-014", "2017 SBS골프 대한민국 골프대전", "2017-03-16", "2017-03-19", "홀 3, 4, 5", "exhibition", 53600], + ["kx2017-015", "2017 캠핑 & 피크닉페어", "2017-03-16", "2017-03-19", "홀 7, 8", "culture", 41900], + ["kx2017-016", "2017 전역예정장병 취업박람회", "2017-03-22", "2017-03-23", "홀 7", "exhibition", 7000], + ["kx2017-017", "2017 국방부 전역예정장병 취창업박람회", "2017-03-22", "2017-03-23", "홀 미정", "trade", 9100], + ["kx2017-018", "2017 서울모터쇼", "2017-03-31", "2017-04-09", "홀 1, 2, 3, 4, 5, 9, 10, 7, 8", "exhibition", 289900], + ["kx2017-019", "CI KOREA 2017 (국제 화장품원료기술전)", "2017-04-18", "2017-04-21", "홀 8B", "exhibition", 14200], + ["kx2017-020", "국제 제약화장품산업기술 주간(제12회 제약화장품기술전/ 제7회 국제물류산업전/ 제7회 국제의약품전/ 제2회 국제화장품원료기술전/ 제9회 국제화학장치산업전/ 제11회 국제연구실험기자재 및 첨단분석장비전)", "2017-04-18", "2017-04-21", "홀 미정", "trade", 16400], + ["kx2017-021", "COPHEX 2017 (제12회 제약·화장품기술전)", "2017-04-18", "2017-04-21", "홀 10A", "exhibition", 21000], + ["kx2017-022", "KOREA PHARM 2017 (제7회 국제의약품전)", "2017-04-18", "2017-04-21", "홀 8A", "exhibition", 17100], + ["kx2017-023", "KOREA CHEM 2017 (제9회 국제화학장치산업전)", "2017-04-18", "2017-04-21", "홀 10B", "trade", 21500], + ["kx2017-024", "KOREA LAB 2017 (제11회 국제연구·실험 및 첨단분석장비전)", "2017-04-18", "2017-04-21", "홀 7B", "exhibition", 22800], + ["kx2017-025", "KOREA MAT 2017 (제7회 국제물류산업전)", "2017-04-18", "2017-04-21", "홀 9", "trade", 24300], + ["kx2017-026", "2017 코리아 나라장터 엑스포", "2017-04-19", "2017-04-21", "홀 4, 5", "exhibition", 34400], + ["kx2017-027", "제 16회 코베 베이비페어", "2017-04-20", "2017-04-23", "홀 2, 3", "culture", 39200], + ["kx2017-028", "2017 국제모션컨트롤 및 머신비전산업전", "2017-04-25", "2017-04-27", "홀 8A", "trade", 15800], + ["kx2017-029", "국제전자회로산업전, 국제전자실장산업전", "2017-04-25", "2017-04-27", "홀 7", "tech", 15500], + ["kx2017-030", "제14회 동아 홈&리빙페어 / 제11회 동아 기프트쇼", "2017-04-27", "2017-04-30", "홀 5", "culture", 15100], + ["kx2017-031", "제9회 동아 스포츠·레저산업 박람회", "2017-04-27", "2017-04-30", "홀 1", "culture", 24400], + ["kx2017-032", "제44회 MBC건축박람회/2017 서울 디지털프린팅・사인엑스포/2017 동아 공공시설 및 조경박람회/2017 동아 조명박람회", "2017-04-27", "2017-04-30", "홀 2, 3", "tech", 33700], + ["kx2017-033", "2017 동아 가구엑스포 / 제9회 동아 차・공예 박람회", "2017-04-27", "2017-04-30", "홀 4", "culture", 24100], + ["kx2017-034", "2017 튼튼쑥쑥 어린이 박람회", "2017-04-28", "2017-04-30", "홀 10A", "exhibition", 10600], + ["kx2017-035", "2017 플레이쇼 키즈 & 키덜트", "2017-05-03", "2017-05-05", "홀 9B", "culture", 19200], + ["kx2017-036", "2017 상반기 글로벌 취업 상담회", "2017-05-11", "2017-05-12", "홀 2A, 2B", "exhibition", 10800], + ["kx2017-037", "2017 서울국제식품산업대전", "2017-05-16", "2017-05-19", "홀 1, 2, 3, 4, 5, 7, 8", "trade", 139800], + ["kx2017-038", "2017 국토교통기술대전", "2017-05-24", "2017-05-26", "홀 10A", "exhibition", 15100], + ["kx2017-039", "2017 한국국제건설기계전", "2017-05-24", "2017-05-27", "홀 1, 2, 옥외2", "trade", 34300], + ["kx2017-040", "2017 플레이엑스포", "2017-05-25", "2017-05-28", "홀 6, 7, 8", "exhibition", 43200], + ["kx2017-041", "2017 경기국제보트쇼", "2017-05-25", "2017-05-28", "홀 3, 4, 5", "exhibition", 44000], + ["kx2017-042", "2017 트럭 & 버스 메가페어", "2017-05-25", "2017-05-28", "홀 옥외3", "culture", 18800], + ["kx2017-043", "신세계그룹&파트너사 채용박람회", "2017-05-31", "2017-05-31", "홀 1A", "exhibition", 4800], + ["kx2017-044", "2017 신세계그룹&파트너사 채용박람회", "2017-05-31", "2017-05-31", "홀 미정", "exhibition", 6200], + ["kx2017-045", "2017 더 골프 쇼", "2017-06-08", "2017-06-11", "홀 1", "exhibition", 21300], + ["kx2017-046", "대한민국세계여성발명대회여성발명품박람회", "2017-06-08", "2017-06-11", "홀 2A, 2B", "exhibition", 16500], + ["kx2017-047", "제2회 피트니스 · 보디빌딩 · 스포츠 박람회", "2017-06-09", "2017-06-11", "홀 7A, 6C", "exhibition", 24800], + ["kx2017-048", "2017 대한민국 명품특산물 페스티벌", "2017-06-12", "2017-06-18", "홀 10, 10", "culture", 26900], + ["kx2017-049", "2017 우수중소기업 마케팅대전", "2017-06-15", "2017-06-17", "홀 5A, 5B", "exhibition", 18100], + ["kx2017-050", "메가쇼 2017 시즌1", "2017-06-15", "2017-06-18", "홀 7", "exhibition", 17500], + ["kx2017-051", "국제 아웃도어 캠핑페스티발", "2017-06-16", "2017-06-18", "홀 9", "culture", 19300], + ["kx2017-052", "2017 KB굿잡 우수기업 취업박람회", "2017-06-22", "2017-06-23", "홀 2", "exhibition", 9600], + ["kx2017-053", "제25회 Mom & Baby EXPO 국제임신출산 유아교육박람회", "2017-06-22", "2017-06-25", "홀 7, 8", "culture", 31800], + ["kx2017-054", "2017 60+ 시니어 일자리 한마당", "2017-06-27", "2017-06-27", "홀 2A", "exhibition", 6500], + ["kx2017-055", "제15회 국제 LED & OLED EXPO 2017", "2017-06-27", "2017-06-29", "홀 3, 4", "exhibition", 31100], + ["kx2017-056", "2017 경기도 교통안전 박람회", "2017-06-28", "2017-06-29", "홀 5", "trade", 13000], + ["kx2017-057", "2017 로보유니버스 & K 드론", "2017-06-28", "2017-06-30", "홀 7B", "tech", 14000], + ["kx2017-058", "2017 VR 서밋", "2017-06-28", "2017-06-30", "홀 7A", "tech", 15400], + ["kx2017-059", "2017 인사이드 3D프린팅 컨퍼런스 & 엑스포", "2017-06-28", "2017-06-30", "홀 6", "exhibition", 16900], + ["kx2017-060", "2017 글로벌 센서 포럼", "2017-06-28", "2017-06-30", "홀 8A", "exhibition", 18700], + ["kx2017-061", "2017년 협동조합 박람회", "2017-06-30", "2017-07-02", "홀 2A, 2B", "exhibition", 25500], + ["kx2017-062", "FCI 인터내셔널 도그쇼", "2017-07-01", "2017-07-02", "홀 8B", "exhibition", 8900], + ["kx2017-063", "2017 생활 화학 안전주간 (2017 케미라이프엑스포)", "2017-07-02", "2017-07-04", "홀 5, 5", "trade", 18300], + ["kx2017-064", "대한치과기공사협회 제53차 국제학술대회(KDTEX 2017)", "2017-07-08", "2017-07-09", "홀 7", "exhibition", 7800], + ["kx2017-065", "제15회 국제 나노기술심포지엄 및 나노융합대전", "2017-07-12", "2017-07-14", "홀 7, 8A", "exhibition", 36000], + ["kx2017-066", "2017 케이팜(귀농귀촌박람회) / 친환경에코쇼", "2017-07-13", "2017-07-15", "홀 8B", "trade", 13400], + ["kx2017-067", "2017 코리아 하우징리빙페어", "2017-07-13", "2017-07-16", "홀 3", "culture", 23100], + ["kx2017-068", "제7회 2017 세계청소년교류엑스포", "2017-08-01", "2017-08-03", "홀 5A", "exhibition", 17100], + ["kx2017-069", "제21회 대한민국과학창의축전", "2017-08-10", "2017-08-15", "홀 3, 4A, 4B, 5", "exhibition", 63900], + ["kx2017-070", "2017 미베 베이비엑스포 / 미래 유아교육박람회", "2017-08-18", "2017-08-21", "홀 3, 4", "culture", 30100], + ["kx2017-071", "제15회 동아 홈&리빙페어회", "2017-08-24", "2017-08-27", "홀 5A", "culture", 19300], + ["kx2017-072", "제12회 동아 기프트쇼 / 제10회 동아 차·공예박람회", "2017-08-24", "2017-08-27", "홀 5B", "culture", 24100], + ["kx2017-073", "2017 서울 국제 안경산업박람회", "2017-08-24", "2017-08-27", "홀 1", "exhibition", 20900], + ["kx2017-074", "제2회 대한민국 국제 관광박람회", "2017-08-24", "2017-08-27", "홀 7", "exhibition", 20400], + ["kx2017-075", "제9회 국제접착코팅소재산업전시회 (2017 아템페어)", "2017-08-30", "2017-09-01", "홀 7A", "trade", 31800], + ["kx2017-076", "국제음향·영상산업전 2017", "2017-08-30", "2017-09-01", "홀 8A", "trade", 18900], + ["kx2017-077", "대한민국 교회건축·리모델링박람회 2017", "2017-08-30", "2017-09-01", "홀 8B", "exhibition", 25500], + ["kx2017-078", "한국국제가구 및 인테리어산업대전", "2017-08-30", "2017-09-03", "홀 1, 2, 3", "trade", 91900], + ["kx2017-079", "2017 대한민국 행정 홍보대전", "2017-08-31", "2017-09-03", "홀 4A, 4B, 5A, 5B", "exhibition", 41200], + ["kx2017-080", "2017년 한국관상어산업박람회 및 관상어품평회", "2017-09-01", "2017-09-03", "홀 7B", "exhibition", 12700], + ["kx2017-081", "2017 스마트시티 이노베이션 서밋 아시아", "2017-09-06", "2017-09-08", "홀 8", "tech", 13000], + ["kx2017-082", "2017 세계태양에너지엑스포", "2017-09-06", "2017-09-08", "홀 1", "trade", 12300], + ["kx2017-083", "제10회 국제자원순환산업전", "2017-09-06", "2017-09-08", "홀 7A", "trade", 15600], + ["kx2017-084", "2017 한국 중장비 전시회", "2017-09-06", "2017-09-08", "홀 7B", "trade", 15400], + ["kx2017-085", "2017 오토모티브위크", "2017-09-08", "2017-09-10", "홀 2, 5B, 3, 4", "exhibition", 57100], + ["kx2017-086", "2017 친환경 탈것 한마당 (에코비히클쇼)", "2017-09-08", "2017-09-10", "홀 5A", "trade", 13500], + ["kx2017-087", "2017 오토모티브위크 사전등록", "2017-09-08", "2017-09-10", "홀 2, 3, 4, 5B", "exhibition", 61400], + ["kx2017-088", "2017 친환경 탈것 한마당 사전등록", "2017-09-08", "2017-09-10", "홀 5A", "trade", 13200], + ["kx2017-089", "2017 글로벌소재부품산업대전(IMAC 2017)", "2017-09-13", "2017-09-15", "홀 3", "trade", 13300], + ["kx2017-090", "2017 글로벌모바일비전(GMV 2017)", "2017-09-13", "2017-09-15", "홀 4", "exhibition", 13200], + ["kx2017-091", "2017 로보월드", "2017-09-13", "2017-09-16", "홀 1A, 2, 1B", "exhibition", 49000], + ["kx2017-092", "케이샵 2017", "2017-09-14", "2017-09-16", "홀 8A", "exhibition", 10700], + ["kx2017-093", "제41회 프랜차이즈산업박람회", "2017-09-14", "2017-09-16", "홀 7", "exhibition", 13200], + ["kx2017-094", "2017 대한민국에너지대전", "2017-09-19", "2017-09-22", "홀 4, 5", "trade", 29500], + ["kx2017-095", "한국포장전 2017", "2017-09-19", "2017-09-22", "홀 7, 8A", "exhibition", 45200], + ["kx2017-096", "제 17회 코베 베이비페어", "2017-09-21", "2017-09-24", "홀 2, 3", "culture", 28300], + ["kx2017-097", "2017 키덜트 & 코믹스, 캐릭터페어", "2017-09-21", "2017-09-24", "홀 미정", "culture", 16800], + ["kx2017-098", "제9회 대한민국 뷰티박람회", "2017-10-12", "2017-10-15", "홀 3, 4, 5", "culture", 52300], + ["kx2017-099", "2017 슈퍼맘페어(엄마들이 만든 따뜻한 마켓)", "2017-10-13", "2017-10-15", "홀 제1전시장 3, 4홀 출입구 야외광장", "culture", 17000], + ["kx2017-100", "제14회 한국 자동차산업 전시회", "2017-10-18", "2017-10-20", "홀 7, 8", "exhibition", 35400], + ["kx2017-101", "2017 카페&베이커리페어", "2017-10-18", "2017-10-21", "홀 9B, 10A", "culture", 29000], + ["kx2017-102", "2017 호텔쇼", "2017-10-18", "2017-10-21", "홀 10B", "exhibition", 20100], + ["kx2017-103", "2017 한국기계전", "2017-10-24", "2017-10-27", "홀 1A, 2, 3, 4, 5", "trade", 104300], + ["kx2017-104", "2017 서울국제공구전", "2017-10-24", "2017-10-27", "홀 1", "trade", 24000], + ["kx2017-105", "금속산업대전 2017", "2017-10-24", "2017-10-27", "홀 7, 8", "trade", 38000], + ["kx2017-106", "제54회 전국도서관대회", "2017-10-25", "2017-10-26", "홀 9B", "culture", 12600], + ["kx2017-107", "2017 대한민국우수상품전", "2017-11-01", "2017-11-04", "홀 1, 2, 3", "exhibition", 75600], + ["kx2017-108", "2017 진로직업체험박람회", "2017-11-02", "2017-11-04", "홀 9", "exhibition", 18000], + ["kx2017-109", "국제주방&욕실박람회", "2017-11-02", "2017-11-05", "홀 7", "exhibition", 26000], + ["kx2017-110", "디자인코리아 2017", "2017-11-08", "2017-11-12", "홀 4, 5", "exhibition", 60800], + ["kx2017-111", "시니어리빙&복지박람회", "2017-11-09", "2017-11-11", "홀 2, 3", "culture", 28100], + ["kx2017-112", "마이스타일 트렌드페어 2017", "2017-11-09", "2017-11-11", "홀 1A, 1B", "culture", 12800], + ["kx2017-113", "제26회 국제임신출산_육아용품박람회(맘앤베이비엑스포)", "2017-11-09", "2017-11-12", "홀 7, 8", "culture", 33900], + ["kx2017-114", "코리아우드쇼 2017", "2017-11-09", "2017-11-12", "홀 10A", "exhibition", 26000], + ["kx2017-115", "2017 한국골프용품전", "2017-11-09", "2017-11-12", "홀 10B", "exhibition", 24600], + ["kx2017-116", "제3회 대한민국 안전산업박람회", "2017-11-15", "2017-11-17", "홀 2, 3B, 4", "trade", 38100], + ["kx2017-117", "2017 첨단농축산기자재전시회", "2017-11-15", "2017-11-17", "홀 9", "exhibition", 13400], + ["kx2017-118", "2017 국제도로교통박람회", "2017-11-15", "2017-11-17", "홀 1", "exhibition", 11400], + ["kx2017-119", "2017 기상기후산업 박람회", "2017-11-15", "2017-11-17", "홀 3A", "exhibition", 17300], + ["kx2017-120", "메가쇼 2017 시즌 II", "2017-11-16", "2017-11-19", "홀 7, 8A, 8B", "exhibition", 42200], + ["kx2017-121", "2017 대한민국 과일산업대전", "2017-11-17", "2017-11-19", "홀 10A", "trade", 12000], + ["kx2017-122", "2017 대한민국 미래교육박람회", "2017-11-23", "2017-11-25", "홀 3, 4", "exhibition", 34000], + ["kx2017-123", "한이음 엑스포 2017", "2017-11-24", "2017-11-25", "홀 1A", "exhibition", 12800], + ["kx2017-124", "SW 중심대학 전시관", "2017-11-24", "2017-11-25", "홀 1B", "exhibition", 11300], + ["kx2017-125", "2017 SW교육 페스티벌", "2017-11-24", "2017-11-25", "홀 2", "culture", 11600], + ["kx2017-126", "2017 대한민국펫산업박람회(2017 케이펫페어 일산)", "2017-11-24", "2017-11-26", "홀 7, 8", "culture", 23400], + ["kx2017-127", "2017 소재부품 뿌리산업 주간", "2017-11-28", "2017-11-30", "홀 4A", "trade", 10700], + ["kx2017-128", "2017 인사이드 핀테크 컨퍼런스 & 엑스포", "2017-11-30", "2017-12-01", "홀 6", "tech", 20900], + ["kx2017-129", "제10회 대한민국전시산업전", "2017-12-07", "2017-12-08", "홀 7A", "trade", 9700], + ["kx2017-130", "2017 경기환경산업전", "2017-12-08", "2017-12-09", "홀 7B", "trade", 11800], + ["kx2017-131", "[1부] 2017 코리아 크리스마스 페어", "2017-12-08", "2017-12-10", "홀 3, 4", "culture", 26900], + ["kx2017-132", "[1부] 2017 K-베이비키즈페어", "2017-12-08", "2017-12-10", "홀 5", "culture", 18200], + ["kx2017-133", "[2부] 2017 코리아 크리스마스 페어", "2017-12-15", "2017-12-17", "홀 3, 4", "culture", 36500], + ["kx2017-134", "[2부] 2017 K-베이비키즈페어", "2017-12-15", "2017-12-17", "홀 5", "culture", 13800], + ["kx2018-001", "2018 교육대전", "2018-01-11", "2018-01-12", "홀 3", "exhibition", 10100], + ["kx2018-002", "2018 한국골프산업박람회", "2018-01-24", "2018-01-26", "홀 7", "exhibition", 12700], + ["kx2018-003", "제27회 국제임신출산_육아용품박람회(맘앤베이비엑스포)", "2018-02-01", "2018-02-04", "홀 4, 5", "culture", 42000], + ["kx2018-004", "2018 오초 페스티벌 Lv.1", "2018-02-02", "2018-02-04", "홀 3A, 3B", "culture", 11400], + ["kx2018-005", "2018 자동차 경량화 기술 산업전", "2018-02-07", "2018-02-09", "홀 7B", "trade", 16200], + ["kx2018-006", "데이터실수정1001", "2018-02-17", "2018-02-17", "홀 미정", "exhibition", 3600], + ["kx2018-007", "2018 한국건축기계설비전시회", "2018-02-22", "2018-02-24", "홀 6", "trade", 12500], + ["kx2018-008", "대한민국건설기술산업대전", "2018-02-22", "2018-02-24", "홀 1전시장 그랜드볼륨", "trade", 15200], + ["kx2018-009", "2018 경향하우징페어", "2018-02-22", "2018-02-25", "홀 2, 3, 4, 5A", "culture", 76200], + ["kx2018-010", "2018 리빙앤라이프스타일", "2018-02-22", "2018-02-25", "홀 1", "culture", 21400], + ["kx2018-011", "제1회 대한민국 지방정부일자리 정책박람회", "2018-03-08", "2018-03-11", "홀 4, 5", "exhibition", 50500], + ["kx2018-012", "2018 SBS골프 대한민국 골프대전", "2018-03-08", "2018-03-11", "홀 2, 3", "exhibition", 47100], + ["kx2018-013", "2018 한국국제낚시박람회", "2018-03-09", "2018-03-11", "홀 9", "exhibition", 11100], + ["kx2018-014", "Automotive Testing Expo 2018", "2018-03-13", "2018-03-15", "홀 10", "exhibition", 18100], + ["kx2018-015", "세계보안엑스포 2018", "2018-03-14", "2018-03-16", "홀 4, 5", "exhibition", 26400], + ["kx2018-016", "2018 캠핑&피크닉 페어", "2018-03-15", "2018-03-18", "홀 7, 8", "culture", 43400], + ["kx2018-017", "제2회 코리아 스마트모빌리티 페어(코빌 2018)", "2018-03-16", "2018-03-18", "홀 3A, 3B", "tech", 17700], + ["kx2018-018", "2018 국제 물 산업 전시 및 컨퍼런스", "2018-03-20", "2018-03-23", "홀 10", "exhibition", 25800], + ["kx2018-019", "2018 전역예정장병 취업박람회", "2018-03-21", "2018-03-22", "홀 7", "exhibition", 7800], + ["kx2018-020", "2018 홈쇼핑·중소기업 상생 주간", "2018-03-22", "2018-03-25", "홀 미정", "culture", 22900], + ["kx2018-021", "2018 매경창업&프랜차이즈 쇼", "2018-03-22", "2018-03-25", "홀 3", "exhibition", 15000], + ["kx2018-022", "2018 스위트코리아", "2018-03-22", "2018-03-25", "홀 2", "exhibition", 14700], + ["kx2018-023", "서울국제공작기계전(SIMTOS 2018)", "2018-04-03", "2018-04-07", "홀 1, 2, 3, 4, 5, 7, 8, 9, 10", "trade", 189700], + ["kx2018-024", "제3회 국제화장품원료기술전", "2018-04-17", "2018-04-19", "홀 8B", "exhibition", 15400], + ["kx2018-025", "제8회 국제의약품·바이오산업전", "2018-04-17", "2018-04-19", "홀 8A", "trade", 11700], + ["kx2018-026", "제12회 국제연구·실험 및 첨단분석장비전", "2018-04-17", "2018-04-20", "홀 7", "exhibition", 20400], + ["kx2018-027", "제10회 국제화학장치산업전", "2018-04-17", "2018-04-20", "홀 5", "trade", 20000], + ["kx2018-028", "제21회 국제포장기자재전(KOREA PACK 2018)", "2018-04-17", "2018-04-20", "홀 1, 2, 3", "exhibition", 48300], + ["kx2018-029", "제13회 국제제약∙바이오∙화장품기술전", "2018-04-17", "2018-04-20", "홀 4", "exhibition", 20500], + ["kx2018-030", "잡월드 ROADSHOW「내일희망 채용박람회」", "2018-04-20", "2018-04-20", "홀 6C", "exhibition", 4300], + ["kx2018-031", "2018 국제전자회로산업전", "2018-04-24", "2018-04-26", "홀 7A", "tech", 11300], + ["kx2018-032", "2018 국제모션컨트롤 및 머신비전산업전", "2018-04-24", "2018-04-26", "홀 7B", "trade", 15600], + ["kx2018-033", "2018 코리아 나라장터 엑스포", "2018-04-25", "2018-04-27", "홀 4, 5", "exhibition", 22100], + ["kx2018-034", "2018 서울국제식품산업대전", "2018-05-01", "2018-05-04", "홀 7, 8, 1, 2, 3, 4, 5, 6A, 6B, 6C", "trade", 207500], + ["kx2018-035", "제18회 동아 홈&리빙페어 / 제14회 기프트쇼", "2018-05-10", "2018-05-13", "홀 5", "culture", 15200], + ["kx2018-036", "제10회 동아 가구엑스포 / 제12회 동아 차・공예 박람회", "2018-05-10", "2018-05-13", "홀 4", "culture", 25200], + ["kx2018-037", "제11회 동아 스포츠·레저산업 박람회", "2018-05-10", "2018-05-13", "홀 1", "culture", 21700], + ["kx2018-038", "2018 PlayX4(플레이엑스포)", "2018-05-10", "2018-05-13", "홀 7, 8A, 6, 8B", "exhibition", 47300], + ["kx2018-039", "2018 경기국제보트쇼", "2018-05-24", "2018-05-27", "홀 2, 3, 4, 5", "exhibition", 60200], + ["kx2018-040", "제28회 국제임신출산_육아용품박람회(맘앤베이비엑스포)", "2018-05-31", "2018-06-03", "홀 4, 5", "culture", 53600], + ["kx2018-041", "2018 더 골프 쇼", "2018-06-07", "2018-06-10", "홀 10A", "exhibition", 20200], + ["kx2018-042", "GFood 아울렛 2018", "2018-06-07", "2018-06-10", "홀 1전시장 로비2", "exhibition", 15400], + ["kx2018-043", "2018 하나투어 여행박람회", "2018-06-07", "2018-06-10", "홀 1, 2, 3", "culture", 54700], + ["kx2018-044", "2018 국제아웃도어캠핑&레포츠페스티벌", "2018-06-08", "2018-06-10", "홀 9", "culture", 13500], + ["kx2018-045", "2018 세계태양에너지엑스포", "2018-06-14", "2018-06-16", "홀 5A", "trade", 16600], + ["kx2018-046", "2018 세계탄소제로엑스포", "2018-06-14", "2018-06-16", "홀 5B", "exhibition", 16600], + ["kx2018-047", "2018 우수 중소기업 마케팅대전", "2018-06-14", "2018-06-16", "홀 8B", "exhibition", 11100], + ["kx2018-048", "메가쇼 2018 시즌 1", "2018-06-14", "2018-06-17", "홀 7, 8A", "exhibition", 31100], + ["kx2018-049", "2018 Sea Farm Show 해양수산·양식·식품 박람회", "2018-06-15", "2018-06-17", "홀 10A", "trade", 19200], + ["kx2018-050", "2018 대한민국 식초문화대전", "2018-06-22", "2018-06-23", "홀 2A, 2B", "culture", 8600], + ["kx2018-051", "2018 고양 FCI 국제 도그쇼", "2018-06-23", "2018-06-24", "홀 9A", "exhibition", 10400], + ["kx2018-052", "제16회 국제 LED & OLED EXPO 2018", "2018-06-26", "2018-06-28", "홀 3, 4", "exhibition", 25900], + ["kx2018-053", "인사이드 3D프린팅 컨퍼런스 & 엑스포 2018", "2018-06-27", "2018-06-29", "홀 5", "exhibition", 13900], + ["kx2018-054", "테크노스케이프 아시아 2018", "2018-06-28", "2018-06-30", "홀 미정", "tech", 11500], + ["kx2018-055", "2018 글로벌센서포럼(GSF)", "2018-06-28", "2018-06-30", "홀 8", "exhibition", 10700], + ["kx2018-056", "2018 VR 서밋", "2018-06-28", "2018-06-30", "홀 7B", "tech", 11700], + ["kx2018-057", "2018 로보유니버스 & K 드론", "2018-06-28", "2018-06-30", "홀 7A", "tech", 18700], + ["kx2018-058", "2018 대한민국 세게여성발명대회 및 여성발명품박람회(여성발명왕EXPO)", "2018-06-28", "2018-07-01", "홀 2", "exhibition", 23600], + ["kx2018-059", "제16회 국제 나노기술심포지엄 및 나노융합대전", "2018-07-11", "2018-07-13", "홀 7, 8A", "exhibition", 36900], + ["kx2018-060", "2018 친환경에코쇼/귀농·귀촌 박람회(Eco Show / K-Farm)", "2018-07-19", "2018-07-21", "홀 7", "trade", 15800], + ["kx2018-061", "2018 코리아하우징리빙페어", "2018-07-19", "2018-07-22", "홀 4", "culture", 25700], + ["kx2018-062", "2018 청소년 발명페스티벌", "2018-07-20", "2018-07-22", "홀 5", "culture", 19200], + ["kx2018-063", "KDTEX 2018 국제학술대회", "2018-07-21", "2018-07-22", "홀 8A, 8B", "exhibition", 12000], + ["kx2018-064", "2018 대한민국과학창의축전", "2018-08-09", "2018-08-12", "홀 3, 4", "exhibition", 51000], + ["kx2018-065", "2018 미베 베이비엑스포 / 미래 유아교육박람회", "2018-08-16", "2018-08-19", "홀 3, 4", "culture", 39400], + ["kx2018-066", "2018 한국관상어박람회 및 제4회 해양수산부장관배 한국관상어품평회", "2018-08-17", "2018-08-19", "홀 5", "trade", 11200], + ["kx2018-067", "2018 아템페어 전시회(제 10회 국제 접착·코팅 소재 산업전시회)", "2018-08-22", "2018-08-24", "홀 7A", "trade", 11500], + ["kx2018-068", "한국표면처리산업전", "2018-08-22", "2018-08-24", "홀 미정", "trade", 15600], + ["kx2018-069", "2018 서울 국제 안경산업 박람회", "2018-08-23", "2018-08-26", "홀 미정", "exhibition", 21000], + ["kx2018-070", "제12회 스포츠·레저산업 박람회", "2018-08-23", "2018-08-26", "홀 1", "culture", 15400], + ["kx2018-071", "제15회 기프트·차공예 박람회", "2018-08-23", "2018-08-26", "홀 2B", "culture", 15900], + ["kx2018-072", "제 19회 동아 홈&리빙페어", "2018-08-23", "2018-08-26", "홀 2A", "culture", 24300], + ["kx2018-073", "2018 동아 냉·난방 및 건축설비 박람회", "2018-08-23", "2018-08-26", "홀 3B", "exhibition", 25800], + ["kx2018-074", "제48회 MBC건축박람회", "2018-08-23", "2018-08-26", "홀 3A, 4", "exhibition", 38800], + ["kx2018-075", "2018 정원 및 조경박람회", "2018-08-23", "2018-08-26", "홀 5A, 5B", "exhibition", 23600], + ["kx2018-076", "K-Print 2018", "2018-08-25", "2018-09-01", "홀 미정", "exhibition", 32300], + ["kx2018-077", "케이샵 2018", "2018-08-29", "2018-08-31", "홀 5", "exhibition", 11900], + ["kx2018-078", "제 29회 한국국제가구 및 인테리어산업대전", "2018-08-29", "2018-09-02", "홀 1A, 1B, 2, 3", "trade", 86000], + ["kx2018-079", "2018 글로벌 비즈니스 소싱페어", "2018-08-30", "2018-08-31", "홀 4", "culture", 9300], + ["kx2018-080", "2018 경기도 교통안전 박람회", "2018-09-05", "2018-09-06", "홀 4", "trade", 11800], + ["kx2018-081", "2018 한국 중장비 부품 및 어태치먼트 전시회 (KOHES)", "2018-09-05", "2018-09-07", "홀 7B", "trade", 12000], + ["kx2018-082", "제11회 국제자원순환산업전 (Re-Tech)", "2018-09-05", "2018-09-07", "홀 7A", "trade", 19100], + ["kx2018-083", "국제음향·영상산업전 2018", "2018-09-05", "2018-09-07", "홀 8B", "trade", 12300], + ["kx2018-084", "대한민국 교회건축리모델링박람회 2018", "2018-09-05", "2018-09-07", "홀 8A", "exhibition", 18300], + ["kx2018-085", "대한민국 국제 관광박람회", "2018-09-06", "2018-09-09", "홀 2", "exhibition", 19900], + ["kx2018-086", "한국국제전자담배박람회 2018", "2018-09-07", "2018-09-09", "홀 3", "tech", 16100], + ["kx2018-087", "2018 국제자동화정밀기기전(KOFAS-Seoul 2018)", "2018-09-11", "2018-09-14", "홀 4, 5", "exhibition", 33900], + ["kx2018-088", "2018 대한민국 방위산업전(DX KOREA)", "2018-09-12", "2018-09-16", "홀 7, 8", "trade", 36800], + ["kx2018-089", "2018 킨텍스 디지털 헬스케어 페어", "2018-09-13", "2018-09-16", "홀 3", "tech", 20000], + ["kx2018-090", "FINE FOOD FAIR 2018", "2018-09-14", "2018-09-16", "홀 2", "tech", 18000], + ["kx2018-091", "2018 매경 창업&프랜차이즈 쇼", "2018-09-14", "2018-09-16", "홀 1", "exhibition", 17700], + ["kx2018-092", "스마트시티 서밋 아시아 2018", "2018-09-18", "2018-09-20", "홀 4, 5", "tech", 33900], + ["kx2018-093", "제2회 월드 스마트시티 위크(WSCW)", "2018-09-18", "2018-09-20", "홀 옥외2", "tech", 11900], + ["kx2018-094", "대한민국 에너지대전", "2018-10-02", "2018-10-05", "홀 1, 2", "trade", 46700], + ["kx2018-095", "20회 킨텍스 코베 베이비페어", "2018-10-04", "2018-10-07", "홀 3, 4", "culture", 43600], + ["kx2018-096", "2018 호텔쇼", "2018-10-10", "2018-10-13", "홀 7", "exhibition", 24600], + ["kx2018-097", "2018 로보월드", "2018-10-10", "2018-10-13", "홀 1, 2", "exhibition", 49400], + ["kx2018-098", "2018 대한민국 뷰티박람회(10회)", "2018-10-11", "2018-10-14", "홀 3, 4, 5", "culture", 53400], + ["kx2018-099", "2018 카페&베이커리페어", "2018-10-11", "2018-10-14", "홀 8", "culture", 18500], + ["kx2018-100", "2018 한국골프용품전", "2018-10-11", "2018-10-14", "홀 10B", "exhibition", 18300], + ["kx2018-101", "2018 교육대전", "2018-10-18", "2018-10-20", "홀 9", "exhibition", 15200], + ["kx2018-102", "2018 중·장기복무 제대군인 일자리 박람회", "2018-10-19", "2018-10-19", "홀 6C", "exhibition", 5100], + ["kx2018-103", "2018 오토위크", "2018-10-19", "2018-10-21", "홀 1, 2, 3", "exhibition", 32800], + ["kx2018-104", "2018 경기환경전 & 에코 비히클쇼", "2018-10-19", "2018-10-21", "홀 4", "trade", 14300], + ["kx2018-105", "2018 오토위크(자동차 업계 종사자)", "2018-10-19", "2018-10-21", "홀 1전시장 로비3", "exhibition", 17400], + ["kx2018-106", "2018 소재부품뿌리산업주간", "2018-10-23", "2018-10-25", "홀 5", "trade", 19300], + ["kx2018-107", "2018 진로직업체험박람회", "2018-10-24", "2018-10-26", "홀 7", "exhibition", 15500], + ["kx2018-108", "2018 대한민국우수상품전(G-Fair Korea)", "2018-10-24", "2018-10-27", "홀 1, 2, 3", "exhibition", 67100], + ["kx2018-109", "2018 서울국제공구전", "2018-10-30", "2018-11-02", "홀 1", "trade", 32300], + ["kx2018-110", "금속산업대전 2018", "2018-10-30", "2018-11-02", "홀 2, 3", "trade", 39600], + ["kx2018-111", "2018 국제철강 및 비철금속산업전", "2018-10-31", "2018-11-02", "홀 10", "trade", 32100], + ["kx2018-112", "디자인코리아 2018", "2018-10-31", "2018-11-04", "홀 4, 5", "exhibition", 49000], + ["kx2018-113", "월드 할랄 데이 2018 서울", "2018-11-01", "2018-11-03", "홀 미정", "exhibition", 15900], + ["kx2018-114", "2018 공학교육페스티벌", "2018-11-08", "2018-11-09", "홀 7B, 8A", "culture", 20600], + ["kx2018-115", "2018 시니어 리빙&복지 박람회(SENDEX 2018)", "2018-11-08", "2018-11-10", "홀 4B, 5", "culture", 30500], + ["kx2018-116", "2018 청소년 비즈쿨 페스티벌", "2018-11-08", "2018-11-10", "홀 7A", "culture", 13900], + ["kx2018-117", "2018 메디&슬립 코리아", "2018-11-08", "2018-11-10", "홀 4A", "exhibition", 10900], + ["kx2018-118", "제 29회 국제임신출산_육아용품박람회(맘앤베이비엑스포)", "2018-11-08", "2018-11-11", "홀 1, 2", "culture", 30600], + ["kx2018-119", "2018 코리아 렌탈쇼", "2018-11-08", "2018-11-11", "홀 10", "exhibition", 17300], + ["kx2018-120", "제4회 대한민국 안전산업박람회", "2018-11-14", "2018-11-16", "홀 3A, 4, 2", "trade", 34700], + ["kx2018-121", "2018 한국국제승강기엑스포", "2018-11-14", "2018-11-16", "홀 5", "trade", 11200], + ["kx2018-122", "2018 국제봉제기계&섬유산업전시회", "2018-11-14", "2018-11-16", "홀 9A, 9B", "trade", 11200], + ["kx2018-123", "2018 기상기후산업 박람회", "2018-11-14", "2018-11-16", "홀 3B", "exhibition", 13700], + ["kx2018-124", "메가쇼 2018 시즌2", "2018-11-15", "2018-11-18", "홀 7, 8", "exhibition", 32600], + ["kx2018-125", "2018 대한민국 과일산업대전", "2018-11-16", "2018-11-18", "홀 10A", "trade", 14000], + ["kx2018-126", "남양주 라온프라이빗 입주박람회", "2018-11-17", "2018-11-18", "홀 10B", "exhibition", 7200], + ["kx2018-127", "2018 제7회 대한민국 교육기부 박람회", "2018-11-22", "2018-11-25", "홀 1, 2", "exhibition", 34300], + ["kx2018-128", "2018 대한민국펫산업박람회(케이펫페어 일산)", "2018-11-23", "2018-11-25", "홀 7, 8", "culture", 35500], + ["kx2018-129", "인사이드 핀테크 컨퍼런스 & 엑스포 2018", "2018-11-29", "2018-11-30", "홀 6", "tech", 11200], + ["kx2018-130", "한이음 엑스포 2018", "2018-11-30", "2018-12-01", "홀 3A", "exhibition", 26500], + ["kx2018-131", "SW중심대학전시관", "2018-11-30", "2018-12-01", "홀 3B", "exhibition", 30500], + ["kx2018-132", "제6회 월드식품박람회 2018", "2018-11-30", "2018-12-02", "홀 7A", "culture", 21400], + ["kx2018-133", "제4회 월드와인 및 주류 박람회 2018", "2018-11-30", "2018-12-02", "홀 7B", "exhibition", 27600], + ["kx2018-134", "[1부] 2018 코리아 크리스마스페어 & 기프트 원더랜드", "2018-12-07", "2018-12-09", "홀 3, 4", "culture", 25000], + ["kx2018-135", "[1부] K-베이비페어", "2018-12-07", "2018-12-09", "홀 5", "culture", 17200], + ["kx2018-136", "[2부] 2018 코리아 크리스마스페어 & 기프트 원더랜드", "2018-12-14", "2018-12-16", "홀 3, 4", "culture", 21400], + ["kx2018-137", "[2부] K-베이비페어", "2018-12-14", "2018-12-16", "홀 5", "culture", 15700], + ["kx2019-001", "2019 K-웰니스 설날 착한선물전", "2019-01-25", "2019-01-27", "홀 3A", "exhibition", 15400], + ["kx2019-002", "제30회 국제임신출산_육아용품박람회(맘앤베이비엑스포)", "2019-02-14", "2019-02-17", "홀 7, 8", "culture", 47800], + ["kx2019-003", "2019 코리아빌드위크(코리아빌드)", "2019-02-20", "2019-02-24", "홀 1B, 2, 3, 4, 5B", "exhibition", 124000], + ["kx2019-004", "2019 클린에어엑스포", "2019-02-20", "2019-02-24", "홀 5A", "exhibition", 28800], + ["kx2019-005", "2019 코리아빌드위크(인테리어디자인코리아)", "2019-02-20", "2019-02-24", "홀 7, 8A", "culture", 54300], + ["kx2019-006", "2019 코리아빌드위크(2019 경향부동산페어/대한민국건설산업대전)", "2019-02-20", "2019-02-24", "홀 1A", "trade", 25300], + ["kx2019-007", "2019 캠핑&피크닉페어", "2019-02-28", "2019-03-03", "홀 7, 8", "culture", 41900], + ["kx2019-008", "2019 SBS골프 대한민국 골프대전", "2019-02-28", "2019-03-03", "홀 2, 3", "exhibition", 63100], + ["kx2019-009", "Automotive Testing Expo 2019", "2019-03-06", "2019-03-08", "홀 7A", "exhibition", 19200], + ["kx2019-010", "제15회 한국국제냉난방공조전", "2019-03-12", "2019-03-15", "홀 7, 8", "trade", 41200], + ["kx2019-011", "제24회 국제금형 및 관련기기전", "2019-03-12", "2019-03-16", "홀 1B, 2B, 1A, 2A, 3B", "trade", 94000], + ["kx2019-012", "제25회 국제플라스틱⋅고무산업전시회 (KOPLAS 2019)", "2019-03-12", "2019-03-16", "홀 4, 5", "trade", 38400], + ["kx2019-013", "국제 플라스틱+고무소재 및 복합재료 전시회 (M-KOPLAS 2019)", "2019-03-12", "2019-03-16", "홀 3A", "trade", 18800], + ["kx2019-014", "2019 전역예정장병 취업박람회", "2019-03-20", "2019-03-21", "홀 7", "exhibition", 11000], + ["kx2019-015", "2019서울모터쇼", "2019-03-28", "2019-04-07", "홀 1, 2, 3, 4, 5, 9, 10", "exhibition", 210300], + ["kx2019-016", "2019서울모터쇼(참가업체용)", "2019-03-28", "2019-04-07", "홀 1전시장 로비1", "exhibition", 21600], + ["kx2019-017", "2019 프리미엄펫쇼", "2019-04-12", "2019-04-14", "홀 7", "culture", 12500], + ["kx2019-018", "제약화장품산업기술주간 2019(제9회 국제 의약품 · 바이오산업전)", "2019-04-16", "2019-04-18", "홀 5A, 5B", "trade", 18700], + ["kx2019-019", "제약화장품산업기술주간 2019(제9회 국제물류산업대전, 제 14회 제약바이오화장품기술전, 제11회 국제화학장치산업전)", "2019-04-16", "2019-04-19", "홀 1, 2, 3", "trade", 73200], + ["kx2019-020", "제약화장품산업기술주간 2019(제13회 국제연구실험 및 첨단분석장비전, 제4회 국제화장품원료기술전)", "2019-04-16", "2019-04-19", "홀 4", "exhibition", 24900], + ["kx2019-021", "2019 매경 창업&프랜차이즈 쇼", "2019-04-18", "2019-04-20", "홀 7A", "exhibition", 19200], + ["kx2019-022", "코리아 나라장터 엑스포 2019", "2019-04-24", "2019-04-26", "홀 3, 4, 5", "exhibition", 51400], + ["kx2019-023", "2019 국제전자회로 및 실장산업전", "2019-04-24", "2019-04-26", "홀 7", "tech", 10700], + ["kx2019-024", "제27회 코베 베이비페어", "2019-04-25", "2019-04-28", "홀 1, 2", "culture", 45000], + ["kx2019-025", "제50회 MBC 건축박람회", "2019-05-02", "2019-05-05", "홀 1, 2, 3, 4, 5", "exhibition", 93300], + ["kx2019-026", "2019 서울캣쇼", "2019-05-03", "2019-05-05", "홀 7A", "exhibition", 11800], + ["kx2019-027", "플레이엑스포 2019", "2019-05-09", "2019-05-12", "홀 6, 7, 8", "exhibition", 48200], + ["kx2019-028", "2019 경기국제보트쇼", "2019-05-09", "2019-05-12", "홀 3, 4, 5", "exhibition", 51500], + ["kx2019-029", "서울 슈즈 앤 백 박람회", "2019-05-10", "2019-05-12", "홀 1", "exhibition", 17600], + ["kx2019-030", "2019 경기국제웹툰페어", "2019-05-10", "2019-05-12", "홀 10A", "culture", 14300], + ["kx2019-031", "2019 서울국제식품산업대전", "2019-05-21", "2019-05-24", "홀 1, 2, 3, 4, 5, 6, 7, 8", "trade", 169200], + ["kx2019-032", "2019 국토교통기술대전", "2019-05-22", "2019-05-24", "홀 미정", "exhibition", 13600], + ["kx2019-033", "2019 국토교통기술대전", "2019-05-29", "2019-05-31", "홀 10A, 10B", "exhibition", 13600], + ["kx2019-034", "2019 코리아렌탈쇼", "2019-06-06", "2019-06-09", "홀 5", "exhibition", 21000], + ["kx2019-035", "제31회 맘앤베이비엑스포", "2019-06-06", "2019-06-09", "홀 2, 3", "culture", 33300], + ["kx2019-036", "2019 더 골프쇼", "2019-06-06", "2019-06-09", "홀 10A", "exhibition", 19800], + ["kx2019-037", "2019 하나투어 여행박람회", "2019-06-07", "2019-06-09", "홀 7, 8", "culture", 36200], + ["kx2019-038", "2019 국제아웃도어캠핑&레포츠페스티벌", "2019-06-07", "2019-06-09", "홀 9", "culture", 13100], + ["kx2019-039", "2019 귀어귀촌 박람회", "2019-06-07", "2019-06-09", "홀 10B", "exhibition", 16100], + ["kx2019-040", "2019 대한민국 마케팅 Fair", "2019-06-13", "2019-06-15", "홀 10B", "exhibition", 16900], + ["kx2019-041", "2019 고양 FCI 아시아, 아프리카 & 오세아니아 섹션 쇼", "2019-06-15", "2019-06-16", "홀 9", "exhibition", 9400], + ["kx2019-042", "2019 세계태양에너지엑스포", "2019-06-19", "2019-06-21", "홀 4A", "trade", 15900], + ["kx2019-043", "케이샵 2019", "2019-06-19", "2019-06-21", "홀 1", "exhibition", 15700], + ["kx2019-044", "2019 세계에너지저장시스템엑스포", "2019-06-19", "2019-06-21", "홀 5A", "trade", 17900], + ["kx2019-045", "2019 세계탄소제로엑스포", "2019-06-19", "2019-06-21", "홀 4B, 5B", "exhibition", 28600], + ["kx2019-046", "2019 해양수산·양식·식품 수출박람회(2019 Sea Farm Show)", "2019-06-20", "2019-06-22", "홀 9A", "trade", 15800], + ["kx2019-047", "여성발명왕 Expo", "2019-06-20", "2019-06-23", "홀 9B", "exhibition", 14400], + ["kx2019-048", "메가쇼 2019 시즌1", "2019-06-20", "2019-06-23", "홀 7, 8", "exhibition", 41800], + ["kx2019-049", "2019 국제 LED & OLED EXPO", "2019-06-25", "2019-06-27", "홀 3, 4", "exhibition", 30300], + ["kx2019-050", "인사이드 3D프린팅 2019", "2019-06-26", "2019-06-28", "홀 5", "exhibition", 18400], + ["kx2019-051", "나노코리아 2019", "2019-07-03", "2019-07-05", "홀 4, 5", "exhibition", 37300], + ["kx2019-052", "창립 50주년 기념 고속도로 특별박람회", "2019-07-04", "2019-07-05", "홀 6A, 6B", "exhibition", 8500], + ["kx2019-053", "제2회 한국국제전자담배박람회", "2019-07-05", "2019-07-07", "홀 7", "tech", 12700], + ["kx2019-054", "케이팜 귀농귀촌박람회 2019", "2019-07-18", "2019-07-20", "홀 7", "exhibition", 15000], + ["kx2019-055", "2019 대한민국생활낚시박람회", "2019-07-19", "2019-07-21", "홀 4A, 4B", "exhibition", 11300], + ["kx2019-056", "2019 청소년 발명 페스티벌", "2019-07-25", "2019-07-27", "홀 4", "culture", 13600], + ["kx2019-057", "2019 미베 베이비엑스포 / 미래유아교육박람회", "2019-08-15", "2019-08-18", "홀 4, 5", "culture", 30200], + ["kx2019-058", "제4회 대한민국 국제 관광박람회", "2019-08-15", "2019-08-18", "홀 3", "exhibition", 23600], + ["kx2019-059", "2019 한국관상어산업박람회 및 제5회 해양수산부장관배 한국관상어품평회", "2019-08-16", "2019-08-18", "홀 7A", "trade", 12900], + ["kx2019-060", "제51회 MBC건축박람회", "2019-08-22", "2019-08-25", "홀 3, 4, 1, 2, 5A", "exhibition", 93400], + ["kx2019-061", "제12회 국제자원순환산업전/2019 한국 중장비부품 및 어태치먼트 전시회", "2019-08-28", "2019-08-30", "홀 7", "trade", 13500], + ["kx2019-062", "2019 로보유니버스 & K드론", "2019-08-28", "2019-08-30", "홀 미정", "tech", 15400], + ["kx2019-063", "2019 KOFURN(한국국제가구 및 인테리어산업대전)/한국국제목공기계전시회", "2019-08-29", "2019-09-01", "홀 3, 5, 4", "trade", 71800], + ["kx2019-064", "2019 대한민국 에너지대전", "2019-09-03", "2019-09-06", "홀 1, 2", "trade", 28300], + ["kx2019-065", "2019 월드 스마트시티 엑스포", "2019-09-04", "2019-09-06", "홀 3, 4, 5", "tech", 43800], + ["kx2019-066", "2019 전역예정장병 취업박람회", "2019-09-05", "2019-09-05", "홀 6", "exhibition", 5500], + ["kx2019-067", "2019 한국골프용품전 in Summer", "2019-09-05", "2019-09-08", "홀 7A", "exhibition", 25700], + ["kx2019-068", "대한민국 음향음악산업전 2019", "2019-09-18", "2019-09-20", "홀 8A", "trade", 15800], + ["kx2019-069", "2019 대한민국 교회건축&리모델링 박람회", "2019-09-18", "2019-09-20", "홀 미정", "exhibition", 18600], + ["kx2019-070", "DMZ 페스타", "2019-09-18", "2019-09-21", "홀 5", "exhibition", 17500], + ["kx2019-071", "2019 매경 창업 & 프랜차이즈 쇼", "2019-09-19", "2019-09-21", "홀 4A", "exhibition", 14100], + ["kx2019-072", "2019 티몬국제어린이초등교육박람회", "2019-09-19", "2019-09-22", "홀 7A", "exhibition", 16900], + ["kx2019-073", "제5회 대한민국 안전산업박람회", "2019-09-25", "2019-09-27", "홀 1B, 2, 3, 4B", "trade", 51900], + ["kx2019-074", "2019 기상기후산업 박람회", "2019-09-25", "2019-09-27", "홀 1", "exhibition", 17100], + ["kx2019-075", "2019 국제도로교통박람회", "2019-09-25", "2019-09-27", "홀 5", "exhibition", 11800], + ["kx2019-076", "2019 한국건설안전박람회", "2019-09-25", "2019-09-27", "홀 1A", "trade", 17200], + ["kx2019-077", "2019 경기환경산업전", "2019-10-01", "2019-10-02", "홀 5", "trade", 10800], + ["kx2019-078", "제28회 코베 베이비페어", "2019-10-03", "2019-10-06", "홀 미정", "culture", 14900], + ["kx2019-079", "2019 코베 유아교육전", "2019-10-03", "2019-10-06", "홀 7, 8", "culture", 50900], + ["kx2019-080", "2019 오토살롱위크", "2019-10-03", "2019-10-06", "홀 1, 2, 3, 4", "exhibition", 60100], + ["kx2019-081", "2019 호텔쇼&레스토랑 산업전", "2019-10-09", "2019-10-12", "홀 8, 7", "trade", 42600], + ["kx2019-082", "2019 로보월드", "2019-10-09", "2019-10-12", "홀 1, 2", "exhibition", 34900], + ["kx2019-083", "2019 카페&베이커리페어", "2019-10-09", "2019-10-12", "홀 옥외", "culture", 23300], + ["kx2019-084", "2019 대한민국 뷰티박람회", "2019-10-10", "2019-10-13", "홀 3, 4, 5", "culture", 67800], + ["kx2019-085", "한국포장전 2019", "2019-10-15", "2019-10-18", "홀 7, 8", "exhibition", 51300], + ["kx2019-086", "2019 한국산업대전 (한국기계전 & 머신소프트)", "2019-10-22", "2019-10-25", "홀 2, 3, 4, 5", "trade", 70900], + ["kx2019-087", "2019 한국산업대전 (2019 서울국제공구전)", "2019-10-22", "2019-10-25", "홀 1A, 1B", "trade", 24200], + ["kx2019-088", "2019 교육기부박람회", "2019-10-23", "2019-10-26", "홀 7", "exhibition", 14200], + ["kx2019-089", "2019 대한민국 미래교육 한마당", "2019-10-23", "2019-10-26", "홀 8A", "exhibition", 25100], + ["kx2019-090", "2019 한국골프용품전", "2019-10-24", "2019-10-27", "홀 10B", "exhibition", 22300], + ["kx2019-091", "2019 첨단소재부품뿌리산업기술대전", "2019-10-30", "2019-11-01", "홀 7", "trade", 28200], + ["kx2019-092", "대한민국우수상품전시회(G-FAIR KOREA 2019)", "2019-10-31", "2019-11-03", "홀 1, 2, 3", "tech", 74900], + ["kx2019-093", "2019 산학협력 EXPO", "2019-11-06", "2019-11-08", "홀 4", "exhibition", 18400], + ["kx2019-094", "2019 시니어 라이프 & 복지 박람회", "2019-11-06", "2019-11-08", "홀 3", "exhibition", 14200], + ["kx2019-095", "디자인코리아 페스티벌 2019", "2019-11-06", "2019-11-10", "홀 5", "culture", 20300], + ["kx2019-096", "제4회 2019 경기도 교통안전 박람회", "2019-11-08", "2019-11-09", "홀 7A", "trade", 9200], + ["kx2019-097", "메가쇼 2019 시즌2", "2019-11-14", "2019-11-17", "홀 2, 3, 4", "exhibition", 52500], + ["kx2019-098", "2019 대한민국 과일산업대전", "2019-11-15", "2019-11-17", "홀 미정", "trade", 14400], + ["kx2019-099", "2019 대한민국 목재산업박람회", "2019-11-15", "2019-11-17", "홀 1", "exhibition", 13400], + ["kx2019-100", "2019 청정임산물대전", "2019-11-15", "2019-11-17", "홀 옥외", "exhibition", 16100], + ["kx2019-101", "제33회 맘앤베이비엑스포", "2019-11-21", "2019-11-24", "홀 1, 2", "culture", 37800], + ["kx2019-102", "제26회 국제유아교육박람회", "2019-11-21", "2019-11-24", "홀 옥외", "culture", 21000], + ["kx2019-103", "2019 케이펫페어 일산(대한민국펫산업박람회)", "2019-11-22", "2019-11-24", "홀 7, 8", "culture", 31000], + ["kx2019-104", "제7회 월드식품박람회 2019", "2019-11-29", "2019-12-01", "홀 7", "culture", 30800], + ["kx2019-105", "제5회 월드와인 및 주류박람회 2019", "2019-11-29", "2019-12-01", "홀 옥외", "exhibition", 29900], + ["kx2019-106", "테크핀 아시아 2019", "2019-12-05", "2019-12-06", "홀 6", "tech", 7400], + ["kx2019-107", "2019 코리아 크리스마스 페어", "2019-12-05", "2019-12-08", "홀 3, 4, 5, 6A", "culture", 75800], + ["kx2019-108", "2019 경기마켓데이", "2019-12-05", "2019-12-08", "홀 옥외", "exhibition", 25700], + ["kx2019-109", "2019 대한민국 드론박람회", "2019-12-11", "2019-12-12", "홀 4A", "tech", 8100], + ["kx2019-110", "LH하우징 플랫폼 페스타", "2019-12-18", "2019-12-20", "홀 3A, 3A", "exhibition", 11100], + ["kx2019-111", "2019 코리아 프리미어 도그쇼", "2019-12-20", "2019-12-22", "홀 8B", "exhibition", 13500], + ["kx2020-001", "제3회 2020 K-웰니스 착한선물전", "2020-01-10", "2020-01-12", "홀 3A", "exhibition", 17100], + ["kx2020-002", "제53회 MBC건축박람회", "2020-05-08", "2020-05-11", "홀 2A, 2B, 1, 3", "exhibition", 43900], + ["kx2020-003", "2020 서울 캣 쇼", "2020-05-29", "2020-05-31", "홀 4", "exhibition", 16400], + ["kx2020-004", "2020 더골프쇼 KOREA", "2020-06-04", "2020-06-07", "홀 10A", "exhibition", 23100], + ["kx2020-005", "2020 한국국제낚시박람회", "2020-06-05", "2020-06-07", "홀 3", "exhibition", 18600], + ["kx2020-006", "2020 국제아웃도어캠핑&레포츠페스티벌", "2020-06-05", "2020-06-07", "홀 9", "culture", 11400], + ["kx2020-007", "마이다이어트페어 2020", "2020-06-11", "2020-06-14", "홀 미정", "culture", 19900], + ["kx2020-008", "2020 프리미엄 펫쇼", "2020-06-12", "2020-06-14", "홀 9B", "culture", 11500], + ["kx2020-009", "2020 전역예정장병 취업박람회", "2020-06-17", "2020-06-18", "홀 1", "exhibition", 12900], + ["kx2020-010", "2020 금속산업대전", "2020-06-17", "2020-06-20", "홀 2, 3", "trade", 39600], + ["kx2020-011", "메가쇼 2020 시즌1", "2020-06-18", "2020-06-21", "홀 7, 8", "exhibition", 34100], + ["kx2020-012", "제34회 맘앤베이비 엑스포", "2020-06-25", "2020-06-28", "홀 3, 4", "culture", 29200], + ["kx2020-013", "2020 고양가구박람회", "2020-06-25", "2020-06-28", "홀 2", "exhibition", 22200], + ["kx2020-014", "나노코리아 2020", "2020-07-01", "2020-07-03", "홀 4", "exhibition", 19400], + ["kx2020-015", "아템페어 2020(제12회 국제접착코팅필름융합소재전)", "2020-07-01", "2020-07-03", "홀 5", "trade", 13400], + ["kx2020-016", "수소모빌리티+쇼", "2020-07-01", "2020-07-03", "홀 3", "tech", 19300], + ["kx2020-017", "2020 캠핑앤피크닉페어", "2020-07-02", "2020-07-05", "홀 1, 2", "culture", 43400], + ["kx2020-018", "2020 대한민국 명품특산물 페스티벌", "2020-07-16", "2020-07-19", "홀 미정", "culture", 15400], + ["kx2020-019", "2020 대한민국 생활낚시박람회", "2020-07-17", "2020-07-19", "홀 8", "exhibition", 12100], + ["kx2020-020", "제22회 국제포장기자재전/제10회 국제물류산업대전 제15회 국제제약바이오화장품기술전 제12회 국제화학장치산업전 제14회 국제연구실험 및 첨단분석장비전 제10회 국제의약품바이오산업전 제5회 국제화장품원료기술전", "2020-07-27", "2020-07-30", "홀 1, 2, 3, 4, 5", "trade", 110800], + ["kx2020-021", "대한민국 방방곡곡 여행박람회 / 제5회 대한민국 국제 관광박람회", "2020-08-13", "2020-08-16", "홀 3", "culture", 19600], + ["kx2020-022", "미베 베이비엑스포", "2020-08-13", "2020-08-16", "홀 4, 5", "culture", 48600], + ["kx2020-023", "2020 DMZ 포럼", "2020-09-17", "2020-09-18", "홀 옥외", "exhibition", 11400], + ["kx2020-024", "제23회 대한민국우수상품전시회", "2020-10-21", "2020-10-23", "홀 6", "exhibition", 12200], + ["kx2020-025", "제34회 코베 베이비페어 / 2020 코베 유아교육전", "2020-10-22", "2020-10-25", "홀 7", "culture", 21000], + ["kx2020-026", "2020 홈인테리어테크", "2020-10-22", "2020-10-25", "홀 3", "tech", 24800], + ["kx2020-027", "2020 한국건설안전박람회", "2020-10-28", "2020-10-30", "홀 5", "trade", 18900], + ["kx2020-028", "2020 월드 스마트시티 엑스포", "2020-10-28", "2020-10-30", "홀 미정", "tech", 19100], + ["kx2020-029", "2020 K-방역 엑스포", "2020-10-28", "2020-10-30", "홀 4", "exhibition", 15700], + ["kx2020-030", "2020 첨단소재부품뿌리산업기술대전", "2020-10-28", "2020-10-30", "홀 미정", "trade", 11100], + ["kx2020-031", "2020 로보월드", "2020-10-28", "2020-10-31", "홀 1, 2", "exhibition", 40400], + ["kx2020-032", "제54회 MBC건축박람회", "2020-11-05", "2020-11-08", "홀 3, 1, 2", "exhibition", 42100], + ["kx2020-033", "국제 LED & OLED EXPO 2020", "2020-11-11", "2020-11-13", "홀 7", "exhibition", 11700], + ["kx2020-034", "메가쇼 2020 시즌2", "2020-11-12", "2020-11-15", "홀 2, 3, 4", "exhibition", 54400], + ["kx2020-035", "K-PRINT 2020, K-Label 2020, K-SignAD 2020", "2020-11-12", "2020-11-15", "홀 8", "exhibition", 25000], + ["kx2020-036", "2020 대한민국방위산업전", "2020-11-18", "2020-11-20", "홀 9, 10", "trade", 21400], + ["kx2020-037", "호텔쇼(2020 대한민국 숙박산업대전)", "2020-11-19", "2020-11-22", "홀 1A", "trade", 20500], + ["kx2020-038", "2020 한국국제가구 및 인테리어산업대전", "2020-11-19", "2020-11-22", "홀 2, 3", "trade", 48000], + ["kx2020-039", "제35회 맘앤베이비엑스포", "2020-11-19", "2020-11-22", "홀 4", "culture", 22800], + ["kx2020-040", "2020 카페&베이커리페어 (동시개최: 2020 호텔쇼&레스토랑산업전)", "2020-11-19", "2020-11-22", "홀 1B", "trade", 18000], + ["kx2020-041", "2020 케이펫페어 일산 (대한민국펫산업박람회)", "2020-11-20", "2020-11-22", "홀 7, 8", "culture", 27500], + ["kx2020-042", "RETECH 2020 제13회 국제환경장비 및 자원순환산업전", "2020-11-26", "2020-11-28", "홀 8A", "trade", 12500], + ["kx2020-043", "2020 한국 중장비 전시회 - 부품 및 어태치먼트 판매대전", "2020-11-26", "2020-11-28", "홀 8B", "trade", 15500], + ["kx2020-044", "2020 스마트 건설기술·안전대전", "2020-11-27", "2020-11-27", "홀 10A", "tech", 5700], + ["kx2020-045", "겟잇쇼(월드식품.와인주류박람회)킨텍스 2020", "2020-11-27", "2020-11-29", "홀 7", "culture", 12900], + ["kx2020-046", "2020 경기도청년든든페스티벌", "2020-11-28", "2020-11-28", "홀 1", "culture", 4100], + ["kx2021-001", "2021 캠핑앤피크닉페어", "2021-03-04", "2021-03-07", "홀 7, 8", "culture", 40100], + ["kx2021-002", "제10회 더골프쇼 KOREA 시즌1", "2021-03-11", "2021-03-14", "홀 7A", "exhibition", 16200], + ["kx2021-003", "2021 코리아빌드위크", "2021-03-18", "2021-03-21", "홀 2, 3, 4, 1, 5", "exhibition", 76100], + ["kx2021-004", "제25회 한국국제낚시박람회", "2021-03-26", "2021-03-28", "홀 7", "exhibition", 11200], + ["kx2021-005", "제36회 맘앤베이비엑스포", "2021-04-01", "2021-04-04", "홀 7", "culture", 15300], + ["kx2021-006", "2021 대한민국 기본소득 박람회", "2021-04-28", "2021-04-30", "홀 5A", "exhibition", 16400], + ["kx2021-007", "제56회 MBC건축박람회/공공시설 및 조경박람회/서울조명박람회/디지털프린팅·사인엑스포/홈&리빙페어/기프트쇼/차·공예박람회/가구엑스포/스포츠·레저산업박람회", "2021-05-06", "2021-05-09", "홀 1, 2, 3, 4", "tech", 89100], + ["kx2021-008", "2021 서울캣쇼 21", "2021-05-07", "2021-05-09", "홀 5B", "exhibition", 11300], + ["kx2021-009", "제20회 세계 보안 엑스포 (SECON 2021)", "2021-05-12", "2021-05-14", "홀 3, 4", "exhibition", 26300], + ["kx2021-010", "제41회 코베 베이비페어 & 유아교육전", "2021-05-13", "2021-05-16", "홀 9", "culture", 16400], + ["kx2021-011", "2021 국제포장기자재전 스페셜 & 2021 제약화장품위크, 제11회 국제물류산업대전, 국제포장기자재전 스페셜, 제16회 국제제약∙바이오∙화장품기술전, 제13회 국제화학장치산업전, 제15회 국제연구·실험 및 첨단분석장비전, 제6회 국제화장품원료·기술전", "2021-05-21", "2021-05-28", "홀 미정", "trade", 27200], + ["kx2021-012", "2021 DMZ 포럼", "2021-05-21", "2021-05-22", "홀 6", "exhibition", 9000], + ["kx2021-013", "2021 국제포장기자재전 외", "2021-05-25", "2021-05-28", "홀 2B, 2A, 3, 4, 5", "exhibition", 96000], + ["kx2021-014", "2021 케이펫페어 일산 (대한민국펫산업박람회)", "2021-05-28", "2021-05-30", "홀 7, 8A", "culture", 23700], + ["kx2021-015", "2021 국제 아웃도어 캠핑&레포츠 페스티벌", "2021-06-04", "2021-06-06", "홀 7, 8A", "culture", 38800], + ["kx2021-016", "Bravo! 2021 고양시 중장년일자리 박람회", "2021-06-10", "2021-06-10", "홀 6C", "exhibition", 5800], + ["kx2021-017", "2021 고양가구박람회", "2021-06-10", "2021-06-13", "홀 1", "exhibition", 22400], + ["kx2021-018", "제11회 더골프쇼 KOREA 시즌2", "2021-06-10", "2021-06-13", "홀 7A", "exhibition", 25400], + ["kx2021-019", "2021 세계 태양에너지 엑스포", "2021-06-16", "2021-06-18", "홀 2", "trade", 19300], + ["kx2021-020", "메가쇼 2021 시즌1 (팔도밥상 FAIR)", "2021-06-17", "2021-06-20", "홀 7, 8", "tech", 39500], + ["kx2021-021", "제37회 맘앤베이비엑스포", "2021-06-24", "2021-06-27", "홀 7", "culture", 17800], + ["kx2021-022", "2021 대한민국생활낚시박람회", "2021-07-02", "2021-07-04", "홀 7", "exhibition", 17700], + ["kx2021-023", "2021 국제안전보건전시회", "2021-07-05", "2021-07-08", "홀 8", "trade", 21500], + ["kx2021-024", "나노코리아 2021", "2021-07-07", "2021-07-09", "홀 4A", "exhibition", 15000], + ["kx2021-025", "아템페어 2021", "2021-07-07", "2021-07-09", "홀 5", "culture", 17000], + ["kx2021-026", "2021년 온라인전시회", "2021-07-12", "2021-07-16", "홀 6A", "exhibition", 25300], + ["kx2021-027", "2021 서울국제식품산업대전", "2021-07-27", "2021-07-30", "홀 2, 3A, 3B, 4", "trade", 73300], + ["kx2021-028", "한국 소재 및 복합재료 전시회 (K-Mtech 2021)", "2021-08-25", "2021-08-28", "홀 8B", "trade", 23100], + ["kx2021-029", "제26회 국제 플라스틱 고무산업전 (KOPLAS 2021)", "2021-08-25", "2021-08-28", "홀 8A", "trade", 14100], + ["kx2021-030", "K-PRINT 2021(K-Label, K-signAD)", "2021-08-26", "2021-08-28", "홀 7", "exhibition", 17800], + ["kx2021-031", "궁디팡팡 캣페스타", "2021-09-03", "2021-09-05", "홀 7A", "exhibition", 14500], + ["kx2021-032", "2021 월드 스마트시티 엑스포", "2021-09-08", "2021-09-10", "홀 4, 5", "tech", 36400], + ["kx2021-033", "2021수소모빌리티+쇼", "2021-09-08", "2021-09-11", "홀 9", "tech", 17300], + ["kx2021-034", "2021 대한민국 국제 관광박람회 / 대한민국 방방곡곡 여행박람회", "2021-09-09", "2021-09-12", "홀 7", "culture", 19200], + ["kx2021-035", "2021 달.고.나. 정책지원박람회", "2021-09-13", "2021-09-15", "홀 6B", "exhibition", 16300], + ["kx2021-036", "한국국제승강기엑스포, 한국빌딩·공동주택 및 공장관리산업전", "2021-09-14", "2021-09-16", "홀 5", "trade", 12200], + ["kx2021-037", "한국건설안전박람회", "2021-09-14", "2021-09-16", "홀 2A", "trade", 11300], + ["kx2021-038", "2021년 3차 전역예정장병 온라인 취업박람회", "2021-09-15", "2021-09-17", "홀 6A", "exhibition", 14700], + ["kx2021-039", "2021 코리아 나라장터 엑스포", "2021-09-29", "2021-10-01", "홀 4, 5", "exhibition", 51200], + ["kx2021-040", "2021 대한민국 명품 특산물 페스티벌", "2021-09-30", "2021-10-03", "홀 10", "culture", 21200], + ["kx2021-041", "2021 오토살롱위크", "2021-09-30", "2021-10-03", "홀 1, 2, 3", "exhibition", 87000], + ["kx2021-042", "제44회 코베 베이비페어 & 유아교육전 하반기", "2021-09-30", "2021-10-03", "홀 9", "culture", 28100], + ["kx2021-043", "2021 고양시·KTA 태권도장교육산업박람회", "2021-10-02", "2021-10-03", "홀 6B, 6C", "exhibition", 10400], + ["kx2021-044", "에스씨엠 페어 2021", "2021-10-06", "2021-10-08", "홀 2", "culture", 10600], + ["kx2021-045", "2021 그린뉴딜 엑스포", "2021-10-06", "2021-10-08", "홀 1", "exhibition", 18500], + ["kx2021-046", "제14회 청년일자리 박람회", "2021-10-07", "2021-10-07", "홀 6C", "exhibition", 4100], + ["kx2021-047", "제13회 대한민국 뷰티박람회(K-뷰티 엑스포 2021)", "2021-10-07", "2021-10-09", "홀 4, 5", "culture", 31100], + ["kx2021-048", "2021 경기국제웹툰페어", "2021-10-07", "2021-10-10", "홀 3A", "culture", 24700], + ["kx2021-049", "2021 국제아웃도어캠핑&레포츠페스티벌 더 파이널 시즌", "2021-10-08", "2021-10-10", "홀 9", "culture", 15200], + ["kx2021-050", "2021 한국관상어산업박람회", "2021-10-08", "2021-10-10", "홀 7A", "exhibition", 18100], + ["kx2021-051", "인사이드 3D 프린팅 컨퍼런스 & 엑스포", "2021-10-13", "2021-10-15", "홀 5", "exhibition", 11200], + ["kx2021-052", "2021 국제광융합엑스포", "2021-10-13", "2021-10-15", "홀 3A", "exhibition", 10600], + ["kx2021-053", "2021 소부장뿌리기술대전", "2021-10-13", "2021-10-15", "홀 4", "exhibition", 18900], + ["kx2021-054", "2021 대한민국 에너지대전", "2021-10-13", "2021-10-15", "홀 1, 2", "trade", 29300], + ["kx2021-055", "2021 탄소중립 엑스포", "2021-10-13", "2021-10-15", "홀 3B", "exhibition", 11000], + ["kx2021-056", "2021 카페&베이커리페어", "2021-10-14", "2021-10-17", "홀 10A", "culture", 22500], + ["kx2021-057", "호텔쇼 (2021 대한민국 숙박산업대전)", "2021-10-14", "2021-10-17", "홀 10B", "trade", 23500], + ["kx2021-058", "2021 한국산업대전", "2021-10-19", "2021-10-22", "홀 2, 3, 4A", "trade", 75300], + ["kx2021-059", "2021 한국포장전", "2021-10-19", "2021-10-22", "홀 7, 8A", "exhibition", 40300], + ["kx2021-060", "제12회 더골프쇼 KOREA 시즌3", "2021-10-21", "2021-10-24", "홀 10A", "exhibition", 26000], + ["kx2021-061", "금속산업대전 2021", "2021-10-27", "2021-10-29", "홀 3A, 3B", "trade", 12400], + ["kx2021-062", "2021 로보월드 (국제로봇산업대전)", "2021-10-27", "2021-10-30", "홀 1, 2", "tech", 45200], + ["kx2021-063", "대한민국 우수상품전시회 (G-FAIR KOREA 2021)", "2021-10-28", "2021-10-30", "홀 4, 5", "tech", 29000], + ["kx2021-064", "2021 한국국제가구 및 인테리어산업대전 & 한국국제목공기계전시회 (KOFURN 2021)", "2021-10-28", "2021-10-31", "홀 7, 8", "trade", 46700], + ["kx2021-065", "제28회 식품기기전시회", "2021-11-05", "2021-11-06", "홀 7A", "culture", 9100], + ["kx2021-066", "2021 국제도로교통박람회 (ROTREX 2021)", "2021-11-10", "2021-11-12", "홀 5A, 5B", "exhibition", 15800], + ["kx2021-067", "2021 아시아 콘크리트 엑스포", "2021-11-10", "2021-11-12", "홀 4A", "exhibition", 12800], + ["kx2021-068", "제11회 한국국제건설기계전", "2021-11-10", "2021-11-13", "홀 2, 3", "trade", 49100], + ["kx2021-069", "제38회 맘앤베이비엑스포", "2021-11-11", "2021-11-14", "홀 10", "culture", 19100], + ["kx2021-070", "2021 청소년 발명 페스티벌", "2021-11-11", "2021-11-13", "홀 1A", "culture", 13600], + ["kx2021-071", "2021 한국문화축제 K-Culture Fan Fair", "2021-11-13", "2021-11-14", "홀 7", "culture", 9700], + ["kx2021-072", "2021 글로벌 엔터테인먼트 엑스포", "2021-11-13", "2021-11-14", "홀 8", "exhibition", 7800], + ["kx2021-073", "2021 트래블쇼", "2021-11-18", "2021-11-21", "홀 5A", "exhibition", 23000], + ["kx2021-074", "2021 케이펫페어 일산(하) (대한민국펫산업박람회)", "2021-11-19", "2021-11-21", "홀 7, 8", "culture", 35200], + ["kx2021-075", "제14회 국제환경장비 및 자원순환산업전", "2021-11-24", "2021-11-26", "홀 7", "trade", 14500], + ["kx2021-076", "2021 스마트상점 · 리테일 엑스포", "2021-11-25", "2021-11-27", "홀 1", "tech", 18100], + ["kx2021-077", "2021 디지털 대전환 엑스포", "2021-11-25", "2021-11-28", "홀 2, 3, 4A", "tech", 56400], + ["kx2021-078", "2021서울모빌리티쇼", "2021-11-26", "2021-12-05", "홀 9, 10", "tech", 47300], + ["kx2021-079", "제57회 MBC건축박람회/2021 시니어 라이프&복지 박람회/2021 스포츠·레저·건강산업 박람회/제29회 홈&리빙·기프트·차공예 박람회/2021 정원·조경 및 냉난방·건축설비박람회", "2021-12-02", "2021-12-05", "홀 3, 4A, 4B, 5A", "trade", 66700], + ["kx2021-080", "2021 ICT멘토링 엑스포", "2021-12-03", "2021-12-03", "홀 2A", "exhibition", 6300], + ["kx2021-081", "2021 한국유용곤충산업박람회", "2021-12-16", "2021-12-18", "홀 4", "exhibition", 17800], + ["kx2021-082", "2021 미베 베이비엑스포 & 유아교육박람회", "2021-12-16", "2021-12-19", "홀 5", "culture", 18800], + ["kx2021-083", "2021 대한민국 과학기술대전", "2021-12-22", "2021-12-24", "홀 2, 3", "exhibition", 34000], + ["kx2022-001", "2022 코리아빌드위크", "2022-03-03", "2022-03-06", "홀 2, 3, 4, 5A, 5B", "exhibition", 78400], + ["kx2022-002", "제13회 더골프쇼 KOREA 시즌1", "2022-03-10", "2022-03-13", "홀 10A", "exhibition", 17000], + ["kx2022-003", "2022 캠핑&피크닉페어", "2022-03-10", "2022-03-13", "홀 7, 8", "culture", 46800], + ["kx2022-004", "제39회 맘앤베이비엑스포", "2022-03-10", "2022-03-13", "홀 2A", "culture", 22900], + ["kx2022-005", "2022 경기국제보트쇼", "2022-03-11", "2022-03-13", "홀 4, 5", "exhibition", 25200], + ["kx2022-006", "2022 한국국제낚시박람회", "2022-03-11", "2022-03-13", "홀 3", "exhibition", 18400], + ["kx2022-007", "코리아 나라장터 엑스포", "2022-04-13", "2022-04-15", "홀 3, 4, 5", "exhibition", 46900], + ["kx2022-008", "제21회 세계보안엑스포 (SECON & eGISEC 2022)", "2022-04-20", "2022-04-22", "홀 3, 4, 5A", "exhibition", 45800], + ["kx2022-009", "케이팜 귀농귀촌 박람회", "2022-04-28", "2022-04-30", "홀 7A", "exhibition", 14000], + ["kx2022-010", "제 51회 코베 베이비페어&유아교육전", "2022-05-05", "2022-05-08", "홀 7", "culture", 19700], + ["kx2022-011", "2022 서울캣쇼", "2022-05-06", "2022-05-08", "홀 5A", "exhibition", 13000], + ["kx2022-012", "제60회 MBC건축박람회 / 2022 동아 공공시설 및 조경박람회 / 2022 서울 조명 박람회 / 2022 서울 디지털프린팅·사인 엑스포 / 제32회 동아 홈&리빙페어 / 제23회 서울 기프트쇼 / 제21회 동아 차·공예 박람회 / 제19회 동아 가구 엑스포 / 제19회 동아 스포츠·레저산업 박람회", "2022-05-12", "2022-05-15", "홀 1, 2, 3, 4", "tech", 69000], + ["kx2022-013", "2022 플레이엑스포", "2022-05-12", "2022-05-15", "홀 6, 7, 8A", "exhibition", 49800], + ["kx2022-014", "2022 케이펫페어 일산", "2022-05-20", "2022-05-22", "홀 10", "culture", 17000], + ["kx2022-015", "서울국제생산제조기술전(SIMTOS 2022)", "2022-05-23", "2022-05-27", "홀 1, 2, 3, 4, 5, 6, 7, 8", "trade", 169600], + ["kx2022-016", "2022 국제 아웃도어 캠핑&레포츠 페스티벌", "2022-06-03", "2022-06-05", "홀 10", "culture", 17500], + ["kx2022-017", "2022 서울국제식품산업대전", "2022-06-07", "2022-06-10", "홀 1, 2, 3, 4, 5", "trade", 112000], + ["kx2022-018", "제12회 국제물류산업대전 / 제23회 국제포장기자재전 / 제17회 제약바이오화장품기술전 / 제14회 국제화학장치산업전 / 제16회 국제연구실험 및 첨단분석장비전 / 제12회 국제의약품바이오산업전 / 제7회 국제화장품원료기술전", "2022-06-14", "2022-06-17", "홀 1, 2, 3, 4, 5, 7, 8A", "trade", 107200], + ["kx2022-019", "2022 국제광융합 O2O 엑스포", "2022-06-21", "2022-06-23", "홀 3A", "exhibition", 11300], + ["kx2022-020", "메가쇼 2022 시즌1", "2022-06-23", "2022-06-26", "홀 7, 8", "exhibition", 35100], + ["kx2022-021", "제40회 맘앤베이비엑스포", "2022-06-23", "2022-06-26", "홀 5", "culture", 24000], + ["kx2022-022", "제14회 더골프쇼 KOREA 시즌2", "2022-06-23", "2022-06-26", "홀 2A", "exhibition", 22100], + ["kx2022-023", "2022 고양가구박람회", "2022-06-23", "2022-06-26", "홀 1", "exhibition", 14800], + ["kx2022-024", "2022 RECARE 재활·복지박람회", "2022-06-24", "2022-06-26", "홀 4", "exhibition", 10700], + ["kx2022-025", "2022 세계태양에너지엑스포", "2022-06-29", "2022-07-01", "홀 3, 4A", "trade", 63300], + ["kx2022-026", "킨텍스 상상체험 키즈월드(여름)", "2022-07-01", "2022-08-21", "홀 10", "culture", 32000], + ["kx2022-027", "2022 대한민국 생활낚시박람회", "2022-07-01", "2022-07-03", "홀 5A", "exhibition", 17600], + ["kx2022-028", "2022 국제안전보건전시회", "2022-07-04", "2022-07-07", "홀 7, 8A", "trade", 37100], + ["kx2022-029", "제 20회 국제 나노기술 심포지엄 및 융합전시회 / 아템페어 2022", "2022-07-06", "2022-07-08", "홀 4, 5A", "culture", 37900], + ["kx2022-030", "170회 서울 코믹월드", "2022-07-23", "2022-07-24", "홀 4", "culture", 12200], + ["kx2022-031", "2022 청소년 발명 페스티벌", "2022-08-04", "2022-08-06", "홀 1A", "culture", 12900], + ["kx2022-032", "대한민국 국제 관광박람회(대한민국 방방곡곡 여행박람회) / 아시아스마트관광산업대전", "2022-08-11", "2022-08-14", "홀 3", "tech", 16500], + ["kx2022-033", "반찬전", "2022-08-12", "2022-08-15", "홀 5A, 5B", "exhibition", 25600], + ["kx2022-034", "2022 미베 베이비엑스포 & 유아교육박람회", "2022-08-12", "2022-08-15", "홀 4A", "culture", 17600], + ["kx2022-035", "제61회 MBC건축박람회 / 2022 동아 정원 및 조경박람회 / 2022 서울 냉난방 및 건축설비 박람회 / 제33회 동아 홈&리빙페어 / 제24회 서울 기프트쇼 / 제22회 동아 차·공예 박람회 / 제20회 동아 스포츠·레저산업 박람회 / 2022 센덱스", "2022-08-18", "2022-08-21", "홀 1A, 2, 3A, 3B, 4A, 4B", "trade", 99400], + ["kx2022-036", "2022 전라북도 귀농귀촌 상담홍보전 (신바람 귀농귀촌 전라북도와 함께)", "2022-08-19", "2022-08-21", "홀 8B", "exhibition", 15900], + ["kx2022-037", "K-PRINT 2022 / K-Label 2022 / K-Sign & AD 2022", "2022-08-25", "2022-08-27", "홀 7", "exhibition", 16000], + ["kx2022-038", "2022 여성발명왕EXPO", "2022-08-25", "2022-08-27", "홀 4A", "exhibition", 13700], + ["kx2022-039", "한국국제가구 및 인테리어산업대전", "2022-08-25", "2022-08-28", "홀 1, 2", "trade", 49000], + ["kx2022-040", "2023학년도 대전·세종·충남·충북 지역대학교 수시연합입시박람회", "2022-08-27", "2022-08-27", "홀 6A", "exhibition", 5400], + ["kx2022-041", "2022 스마트건설 EXPO", "2022-08-30", "2022-09-01", "홀 6, 8", "tech", 63200], + ["kx2022-042", "2022 국제물산업박람회", "2022-08-31", "2022-09-02", "홀 1, 2", "exhibition", 49300], + ["kx2022-043", "제6회 월드 스마트시티 엑스포", "2022-08-31", "2022-09-02", "홀 3, 4, 5", "tech", 61100], + ["kx2022-044", "제15회 폐기물·자원순환산업전 / 2022 한국 중장비 전시회 - 건설 및 환경장비 특별전", "2022-08-31", "2022-09-02", "홀 7A, 7B", "trade", 30600], + ["kx2022-045", "2022 이차전지 소재부품 및 장비전/한국 소재 복합재료 및 장비전", "2022-08-31", "2022-09-03", "홀 10B", "tech", 27500], + ["kx2022-046", "H2 MEET 2022", "2022-08-31", "2022-09-03", "홀 9, 10A", "exhibition", 44400], + ["kx2022-047", "2022 헬스케어·스파 산업박람회", "2022-09-15", "2022-09-17", "홀 1A", "exhibition", 18400], + ["kx2022-048", "2022 오토살롱위크", "2022-09-16", "2022-09-18", "홀 3, 4, 5", "exhibition", 35700], + ["kx2022-049", "금속산업대전 2022", "2022-09-21", "2022-09-23", "홀 3A, 3B", "trade", 17000], + ["kx2022-050", "대한민국방위산업전 2022", "2022-09-21", "2022-09-25", "홀 7, 8A, 8B", "trade", 62100], + ["kx2022-051", "DX KOREA 2022 전력지원체계 전시관", "2022-09-21", "2022-09-25", "홀 9", "exhibition", 27900], + ["kx2022-052", "궁디팡팡 캣페스타", "2022-09-23", "2022-09-25", "홀 2", "exhibition", 14500], + ["kx2022-053", "2022-3차 국군장병 취업박람회", "2022-09-28", "2022-09-28", "홀 6", "exhibition", 5200], + ["kx2022-054", "핸드아티코리아&일러스트코리아", "2022-09-30", "2022-10-03", "홀 7A", "exhibition", 22400], + ["kx2022-055", "제 56회 코베 베이비페어&유아교육전", "2022-09-30", "2022-10-03", "홀 9A, 9B", "culture", 25200], + ["kx2022-056", "희귀반려동물박람회", "2022-10-01", "2022-10-02", "홀 1A", "culture", 11500], + ["kx2022-057", "2022 국제아웃도어캠핑&레포츠페스티벌", "2022-10-01", "2022-10-03", "홀 7B, 8", "culture", 30500], + ["kx2022-058", "국제 모빌리티 산업전", "2022-10-05", "2022-10-07", "홀 1", "tech", 13100], + ["kx2022-059", "대한민국 뷰티박람회", "2022-10-06", "2022-10-08", "홀 4A, 5", "culture", 28100], + ["kx2022-060", "2022 경기국제웹툰페어", "2022-10-06", "2022-10-09", "홀 3A", "culture", 24700], + ["kx2022-061", "2022 카페&베이커리페어", "2022-10-06", "2022-10-09", "홀 2", "culture", 20700], + ["kx2022-062", "제 2회 유통·물류 및 공급망관리 산업전", "2022-10-12", "2022-10-14", "홀 2A, 2B", "trade", 13700], + ["kx2022-063", "22년도 제대(예정)군인 및 보훈가족을 위한 일자리 박람회", "2022-10-13", "2022-10-13", "홀 9B", "exhibition", 3500], + ["kx2022-064", "2022 국제 공구 및 스마트 용접 자동화전", "2022-10-18", "2022-10-21", "홀 4A, 4B", "tech", 18800], + ["kx2022-065", "2022 한국산업대전", "2022-10-18", "2022-10-21", "홀 2A, 3", "trade", 51200], + ["kx2022-066", "제16회 한국국제냉난방공조전", "2022-10-18", "2022-10-21", "홀 1, 2B", "trade", 47700], + ["kx2022-067", "2022 한국건설안전박람회", "2022-10-19", "2022-10-21", "홀 5", "trade", 18300], + ["kx2022-068", "제4회 앙데팡당KOREA 국제예술제", "2022-10-25", "2022-10-27", "홀 6A", "exhibition", 18100], + ["kx2022-069", "2022 로보월드(국제로봇산업대전)", "2022-10-26", "2022-10-29", "홀 1, 2, 3", "tech", 71500], + ["kx2022-070", "제25회 대한민국우수상품전시회", "2022-10-27", "2022-10-29", "홀 4, 5", "exhibition", 22000], + ["kx2022-071", "2022 스마트국토엑스포", "2022-11-02", "2022-11-04", "홀 5", "tech", 18100], + ["kx2022-072", "2022 소부장뿌리기술대전", "2022-11-02", "2022-11-04", "홀 2", "exhibition", 13500], + ["kx2022-073", "2022 탄소중립 EXPO", "2022-11-02", "2022-11-04", "홀 8", "exhibition", 10800], + ["kx2022-074", "2022 대한민국 에너지대전", "2022-11-02", "2022-11-04", "홀 7, 8A", "trade", 24400], + ["kx2022-075", "제15회 더골프쇼 KOREA 시즌3", "2022-11-03", "2022-11-06", "홀 10A", "exhibition", 14500], + ["kx2022-076", "제42회 맘앤베이비엑스포", "2022-11-10", "2022-11-13", "홀 2A, 2B", "culture", 18200], + ["kx2022-077", "트래블쇼 2022", "2022-11-10", "2022-11-13", "홀 5A", "exhibition", 15700], + ["kx2022-078", "메가쇼 2022 시즌 2", "2022-11-10", "2022-11-13", "홀 3, 4, 5B", "exhibition", 53500], + ["kx2022-079", "2022 스마트리테일·펫가전쇼", "2022-11-18", "2022-11-20", "홀 4A, 4B", "tech", 15200], + ["kx2022-080", "제3회 베이프코리아엑스포", "2022-11-18", "2022-11-20", "홀 8A", "exhibition", 13200], + ["kx2022-081", "2022 메가주", "2022-11-18", "2022-11-20", "홀 1, 2, 3", "culture", 34000], + ["kx2022-082", "2022 고양 디지털미디어테크쇼", "2022-11-23", "2022-11-25", "홀 3, 4", "tech", 35800], + ["kx2022-083", "콘텐츠 코리아 2022", "2022-11-23", "2022-11-25", "홀 5", "exhibition", 11900], + ["kx2022-084", "인공지능그랜드챌린지", "2022-11-24", "2022-11-24", "홀 Hall 6", "tech", 4000], + ["kx2022-085", "2022 국제아웃도어캠핑&레포츠페스티벌", "2022-11-25", "2022-11-27", "홀 7B, 8", "culture", 30500], + ["kx2022-086", "2022 Sea Farm Show 수산양식 박람회", "2022-12-01", "2022-12-03", "홀 8B", "exhibition", 11800], + ["kx2022-087", "2022 대한민국 소상공인대회 및 기능경진대회", "2022-12-02", "2022-12-03", "홀 3", "exhibition", 12500], + ["kx2022-088", "애니메이션 X 게임 페스티벌 2022", "2022-12-03", "2022-12-04", "홀 7, 8A", "tech", 20400], + ["kx2022-089", "2022 한국골프산업박람회", "2022-12-07", "2022-12-09", "홀 7", "exhibition", 14700], + ["kx2022-090", "2022 대한민국 과학기술대전", "2022-12-15", "2022-12-17", "홀 1", "exhibition", 17700], + ["kx2022-091", "제7회 KTA 태권도장 교육·산업 박람회", "2022-12-17", "2022-12-18", "홀 7B", "exhibition", 10800], + ["kx2022-092", "[KOFOOT] 2022 코리아풋볼위크", "2022-12-21", "2022-12-25", "홀 2A, 2B, 3A, 3B", "exhibition", 49100], + ["kx2023-001", "제43회 맘앤베이비엑스포", "2023-02-16", "2023-02-19", "홀 7", "culture", 14500], + ["kx2023-002", "코리아빌드위크", "2023-02-16", "2023-02-19", "홀 1, 2, 3, 4, 5A, 5B", "exhibition", 88300], + ["kx2023-003", "2023 경기국제보트쇼", "2023-03-03", "2023-03-05", "홀 3A, 4, 5", "exhibition", 42200], + ["kx2023-004", "2023 한국국제낚시박람회", "2023-03-03", "2023-03-05", "홀 2A, 2B", "exhibition", 18600], + ["kx2023-005", "2023 캠핑앤피크닉페어", "2023-03-09", "2023-03-12", "홀 7, 8", "culture", 34000], + ["kx2023-006", "제26회 국제금형 및 관련기기전", "2023-03-14", "2023-03-18", "홀 2, 3", "trade", 63100], + ["kx2023-007", "제27회 국제플라스틱 ․ 고무산업전시회 (KOPLAS 2023)", "2023-03-14", "2023-03-18", "홀 4, 5", "trade", 58800], + ["kx2023-008", "2023 서울펫쇼", "2023-03-17", "2023-03-19", "홀 10A", "culture", 16700], + ["kx2023-009", "2023 국제물산업박람회(WATER KOREA)", "2023-03-21", "2023-03-23", "홀 6A, 7, 8A", "exhibition", 41800], + ["kx2023-010", "제22회 세계보안엑스포", "2023-03-29", "2023-03-31", "홀 7, 8", "exhibition", 29100], + ["kx2023-011", "2023서울모빌리티쇼", "2023-03-31", "2023-04-09", "홀 옥외2, 1, 2, 3, 4, 5", "tech", 160100], + ["kx2023-012", "2023 전반기 국군장병 취업박람회", "2023-04-18", "2023-04-19", "홀 10A, 10B", "exhibition", 12400], + ["kx2023-013", "제13회 국제의약품·바이오산업전", "2023-04-18", "2023-04-21", "홀 4A", "trade", 23200], + ["kx2023-014", "제18회 국제제약∙바이오∙화장품기술전", "2023-04-18", "2023-04-21", "홀 2", "exhibition", 21100], + ["kx2023-015", "제13회 국제물류산업대전", "2023-04-18", "2023-04-21", "홀 1A", "trade", 15100], + ["kx2023-016", "제17회 국제연구·실험 및 첨단분석장비전", "2023-04-18", "2023-04-21", "홀 5", "exhibition", 16700], + ["kx2023-017", "제15회 국제화학장치산업전", "2023-04-18", "2023-04-21", "홀 3", "trade", 17100], + ["kx2023-018", "제8회 국제화장품원료·기술전", "2023-04-18", "2023-04-21", "홀 4B", "exhibition", 22900], + ["kx2023-019", "제 1회 국제콜드체인산업전", "2023-04-18", "2023-04-21", "홀 1B", "trade", 19300], + ["kx2023-020", "2023 코리아 나라장터 엑스포", "2023-04-26", "2023-04-28", "홀 3, 4, 5", "exhibition", 57500], + ["kx2023-021", "2023 서울캣쇼", "2023-04-28", "2023-04-30", "홀 7A", "exhibition", 13000], + ["kx2023-022", "코베 베이비페어&유아교육전 - 상반기", "2023-05-04", "2023-05-07", "홀 7", "culture", 23700], + ["kx2023-023", "2023 케이팜 시즌1", "2023-05-11", "2023-05-13", "홀 9B", "exhibition", 11000], + ["kx2023-024", "제63회 MBC건축박람회", "2023-05-11", "2023-05-14", "홀 2, 3, 4, 5", "exhibition", 65300], + ["kx2023-025", "플레이엑스포", "2023-05-11", "2023-05-14", "홀 6, 7, 8", "exhibition", 52000], + ["kx2023-026", "2023 메가주 일산", "2023-05-19", "2023-05-21", "홀 7, 8A", "culture", 36800], + ["kx2023-027", "대한민국 미술박람회 2023", "2023-05-25", "2023-05-28", "홀 8B", "exhibition", 23300], + ["kx2023-028", "2023 국제 아웃도어 캠핑&레포츠 페스티벌 - 5월", "2023-05-26", "2023-05-28", "홀 10, 9B", "culture", 25700], + ["kx2023-029", "2023 서울국제식품산업대전", "2023-05-30", "2023-06-02", "홀 1, 2, 3, 4, 5", "trade", 116900], + ["kx2023-030", "2023 대한민국 고졸인재 채용엑스포", "2023-05-31", "2023-06-01", "홀 6", "exhibition", 30200], + ["kx2023-031", "2023 고양가구박람회", "2023-06-08", "2023-06-11", "홀 1, 2", "exhibition", 35100], + ["kx2023-032", "제16회 더골프쇼 KOREA 시즌1", "2023-06-08", "2023-06-11", "홀 9B", "exhibition", 23100], + ["kx2023-033", "2023 생활낚시박람회", "2023-06-09", "2023-06-11", "홀 10A", "exhibition", 11600], + ["kx2023-034", "메가쇼 2023 시즌1", "2023-06-15", "2023-06-18", "홀 7, 8", "exhibition", 36000], + ["kx2023-035", "국제광융합 O2O EXPO 2023", "2023-06-21", "2023-06-23", "홀 3A", "exhibition", 12800], + ["kx2023-036", "맘앤베이비엑스포", "2023-06-22", "2023-06-25", "홀 10A, 10B", "culture", 19000], + ["kx2023-037", "2023 세계 배터리&충전 인프라 엑스포", "2023-06-27", "2023-06-29", "홀 4A", "tech", 17500], + ["kx2023-038", "2023 세계 태양에너지 엑스포", "2023-06-27", "2023-06-29", "홀 3A", "trade", 10900], + ["kx2023-039", "2023 해양바이오 박람회", "2023-06-28", "2023-06-29", "홀 6B, 6C", "trade", 7300], + ["kx2023-040", "2023 국제안전보건전시회", "2023-07-03", "2023-07-06", "홀 7, 8A", "trade", 46600], + ["kx2023-041", "제21회 국제나노기술 심포지엄 및 융합전시회 / 아템페어 2023", "2023-07-05", "2023-07-07", "홀 4, 5A", "culture", 28500], + ["kx2023-042", "2023 코리아 테마파크&어뮤즈먼트 페어", "2023-07-20", "2023-07-22", "홀 3", "culture", 12900], + ["kx2023-043", "2023 여성발명왕 EXPO", "2023-07-20", "2023-07-22", "홀 4A", "exhibition", 13600], + ["kx2023-044", "2023 SS시즌 캠핑페스타", "2023-07-21", "2023-07-23", "홀 7A", "culture", 11200], + ["kx2023-045", "2023 청소년 발명 페스티벌", "2023-08-03", "2023-08-05", "홀 7A", "culture", 11800], + ["kx2023-046", "코믹월드 2023 Summer", "2023-08-05", "2023-08-06", "홀 3A, 4, 5", "culture", 26400], + ["kx2023-047", "미베 베이비엑스포", "2023-08-10", "2023-08-13", "홀 7", "culture", 24300], + ["kx2023-048", "제8회 대한민국 국제 관광박람회", "2023-08-11", "2023-08-14", "홀 3", "exhibition", 15700], + ["kx2023-049", "제64회 MBC건축박람회", "2023-08-17", "2023-08-20", "홀 3, 4, 5", "exhibition", 60300], + ["kx2023-050", "K-PRINT 2023 / K-Pack 2023 / K-Label 2023 / K-Sign & AD 2023", "2023-08-23", "2023-08-26", "홀 7, 8A", "exhibition", 50500], + ["kx2023-051", "한국국제가구 및 인테리어산업대전", "2023-08-24", "2023-08-27", "홀 2, 3", "trade", 49000], + ["kx2023-052", "경기주류대상", "2023-08-25", "2023-08-27", "홀 6A, 6B", "exhibition", 11600], + ["kx2023-053", "제16회 폐기물·자원순환산업전 (RETECH 2023)", "2023-08-30", "2023-09-01", "홀 7A, 7B", "trade", 17700], + ["kx2023-054", "2023 차세대 분체산업전", "2023-08-30", "2023-09-01", "홀 8A", "trade", 29000], + ["kx2023-055", "마이펫페어 2023 일산", "2023-09-01", "2023-09-03", "홀 10A, 10B", "culture", 11400], + ["kx2023-056", "제1회 고향사랑의 날 기념식 및 고향사랑기부제 박람회", "2023-09-02", "2023-09-04", "홀 9B", "exhibition", 10700], + ["kx2023-057", "2023 월드 스마트시티 엑스포", "2023-09-06", "2023-09-08", "홀 3, 4, 5", "tech", 49000], + ["kx2023-058", "제3회 유통·물류 및 공급망관리 산업전 (SCM FAIR)", "2023-09-06", "2023-09-08", "홀 1, 2", "tech", 32100], + ["kx2023-059", "2023 오토살롱위크", "2023-09-08", "2023-09-10", "홀 7, 8", "exhibition", 23400], + ["kx2023-060", "H2 MEET 2023", "2023-09-13", "2023-09-15", "홀 1, 2, 3", "exhibition", 36800], + ["kx2023-061", "2023 한국건설안전박람회", "2023-09-13", "2023-09-15", "홀 10", "trade", 13000], + ["kx2023-062", "2023 대한민국 안전산업박람회", "2023-09-13", "2023-09-15", "홀 7, 8", "trade", 29800], + ["kx2023-063", "2023 기상기후산업박람회", "2023-09-13", "2023-09-15", "홀 7, 8", "exhibition", 26400], + ["kx2023-064", "2023년 후반기 국군장병 취업박람회", "2023-09-13", "2023-09-14", "홀 Hall 6", "exhibition", 12000], + ["kx2023-065", "이차전지 소재 · 부품 및 장비전 (K-BATTERY SHOW 2023)", "2023-09-13", "2023-09-15", "홀 3A", "tech", 17000], + ["kx2023-066", "궁디팡팡 캣페스타", "2023-09-15", "2023-09-17", "홀 5A, 5B", "exhibition", 14500], + ["kx2023-067", "2023 대한민국 소싱위크", "2023-09-21", "2023-09-22", "홀 2", "exhibition", 12600], + ["kx2023-068", "2023 추석맞이 선물박람회", "2023-09-21", "2023-09-24", "홀 9B", "exhibition", 20700], + ["kx2023-069", "경기국제웹툰페어", "2023-10-06", "2023-10-08", "홀 4A, 4B", "culture", 13000], + ["kx2023-070", "희귀반려동물박람회", "2023-10-07", "2023-10-08", "홀 9B", "culture", 11500], + ["kx2023-071", "2023 국제아웃도어캠핑&레포츠페스티벌 더 파이널 시즌 PART 1", "2023-10-07", "2023-10-09", "홀 7, 8", "culture", 27900], + ["kx2023-072", "2023 로보월드(국제로봇산업대전)", "2023-10-11", "2023-10-14", "홀 1, 2A, 2B, 3", "tech", 64100], + ["kx2023-073", "대한민국 뷰티박람회", "2023-10-12", "2023-10-14", "홀 4, 5", "culture", 28100], + ["kx2023-074", "코베 베이비페어&유아교육전 - 하반기", "2023-10-12", "2023-10-15", "홀 7", "culture", 15900], + ["kx2023-075", "2023 국제공구 및 스마트 용접 자동화전", "2023-10-17", "2023-10-20", "홀 2A, 2B", "tech", 14300], + ["kx2023-076", "2023 소부장뿌리기술대전", "2023-10-18", "2023-10-20", "홀 5", "exhibition", 15300], + ["kx2023-077", "금속산업대전 2023", "2023-10-18", "2023-10-20", "홀 3", "trade", 14500], + ["kx2023-078", "국제 모빌리티 산업전 (KOAA·GTT SHOW)", "2023-10-18", "2023-10-20", "홀 1", "tech", 13400], + ["kx2023-079", "2023 하반기 붐업코리아", "2023-10-18", "2023-10-19", "홀 4", "exhibition", 12400], + ["kx2023-080", "2023 카페&베이커리페어", "2023-10-19", "2023-10-22", "홀 10", "culture", 19400], + ["kx2023-081", "한국포장전 2023", "2023-10-24", "2023-10-27", "홀 7, 8A", "exhibition", 46000], + ["kx2023-082", "제26회 대한민국우수상품전시회", "2023-10-26", "2023-10-28", "홀 1, 2", "exhibition", 33900], + ["kx2023-083", "2023 디지털미디어테크쇼", "2023-11-02", "2023-11-04", "홀 4", "tech", 13900], + ["kx2023-084", "2023 디지털 퓨처쇼", "2023-11-02", "2023-11-04", "홀 5", "tech", 13200], + ["kx2023-085", "제17회 더골프쇼 KOREA 시즌2", "2023-11-02", "2023-11-05", "홀 8A", "exhibition", 15000], + ["kx2023-086", "콘텐츠코리아 2023", "2023-11-02", "2023-11-04", "홀 3A", "exhibition", 19200], + ["kx2023-087", "2023 SW인재페스티벌", "2023-11-02", "2023-11-04", "홀 7A", "culture", 15700], + ["kx2023-088", "2023 소프트웨어(SW) 교육 페스티벌", "2023-11-03", "2023-11-04", "홀 10", "tech", 8500], + ["kx2023-089", "2023 소상공인대회 및 기능경진대회", "2023-11-03", "2023-11-04", "홀 9", "exhibition", 8800], + ["kx2023-090", "2023 스마트국토엑스포", "2023-11-08", "2023-11-10", "홀 10", "tech", 16900], + ["kx2023-091", "2023 한국국제승강기엑스포", "2023-11-08", "2023-11-10", "홀 5", "trade", 12300], + ["kx2023-092", "메가쇼 2023 시즌2&트래블쇼", "2023-11-09", "2023-11-12", "홀 2, 3, 4", "exhibition", 53100], + ["kx2023-093", "2023 콘텐츠 유니버스 코리아", "2023-11-09", "2023-11-11", "홀 7B", "exhibition", 13100], + ["kx2023-094", "2023 대한민국 지방자치 경영혁신 엑스포", "2023-11-16", "2023-11-18", "홀 7", "exhibition", 13000], + ["kx2023-095", "2023 메가주 일산(하)", "2023-11-17", "2023-11-19", "홀 1A, 2, 3, 4, 5", "culture", 89100], + ["kx2023-096", "2023 도로교통박람회", "2023-11-22", "2023-11-24", "홀 3A", "exhibition", 12100], + ["kx2023-097", "2023 경기도 건설신기술 박람회", "2023-11-22", "2023-11-24", "홀 2B", "trade", 13400], + ["kx2023-098", "2023 경기도 교통안전박람회", "2023-11-22", "2023-11-24", "홀 3B", "trade", 12700], + ["kx2023-099", "2023 스마트건설 EXPO", "2023-11-22", "2023-11-24", "홀 1, 2A", "tech", 35500], + ["kx2023-100", "제14회 고양가구박람회 & 리빙아트페스타", "2023-11-23", "2023-11-26", "홀 4, 5", "culture", 48800], + ["kx2023-101", "제45회 맘앤베이비엑스포", "2023-11-23", "2023-11-26", "홀 7", "culture", 21100], + ["kx2023-102", "2023 국제아웃도어캠핑&레포츠페스티벌 더 파이널 시즌 PART 2", "2023-11-24", "2023-11-26", "홀 10", "culture", 18400], + ["kx2023-103", "2023 대한민국 과일산업대전", "2023-11-24", "2023-11-26", "홀 9B", "trade", 15400], + ["kx2023-104", "2023 순환경제 페스티벌", "2023-11-29", "2023-11-30", "홀 9", "trade", 12200], + ["kx2023-105", "2023 늘봄학교·교육기부 박람회", "2023-11-30", "2023-12-03", "홀 7", "exhibition", 21600], + ["kx2023-106", "2023 국제봉제기계섬유산업전", "2023-11-30", "2023-12-02", "홀 8", "trade", 25300], + ["kx2023-107", "2023 월드풋볼페스티벌", "2023-12-01", "2023-12-03", "홀 1B, 2", "culture", 29700], + ["kx2023-108", "애니메이션 X 게임 페스티벌", "2023-12-02", "2023-12-03", "홀 3, 4, 5", "tech", 25600], + ["kx2023-109", "코믹월드 2023 WINTER", "2023-12-16", "2023-12-17", "홀 2A, 3, 4, 5", "culture", 51000], + ["kx2024-001", "마이펫페어 2024 일산 파트1", "2024-01-12", "2024-01-14", "홀 7", "culture", 14400], + ["kx2024-002", "2024 카페디저트페어", "2024-01-25", "2024-01-28", "홀 7A", "culture", 19500], + ["kx2024-003", "2024 코리아빌드위크", "2024-02-21", "2024-02-24", "홀 1, 2, 3, 4, 5", "exhibition", 74300], + ["kx2024-004", "2024 캠프닉페어(캠핑&피크닉페어)", "2024-02-29", "2024-03-03", "홀 3, 4, 5", "culture", 95600], + ["kx2024-005", "제46회 맘앤베이비엑스포", "2024-02-29", "2024-03-03", "홀 7", "culture", 24800], + ["kx2024-006", "제18회 더골프쇼 KOREA 시즌1", "2024-03-07", "2024-03-10", "홀 7A", "exhibition", 20300], + ["kx2024-007", "2024 경기국제보트쇼", "2024-03-08", "2024-03-10", "홀 3A, 4, 5", "exhibition", 33100], + ["kx2024-008", "2024 한국국제낚시박람회 (KOFISH 2024)", "2024-03-08", "2024-03-10", "홀 2A, 2B", "exhibition", 13100], + ["kx2024-009", "2024 서울펫쇼", "2024-03-15", "2024-03-17", "홀 7", "culture", 11600], + ["kx2024-010", "희귀반려동물박람회", "2024-03-16", "2024-03-17", "홀 8A", "culture", 11500], + ["kx2024-011", "제23회 세계 보안 엑스포", "2024-03-20", "2024-03-22", "홀 3, 4, 5A", "exhibition", 38000], + ["kx2024-012", "Automotive Testing Expo 2024", "2024-03-20", "2024-03-22", "홀 10B", "exhibition", 13300], + ["kx2024-013", "서울국제생산제조기술전(SIMTOS 2024)", "2024-04-01", "2024-04-05", "홀 1, 10, 2, 3, 4, 5, 7, 8A, 8B, 9", "trade", 245500], + ["kx2024-014", "2024 서울캣쇼", "2024-04-12", "2024-04-14", "홀 7B", "exhibition", 15700], + ["kx2024-015", "코리아 나라장터 엑스포 2024", "2024-04-17", "2024-04-19", "홀 3, 4, 5", "exhibition", 52200], + ["kx2024-016", "2024 국제콜드체인산업전", "2024-04-23", "2024-04-26", "홀 9B", "trade", 22300], + ["kx2024-017", "제14회 국제물류산업대전", "2024-04-23", "2024-04-26", "홀 10", "trade", 15200], + ["kx2024-018", "제9회 국제화장품원료기술전", "2024-04-23", "2024-04-26", "홀 7B", "exhibition", 22700], + ["kx2024-019", "제18회 국제연구·실험 및 첨단분석장비전", "2024-04-23", "2024-04-26", "홀 8", "exhibition", 21900], + ["kx2024-020", "제14회 국제의약품·바이오산업전", "2024-04-23", "2024-04-26", "홀 7A", "trade", 25900], + ["kx2024-021", "2024년 상반기 국군장병 취업박람회", "2024-04-29", "2024-04-30", "홀 10", "exhibition", 10700], + ["kx2024-022", "제66회 MBC 건축박람회 외(통합명칭)", "2024-05-09", "2024-05-12", "홀 1A, 1B, 2A, 2B, 3, 4A", "exhibition", 60900], + ["kx2024-023", "2024 킨텍스 코베 베이비페어&유아교육전", "2024-05-09", "2024-05-12", "홀 10A, 10B", "culture", 22800], + ["kx2024-024", "2024 메가주 일산(상)", "2024-05-17", "2024-05-19", "홀 1A, 2, 3", "culture", 32100], + ["kx2024-025", "2024 플레이엑스포", "2024-05-23", "2024-05-26", "홀 3, 4, 5A, 5B", "exhibition", 58300], + ["kx2024-026", "2024 국제아웃도어캠핑&레포츠페스티벌(GOCAF 2024)", "2024-05-24", "2024-05-26", "홀 1, 2", "culture", 33500], + ["kx2024-027", "메가쇼 2024 시즌 1", "2024-05-30", "2024-06-02", "홀 2, 3", "exhibition", 41000], + ["kx2024-028", "트래블쇼 2024 시즌 1", "2024-05-30", "2024-06-02", "홀 2A", "exhibition", 25800], + ["kx2024-029", "2024 서울국제식품산업대전", "2024-06-11", "2024-06-14", "홀 1, 2, 3, 4, 5, 7, 8", "trade", 118800], + ["kx2024-030", "2024 농업기술박람회", "2024-06-11", "2024-06-14", "홀 10B", "exhibition", 16400], + ["kx2024-031", "2024 대한민국 고졸 인재 채용엑스포", "2024-06-12", "2024-06-13", "홀 6", "exhibition", 11700], + ["kx2024-032", "2024 케이팜 박람회", "2024-06-13", "2024-06-15", "홀 10A", "exhibition", 14200], + ["kx2024-033", "2024 여성발명왕EXPO(대한민국 세계여성발명대회 및 여성발명품박람회)", "2024-06-20", "2024-06-22", "홀 3A", "exhibition", 15500], + ["kx2024-034", "제19회 더골프쇼 KOREA 시즌2", "2024-06-20", "2024-06-23", "홀 10A", "exhibition", 25700], + ["kx2024-035", "제47회 맘앤베이비엑스포", "2024-06-20", "2024-06-23", "홀 1", "culture", 23000], + ["kx2024-036", "2024 생활낚시박람회", "2024-06-21", "2024-06-23", "홀 7A", "exhibition", 14200], + ["kx2024-037", "2024 로컬 콘텐츠 페스타", "2024-06-21", "2024-06-23", "홀 2", "exhibition", 12200], + ["kx2024-038", "2024 세계 배터리&충전인프라 엑스포", "2024-06-25", "2024-06-27", "홀 5A", "tech", 15100], + ["kx2024-039", "퀀텀코리아 2024", "2024-06-25", "2024-06-27", "홀 8B", "exhibition", 11200], + ["kx2024-040", "2024 세계 태양에너지 엑스포", "2024-06-25", "2024-06-27", "홀 4A", "trade", 15200], + ["kx2024-041", "국제광융합 O2O EXPO 2024", "2024-06-26", "2024-06-28", "홀 3A", "exhibition", 12200], + ["kx2024-042", "2024 고양가구박람회", "2024-06-27", "2024-06-30", "홀 1, 2A", "exhibition", 34600], + ["kx2024-043", "2024 국제안전보건전시회", "2024-07-01", "2024-07-04", "홀 7, 8", "trade", 33900], + ["kx2024-044", "제22회 국제 나노기술심포지엄 및 융합전시회", "2024-07-03", "2024-07-05", "홀 4A, 4B, 5A, 5B", "exhibition", 25100], + ["kx2024-045", "2024 중앙 재테크 박람회", "2024-07-10", "2024-07-11", "홀 6", "tech", 9100], + ["kx2024-046", "2025학년도 경기도교육청 수시 대학입학정보박람회", "2024-07-19", "2024-07-20", "홀 6", "exhibition", 7700], + ["kx2024-047", "2024년 제3회 킨텍스 캠핑페스타", "2024-07-19", "2024-07-21", "홀 7", "culture", 18900], + ["kx2024-048", "대한민국 국제 관광박람회", "2024-07-19", "2024-07-22", "홀 8", "exhibition", 19900], + ["kx2024-049", "K-PRINT 2024", "2024-08-21", "2024-08-24", "홀 7, 8A", "exhibition", 39500], + ["kx2024-050", "제67회 MBC건축박람회/2024 서울 냉·난방 및 건축설비 박람회/제39회 동아 홈&리빙 페어/제28회 동아 기프트 쇼/제26회 동아 차·공예 박람회/제24회 동아 스포츠·레저·골프산업 박람회/2024 센덱스(재활&복지)박람회", "2024-08-22", "2024-08-25", "홀 1A, 2A, 2B, 3", "culture", 51300], + ["kx2024-051", "2024 마이펫페어 일산 Part.2", "2024-08-23", "2024-08-25", "홀 10A, 10B", "culture", 17900], + ["kx2024-052", "2024 한국관상어산업박람회", "2024-08-23", "2024-08-25", "홀 5A", "exhibition", 12200], + ["kx2024-053", "제17회 폐기물자원순환산업전", "2024-08-28", "2024-08-30", "홀 10A", "trade", 11200], + ["kx2024-054", "2024 차세대 분체산업전", "2024-08-28", "2024-08-30", "홀 10B", "trade", 11200], + ["kx2024-055", "2024 콘텐츠유니버스 코리아", "2024-08-29", "2024-08-30", "홀 6", "exhibition", 9700], + ["kx2024-056", "2024 킨텍스 미베 베이비페어&유아교육전", "2024-08-29", "2024-09-01", "홀 2", "culture", 23200], + ["kx2024-057", "2024 한국국제가구 및 인테리어산업대전", "2024-08-29", "2024-09-01", "홀 7, 8, 2전시장 로비 7-1", "trade", 58600], + ["kx2024-058", "2024 경기주류대상", "2024-08-31", "2024-09-01", "홀 1A", "exhibition", 22500], + ["kx2024-059", "2024 월드 스마트시티 엑스포", "2024-09-03", "2024-09-05", "홀 3, 4, 5", "tech", 55700], + ["kx2024-060", "제1회 특화망 기술 산업전", "2024-09-04", "2024-09-06", "홀 1A, 1B", "trade", 12100], + ["kx2024-061", "제4회 유통·물류 및 공급망관리 산업전", "2024-09-04", "2024-09-06", "홀 2A, 2B", "trade", 15200], + ["kx2024-062", "2024 대한민국 소싱위크", "2024-09-05", "2024-09-06", "홀 9B", "exhibition", 7700], + ["kx2024-063", "2024 오토살롱위크", "2024-09-06", "2024-09-08", "홀 7, 8", "exhibition", 33300], + ["kx2024-064", "2024 궁디팡팡 캣페스타", "2024-09-06", "2024-09-08", "홀 10A, 10B", "exhibition", 13800], + ["kx2024-065", "H2 MEET 2024", "2024-09-25", "2024-09-27", "홀 4, 5", "exhibition", 28200], + ["kx2024-066", "이차전지 소재 · 부품 및 장비전 (K-BATTERY SHOW 2024) / 한국 소재 · 복합재료 및 장비전 (K-Mtech 2024)", "2024-09-25", "2024-09-27", "홀 3", "tech", 14500], + ["kx2024-067", "2024 한국국제냉난방공조전", "2024-09-25", "2024-09-27", "홀 1, 2", "trade", 33000], + ["kx2024-068", "제6회 대한민국방위산업전", "2024-09-25", "2024-09-28", "홀 7, 8A", "trade", 43100], + ["kx2024-069", "2024 국제아웃도어캠핑&레포츠페스티벌 더 파이널 시즌 PART 1", "2024-10-04", "2024-10-06", "홀 7, 8", "culture", 21600], + ["kx2024-070", "경기도 5070 일자리박람회", "2024-10-07", "2024-10-08", "홀 3", "exhibition", 10800], + ["kx2024-071", "2024 킨텍스 코베 베이비페어&유아교육전", "2024-10-10", "2024-10-13", "홀 10", "culture", 22800], + ["kx2024-072", "트리우스 광명 1차 입주박람회", "2024-10-12", "2024-10-13", "홀 8B", "exhibition", 10100], + ["kx2024-073", "2024 국제 공구 및 스마트 용접 자동화전", "2024-10-15", "2024-10-18", "홀 3A, 3B", "tech", 18600], + ["kx2024-074", "2024 국제 모빌리티 산업전", "2024-10-16", "2024-10-18", "홀 2A, 2B", "tech", 17100], + ["kx2024-075", "2024 한국건설안전박람회", "2024-10-16", "2024-10-18", "홀 4", "trade", 17300], + ["kx2024-076", "2024 아시아콘크리트엑스포", "2024-10-16", "2024-10-18", "홀 5A, 5B", "exhibition", 19000], + ["kx2024-077", "금속산업대전 2024", "2024-10-16", "2024-10-18", "홀 1", "trade", 12900], + ["kx2024-078", "2024 K-뷰티엑스포 코리아", "2024-10-17", "2024-10-19", "홀 7, 8", "culture", 30300], + ["kx2024-079", "2024 카페&베이커리페어", "2024-10-17", "2024-10-20", "홀 10", "culture", 15700], + ["kx2024-080", "2024 경기국제웹툰페어", "2024-10-17", "2024-10-20", "홀 9A, 9B", "culture", 17600], + ["kx2024-081", "2024 경기북부 일자리박람회", "2024-10-22", "2024-10-22", "홀 6", "exhibition", 3500], + ["kx2024-082", "2024 하반기 붐업코리아 수출상담회", "2024-10-22", "2024-10-23", "홀 1", "trade", 12600], + ["kx2024-083", "콘텐츠코리아 2024", "2024-10-23", "2024-10-25", "홀 7A", "exhibition", 13200], + ["kx2024-084", "2024 고양 드론ㆍ도심항공모빌리티 박람회", "2024-10-23", "2024-10-25", "홀 10B", "tech", 11200], + ["kx2024-085", "2024 디지털미디어테크쇼", "2024-10-23", "2024-10-25", "홀 8A", "tech", 13900], + ["kx2024-086", "2024 디지털퓨처쇼", "2024-10-23", "2024-10-25", "홀 7B", "tech", 15600], + ["kx2024-087", "The AI Show 2024", "2024-10-23", "2024-10-25", "홀 10A", "tech", 18700], + ["kx2024-088", "2024 로보월드", "2024-10-23", "2024-10-26", "홀 1, 2, 3", "exhibition", 53800], + ["kx2024-089", "제12회 한국국제건설기계전", "2024-10-24", "2024-10-27", "홀 4, 5", "trade", 50900], + ["kx2024-090", "2024 소부장뿌리기술대전", "2024-10-30", "2024-11-01", "홀 1", "exhibition", 27500], + ["kx2024-091", "2024 아시아 기계 & 제조 산업전", "2024-10-30", "2024-11-02", "홀 2A, 3A, 3B", "trade", 59100], + ["kx2024-092", "제27회 대한민국우수상품전시회(G-FAIR KOREA 2024)", "2024-10-31", "2024-11-02", "홀 4, 5", "tech", 57800], + ["kx2024-093", "올림픽파크포레온 입주박람회 2차", "2024-11-02", "2024-11-03", "홀 6", "exhibition", 7300], + ["kx2024-094", "제16회 2024 K-GEO Festa", "2024-11-06", "2024-11-08", "홀 10", "exhibition", 13900], + ["kx2024-095", "2024년 「고양시 중장년 일자리박람회」", "2024-11-07", "2024-11-07", "홀 6B", "exhibition", 5500], + ["kx2024-096", "메가쇼 2024 시즌 2", "2024-11-07", "2024-11-10", "홀 3, 4, 5A", "exhibition", 55400], + ["kx2024-097", "2024 가을 고양가구박람회", "2024-11-14", "2024-11-17", "홀 7", "exhibition", 16200], + ["kx2024-098", "2024 메가주 일산(하)", "2024-11-15", "2024-11-17", "홀 1, 2, 3, 4, 5A", "culture", 87900], + ["kx2024-099", "2024 보드게임페스타(일산)", "2024-11-16", "2024-11-17", "홀 8A", "tech", 8300], + ["kx2024-100", "2024 스마트건설 EXPO", "2024-11-20", "2024-11-22", "홀 1, 2", "tech", 27600], + ["kx2024-101", "제48회 맘앤베이비엑스포", "2024-11-21", "2024-11-24", "홀 7", "culture", 19800], + ["kx2024-102", "2024 K- 카 케어 페스티벌", "2024-11-23", "2024-11-23", "홀 6A, 6B", "culture", 4500], + ["kx2024-103", "2024 국제아웃도어캠핑&레포츠페스티벌 더 파이널 시즌 PART 2", "2024-11-29", "2024-12-01", "홀 7, 8A", "culture", 53800], + ["kx2024-104", "힐스테이트 관악 센트씨엘 입주박람회", "2024-11-30", "2024-12-01", "홀 10B", "exhibition", 19300], + ["kx2024-105", "2024 우리쌀·우리술 K-라이스페스타", "2024-12-06", "2024-12-08", "홀 7A, 7B", "exhibition", 15200], + ["kx2024-106", "힐스테이트 검단 포레스트 입주박람회", "2024-12-06", "2024-12-08", "홀 6B, 6C", "exhibition", 15300], + ["kx2024-107", "힐스테이트 검단 웰카운티 아파트 1차 입주박람회", "2024-12-07", "2024-12-08", "홀 8B", "exhibition", 11700], + ["kx2024-108", "2024 대한민국 교육혁신 박람회", "2024-12-13", "2024-12-15", "홀 10", "exhibition", 18400], + ["kx2025-001", "2025 까사 가구쇼", "2025-01-09", "2025-01-12", "홀 3", "exhibition", 16500], + ["kx2025-002", "카페디저트페어", "2025-01-16", "2025-01-19", "홀 7A", "culture", 17700], + ["kx2025-003", "마이펫페어 2025 일산 Part1", "2025-01-17", "2025-01-19", "홀 3", "culture", 14200], + ["kx2025-004", "2025 코리아빌드위크", "2025-02-19", "2025-02-22", "홀 1, 2, 3, 4, 5", "exhibition", 110600], + ["kx2025-005", "제49회 맘앤베이비엑스포", "2025-02-27", "2025-03-02", "홀 1", "culture", 29500], + ["kx2025-006", "경기국제보트쇼", "2025-02-28", "2025-03-02", "홀 3A, 4, 5", "exhibition", 60600], + ["kx2025-007", "2025 한국국제낚시박람회", "2025-02-28", "2025-03-02", "홀 2", "exhibition", 27500], + ["kx2025-008", "코리아 나라장터 엑스포 2025", "2025-03-05", "2025-03-07", "홀 1, 2, 3", "exhibition", 56400], + ["kx2025-009", "2025 코리아나라장터엑스포", "2025-03-05", "2025-03-07", "홀 미정", "exhibition", 13900], + ["kx2025-010", "2025 펫쇼코리아(상)", "2025-03-07", "2025-03-09", "홀 7", "culture", 11700], + ["kx2025-011", "제27회 국제금형 및 관련산업전", "2025-03-11", "2025-03-14", "홀 2, 3", "trade", 47300], + ["kx2025-012", "제28회 국제플라스틱 · 고무산업전시회 (KOPLAS 2025) / 한국 소재 · 복합재료 및 장비전 (K-Mtech 2025)", "2025-03-11", "2025-03-14", "홀 4, 5", "trade", 47100], + ["kx2025-013", "2025 캠핑&피크닉페어", "2025-03-13", "2025-03-16", "홀 7, 8", "culture", 29600], + ["kx2025-014", "제20회 JTBC GOLF × 더골프쇼 코리아 시즌1", "2025-03-13", "2025-03-16", "홀 10A", "exhibition", 20400], + ["kx2025-015", "2025 고양 가구엑스포", "2025-03-13", "2025-03-16", "홀 9A, 9B", "exhibition", 19300], + ["kx2025-016", "제24회 세계 보안 엑스포 & 제13회 전자정부 정보보호 솔루션 페어", "2025-03-19", "2025-03-21", "홀 3, 4, 5", "tech", 47800], + ["kx2025-017", "2025 국제물산업박람회(WATER KOREA)", "2025-03-19", "2025-03-21", "홀 7, 8", "exhibition", 28000], + ["kx2025-018", "2025서울모빌리티쇼", "2025-04-03", "2025-04-13", "홀 1, 2, 3, 4, 5", "tech", 192100], + ["kx2025-019", "2025 경기도 기후테크 전시회", "2025-04-15", "2025-04-16", "홀 6C", "tech", 7400], + ["kx2025-020", "2025 ESG 지속가능 패키징 페어", "2025-04-22", "2025-04-25", "홀 4B", "culture", 14400], + ["kx2025-021", "제20회 국제제약·바이오·화장품기술전", "2025-04-22", "2025-04-25", "홀 5", "exhibition", 15100], + ["kx2025-022", "코리아랩 (제19회 국제연구•실험 및 첨단분석장비전)", "2025-04-22", "2025-04-25", "홀 8", "exhibition", 25000], + ["kx2025-023", "제10회 코스메틱 인사이드 코리아", "2025-04-22", "2025-04-25", "홀 7B", "exhibition", 25400], + ["kx2025-024", "코리아팜 & 바이오 (제15회 국제의약품•바이오산업전)", "2025-04-22", "2025-04-25", "홀 7A", "trade", 16900], + ["kx2025-025", "제17회 국제화학장치산업전", "2025-04-22", "2025-04-25", "홀 4A", "trade", 19200], + ["kx2025-026", "제3회 국제콜드체인산업전", "2025-04-22", "2025-04-25", "홀 3A", "trade", 24600], + ["kx2025-027", "제 15회 국제물류산업대전", "2025-04-22", "2025-04-25", "홀 2, 3B", "trade", 36400], + ["kx2025-028", "2025 냥냥펀치 캣쇼 일산", "2025-05-02", "2025-05-04", "홀 미정", "exhibition", 13700], + ["kx2025-029", "2025년 국군장병 취업박람회", "2025-05-07", "2025-05-08", "홀 10", "exhibition", 12000], + ["kx2025-030", "제68회 MBC건축박람회 외(통합명칭)", "2025-05-08", "2025-05-11", "홀 3A, 4, 5", "exhibition", 52100], + ["kx2025-031", "2025 코베 베이비페어&유아교육전", "2025-05-08", "2025-05-11", "홀 7", "culture", 20500], + ["kx2025-032", "Shanghai Fair going to Korea - Trade Show 2025", "2025-05-13", "2025-05-14", "홀 6B, 6C", "exhibition", 9300], + ["kx2025-033", "트래블쇼 & 한국축제박람회", "2025-05-15", "2025-05-18", "홀 5A", "exhibition", 22100], + ["kx2025-034", "메가쇼 2025 시즌1", "2025-05-15", "2025-05-18", "홀 4, 5B", "exhibition", 28600], + ["kx2025-035", "2025 메가주 일산(상)", "2025-05-16", "2025-05-18", "홀 7, 8", "culture", 36300], + ["kx2025-036", "25년 한국시니어클럽 노인일자리 박람회", "2025-05-21", "2025-05-21", "홀 9B", "exhibition", 4500], + ["kx2025-037", "2025 플레이엑스포", "2025-05-22", "2025-05-25", "홀 3, 4, 5", "exhibition", 51000], + ["kx2025-038", "2025여성발명왕EXPO", "2025-05-22", "2025-05-24", "홀 10A", "exhibition", 11500], + ["kx2025-039", "2025 국제아웃도어캠핑&레포츠페스티벌", "2025-05-23", "2025-05-25", "홀 7, 8", "culture", 37100], + ["kx2025-040", "2025 대한민국 고졸 인재 채용엑스포", "2025-05-28", "2025-05-29", "홀 10B", "exhibition", 10200], + ["kx2025-041", "2025 코리아 스피릿페스타", "2025-05-29", "2025-06-01", "홀 7B", "exhibition", 21000], + ["kx2025-042", "제21회 더골프쇼 KOREA 시즌2", "2025-05-29", "2025-06-01", "홀 8A", "exhibition", 24300], + ["kx2025-043", "2025 카페&베이커리페어 시즌1", "2025-05-29", "2025-06-01", "홀 7A", "culture", 29600], + ["kx2025-044", "2025 생활낚시박람회", "2025-05-30", "2025-06-01", "홀 10A", "exhibition", 30500], + ["kx2025-045", "제50회 맘앤베이비엑스포", "2025-06-05", "2025-06-08", "홀 10", "culture", 17700], + ["kx2025-046", "SEOUL FOOD 2025", "2025-06-10", "2025-06-13", "홀 1, 2, 3, 4, 5, 7, 8", "exhibition", 156900], + ["kx2025-047", "제17회 고양가구박람회", "2025-06-19", "2025-06-22", "홀 7, 8", "exhibition", 40400], + ["kx2025-048", "국제광융합엑스포 2025", "2025-06-25", "2025-06-27", "홀 7A", "exhibition", 18400], + ["kx2025-049", "2025 코리아 카케어 페스티벌", "2025-06-28", "2025-06-29", "홀 6B, 6C", "culture", 12800], + ["kx2025-050", "나노코리아 2025", "2025-07-02", "2025-07-04", "홀 1, 2", "exhibition", 21800], + ["kx2025-051", "2025년 제4회 킨텍스 캠핑페스타", "2025-07-04", "2025-07-06", "홀 3", "culture", 11400], + ["kx2025-052", "2025 국제안전보건전시회", "2025-07-07", "2025-07-10", "홀 7, 8", "trade", 35000], + ["kx2025-053", "2025 펫쇼코리아(하)", "2025-07-18", "2025-07-20", "홀 10", "culture", 17200], + ["kx2025-054", "2025 대한민국 국제 관광박람회", "2025-07-18", "2025-07-21", "홀 7", "exhibition", 19000], + ["kx2025-055", "2025 어린이 통합 안전 박람회", "2025-07-24", "2025-07-26", "홀 7A", "trade", 13100], + ["kx2025-056", "2025 고양 우수중소기업&농수산물박람회", "2025-07-31", "2025-08-04", "홀 1A", "exhibition", 19200], + ["kx2025-057", "2025 코베 베이비페어&유아교육전", "2025-08-07", "2025-08-10", "홀 10", "culture", 20500], + ["kx2025-058", "2025 카페디저트페어", "2025-08-13", "2025-08-16", "홀 7A", "culture", 21900], + ["kx2025-059", "2025 한국관상어산업박람회 및 품평회", "2025-08-15", "2025-08-17", "홀 5", "exhibition", 17300], + ["kx2025-060", "K-PRINT 2025", "2025-08-20", "2025-08-23", "홀 7, 8A", "exhibition", 43100], + ["kx2025-061", "제69회 건축박람회 외(통합명칭)", "2025-08-21", "2025-08-24", "홀 3, 4", "exhibition", 34200], + ["kx2025-062", "2025 차세대 분체산업전", "2025-08-27", "2025-08-29", "홀 10B", "trade", 14400], + ["kx2025-063", "제18회 폐기물·자원순환산업전(RETECH 2025)", "2025-08-27", "2025-08-29", "홀 10A", "trade", 17800], + ["kx2025-064", "2025 한국국제가구 및 인테리어산업대전", "2025-08-28", "2025-08-31", "홀 7, 8, 2전시장 로비 7-1", "trade", 34000], + ["kx2025-065", "2025년 사회리더 대학생 멘토링 리더십콘서트", "2025-08-30", "2025-08-30", "홀 6", "exhibition", 4600], + ["kx2025-066", "2025 경기도교육청 직업계고 취창업박람회", "2025-09-02", "2025-09-03", "홀 7B", "exhibition", 12600], + ["kx2025-067", "2025 대한민국 글로벌 소싱위크", "2025-09-04", "2025-09-05", "홀 7A", "exhibition", 8500], + ["kx2025-068", "마이펫페어 2025 일산 Part2", "2025-09-05", "2025-09-07", "홀 10", "culture", 11700], + ["kx2025-069", "2025 이차전지 소재 · 부품 및 장비전(K-BATTERY)", "2025-09-10", "2025-09-12", "홀 4, 5", "tech", 24700], + ["kx2025-070", "제5회 유통·물류 및 공급망관리 산업전", "2025-09-10", "2025-09-12", "홀 3", "trade", 15900], + ["kx2025-071", "제2회 특화망 기술 산업전", "2025-09-10", "2025-09-12", "홀 2", "trade", 14400], + ["kx2025-072", "제1회 대한민국 산업단지 수출박람회", "2025-09-10", "2025-09-12", "홀 1", "trade", 16100], + ["kx2025-073", "2025 K-뷰티엑스포 코리아", "2025-09-11", "2025-09-13", "홀 7, 8", "culture", 39000], + ["kx2025-074", "2025 대한민국 안전산업박람회", "2025-09-17", "2025-09-19", "홀 1, 2, 3", "trade", 35800], + ["kx2025-075", "2025 한국건설ㆍ안전박람회", "2025-09-17", "2025-09-19", "홀 4", "trade", 14500], + ["kx2025-076", "2025 경기국제웹툰페어", "2025-09-18", "2025-09-21", "홀 5A", "culture", 24100], + ["kx2025-077", "2025 궁디팡팡 캣페스타", "2025-09-19", "2025-09-21", "홀 10", "exhibition", 12400], + ["kx2025-078", "2025 오토살롱테크코리아", "2025-09-19", "2025-09-21", "홀 7", "tech", 13200], + ["kx2025-079", "2025 아시아 기계 & 제조산업전", "2025-09-23", "2025-09-26", "홀 2, 3", "trade", 44100], + ["kx2025-080", "2025 K-GEO Festa", "2025-09-24", "2025-09-26", "홀 5", "exhibition", 14000], + ["kx2025-081", "2025 월드 시티테크 엑스포", "2025-09-24", "2025-09-26", "홀 4, 5", "tech", 26600], + ["kx2025-082", "2025 국제아웃도어캠핑&레포츠페스티벌 더 파이널 시즌 PART 1", "2025-09-26", "2025-09-28", "홀 7, 8", "culture", 35000], + ["kx2025-083", "경기도 5070 일자리박람회", "2025-10-14", "2025-10-14", "홀 6", "exhibition", 5400], + ["kx2025-084", "2025 코베 베이비페어&유아교육전", "2025-10-16", "2025-10-19", "홀 4A, 5A", "culture", 41100], + ["kx2025-085", "제18회 고양가구박람회", "2025-10-16", "2025-10-19", "홀 2, 3", "exhibition", 33500], + ["kx2025-086", "제24회 고양국제아트페어", "2025-10-16", "2025-10-19", "홀 6B", "culture", 19600], + ["kx2025-087", "서울 ADEX 2025(서울 국제 항공우주 및 방위산업 전시회 2025)", "2025-10-20", "2025-10-24", "홀 10, 7, 8, 9", "trade", 110600], + ["kx2025-088", "2025 수출붐업코리아 Week", "2025-10-21", "2025-10-22", "홀 1, 2", "trade", 14900], + ["kx2025-089", "제22회 국제모빌리티산업전", "2025-10-22", "2025-10-24", "홀 4", "tech", 14600], + ["kx2025-090", "2025 청소년비즈쿨 페스티벌", "2025-10-27", "2025-10-29", "홀 10A", "culture", 16000], + ["kx2025-091", "2025 국제도로교통박람회", "2025-10-28", "2025-10-30", "홀 7A", "exhibition", 13100], + ["kx2025-092", "2025 국제 공구 및 스마트 용접 자동화전", "2025-10-28", "2025-10-31", "홀 2", "tech", 17700], + ["kx2025-093", "한국포장전 2025", "2025-10-28", "2025-10-31", "홀 7B, 8", "exhibition", 48000], + ["kx2025-094", "2025 소부장뿌리 기술대전", "2025-10-29", "2025-10-31", "홀 1", "exhibition", 17600], + ["kx2025-095", "금속산업대전 2025", "2025-10-29", "2025-10-31", "홀 3", "trade", 15500], + ["kx2025-096", "제28회 대한민국우수상품전시회(G-FAIR KOREA 2025)", "2025-10-30", "2025-11-01", "홀 4, 5", "tech", 37300], + ["kx2025-097", "2025 대한민국 소상공인대회", "2025-10-31", "2025-11-01", "홀 9", "exhibition", 22400], + ["kx2025-098", "2025 드론 · 도심항공모빌리티 박람회", "2025-11-05", "2025-11-07", "홀 4A", "tech", 14200], + ["kx2025-099", "Solar Asia 2025", "2025-11-05", "2025-11-07", "홀 5A", "exhibition", 15200], + ["kx2025-100", "Battery Asia Show 2025", "2025-11-05", "2025-11-07", "홀 5B", "exhibition", 18100], + ["kx2025-101", "2025 스마트건설·안전·AI 엑스포", "2025-11-05", "2025-11-07", "홀 7, 8", "tech", 24900], + ["kx2025-102", "2025 경기도 건설신기술 박람회", "2025-11-05", "2025-11-07", "홀 8B", "trade", 11900], + ["kx2025-103", "2025 카페&베이커리페어 시즌2", "2025-11-05", "2025-11-08", "홀 10A", "culture", 19600], + ["kx2025-104", "2025 로보월드", "2025-11-05", "2025-11-08", "홀 1, 2", "exhibition", 40500], + ["kx2025-105", "2025 디지털미디어테크쇼", "2025-11-05", "2025-11-07", "홀 3A", "tech", 17100], + ["kx2025-106", "디지털퓨처쇼 2025", "2025-11-05", "2025-11-07", "홀 3B", "tech", 12800], + ["kx2025-107", "2025 마이프차 프랜차이즈 페스타", "2025-11-05", "2025-11-08", "홀 10B", "exhibition", 24500], + ["kx2025-108", "2025 고양시 중장년 일자리 박람회", "2025-11-06", "2025-11-06", "홀 6C", "exhibition", 5900], + ["kx2025-109", "2025 대한민국 가상융합산업대전", "2025-11-12", "2025-11-14", "홀 5", "trade", 10900], + ["kx2025-110", "콘텐츠코리아 2025", "2025-11-13", "2025-11-15", "홀 1", "exhibition", 17500], + ["kx2025-111", "메가쇼 2025 시즌2", "2025-11-13", "2025-11-16", "홀 2A, 3, 4", "exhibition", 49400], + ["kx2025-112", "제22회 더골프쇼 KOREA 시즌3", "2025-11-13", "2025-11-16", "홀 9B", "exhibition", 21300], + ["kx2025-113", "2025 아세안 무역전시회", "2025-11-13", "2025-11-16", "홀 6B, 6C", "trade", 25900], + ["kx2025-114", "2025년 한이음 드림업 엑스포", "2025-11-19", "2025-11-19", "홀 8B", "exhibition", 4800], + ["kx2025-115", "제1회 한국문신산업박람회 (TEXPO)", "2025-11-20", "2025-11-22", "홀 9", "exhibition", 13900], + ["kx2025-116", "2025 메가주 일산(하)", "2025-11-21", "2025-11-23", "홀 1A, 2, 3, 4", "culture", 71200], + ["kx2025-117", "2025 경기어린이박람회", "2025-11-21", "2025-11-23", "홀 8A", "exhibition", 12900], + ["kx2025-118", "2025 대한민국 AI교육 페스티벌", "2025-11-21", "2025-11-22", "홀 7B", "tech", 9500], + ["kx2025-119", "2025 고양 가구엑스포", "2025-11-27", "2025-11-30", "홀 9", "exhibition", 19300], + ["kx2025-120", "제51회 맘앤베이비엑스포", "2025-11-27", "2025-11-30", "홀 10", "culture", 16200], + ["kx2025-121", "2025 우리쌀·우리술 K- 라이스페스타", "2025-11-28", "2025-11-30", "홀 3", "exhibition", 15900], + ["kx2025-122", "2025 국제아웃도어캠핑&레포츠페스티벌", "2025-11-28", "2025-11-30", "홀 4, 5", "culture", 37100], + ["kx2025-123", "H2 MEET 2025", "2025-12-04", "2025-12-07", "홀 6C, 7A, 7B, 8", "exhibition", 51800], + ["kx2025-124", "제97회 국제친선을위한 세계아동 미술대회 입상작품 전시회", "2025-12-19", "2025-12-21", "홀 6B", "exhibition", 14600], + ["kx2026-001", "마이펫페어 2026 일산 Part1", "2026-01-16", "2026-01-18", "홀 7", "culture", 11600], + ["kx2026-002", "2026 카페디저트페어", "2026-01-22", "2026-01-25", "홀 7A", "culture", 17900], + ["kx2026-003", "2026 코리아빌드위크", "2026-02-04", "2026-02-07", "홀 1, 2, 3, 4, 5", "exhibition", 103300], + ["kx2026-004", "제52회 맘앤베이비엑스포", "2026-02-26", "2026-03-01", "홀 7", "culture", 24200], + ["kx2026-005", "2026 서울경기 밀리언쇼 우수중소기업&농특산품박람회", "2026-03-05", "2026-03-09", "홀 1A", "exhibition", 20200], + ["kx2026-006", "2026 캠핑앤피크닉페어", "2026-03-06", "2026-03-08", "홀 7, 8", "culture", 27000], + ["kx2026-007", "2026 한국국제낚시박람회", "2026-03-06", "2026-03-08", "홀 2", "exhibition", 17200], + ["kx2026-008", "2026 경기국제보트쇼", "2026-03-06", "2026-03-08", "홀 3, 4, 5", "exhibition", 36200], + ["kx2026-009", "2026 고양 가구엑스포 & 홈앤리빙 더쇼 & 큐브 현대미술展", "2026-03-12", "2026-03-15", "홀 10", "culture", 18400], + ["kx2026-010", "2026 펫쇼코리아(상)", "2026-03-13", "2026-03-15", "홀 7", "culture", 15200], + ["kx2026-011", "오토모티브 테스팅 엑스포 2026", "2026-03-18", "2026-03-20", "홀 10B", "exhibition", 17000], + ["kx2026-012", "제25회 세계 보안 엑스포 & 제14회 전자정부 정보보호 솔루션 페어", "2026-03-18", "2026-03-20", "홀 3, 4, 5", "tech", 38800], + ["kx2026-013", "코리아 나라장터 엑스포 2026", "2026-03-25", "2026-03-27", "홀 2B, 3, 4, 5", "exhibition", 51100], + ["kx2026-014", "국제의약품·바이오산업전", "2026-03-31", "2026-04-03", "홀 7A", "trade", 18300], + ["kx2026-015", "국제물류산업대전", "2026-03-31", "2026-04-03", "홀 9B", "trade", 26300], + ["kx2026-016", "국제연구·실험 및 첨단분석장비전", "2026-03-31", "2026-04-03", "홀 7A", "exhibition", 29000], + ["kx2026-017", "국제다크팩토리산업전", "2026-03-31", "2026-04-03", "홀 10B", "trade", 25500], + ["kx2026-018", "국제콜드체인산업전", "2026-03-31", "2026-04-03", "홀 10A", "trade", 20700], + ["kx2026-019", "코스메틱 인사이드 코리아", "2026-03-31", "2026-04-03", "홀 7A", "exhibition", 24400], + ["kx2026-020", "국제화학장치·공정기술전", "2026-03-31", "2026-04-03", "홀 4A", "trade", 20800], + ["kx2026-021", "국제제약·바이오·화장품기술전", "2026-03-31", "2026-04-03", "홀 4B, 5", "exhibition", 57000], + ["kx2026-022", "제 2회 ESG 지속가능패키징페어", "2026-03-31", "2026-04-03", "홀 1A", "culture", 24600], + ["kx2026-023", "제 25회 국제포장기자재전", "2026-03-31", "2026-04-03", "홀 1B, 2, 3", "exhibition", 67700], + ["kx2026-024", "서울국제생산제조기술전(SIMTOS 2026)", "2026-04-13", "2026-04-17", "홀 1, 2, 3, 4, 5, 7, 8, 9, 10", "trade", 251500], + ["kx2026-025", "2026 냥냥펀치 캣쇼 일산", "2026-04-24", "2026-04-26", "홀 7B", "exhibition", 11200], + ["kx2026-026", "2026 대한민국 과학축제 in 경기 (with 대한민국 과학기술대전)", "2026-04-24", "2026-04-26", "홀 4", "exhibition", 14400], + ["kx2026-027", "2026 AI Co-Scientist Challenge Korea 시상식 및 컨퍼런스", "2026-04-24", "2026-04-25", "홀 5A", "tech", 11800], + ["kx2026-028", "2026년 제17기 사회리더 대학생 멘토링 발대식", "2026-04-25", "2026-04-25", "홀 6", "exhibition", 5100], + ["kx2026-029", "2026년 국군장병 취업박람회", "2026-04-28", "2026-04-29", "홀 7A, 7B", "exhibition", 10600], + ["kx2026-030", "제70회 MBC건축박람회 외(통합명칭)", "2026-05-07", "2026-05-10", "홀 3A, 4, 5", "exhibition", 63600], + ["kx2026-031", "메가쇼 & 트래블쇼 2026 시즌 1", "2026-05-14", "2026-05-17", "홀 4, 5", "exhibition", 45500], + ["kx2026-032", "2026 코베 베이비페어&유아교육전 (5월)", "2026-05-14", "2026-05-17", "홀 10A-1, 10B-1", "culture", 24700], + ["kx2026-033", "2026 플레이엑스포", "2026-05-21", "2026-05-24", "홀 2A, 3A, 3B, 4A, 4B, 5A, 5B, 5B-2", "exhibition", 82400], + ["kx2026-034", "2026 국제아웃도어캠핑&레포츠페스티벌(5월)", "2026-05-23", "2026-05-25", "홀 7, 8", "culture", 31000], + ["kx2026-035", "2026 메가주 시즌1", "2026-05-29", "2026-05-31", "홀 7, 8", "culture", 29700], + ["kx2026-036", "2026 마이프차 프랜차이즈창업박람회 시즌 1", "2026-06-04", "2026-06-06", "홀 10B", "exhibition", 19200], + ["kx2026-037", "2026 카페&베이커리페어 시즌1", "2026-06-04", "2026-06-07", "홀 10A", "culture", 15300], + ["kx2026-038", "2026 서울국제식품산업대전", "2026-06-09", "2026-06-12", "홀 1, 2, 3, 4, 5, 7, 8", "trade", 128600], + ["kx2026-039", "2026 대한민국 직업계고 고졸인재 채용엑스포", "2026-06-10", "2026-06-11", "홀 6", "exhibition", 12900], + ["kx2026-040", "제53회 맘앤베이비엑스포", "2026-06-18", "2026-06-21", "홀 10", "culture", 16300], + ["kx2026-041", "2026고양가구박람회", "2026-06-18", "2026-06-21", "홀 7, 8", "exhibition", 36300], + ["kx2026-042", "2026 어린이안전박람회", "2026-06-25", "2026-06-27", "홀 7A", "trade", 13200], + ["kx2026-043", "렙타일페어", "2026-06-27", "2026-06-28", "홀 6B", "culture", 7900], + ["kx2026-044", "대한민국 국제 관광박람회", "2026-07-03", "2026-07-05", "홀 10", "exhibition", 14900], + ["kx2026-045", "2026년 제5회 킨텍스 캠핑페스타", "2026-07-03", "2026-07-05", "홀 2", "culture", 12300], + ["kx2026-046", "AI안전보건박람회(2026국제안전보건전시회)", "2026-07-06", "2026-07-09", "홀 7, 8", "tech", 49400], + ["kx2026-047", "제24회 국제 나노기술융합전시회", "2026-07-08", "2026-07-10", "홀 3, 4", "exhibition", 22100], + ["kx2026-048", "국제광융합엑스포 2026", "2026-07-08", "2026-07-10", "홀 2A", "exhibition", 12700], + ["kx2026-049", "KDTEX 2026", "2026-07-17", "2026-07-19", "홀 7B, 8A, 8B", "exhibition", 23800], + ["kx2026-050", "2026 펫쇼코리아(하)", "2026-07-24", "2026-07-26", "홀 10", "culture", 11400], + ["kx2026-051", "2026 코베 베이비페어&유아교육전", "2026-08-06", "2026-08-09", "홀 7A-1, 7B-1", "culture", 17600], + ["kx2026-052", "2026 카페디저트페어", "2026-08-13", "2026-08-16", "홀 10A", "culture", 17900], + ["kx2026-053", "K-PRINT 2026", "2026-08-19", "2026-08-22", "홀 7A, 7B, 8A, 8B-1", "exhibition", 31500], + ["kx2026-054", "제71회 MBC건축박람회 외(통합명칭)", "2026-08-20", "2026-08-23", "홀 4, 5", "exhibition", 28800], + ["kx2026-055", "2026 생활낚시박람회", "2026-08-21", "2026-08-23", "홀 10B", "exhibition", 18600], + ["kx2026-056", "2026 월드 시티테크 엑스포", "2026-08-26", "2026-08-28", "홀 4", "tech", 19200], + ["kx2026-057", "한국국제가구 및 인테리어산업대전 (코펀)", "2026-08-27", "2026-08-30", "홀 7, 8", "trade", 48700], + ["kx2026-058", "마이펫페어 2026 일산 Part 2", "2026-08-28", "2026-08-30", "홀 10", "culture", 12400], + ["kx2026-059", "제19회 폐기물·자원순환산업전(RETECH) & 차세대 분체·유체기술전(A-POWDER TECH)", "2026-09-02", "2026-09-04", "홀 7", "trade", 15100], + ["kx2026-060", "제6회 국제 운송 · 공급망관리 산업전", "2026-09-09", "2026-09-11", "홀 5", "trade", 12100], + ["kx2026-061", "이차전지 소재 · 부품 및 장비전 (K-BATTERY SHOW 2026)", "2026-09-09", "2026-09-11", "홀 7A-1, 7B-1", "tech", 15100], + ["kx2026-062", "2026 로컬 콘텐츠 페스타", "2026-09-18", "2026-09-20", "홀 4", "exhibition", 18800], + ["kx2026-063", "2026 코베 베이비페어&유아교육전", "2026-10-08", "2026-10-11", "홀 10", "culture", 17600], + ["kx2026-064", "2026 국제아웃도어캠핑&레포츠페스티벌 더 파이널 시즌 PART 1", "2026-10-09", "2026-10-11", "홀 7, 8", "culture", 23800], + ["kx2026-065", "2026 도시안전기술박람회", "2026-10-14", "2026-10-16", "홀 1", "trade", 17800], + ["kx2026-066", "2026 한국건설·안전박람회", "2026-10-14", "2026-10-16", "홀 2", "trade", 14500], + ["kx2026-067", "2026 K-뷰티엑스포 코리아", "2026-10-15", "2026-10-17", "홀 3, 4, 5", "culture", 48400], + ["kx2026-068", "국제 모빌리티 산업전", "2026-10-21", "2026-10-23", "홀 3", "tech", 13100], + ["kx2026-069", "2026 TEX+VISION", "2026-10-21", "2026-10-23", "홀 10", "exhibition", 19000], + ["kx2026-070", "2026 국제 공구 및 스마트 용접 자동화전", "2026-10-27", "2026-10-30", "홀 2A, 2B-1", "tech", 19400], + ["kx2026-071", "2026 솔라 아시아", "2026-10-28", "2026-10-30", "홀 7", "exhibition", 10700], + ["kx2026-072", "2026 카페&베이커리페어 시즌2", "2026-10-29", "2026-11-01", "홀 10", "culture", 29300], + ["kx2026-073", "2026 드론·도심항공모빌리티 박람회", "2026-11-04", "2026-11-06", "홀 2", "tech", 13200], + ["kx2026-074", "WORLD HYDROGEN EXPO 2026 (H₂ MEET 2026)", "2026-11-04", "2026-11-06", "홀 6C, 7, 8", "exhibition", 47900], + ["kx2026-075", "한국국제승강기엑스포 2026", "2026-11-11", "2026-11-13", "홀 5", "trade", 17500], + ["kx2026-076", "2026 메가주 일산 시즌2", "2026-11-20", "2026-11-22", "홀 2, 3, 4, 5", "culture", 63900], + ["kx2026-077", "2026 국제아웃도어캠핑&레포츠페스티벌 더 파이널 시즌 PART 2", "2026-11-27", "2026-11-29", "홀 8A, 7", "culture", 34500], +]; diff --git a/src/frontend/src/screens/schedule/sampleSchedule.ts b/src/frontend/src/screens/schedule/sampleSchedule.ts index 5ac1b65..098f35d 100644 --- a/src/frontend/src/screens/schedule/sampleSchedule.ts +++ b/src/frontend/src/screens/schedule/sampleSchedule.ts @@ -2,12 +2,14 @@ * 전시 일정 관리 표시 데이터 (SCR-15 · M1·M6). * ★ 경계면(부분 실 + 부분 샘플): * - 행사(Event) 목록·기간·배정 홀은 실 API 가능(Event 엔티티 존재). - * 현재는 인증 응답 workspaces(WorkspaceDto)를 실 원천으로 사용하며, 없으면 아래 샘플로 폴백한다. - * - 홀 가동률 집계·외부 행사일정 연동은 미구현 → 샘플. 캘린더 점유 매트릭스는 M1 배정 정합 후 실 전환. + * 현재는 인증 응답 workspaces(WorkspaceDto)를 실 원천으로 사용하며, 없으면 크롤링 데이터로 폴백한다. + * - 폴백 원천: kintex.com 크롤링 10년치(2016~2026, 1,098건) — kintexEvents.ts (백엔드 V10 시드와 동일). + * - estVisitors·카테고리는 추정치(샘플). 홀 가동률·캘린더 점유는 크롤링 데이터 기반 계산. */ import type { FlowStatus } from '../../components/ui/Badge'; +import { KINTEX_EVENT_ROWS, type KxCategory } from './kintexEvents'; -export type EventCategory = 'exhibition' | 'culture' | 'trade' | 'tech'; +export type EventCategory = KxCategory; export type EventStatus = 'ongoing' | 'upcoming' | 'ended'; export interface ScheduleEvent { @@ -16,6 +18,8 @@ export interface ScheduleEvent { category: EventCategory; status: EventStatus; period: string; // "2026.09.05–09.08" + start: string; // "2026-09-05" + end: string; // "2026-09-08" halls: string; // "홀 4, 5" estVisitors: string; // "25,000명" designStatus?: FlowStatus; @@ -34,66 +38,122 @@ export const STATUS_LABEL: Record = { ended: '종료', }; -export const SAMPLE_EVENTS: ScheduleEvent[] = [ - { - id: 'smf2026', - title: '스마트팩토리 코리아', - category: 'trade', - status: 'ongoing', - period: '2026.09.05–09.08', - halls: '홀 4, 5', - estVisitors: '25,000명', - designStatus: 'building', - thumbIdx: 0, - }, - { - id: 'health2026', - title: '국제 헬스케어 위크', - category: 'exhibition', - status: 'upcoming', - period: '2026.09.22–09.25', - halls: '홀 7', - estVisitors: '18,000명', - designStatus: 'submitted', - thumbIdx: 1, - }, - { - id: 'foodtech2026', - title: '푸드테크 엑스포', - category: 'tech', - status: 'upcoming', - period: '2026.10.10–10.13', - halls: '홀 3', - estVisitors: '12,000명', - designStatus: 'draft', - thumbIdx: 2, - }, - { - id: 'living2026', - title: '리빙·인테리어 페어', - category: 'culture', - status: 'ended', - period: '2026.08.14–08.17', - halls: '홀 1, 2', - estVisitors: '30,000명', - designStatus: 'inspected', - thumbIdx: 0, - }, -]; +const TODAY_YMD = '2026-07-11'; // currentDate 고정(데모) — ExhibitionSchedulePage.TODAY 와 정합 + +function statusOf(start: string, end: string): EventStatus { + if (end < TODAY_YMD) return 'ended'; + if (start > TODAY_YMD) return 'upcoming'; + return 'ongoing'; +} + +/** "2016-01-28","2016-01-31" → "2016.01.28–01.31" */ +function periodLabel(start: string, end: string): string { + const s = start.split('-').join('.'); + const e = end.split('-').join('.'); + return e.startsWith(s.slice(0, 5)) ? `${s}–${e.slice(5)}` : `${s}–${e}`; +} + +// 진행중 데모 표시용 설계 상태(샘플) — 진행중·임박 행사에만 부여 +const DESIGN_CYCLE: FlowStatus[] = ['building', 'submitted', 'draft', 'inspected']; + +/** 크롤링 10년치 전체 (1,098건, 시작일 오름차순). */ +export const ALL_EVENTS: ScheduleEvent[] = KINTEX_EVENT_ROWS.map((r, i) => { + const [id, title, start, end, halls, category, visitors] = r; + const status = statusOf(start, end); + return { + id, + title, + category, + status, + period: periodLabel(start, end), + start, + end, + halls, + estVisitors: `${visitors.toLocaleString()}명`, + designStatus: status !== 'ended' ? DESIGN_CYCLE[i % DESIGN_CYCLE.length] : undefined, + thumbIdx: i % 3, + }; +}); + +/** 데이터에 존재하는 연도 내림차순 목록 (연도 필터용). */ +export const EVENT_YEARS: number[] = [...new Set(ALL_EVENTS.map((e) => Number(e.start.slice(0, 4))))].sort( + (a, b) => b - a, +); + +/** 하위 호환: 기존 SAMPLE_EVENTS — 오늘 기준 진행중·예정 상위 목록. */ +export const SAMPLE_EVENTS: ScheduleEvent[] = ALL_EVENTS.filter((e) => e.status !== 'ended').slice(0, 12); + +/** 해당 월에 행사가 있는 날짜 집합 — key: 'YYYY-M-D' (미니 캘린더 하이라이트). */ +export function eventDaysFor(year: number, month0: number, events: ScheduleEvent[] = ALL_EVENTS): Set { + const days = new Set(); + const last = new Date(year, month0 + 1, 0).getDate(); + const mStart = `${year}-${String(month0 + 1).padStart(2, '0')}-01`; + const mEnd = `${year}-${String(month0 + 1).padStart(2, '0')}-${String(last).padStart(2, '0')}`; + for (const e of events) { + if (e.end < mStart || e.start > mEnd) continue; + const from = e.start > mStart ? Number(e.start.slice(8, 10)) : 1; + const to = e.end < mEnd ? Number(e.end.slice(8, 10)) : last; + for (let d = from; d <= to; d++) days.add(`${year}-${month0 + 1}-${d}`); + } + return days; +} + +/** 해당 월 날짜(YYYY-MM-DD) → 그 날 진행 중인 행사 목록 (WISE 달력 월 그리드용). */ +export function eventsByDateFor(year: number, month0: number, events: ScheduleEvent[] = ALL_EVENTS): Map { + const map = new Map(); + const last = new Date(year, month0 + 1, 0).getDate(); + const mm = String(month0 + 1).padStart(2, '0'); + for (const e of events) { + if (e.end < `${year}-${mm}-01` || e.start > `${year}-${mm}-${String(last).padStart(2, '0')}`) continue; + const from = e.start.slice(0, 7) === `${year}-${mm}` ? Number(e.start.slice(8, 10)) : 1; + const to = e.end.slice(0, 7) === `${year}-${mm}` ? Number(e.end.slice(8, 10)) : last; + for (let d = from; d <= to; d++) { + const key = `${year}-${mm}-${String(d).padStart(2, '0')}`; + const arr = map.get(key) ?? []; + arr.push(e); + map.set(key, arr); + } + } + return map; +} export interface HallUtilRow { label: string; pct: number; } -export const HALL_UTILIZATION: HallUtilRow[] = [ - { label: '홀 1-3', pct: 85 }, - { label: '홀 4-5', pct: 40 }, - { label: '홀 6-8', pct: 62 }, - { label: '홀 9-10', pct: 18 }, + +const HALL_GROUPS: { label: string; lo: number; hi: number }[] = [ + { label: '홀 1-3', lo: 1, hi: 3 }, + { label: '홀 4-5', lo: 4, hi: 5 }, + { label: '홀 6-8', lo: 6, hi: 8 }, + { label: '홀 9-10', lo: 9, hi: 10 }, ]; -// 미니 캘린더 하이라이트 날짜(샘플) — key: 'YYYY-M-D' -export const EVENT_DAYS = new Set([ - '2026-8-14', '2026-8-15', '2026-8-16', '2026-8-17', - '2026-9-5', '2026-9-6', '2026-9-7', '2026-9-8', -]); +function hallNums(halls: string): number[] { + return (halls.match(/\d+/g) ?? []).map(Number).filter((n) => n >= 1 && n <= 10); +} + +/** 크롤링 데이터 기반 월별 홀 그룹 가동률 — (그룹 홀에 행사가 있는 날 수)/(월 일수). */ +export function hallUtilizationFor(year: number, month0: number): HallUtilRow[] { + const last = new Date(year, month0 + 1, 0).getDate(); + const mm = String(month0 + 1).padStart(2, '0'); + const mStart = `${year}-${mm}-01`; + const mEnd = `${year}-${mm}-${String(last).padStart(2, '0')}`; + return HALL_GROUPS.map((g) => { + const busy = new Set(); + for (const e of ALL_EVENTS) { + if (e.end < mStart || e.start > mEnd) continue; + if (!hallNums(e.halls).some((n) => n >= g.lo && n <= g.hi)) continue; + const from = e.start > mStart ? Number(e.start.slice(8, 10)) : 1; + const to = e.end < mEnd ? Number(e.end.slice(8, 10)) : last; + for (let d = from; d <= to; d++) busy.add(d); + } + return { label: g.label, pct: Math.round((busy.size / last) * 100) }; + }); +} + +/** 하위 호환: 2026-08 기준 가동률. */ +export const HALL_UTILIZATION: HallUtilRow[] = hallUtilizationFor(2026, 7); + +/** 하위 호환: 미니 캘린더 하이라이트(2026-08). */ +export const EVENT_DAYS = eventDaysFor(2026, 7); diff --git a/src/frontend/src/screens/schedule/schedule.css b/src/frontend/src/screens/schedule/schedule.css index a39cecd..b3be269 100644 --- a/src/frontend/src/screens/schedule/schedule.css +++ b/src/frontend/src/screens/schedule/schedule.css @@ -205,6 +205,121 @@ background: var(--color-white); } +/* 목록 건수 */ +.kx-sched__count { + font-size: var(--fs-caption); + color: var(--color-neutral-500); +} + +/* WISE(UIWS ScheduleCalendar) 참조 월 캘린더 */ +.kx-mcal { + min-width: 0; +} +.kx-mcal__toolbar { + display: flex; + align-items: center; + gap: var(--space-2); + margin-bottom: var(--space-3); +} +.kx-mcal__title { + font-size: var(--fs-h3); + color: var(--color-neutral-900); + margin: 0 var(--space-2); +} +.kx-mcal__nav { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 4px 10px; + border: var(--border-card); + border-radius: var(--radius-sm); + background: var(--color-white); + color: var(--color-neutral-700); + font-size: var(--fs-caption); + cursor: pointer; +} +.kx-mcal__nav:hover { + background: var(--color-neutral-050); +} +.kx-mcal__grid { + display: grid; + grid-template-columns: repeat(7, 1fr); + border-top: var(--border-card); + border-left: var(--border-card); +} +.kx-mcal__dow { + text-align: center; + font-size: var(--fs-caption); + font-weight: 600; + color: var(--color-neutral-500); + padding: 6px 0; + background: var(--color-neutral-050); + border-right: var(--border-card); + border-bottom: var(--border-card); +} +.kx-mcal__cell { + min-height: 96px; + padding: 4px; + display: flex; + flex-direction: column; + gap: 2px; + border-right: var(--border-card); + border-bottom: var(--border-card); + overflow: hidden; +} +.kx-mcal__cell.is-out { + background: var(--color-neutral-050); +} +.kx-mcal__cell.is-today { + background: var(--color-primary-050); +} +.kx-mcal__daynum { + font-size: var(--fs-caption); + color: var(--color-neutral-700); +} +.kx-mcal__cell.is-sun .kx-mcal__daynum, +.kx-mcal__dow.is-sun { + color: #d64545; +} +.kx-mcal__cell.is-sat .kx-mcal__daynum, +.kx-mcal__dow.is-sat { + color: var(--color-primary-600); +} +.kx-mcal__cell.is-today .kx-mcal__daynum { + font-weight: 700; + color: var(--color-primary-700); +} +.kx-mcal__chip { + font-size: 11px; + line-height: 1.35; + padding: 1px 6px; + border-radius: var(--radius-sm); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: default; +} +.kx-mcal__chip.is-exhibition { + background: var(--color-primary-050); + color: var(--color-primary-700); +} +.kx-mcal__chip.is-culture { + background: #fdeef3; + color: #b83268; +} +.kx-mcal__chip.is-trade { + background: #fff4e5; + color: #9a6b1f; +} +.kx-mcal__chip.is-tech { + background: #e6f4ee; + color: #1f7a54; +} +.kx-mcal__more { + font-size: 11px; + color: var(--color-neutral-500); +} + @media (max-width: 1100px) { .kx-sched__split { grid-template-columns: 1fr; diff --git a/stitch_kintex_ai_system_architect/scr_18_hall_booking/code.html b/stitch_kintex_ai_system_architect/scr_18_hall_booking/code.html new file mode 100644 index 0000000..ebfa339 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_18_hall_booking/code.html @@ -0,0 +1,446 @@ + + + + + + + + + + + + + + + + + +
    + +
    +
    +

    홀 배정 및 즉시 견적

    +
    +auto_awesome +AI 기반 자동 배정 최적화 활성 +
    +
    +
    +
    + +search +
    +
    +notifications +settings +
    M
    +
    +
    +
    + +
    + +
    +
    +
    + +

    2024년 11월

    + +
    +
    +
    +
    +예약 +
    +가배정 +
    +공실 +
    +
    + + +
    +
    +
    +
    + +
    +
    홀 / 일자
    + +
    11/01 (금)
    +
    11/02 (토)
    +
    11/03 (일)
    +
    11/04 (월)
    +
    11/05 (화)
    +
    11/06 (수)
    +
    11/07 (목)
    +
    11/08 (금)
    +
    11/09 (토)
    +
    11/10 (일)
    +
    11/11 (월)
    +
    11/12 (화)
    +
    11/13 (수)
    +
    11/14 (목)
    +
    + +
    + +
    +
    홀1
    +
    행사 A
    +
    +
    +
    가예약 B
    +
    +
    +
    +
    +
    +
    + +
    +
    홀1A
    +
    +
    +
    +
    +선택된 기간 +
    +
    +
    +
    +
    + +
    +
    홀1B
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    홀2
    +
    +
    +
    국제 식품전
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    홀3
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    홀4
    +
    +
    +
    + +
    +
    홀5
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +info +
    +

    배정 시 유의사항

    +
      +
    • Half-Hall 배정 시 전시장 가동률에 따라 AI가 인접 홀 동선을 자동 계산합니다.
    • +
    • 장치일 및 철거일은 본행사 임대료의 50%가 적용됩니다.
    • +
    +
    +
    +
    + +
    + +
    +
    + +

    배정 신청

    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    + +
    + +
    +auto_awesome +
    +

    + 자동 견적(가견적) + AI Draft +

    +
    +
    +기본 임대료 +
    +

    2,250원/㎡ × 4,941㎡ × 4일

    +

    ₩44,469,000

    +
    +
    +
    +성수기 할증 (+10%) +₩4,446,900 +
    +
    +제1전시장 프리미엄 (+10%) +₩4,446,900 +
    +
    +관리비 예치금 (15%) +₩5,337,200 +
    +
    +
    +총 합계 +₩58,700,000 +
    +

    + * 위 금액은 공시요율 기준 가견적이며, 실제 청구 금액은 킨텍스 심사 결과 및 확정 배정 조건에 따라 변동될 수 있습니다. +

    +
    + +
    + + +
    +
    +
    + + +
    + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_19_booth_inventory/code.html b/stitch_kintex_ai_system_architect/scr_19_booth_inventory/code.html new file mode 100644 index 0000000..50c5025 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_19_booth_inventory/code.html @@ -0,0 +1,312 @@ + + + + + +Booth Sales Inventory - KINTEX AI System + + + + + + + + + + + +
    + +
    +
    +

    부스 판매 인벤토리

    +
    +search + +
    +
    +
    + + +
    +
    +
    AD
    +notifications +
    +
    +
    + +
    +
    +
    총 부스
    +
    510
    +
    +
    +
    판매
    +
    128
    +
    +
    +
    홀드
    +
    12
    +
    +
    +
    예약
    +
    22
    +
    +
    +
    잔여
    +
    348
    +
    +
    +
    판매율
    +
    25%
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    + + +
    +
    +
    판매
    +
    홀드
    +
    예약
    +
    공실
    +
    +
    +
    +
    + +
    Hall 1 - Exhibition Center I
    + +
    + +
    A-101
    +
    A-102
    +
    A-103
    +
    A-104
    +
    A-105
    +
    A-106
    +
    A-107
    +
    A-108
    +
    A-109
    +
    A-110
    +
    +
    +
    B-101
    +
    B-102
    +
    B-103
    +
    B-104
    +
    B-105
    +
    B-106
    +
    B-107
    +
    B-108
    +
    B-109
    +
    B-110
    +
    + +
    +Main Aisle 100 +
    +
    +
    C-101
    +
    C-102
    +
    C-103
    +
    C-104
    +
    C-105
    +
    C-106
    +
    C-107
    +
    C-108
    +
    C-109
    +
    C-110
    +
    + +
    + + + +
    +
    +
    +
    + +
    +
    +
    +판매 중 +부스 상세 정보 +
    +

    A-102 · 독립부스 · 6×3m

    +
    +
    + +
    + + +
    + +
    + +
    + +search +
    +
    + +
    +
    +기본 단가 +₩13,500,000 +
    +
    +
    +프리미엄 계수 +spark +
    +1.2 +
    +
    +최종 판매가 +₩16,200,000 +
    +
    + +
    + + +
    +
    +
    + + +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_20_booth_self_select/code.html b/stitch_kintex_ai_system_architect/scr_20_booth_self_select/code.html new file mode 100644 index 0000000..2d18fc3 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_20_booth_self_select/code.html @@ -0,0 +1,364 @@ + + + + + +KINTEX Exhibitor Portal - Booth Selection + + + + + + + + + +
    +
    + KINTEX EXHIBITOR PORTAL +
    + +
    +help_outline +account_circle +
    +
    + + + +
    + +
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    + +
    + +
    +
    +
    + +
    +A-101 +lock +
    + +
    +A-102 +₩13,500,000 + +
    + A-102 · 6×3m · 독립 · ₩13,500,000 +
    +
    +
    + +
    +A-103 +₩13.5M +
    +
    +A-104 +₩13.5M +
    +
    +A-105 +₩13.5M +
    + +
    +A-106 +lock +
    +
    +A-107 +
    +
    +A-108 +
    +
    +Hallway +
    +
    +A-109 +
    +
    +
    + +
    + + + +
    + +
    + Main Entrance +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +

    부스 A-102

    +

    Exhibition Hall 1, Zone A

    +
    +D-15 +
    +
    +
    +aspect_ratio +독립 부스 · 6m × 3m (18sqm) +
    +
    + + +
    +
    +
    +
    +기본 부스 임차료 +₩13,500,000 +
    +
    +옵션 합계 +₩1,350,000 +
    +
    +합계 +
    +
    ₩14,850,000
    +
    (부가가치세 포함)
    +
    +
    +
    +
    +
    + +
    + +
    +
    +auto_awesome +
    +

    AI INSIGHT

    +

    + A-102 부스는 메인 출입구와 인접하여 유동인구가 타 구역 대비 22% 높을 것으로 예측됩니다. +

    +
    +
    +
    + +
    + + +
    +
    +
    + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_21_settlement/code.html b/stitch_kintex_ai_system_architect/scr_21_settlement/code.html new file mode 100644 index 0000000..93bafba --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_21_settlement/code.html @@ -0,0 +1,471 @@ + + + + + +정산 및 결제 관리 - KINTEX AI System + + + + + + + + + + + +
    + +
    +
    + +
    +
    +
    +search + +
    + +
    +
    +
    +

    Admin Profile

    +

    System Administrator

    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +

    정산 및 결제 관리

    +

    KINTEX AI 시스템의 현재 재무 상태와 결제 일정을 한눈에 확인하십시오.

    +
    +
    + + +
    +
    + +
    + +
    +
    +청구 총액 +account_balance_wallet +
    +
    +

    ₩64,500,000

    +

    총 4건의 청구 항목

    +
    +
    + +
    +
    +납부 완료 +check_circle +
    +
    +

    ₩38,700,000

    +
    +
    +
    +
    +60% +
    +
    +
    + +
    +
    +미납 금액 +warning +
    +
    +

    ₩25,800,000

    +

    즉시 확인이 필요한 항목

    +
    +
    + +
    +
    +다음 납부 마감 +calendar_today +
    +
    +
    +

    2026.07.15

    +

    중도금 (30%)

    +
    +D-9 +
    +
    +
    + +
    + +
    +
    +

    납부 일정 및 내역

    +
    +상태 필터: + +
    +
    +
    +
  • {r.ai === 'block' ? ( - ⚠ 차단 {r.aiCount} + 차단 {r.aiCount} ) : ( - ✓ 통과 + 통과 )} {r.status}
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    항목금액기한상태액션
    계약금 (20%)₩12,900,0002026.06.01 + + 완료 + + + +
    중도금 (30%)₩19,350,0002026.07.15 + + 미납 + + + +
    잔금 (30%) + 예치금 (15%)₩29,025,0002026.08.10 + + 대기 + +-
    유틸리티 추가 비용₩3,225,0002026.08.20 + + 대기 + +-
    +
    + + +
    + +
    +

    결제 수단 선택

    +
    + + + +
    +
    +add_card +

    새로운 카드 등록

    +
    +
    + +
    +
    +

    예치금 정산 요약

    +info +
    +
    +
    +총 예치금 +₩9,600,000 +
    +
    +실사용 금액 +₩7,100,000 +
    +
    +
    +환급 예정액 +₩2,500,000 +
    +
    +
    + + + +
    +
    +
    +auto_awesome +
    +
    AI 지출 분석
    +

    지난 행사 대비 유틸리티 비용이 15% 절감되었습니다. 예치금 환급 신청 시 계좌 정보를 다시 한번 확인하십시오.

    +
    +
    +
    +
    + + +
    +
    +

    사후 정산 세부 내역

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    항목 코드세부 내역단가/수량합계 금액정산 상태
    UT-2601전기사용료 (Primary Line)₩1,500 / 1,200kWh₩1,800,000 + + 산정 완료 + +
    WM-2605폐기물 처리비 (산업폐기물 포함)₩50,000 / 15.5t₩775,000 + + 산정 완료 + +
    CS-2602청소 용역비 (야간/상설)₩325,000 / 2회₩650,000 + + 산정 중 + +
    +
    +
    +상기 내역은 실제 사용량에 따라 변동될 수 있습니다. +
    +
    + + +
    +

    © 2024 KINTEX AI Exhibition Management System. All Rights Reserved. Managed by Financial Infrastructure Div.

    +
    + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_24_contractor_rfq/code.html b/stitch_kintex_ai_system_architect/scr_24_contractor_rfq/code.html new file mode 100644 index 0000000..9015f36 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_24_contractor_rfq/code.html @@ -0,0 +1,479 @@ + + + + + +Contractor Matching & RFQ - KINTEX AI System + + + + + + + + + + + +
    +
    +

    Contractor Matching & RFQ - KINTEX AI System

    + +
    +
    +
    + +search +
    + +
    + + +
    +
    +
    + +
    + +
    + +
    +
    + + + + + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + 총 124개 업체 검색됨 +
    +
    +
    + +
    + +
    +
    +
    +
    +

    (주)공간디자인

    + +auto_awesome + AI 추천 · 부스 규모·업종 적합 + +
    +
    +verified + 킨텍스 등록업체 +
    +
    +
    +
    +
    카테고리
    +
    전시디자인설치
    +
    +
    +
    활동 지역
    +
    경기 고양 / 서울
    +
    +
    +
    신뢰도 지표
    +
    규정 준수 98% · 시공 42건
    +
    +
    +
    평점
    +
    +star + 4.8/5.0 +
    +
    +
    +
    +
    +#친환경소재 +#대형부스전문 +#DX시공 +
    +
    + + +
    +
    +
    + +
    +
    +
    +

    유니버셜 아트텍

    +
    +
    +verified + 킨텍스 등록업체 +
    +
    +
    +
    +
    카테고리
    +
    전시디자인설치
    +
    +
    +
    활동 지역
    +
    경기 파주 / 고양
    +
    +
    +
    신뢰도 지표
    +
    규정 준수 95% · 시공 31건
    +
    +
    +
    평점
    +
    +star + 4.6/5.0 +
    +
    +
    +
    +
    +#리깅가능 +#중형부스 +
    +
    + + +
    +
    +
    + +
    +
    +
    +

    (주)킨텍스모듈

    +
    +
    +verified + 킨텍스 등록업체 +
    +
    +
    +
    +
    카테고리
    +
    전시디자인설치
    +
    +
    +
    활동 지역
    +
    전국
    +
    +
    +
    신뢰도 지표
    +
    규정 준수 99% · 시공 156건
    +
    +
    +
    평점
    +
    +star + 4.9/5.0 +
    +
    +
    +
    +
    +#모듈러부스 +#빠른시공 +#가성비 +
    +
    + + +
    +
    +
    +
    +
    + + +
    + +
    +
    +
    +auto_awesome +
    +
    +
    AI Matching Engine
    +
    도면 분석 결과, 12개 업체가 적합합니다
    +
    +
    1
    +
    +
    + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_28_quote_authoring/code.html b/stitch_kintex_ai_system_architect/scr_28_quote_authoring/code.html new file mode 100644 index 0000000..ee83e9c --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_28_quote_authoring/code.html @@ -0,0 +1,378 @@ + + + + + +KINTEX AI System - 견적서 작성 + + + + + + + + + +
    +
    +KINTEX AI System + +
    +
    +
    +search + +
    + + + +
    +
    +
    +
    + +
    + +
    +

    견적서 작성

    +
    +auto_awesome +AI 기반 자동 입력 활성화 +
    +
    + +
    +info +

    물량서(BOQ)에서 항목 및 수량이 AI로 자동 입력되었습니다. 단가를 입력하여 견적을 완성하세요.

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    공종자재/품명수량단가 (₩)금액 (₩)
    골조알루미늄 프로파일48 + +2,160,000
    조명LED 매립등24 + +360,000
    바닥재니들펀치 카펫 (회색)120 + +1,440,000
    가구상담용 테이블/체어 세트8 + +3,040,000
    그래픽대형 배너 실사출력6 + +636,000
    +
    + +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +소계 +₩7,636,000 +
    +
    +부가세(10%) +₩763,600 +
    +
    +
    +최종 견적 총액 +₩8,399,600 +
    +
    +
    +
    + + +
    +
    + +
    +
    + +
    +

    최종 저장: 14:32:05

    +
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_a1/code.html b/stitch_kintex_ai_system_architect/scr_a1/code.html new file mode 100644 index 0000000..7bbf862 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_a1/code.html @@ -0,0 +1,487 @@ + + + + + +KINTEX AI Exhibition Manager - 사용자 관리 + + + + + + + + + + + +
    + +
    +
    + + + +
    +search + +
    +
    +
    + + +
    +
    +
    관리
    +운영지원팀 +
    +
    +
    + +
    +
    +
    +

    사용자 관리

    +

    플랫폼에 등록된 모든 사용자의 권한과 상태를 관리합니다.

    +
    + +
    + +
    +
    +

    전체 사용자

    +

    1,284

    +
    +
    +

    활성 계정

    +

    1,240

    +
    +
    +

    2FA 미설정

    +

    12

    +
    +
    +

    잠김 계정

    +

    3

    +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    이름이메일역할소속상태2FA최근 접속작업
    +
    +
    +김민수 +
    +
    admin***@kintex.com +관리자 +KINTEX 본사 + + 활성 + + +
    +verified_user +2FA 설정됨 +
    +
    2023-11-20 14:22 + +
    +
    +
    +이서연 +
    +
    sy.lee***@company.com +주최자 +글로벌 엑스포 + + 활성 + + +
    +verified_user +2FA 설정됨 +
    +
    2023-11-20 09:15 + +
    +
    +
    +박지훈 +
    +
    jihoon***@booth.net +참가업체 +테크트리 주식회사 + + 잠금 + + +
    +no_encryption +미설정 +
    +
    2023-11-18 17:40 + +
    +
    +
    +최현우 +
    +
    hwchoi***@system.kr +장치업체 +대성 인테리어 + + 활성 + + +
    +verified_user +2FA 설정됨 +
    +
    2023-11-19 11:05 + +
    +
    +
    +정미경 +
    +
    mk.jung***@kintex.com +홀매니저 +KINTEX 1전시장 + + 비활성 + + +
    +verified_user +2FA 설정됨 +
    +
    2023-10-30 16:55 + +
    +
    + +
    +총 1,284개 중 1-5 표시 +
    + + + + +... + + +
    +
    +
    +
    +
    + +
    +
    +

    사용자 정보 수정

    + +
    +
    + +
    +
    + +
    +

    김민수

    +

    admin***@kintex.com

    +

    KINTEX 본사

    +
    + +
    + + +

    역할에 따라 시스템 접근 권한이 즉시 변경됩니다.

    +
    + +
    + +
    +
    +
    +활성화 상태 +
    + +
    +
    + +
    + + + +
    +
    +
    + + +
    +
    + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_a10/code.html b/stitch_kintex_ai_system_architect/scr_a10/code.html new file mode 100644 index 0000000..fa1f6e7 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_a10/code.html @@ -0,0 +1,347 @@ + + + + + +KINTEX AI 전시관리 - AI 플랫폼 설정 + + + + + + + + + + + +
    + +
    +
    +시스템 설정 > AI 플랫폼 설정 +
    +
    +
    +search + +
    + +
    +
    +
    +

    Administrator

    +

    Super Admin

    +
    + +
    +
    +
    + +
    + +
    +

    + AI 플랫폼 설정 + CORE ENGINE +

    +

    시스템의 핵심 AI 엔진 및 추론 파라미터를 구성합니다.

    +
    + +
    + +
    +
    +
    +
    +psychology +

    텍스트/추론 설정

    +
    +ACTIVE +
    +
    + +
    + +
    +
    +
    +
    +기본 (Primary) +Claude 3.5 Sonnet +
    +
    +arrow_forward +
    +
    +
    +폴백 (Fallback) +Ollama / Llama 3 +
    +
    +
    +
    + +
    +
    +
    + +0.7 +
    + +

    창의성 수치: 낮을수록 정확하고 일관된 답변을 생성합니다.

    +
    +
    +
    + +40 +
    + +

    상위 K개 토큰 샘플링 범위: 텍스트의 다양성을 조절합니다.

    +
    +
    + +
    +
    +
    +verified +
    +
    +

    근거 검증 (RAG) 활성화

    +

    전시 가이드라인 및 규정 데이터를 참조하여 답변의 신뢰도를 높입니다.

    +
    +
    + +
    +
    +
    +
    + +
    + +
    +
    +image +

    이미지 (시공 예상)

    +
    +
    +
    + +
    +auto_awesome +
    +

    나노바나나

    +

    gemini-3.1-flash-image-preview

    +
    +
    +
    +
    +
    + +
    + +건 / 월 +
    +
    +
    + +
    + +KRW +
    +
    +
    +
    +
    + +
    +key_off +
    +

    보안 정보

    +

    + API 키는 서버 환경 변수(env)로 안전하게 관리되며 보안상 화면에 표시되지 않습니다. 수정을 원하시면 인프라 관리자에게 문의하십시오. +

    +
    + +
    +
    +
    + +
    + + +
    +
    + + +
    + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_a2/code.html b/stitch_kintex_ai_system_architect/scr_a2/code.html new file mode 100644 index 0000000..c3e4688 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_a2/code.html @@ -0,0 +1,423 @@ + + + + + +KINTEX AI Manager - RBAC + + + + + + + + + + + +
    +
    +KINTEX AI Manager +
    +역할 및 권한 관리 (RBAC) +
    +
    +
    + + +
    +
    +
    +

    Admin User

    +

    System Manager

    +
    +
    +person +
    +
    +
    +
    + +
    +
    + +
    +
    +

    +account_tree + 역할 그룹 계층 +

    + +
    +
    + +
    +
    +hub 플랫폼 (Platform) +
    +
    +
    +shield_person 플랫폼 슈퍼관리자 +
    +
    +
    + +
    +
    +corporate_fare 테넌트 (Tenant) +
    +
    +
    +admin_panel_settings 테넌트 관리자 +
    +
    +
    + +
    +
    +event_available 행사 (Event) +
    +
    +
    +campaign 주최자 (Organizer) +
    +
    +storefront 참가업체 (Exhibitor) +
    +
    +build 장치업체 (Contractor) +
    +
    +meeting_room 홀매니저 (Hall Manager) +
    +
    +
    +
    +
    +
    +
    +group_add +

    새 역할 그룹 생성

    +
    +
    +
    +
    + +
    +
    +
    +

    권한 상세 설정

    +테넌트 관리자 +
    +
    + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    메뉴 / 기능 카테고리조회 (Read)생성 (Create)수정 (Update)삭제 (Delete)승인 (Approve)
    +
    +
    +dashboard +
    +
    +

    대시보드

    +

    전체 운영 현황 모니터링

    +
    +
    +
    +
    +
    +calendar_month +
    +
    +

    전시회 일정

    +

    연간 전시 스케줄 관리

    +
    +
    +
    +
    +
    +grid_view +
    +
    +
    +

    부스 배치 에디터

    +auto_awesome +
    +

    AI 부스 자동 배치 및 편집

    +
    +
    +
    +
    +
    +receipt_long +
    +
    +

    유틸리티 신청

    +

    전기, 급배수, 랜선 등 신청

    +
    +
    +
    +
    +
    +account_balance_wallet +
    +
    +

    결제/정산

    +

    대금 청구 및 정산 처리

    +
    +
    +
    +
    +
    +manage_accounts +
    +
    +

    사용자 관리

    +

    계정 생성 및 권한 위임

    +
    +
    +
    +
    +
    +
    +info + 선택된 권한은 실시간으로 저장되지 않습니다. 상단의 저장 버튼을 눌러주세요. +
    +
    +
    +
    JD
    +
    MK
    +
    +5
    +
    +

    현재 7명의 사용자가 이 역할을 가지고 있습니다.

    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_a3/code.html b/stitch_kintex_ai_system_architect/scr_a3/code.html new file mode 100644 index 0000000..813689a --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_a3/code.html @@ -0,0 +1,418 @@ + + + + + +KINTEX AI - 공통코드 관리 + + + + + + + + + + + +
    + +
    +
    + +
    +
    + +
    + + +
    +
    +
    +

    Admin Profile

    +

    시스템 관리자

    +
    + +
    +
    +
    +
    + +
    + +
    +
    +

    코드 그룹 목록

    + +
    +
    +
    + + + + + + + +
    +
    +
    + +
    +
    +
    +

    공종 분류 상세 코드

    +

    그룹 ID: CATEGORY_14_TYPE

    +
    +
    + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    코드코드명(KR)정렬 순서구분사용 여부관리
    C001건축/목공1 +전역 + +
    +
    +활성 +
    +
    +
    + + +
    +
    C002전기/조명2 +테넌트 재정의 + +
    +
    +활성 +
    +
    +
    + + +
    +
    C003가구/비품3 +전역 + +
    +
    +활성 +
    +
    +
    + + +
    +
    C004리깅/현수막4 +전역 + +
    +
    +비활성 +
    +
    +
    + + +
    +
    +
    + +
    +전체 4개 항목 중 1-4 표시 +
    + + +
    +1 +
    + + +
    +
    +
    +
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_a4/code.html b/stitch_kintex_ai_system_architect/scr_a4/code.html new file mode 100644 index 0000000..c79aaa1 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_a4/code.html @@ -0,0 +1,469 @@ + + + + + +KINTEX AI 전시관리 - 메뉴 관리 + + + + + + + + + + + +
    +
    +

    메뉴 관리

    +
    + +
    +
    +
    +search + +
    +
    + + +
    +
    AD
    +Admin +
    +
    +
    +
    + +
    +
    + +
    +
    +

    +account_tree + 메뉴 트리 +

    + +
    +
    + +
    +drag_indicator +dashboard +대시보드 + +
    + +
    +
    +drag_indicator +calendar_today +전시회 일정 + +
    +
    +
    +event_list +진행중인 전시 +
    +
    +history +과거 이력 +
    +
    +
    + +
    +drag_indicator +layers +부스 배치도 + +
    + +
    +drag_indicator +account_tree +메뉴 관리 + +
    + +
    +drag_indicator +settings +시스템 설정 + +
    +
    +
    + +
    +
    +
    +

    +edit_note + 메뉴 상세 설정 +

    +
    +ID: MN-004 + +auto_awesome + AI 최적화됨 + +
    +
    +
    + +
    +
    + + +
    +
    + +
    +/admin/ + +
    +
    +
    + + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    + + + + + + +
    +
    + +
    +
    +
    +info +
    +
    +

    고급 설정

    +

    해당 메뉴의 노출 정책 및 연동 모듈을 설정합니다. AI가 메뉴 사용 패턴을 분석하여 비활성화 여부를 제안할 수 있습니다.

    +
    + + +
    +
    +
    +
    +
    +
    + + +
    +
    + +
    +
    +
    +trending_up +
    +
    +

    지난 30일 방문수

    +

    1,248회

    +

    전월 대비 +12.4% 증가

    +
    +
    +
    +
    +speed +
    +
    +

    평균 로딩 속도

    +

    0.42s

    +

    최적화 상태: 매우 좋음

    +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_a7/code.html b/stitch_kintex_ai_system_architect/scr_a7/code.html new file mode 100644 index 0000000..947a42b --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_a7/code.html @@ -0,0 +1,411 @@ + + + + + +KINTEX AI 전시관리 - Admin Console + + + + + + + + + +
    + + + +
    + +
    +
    +menu +

    KINTEX AI 전시관리

    +
    +
    +
    +search + +
    +
    +notifications +settings +help +
    +person +
    +
    +
    +
    + +
    + + + +
    +
    +

    홀 마스터 관리

    +

    전시장 홀 정보 및 물리적 규격 마스터 데이터를 관리합니다.

    +
    +
    + + +
    +
    + +
    + + + + + +
    + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    면적(㎡)규격(L×W×H)바닥하중부스수(Max)관리
    홀 110,611㎡126×84×12m5t/㎡480 + + +
    홀 210,611㎡126×84×12m5t/㎡480 + + +
    홀 711,290㎡126×90×12m5t/㎡510 + + +
    홀 811,290㎡126×90×15m5t/㎡510 + + +
    홀 1010,000㎡100×100×15m5t/㎡450 + + +
    +
    + +
    + +
    +
    +auto_awesome +AI 최적화 분석 +
    +

    현재 홀 7의 부스 배치 밀도가 다른 전시홀 대비 12% 높습니다. 안전 통로 확보를 위한 시뮬레이션을 권장합니다.

    + +
    + +
    +
    +
    +

    트렌치 좌표 CAD 업로드

    +
    + + 미확보 · 킨텍스 협의 +
    +
    +upload_file +
    +

    + 홀7, 8 전용 지하 트렌치 정밀 좌표 데이터가 누락되었습니다. 킨텍스 시설팀 협의 후 최신 .dwg 파일을 업로드하십시오. +

    +
    +add_circle +Click to upload +
    +
    + +
    +

    홀 이용 현황 요약

    +
    +
    +
    +전체 홀 가동률 +68% +
    +
    +
    +
    +
    +
    +
    +부스 점유 면적 +42,500㎡ +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m10/code.html b/stitch_kintex_ai_system_architect/scr_m10/code.html new file mode 100644 index 0000000..091fd73 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m10/code.html @@ -0,0 +1,250 @@ + + + + + +KINTEX Gate Pass - Move-in & Safety + + + + + + + + + + +
    +
    +construction +

    KINTEX Gate Pass

    +
    + +
    +
    + +
    +
    + +
    +Gate Pass Entry +
    D-2
    +
    +
    + +
    + +
    + +
    +
    +

    차량 번호

    +

    12가 3456

    +
    +
    +

    지정 도크

    +

    도크 3

    +
    +
    +
    +schedule +08:00 - 09:00 +
    +장치 기간 +
    +
    +
    +
    +
    + +
    +

    + 안전 체크리스트 + +warning 필수 확인 + +

    +
    + + + + + + +
    +
    + +
    +
    +

    슬롯 시간

    +

    D-0 09:00

    +
    + +
    +
    + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m11/code.html b/stitch_kintex_ai_system_architect/scr_m11/code.html new file mode 100644 index 0000000..7432574 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m11/code.html @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + +
    + +

    알림

    + +
    +
    + +
    +
    + + + + + +
    +
    + +
    +전체 12개 + +
    + +
    + +
    +
    +
    +calendar_today +
    +
    +
    +
    +

    전시 부스 신청 마감 임박

    +방금 전 +
    +

    2026 하반기 통합 전시회 부스 신청이 3시간 뒤 마감됩니다.

    +
    +
    +
    +
    +
    + +
    +
    +
    +check_circle +
    +
    +
    +
    +

    부스 배치도 승인 완료

    +2시간 전 +
    +

    요청하신 제1전시장 A-102 구역의 배치가 승인되었습니다.

    +
    +
    +
    +
    +
    + +
    +
    +
    +payments +
    +
    +
    +
    +

    전기/통신 사용료 결제 안내

    +5시간 전 +
    +

    신청하신 유틸리티 비용에 대한 세금계산서가 발행되었습니다.

    +
    +
    + +
    +
    +
    +auto_awesome +
    +
    +
    +
    +

    AI 관람객 동선 최적화 제안

    +어제 +
    +

    데이터 분석 결과, 입구 혼잡도 개선을 위한 부스 배치 조정안이 생성되었습니다.

    +
    +
    + +
    +
    +
    +gavel +
    +
    +
    +
    +

    잔여 부스 공개 입찰 낙찰

    +2일 전 +
    +

    (주)킨텍스글로벌이 제2전시장 C-203 구역 낙찰자로 선정되었습니다.

    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m12/code.html b/stitch_kintex_ai_system_architect/scr_m12/code.html new file mode 100644 index 0000000..ddf2bfb --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m12/code.html @@ -0,0 +1,294 @@ + + + + + +KINTEX AI System - SCR-M12 + + + + + + + + + +
    +
    + +

    검수 및 승인

    +
    + +
    + +
    + + +
    + +
    +
    +
    +
    +description +신고서류 · 부스배치도 +
    +
    D-5
    +
    +
    +

    (주)테크솔루션

    +

    2024년 KINTEX 월드박람회

    +
    +
    +검수 중 +
    +auto_awesome +AI 분석 완료 +
    +
    +
    + +
    + +
    +
    +
    +
    +picture_as_pdf +

    부스배치도_A102.pdf

    +

    1.2 MB • Ver 1.2

    +
    +
    + +
    + +
    +
    +auto_awesome +
    +
    +AI Agent +

    AI 검수 요약

    +
    +
      +
    • +warning +
      +

      소방 통로 확보 미흡 (1.2m)

      +

      규정상 최소 2.0m 이상의 통로가 확보되어야 합니다.

      +
      +
    • +
    • +bolt +
      +

      허용 전력 초과 (5kW)

      +

      할당된 전력(3kW) 대비 66% 초과 사용이 감지되었습니다.

      +
      +
    • +
    +
    + +
    +
    +

    제출일시

    +

    2024.10.22 14:30

    +
    +
    +

    담당자

    +

    김민수 팀장

    +
    +
    +
    +
    + +
    +
    +
    +assignment +운영 매뉴얼 +
    +

    (주)글로벌비즈

    +
    +
    +승인 대기 +D-3 +
    +
    +
    +
    +
    +verified_user +보안 준수 확인서 +
    +

    코리아네트웍스

    +
    +
    +승인 대기 +D-12 +
    +
    +
    +
    + +
    +
    + + +
    +
    + +
    + + + + + +
    + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m13/code.html b/stitch_kintex_ai_system_architect/scr_m13/code.html new file mode 100644 index 0000000..c02fc6c --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m13/code.html @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + +
    + +

    입찰 현황 모니터링

    + +
    +
    + +
    +
    +
    +
    +입찰 진행 중 +

    전기공사 - 제1전시장 1홀

    +
    +
    +timer +마감 01:24:36 +
    +
    +
    +
    +

    내 현재 순위

    +

    2위

    +
    +
    +

    현재 최저가

    +

    ₩8,400,000

    +
    +
    +
    +최근 업데이트: 방금 전 +
    +auto_awesome +AI 최적화 분석 중 +
    +
    +
    + +
    +
    +

    + 실시간 순위 현황 + +

    +총 8개 업체 +
    +
    + +
    +1 +
    +

    (주)에이텍

    +

    입찰가: ₩8,320,000

    +
    +trending_flat +
    + +
    +2 +
    +
    +

    나 (본인)

    +ME +
    +

    입찰가: ₩8,400,000

    +
    +
    +arrow_drop_down +1위와 ₩80,000 차 +
    +
    + +
    +3 +
    +

    (주)씨앤씨

    +

    입찰가: ₩8,450,000

    +
    +arrow_drop_down +
    + +
    +4 +
    +

    (주)디자인랩

    +

    입찰가: ₩8,520,000

    +
    +arrow_drop_up +
    +
    +
    + +
    +info +

    + 견적서 작성 및 재응찰은 웹 버전에서 진행해 주세요. 모바일에서는 실시간 현황 모니터링만 지원합니다. +

    +
    +
    + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m14/code.html b/stitch_kintex_ai_system_architect/scr_m14/code.html new file mode 100644 index 0000000..5e23f85 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m14/code.html @@ -0,0 +1,297 @@ + + + + + +스마트팩토리 코리아 2026 - 티켓 구매 + + + + + + + + + + +
    + +

    알림

    + +
    +
    + +
    +
    +calendar_today +스마트팩토리 코리아 2026 · 제2전시장 홀7 +
    +
    + +
    +
    +
    1
    +권종 +
    +
    +
    +
    2
    +예매자 +
    +
    +
    +
    3
    +결제 +
    +
    +
    +
    4
    +완료 +
    +
    + +
    + +
    +
    +
    +
    +

    일반권

    +
    +₩0 +사전등록 할인가 +
    +
    +
    + +0 + +
    +
    +
    + +
    +
    +
    +
    +
    +

    바이어권

    + +auto_awesome + AI OPTIMIZED + +
    +

    무료 (자격심사 필요)

    +
    +
    + +0 + +
    +
    +

    ※ 업종 및 직무 AI 매칭을 통해 추천된 권종입니다.

    +
    + +
    +
    +
    +
    +

    단체권

    +
    +₩9,000 +10% OFF +
    +

    10인 이상 단체 구매시 적용

    +
    +
    + +1 + +
    +
    +
    + +
    +
    +
    +
    +
    +

    VIP권

    +매진 +
    +₩50,000 +
    +
    + +0 + +
    +
    +
    +
    + +
    +

    결제 수단 미리보기

    +
    +
    신용카드
    +
    간편결제
    +
    계좌이체
    +
    +
    +
    +security +

    + 결제는 PG사 보안 페이지에서 진행되며 카드정보는 저장되지 않습니다. 스마트팩토리 코리아 운영국은 고객님의 소중한 정보를 보호합니다. +

    +
    +
    +
    +
    + +
    +
    +선택한 티켓 1매 +합계 ₩9,000 +
    + +
    + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m4/code.html b/stitch_kintex_ai_system_architect/scr_m4/code.html new file mode 100644 index 0000000..eeb0b99 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m4/code.html @@ -0,0 +1,329 @@ + + + + + +KINTEX Lead Capture + + + + + + + + + + + +
    +
    + +

    KINTEX Lead Capture

    +
    +
    + +
    +
    +
    + +
    + +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    관람객 배지 QR을 스캔하세요

    + + +
    +
    + +
    + +
    +
    +
    +
    + +
    +
    +
    +

    정관람

    +바이어 +
    +

    (주)미래기술 · 전략기획팀 팀장

    +
    +
    +D-5 + +check_circle + 개인정보 동의 + +
    +
    + +
    +

    관심도 (Interest Level)

    +
    +star +star +star +star +star +
    +
    + +
    +

    주요 관심분야 (AI 추천)

    +
    + +auto_awesome AI 솔루션 + +전시 기획 +빅데이터 + +
    +
    + +
    +

    상담 메모

    +
    + + +
    +
    + +
    + +
    +
    +
    +오프라인 저장 · 동기화 대기 +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m5/code.html b/stitch_kintex_ai_system_architect/scr_m5/code.html new file mode 100644 index 0000000..379747f --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m5/code.html @@ -0,0 +1,313 @@ + + + + + +KINTEX AI Visitor Home + + + + + + + + + + +
    +
    +menu +

    KINTEX AI

    +
    +
    +qr_code_scanner +notifications +
    +
    +
    + +
    +
    +
    +
    + +
    +
    KINTEX VISITOR
    +
    바이어
    +
    + +

    정관람

    +

    KINTEX Global Summit 2024

    + +
    + +
    +

    체크인 시 화면을 보여주세요

    + +
    +
    +
    +
    + +
    +
    +

    오늘 일정

    + +
    +
    + +
    +
    +14 +00 +
    +
    +

    메타버스 트렌드 컨퍼런스

    +

    컨퍼런스룸 201호

    +
    +chevron_right +
    + +
    +
    +16 +30 +
    +
    +

    네트워킹 파티 - 바이어 세션

    +

    그랜드볼룸 A

    +
    +chevron_right +
    +
    +
    + +
    +
    +

    추천 세션·부스

    +auto_awesome +
    +
    + +
    +
    +
    +auto_awesome AI 추천 +
    +
    +
    +

    차세대 AI 데이터 솔루션

    +

    1홀 A-205 • 테크노랩

    +
    +
    + +
    +
    +
    +auto_awesome AI 추천 +
    +
    +
    +

    스마트 팩토리 운영 사례

    +

    컨퍼런스룸 302 • 강연

    +
    +
    +
    +
    + +
    +

    즐겨찾기 부스

    +
    + +
    +
    +
    T
    +
    +

    테크솔루션

    +

    1홀 A-102

    +
    +
    +star +
    + +
    +
    +
    M
    +
    +

    미래산업기기

    +

    2홀 B-110

    +
    +
    +star +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m6/code.html b/stitch_kintex_ai_system_architect/scr_m6/code.html new file mode 100644 index 0000000..d1a319c --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m6/code.html @@ -0,0 +1,410 @@ + + + + + + + + + + + + + + + +
    +
    + +
    +
    +
    +search +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +

    KINTEX HALL 1

    +
    + +
    + +
    A-101
    +
    +A-102 +한빛로보틱스 +
    +
    A-103
    +
    A-104
    +
    준비중
    +
    B-101
    +
    B-102
    +
    B-103
    +
    B-104
    +
    B-105
    +
    B-106
    + +
    +
    +wc +화장실 +
    +
    +emergency +비상구 +
    +
    + +
    C-101
    +
    C-102
    +
    C-103
    +
    C-104
    +
    C-105
    +
    C-106
    +
    + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    + 내 위치 +
    +
    + +
    +location_on +
    +
    +
    +
    + +
    + + + +
    + +
    +
    +info + 존 레벨 안내 (정밀 측위 미지원 시) +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +도착지 +

    A-102 · 한빛로보틱스

    +
    +
    +
    +directions_walk +도보 3분 +
    +| +약 140m +
    +
    + +
    +
    +
    +가장 가까운 입구 +1홀 정문 +
    +
    +근처 편의시설 +동측 화장실 (20m) +
    +
    +
    + + +
    +
    +
    + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m7/code.html b/stitch_kintex_ai_system_architect/scr_m7/code.html new file mode 100644 index 0000000..c3ea586 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m7/code.html @@ -0,0 +1,290 @@ + + + + + +KINTEX AI - Visitor Floor Plan + + + + + + + + + + +
    + +
    +
    +menu +

    KINTEX AI

    +
    +qr_code_scanner +
    + +
    +
    +search + +
    +
    + + + + +
    +
    + +
    + +
    +
    + +
    A-101
    +
    A-102
    +
    A-103
    +
    REST
    + +
    B-201
    +
    B-202
    +
    B-203
    + +
    ENTRANCE HALL
    + +
    C-301
    +
    C-302
    +
    C-303
    +
    C-304
    +
    +
    + +
    + + + + +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +A-102 + +auto_awesome AI 매칭 추천 + +
    +

    한빛로보틱스

    +

    로봇/자동화 · 스마트 팩토리

    +
    + +
    +
    + + +
    +
    +
    +near_me +
    +

    현재 위치에서 도보 3분

    +

    최단 경로 안내 가능

    +
    +
    +
    +inventory_2 +
    +

    주요 전시 품목

    +

    산업용 협동 로봇, 물류 자동화 솔루션, 자율주행 AMR 플랫폼

    +
    +
    +
    +
    +
    + + + + +
    + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m8/code.html b/stitch_kintex_ai_system_architect/scr_m8/code.html new file mode 100644 index 0000000..fc59423 --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m8/code.html @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + +
    +
    +menu +

    KINTEX Matchmaking

    +
    +
    +search +account_circle +
    +
    +
    + +
    +
    +

    + AI 추천 매칭 + auto_awesome +

    + +
    +
    + +
    +
    +
    + +
    +
    +
    +auto_awesome + AI 매칭 92% +
    +

    김민수

    +

    (주)테크솔루션 | CTO

    +
    +
    +
    +AI 솔루션 +스마트 팩토리 +클라우드 +
    +
    + + +
    +
    + +
    +
    +
    + +
    +

    미팅 예약

    +
    + + + + + +
    +
    +10:00 +11:00 +12:00 +13:00 +14:00 +15:00 +
    +
    + +
    +

    내 미팅 일정

    +
    + +
    +
    +
    +
    +14:00 - 14:30 +

    이영희 대표

    +

    글로벌 리서치 그룹

    +
    +
    확정
    +
    +
    +
    +location_on + 1홀 B-102 부스 +
    + +
    +
    + +
    +
    +
    +
    +16:00 - 16:30 +

    박성진 팀장

    +

    미래 로보틱스

    +
    +
    승인 대기
    +
    +
    +
    +meeting_room + 미팅룸 A-04 +
    + +
    +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_m9/code.html b/stitch_kintex_ai_system_architect/scr_m9/code.html new file mode 100644 index 0000000..dac373e --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_m9/code.html @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + +
    + +

    Schedule

    + +
    +
    + + + +
    +
    +auto_awesome +

    AI 추천 세션 ✨

    +
    +
    + +
    +
    +
    + +bolt 98% 매칭 + +10:00 - 10:50 +
    +

    AI 비즈니스 트랜스포메이션 2026: 미래 전략

    +

    김민수 본부장 · KINTEX IT Lab

    +
    +기술 +전략 +
    +
    + +
    +
    +
    + +bolt 92% 매칭 + +11:00 - 11:50 +
    +

    스마트 전시 공간 기획과 AI 도슨트의 결합

    +

    박서연 책임 · 글로벌 마케팅 그룹

    +
    +비즈니스 +디자인 +
    +
    +
    +
    + +
    + +
    +
    +10:00 +
    +
    +
    +
    +
    10:00 - 10:50 | Room A
    +

    AI 기반 전시 데이터 분석 및 성과 극대화 솔루션

    +
    최현우 팀장 · 데이터솔루션 본부
    +
    +기술 +데이터 +
    +
    + +
    +
    + +
    +
    +11:00 +
    +
    +
    +
    +
    11:00 - 11:50 | Room B
    +

    지속 가능한 전시 문화를 위한 그린테크 혁신

    +
    이주환 교수 · 서울대학교 환경공학
    +
    +ESG +비즈니스 +
    +
    + +
    +
    +
    +
    +11:00 - 11:50 | Main Stage + +bolt AI 추천 + +
    +

    스마트 전시 공간 기획과 AI 도슨트의 결합

    +
    박서연 책임 · 글로벌 마케팅 그룹
    +
    +마케팅 +
    +
    + +
    +
    + +
    +
    +13:00 +
    +
    +
    +
    +
    13:00 - 13:50 | VIP Lounge
    +

    B2B 네트워킹의 기술: 성과를 이끄는 비즈니스 매너

    +
    Sarah J. Miller · G-Exhibitions CEO
    +
    +네트워킹 +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/stitch_kintex_ai_system_architect/scr_p8/code.html b/stitch_kintex_ai_system_architect/scr_p8/code.html new file mode 100644 index 0000000..e43626d --- /dev/null +++ b/stitch_kintex_ai_system_architect/scr_p8/code.html @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + +
    + +
    +

    예매 확인 · 취소

    +

    비회원 예매 내역을 조회하고 취소하실 수 있습니다.

    +
    + +
    +
    +
    +
    + + +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    +
    +
    +
    +예매완료 +

    스마트팩토리 코리아 2026

    +

    예매번호 KTX-2026-018245

    +
    +
    +

    결제일시

    +

    2025-10-24 14:32

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + +
    권종수량상세
    +
    +qr_code_2 +
    +일반권 (Early Bird) +
    2매 +₩ 30,000 +
    +
    +qr_code_2 +
    +학생/군인 할인 +
    1매 +₩ 10,000 +
    +
    +
    +
    +account_circle +
    +

    예매자 정보

    +

    jo***@ex***.com · 010-****-1234

    +
    +
    +
    +

    총 결제 금액

    +

    ₩ 40,000

    +
    +
    +
    +
    + + +
    +
    + + + + \ No newline at end of file