diff --git a/docs/DESIGN_SYSTEM_NIFTY.md b/docs/DESIGN_SYSTEM_NIFTY.md
new file mode 100644
index 0000000..4b82a25
--- /dev/null
+++ b/docs/DESIGN_SYSTEM_NIFTY.md
@@ -0,0 +1,96 @@
+# 킨텍스 디자인 시스템 — Nifty × kx 토큰 (학습 문서)
+
+> **목적:** 소유자 확정(2026-07-12) — 킨텍스 공통 UI의 디자인 기준 = **Nifty(themeon, Bootstrap 5 기반 관리자 테마)**. 이 문서는 Nifty 컴포넌트 패턴을 킨텍스 **kx 디자인 토큰**으로 번역하는 단일 규칙서다. 모든 UI 작업(에이전트·수동)은 착수 전 이 문서를 읽고 준수한다.
+> 레퍼런스 인덱스: `docs/analysis/nifty-design-refs.md`. 토큰 정본: `src/frontend/src/styles/tokens.css`.
+
+---
+
+## 0. 대원칙 (불변)
+1. **kx 토큰으로만 번역** — Nifty/Bootstrap의 hex·px를 그대로 쓰지 말고 아래 토큰에 매핑. 하드코딩 hex 금지.
+2. **다크/라이트 양 테마** — 색은 `--color-*`(테마 remap) 사용. `--dk-*` 직접 참조 금지.
+3. **이모지 금지** — 아이콘은 선(stroke) SVG(`components/ui/icons.tsx`)만.
+4. **WISE 우선** — 셸·시스템관리·공통기능·캘린더는 WISE(UIWS) 구조가 정본. Nifty는 WISE에 없는 **공통 컴포넌트 스타일**(테이블·카드·버튼·드롭다운·리스트그룹·모달·뱃지·알럿·탭)의 레퍼런스.
+5. **접근성** — focus-visible 링(`--focus-ring`)·role/aria·WCAG AA·`prefers-reduced-motion`.
+
+## 1. 토큰 매핑표 (Nifty/Bootstrap → kx)
+
+### 색상 (Bootstrap contextual → kx)
+| Nifty/BS | 의미 | kx 토큰 |
+|---|---|---|
+| primary | 주 액션 | `--color-primary-600`(#0066b3) / hover `--color-primary-700`(#004c86) |
+| success | 성공·승인 | `--color-success`(#0e8a5f) / bg `--color-success-bg` |
+| warning | 경고·임박 | `--color-warning`(#b45309) / bg `--color-warning-bg` |
+| danger | 위험·오류 | `--color-error`(#d92d20) / bg `--color-error-bg` |
+| info | 보조 정보 | `--color-primary-050/100` + `--color-primary-700` 텍스트 |
+| default/light | 중립 | `--color-neutral-100` bg / `--color-neutral-700` 텍스트 / `--border-card` |
+| dark | 강조 텍스트 | `--color-neutral-900`(#101828) |
+| (AI 전용) | AI 기능 | `--color-ai-accent`(#6d4aff) / surface `--color-ai-surface` |
+
+### 타이포 (Nifty 위계 → kx `--fs-*`)
+| 용도 | kx 토큰 |
+|---|---|
+| 페이지 타이틀(h1) | `--fs-h1` 24/32 |
+| 섹션(h2) | `--fs-h2` 20/28 |
+| 카드 제목(h3) | `--fs-h3` 16/24 |
+| 본문 | `--fs-body` 14/22 |
+| 캡션·헤더셀·메타 | `--fs-caption` 12/18 |
+| 숫자열 | `--fs-body` + `font-variant-numeric: tabular-nums`(`.tnum`) |
+| 코드/모노 | `--fs-mono` 13 / `--font-mono` |
+
+### 간격·모양
+| Nifty | kx |
+|---|---|
+| 컴포넌트 내부 패딩 | `--space-2`(8) ~ `--space-4`(16) |
+| 카드 패딩 | `--space-4`(16) ~ `--space-5`(24) |
+| 요소 간 gap | `--space-1~3` |
+| 섹션 간 | `--space-5`(24, 상한 `--space-6` 32) |
+| 버튼·인풋 radius | `--radius-sm`(4) |
+| 카드·패널 radius | `--radius-lg`(8) |
+| 배지·필·토글 | `--radius-pill` |
+
+## 2. 컴포넌트 규격 (Nifty 패턴 → kx 구현)
+
+### 2.1 버튼 (`Button` / `.kx-btn`)
+- **변형**: primary(채움) · secondary(아웃라인) · ghost(투명) · danger. Nifty의 8색 전부를 만들지 말고 **의미 단위**(주/보조/위험/AI)로 수렴.
+- **크기**: `sm`(높이 28·`--fs-caption`) · `md`(기본 36·`--fs-body`) · `lg`(44). Nifty xs~lg를 3단으로.
+- **상태**: hover(명도 1단계)·active·disabled(opacity .5·cursor not-allowed)·loading(스피너). focus-visible 링 필수.
+- **아이콘**: leadingIcon(선 SVG)·icon-only(정사각·aria-label). 버튼 그룹은 인접 radius 접합.
+- **블록**: `block`=100% 폭.
+
+### 2.2 카드 (`.kx-card`)
+- 구조: `kx-card`(테두리 `--border-card`·radius-lg·bg `--color-white`) > `kx-card__head`(제목 h3·우측 액션) · body · `kx-card__foot`(선택).
+- Nifty 변형: **컬러 좌측 액센트 바**(상태 카드), **KPI 타일**(수치 강조 `--fs-display`), **미디어 카드**(포스터). 그림자는 과하지 않게(hover만 약한 elevation).
+
+### 2.3 테이블 (`.kx-table` — Nifty "Advanced table headers")
+- thead th: bg `--color-neutral-050`·`--fs-caption`·중간색(`--color-neutral-500`)·좌정렬·`border-bottom: 2px --color-neutral-200`·sticky top.
+- tbody td: `--fs-body`·`--color-neutral-700`·패딩 10px 12px·`border-bottom 1px`.
+- zebra(`--zebra`)·행 hover(`--color-primary-050`)·선택행(`--color-primary-100`)·숫자열 `.kx-num` 우정렬 tnum.
+- 인터랙티브(정렬·필터·페이지·CSV)는 **Tabulator**로 별도(§3).
+
+### 2.4 드롭다운 / 셀렉트
+- 트리거(버튼) + 메뉴(카드형·radius-sm·그림자 약)·항목 hover(`--color-primary-050`)·구분선·아이콘·위험 항목(danger 색). 키보드(↑↓·Esc·Enter)·aria-expanded.
+
+### 2.5 리스트 그룹 (`.kx-list-group`)
+- 목록형(공지·활동·검색결과): 행 = 아이콘/점 + 제목 + 메타 + 우측 배지/시간. hover·active·구분선. 링크형은 전체 행 클릭.
+
+### 2.6 뱃지·필·알럿·탭·프로그레스·페이지네이션·모달·툴팁·offcanvas
+- **뱃지/필**: `--radius-pill`·`--fs-caption`·상태색 bg+text(위 색표).
+- **알럿**: 상태색 배경(연)+좌측 액센트+아이콘+닫기. info/success/warn/danger.
+- **탭/세그**: `.kx-seg`(현존) — 활성 밑줄 또는 채움. role=tablist.
+- **모달/메시지박스**: 백드롭(반투명)+카드(radius-lg)+헤더/바디/푸터(액션 버튼 우측). 확인/경고 메시지박스는 아이콘+제목+본문+2버튼. focus trap·Esc.
+- **offcanvas**: 우/좌 슬라이드 패널(모바일 드로어·상세 슬라이드). 백드롭·Esc·트랜지션(reduced-motion 존중).
+- **프로그레스/페이지네이션**: 상태색·`--radius-pill`(bar)·현재 페이지 강조.
+
+## 3. Tabulator (인터랙티브 데이터 그리드)
+- 도입 시: 테마 CSS를 kx 토큰으로 오버라이드(헤더=§2.3 스타일 정합)·다크 대응·한글 로케일·CSV export. 관리자/대용량 목록에 파일럿 후 확산. 정적 표는 `.kx-table` 유지.
+
+## 4. 적용 절차 (에이전트 지침)
+1. 이 문서 + `nifty-design-refs.md` 읽기 → 대상 요소의 Nifty 패턴 파악.
+2. 해당 URL을 WebFetch로 확인(구조·변형·상태) → §1 토큰으로 번역.
+3. 공통 컴포넌트(`components/ui/*`·`shared.css`)에 표준 확립 → 화면별 산재 스타일 수렴(중복 제거).
+4. 검증: `tsc -b --force`·`vite build` EXIT 0 + 다크/라이트 스팟 + 헤드리스 렌더 대조.
+
+## 변경 이력
+| 일자 | 내용 |
+|---|---|
+| 2026-07-12 | 최초 작성 — Nifty(BS5) → kx 토큰 매핑·컴포넌트 규격·적용 절차. 소유자 "Nifty 스타일 학습" 지시 |
diff --git a/docs/PLANNING.md b/docs/PLANNING.md
index aa24409..90d46bb 100644
--- a/docs/PLANNING.md
+++ b/docs/PLANNING.md
@@ -1,6 +1,6 @@
# 킨텍스 AI 전시·행사시스템 (KINTEX AI Exhibition & Event System) 기획서
-> 작성: 기획 에이전트(planner) · 작성일: 2026-07-11 · 최종 갱신: 2026-07-12 · 버전: **v3.2**
+> 작성: 기획 에이전트(planner) · 작성일: 2026-07-11 · 최종 갱신: 2026-07-12 · 버전: **v3.3**
> **제품 공식 명칭(소유자 확정 2026-07-12): "KINTEX AI 전시·행사시스템"** — 기존 "자동전시시스템(Exhibition Automation Platform)"을 대체한다. 전시(exhibition)뿐 아니라 행사·이벤트(event) 전반을 포괄한다는 의미를 명칭에 반영. 본 명칭이 정본이며, 이하 본문·3-트랙 기획·타 문서 후속 반영 시 이 명칭을 사용한다(기존 "자동전시시스템" 표기는 문맥 보존을 위해 삭제하지 않고 잔존하나, 신규 표기는 정본 명칭을 따른다).
> 근거 문서: `docs/analysis/kintex-website.md` (킨텍스 웹사이트 25개 페이지 + 전시주최자매뉴얼 PDF + 참가업체 매뉴얼 PDF 전수 분석, 2026-07-11)
> 근거 문서: `docs/analysis/coex-website.md` (코엑스 VISITOR/BUSINESS/CYBER 3-사이트 IA 분석 + 킨텍스 대비표, 2026-07-12) — §2-2 대외 접점 3-트랙 재구성 확정 근거
@@ -10,6 +10,7 @@
> **v2.0 정체성 확장**: 본 시스템은 "부스 시공 도구"를 넘어 **킨텍스 자동전시시스템(Exhibition Automation Platform)** 이다 — 전시 기획·판매·시공·운영·관람객·사후분석 전 주기를 아우른다. 기존 M2~M5 부스 시공 코어(P0)·나노바나나 파이프라인(§6)·확정 스택(§8)은 **보존**하고, 그 위에 관람객/비즈매칭/마케팅·공개사이트/wayfinding/현장운영/공사 옥션/BI/CMS/관리자 모듈(M10~M18)을 확장한다.
> **v3.0 정체성 확장 — 다중 전시관 SaaS**: 본 시스템은 **킨텍스 전용에서 다중 전시관(Venue) 멀티테넌트 SaaS**로 확장한다. 킨텍스(KINTEX)는 **기준 테넌트(테넌트 #1)** 로 유지되고, 코엑스(COEX) 등 여타 전시관을 테넌트로 온보딩한다. 기능(M1~M18·§5B·§6 나노바나나·§8 확정 스택)은 **전부 보존**하고, 그 위에 **테넌트 격리 레이어(tenant_id 전파·컨텍스트 해소·역할 계층)** 만 얹는다 — 기능은 그대로, 데이터·권한·컨텍스트가 전시관 단위로 분리된다. 상세는 **§1A**, 격리 전략은 **§8-2**.
> **v3.2 대외 접점 3-트랙 재구성**: 코엑스(COEX) IA 분석(3-사이트 분리: VISITOR/BUSINESS/CYBER)을 근거로, 대외 접점을 **visitor(관람객) / business(비즈니스=주최자·참가업체) / agency(에이전시=공사·장치·협력사)** 3개 트랙으로 분리·재편한다. 기존 6역할·7페르소나(§2)·포털 매트릭스(§2-1)·모듈(M1~M18)·화면(SCR-*)은 **전부 보존**하고, 그 위에 **트랙 셸(track shell) — 랜딩 3-분기 + 트랙별 서브홈 + 트랙-스코프 로그인 후 홈** IA 레이어만 얹는다. 상세는 **§2-2**(내부 역할 ops·admin은 대외 트랙 외부에 그대로 유지).
+> **v3.3 로그인 사용자 구분 → 메인페이지·메뉴 그룹 결정 규칙**: 소유자 확정(2026-07-12) — **"로그인한 사용자의 사용자 구분으로 메인페이지가 결정되고, 좌측 메뉴 그룹도 결정된다."** §2-2의 3-트랙(visitor/business/agency) + 내부 역할(ops/admin) 위에, **미인증 기본 진입 = visitor 공개 관람 랜딩**, **인증 후 역할별 전용 메인**(business 메인·agency 메인·visitor 메인은 신규 화면 — designer Stitch 의뢰, admin/내부 = 관리 메인 또는 비즈니스 메인+시스템관리)과 **트랙별 좌측 메뉴 그룹 노출/숨김**을 결정하는 규칙을 **§2-3**에 확정한다. 기존 §2·§2-2·화면(SCR-*)·라우트는 보존(안 B 정합), 결정 규칙·트랙 메인 화면 목록만 순증. src·design.md 미수정(frontend·designer 후속).
> **문서 소유권**: 본 PLANNING.md는 planner만 수정한다. design.md·타 문서는 이 문서 변경 시 "designer/planner 후속 반영 필요"로만 표기하고 직접 수정하지 않는다.
---
@@ -257,6 +258,110 @@
---
+## 2-3. 사용자 구분 → 랜딩(메인페이지)·메뉴/탭 그룹 결정 규칙 — 웹+모바일 공통 (v3.3, 소유자 확정 2026-07-12)
+
+> **소유자 확정 규칙**: **"로그인한 사용자의 사용자 구분(역할)으로 메인페이지가 결정되고, 좌측 메뉴 그룹(모바일은 하단 탭바)도 결정된다."** 두 가지 축을 확정한다 — ① **미인증(비로그인) 기본 진입 = visitor 트랙**(공개 관람 랜딩), ② **인증 후에는 역할이 매핑되는 트랙의 전용 메인**으로 랜딩하고 메뉴/탭 그룹을 트랙-스코프로 노출한다. **웹과 모바일 앱은 동일 로직**을 따른다(모바일은 셸 대신 하단 탭바 구성이 역할별로 결정).
+> **보존·경계**: §2 6역할/7페르소나·§2-2 3-트랙·§2-1 포털 매트릭스·기존 화면(SCR-*)·기존 딥 라우트(안 B, §2-2-4)를 **삭제·재작성하지 않는다.** 본 절은 그 위에 **랜딩 결정 로직 + 트랙별 메뉴/탭 가시성 + 신규 트랙 메인 화면 목록**만 순증한다. 본 절은 §2-2-3(C)의 "트랙-스코프 홈" 권고를 소유자 확정으로 **정밀화**한다 — 기존 홈(SCR-02/05/27)을 그대로 재사용하는 대신, **각 트랙 전용 "메인" 신규 화면(designer Stitch 의뢰)이 기존 역할 위젯을 조합**하는 형태로 확정.
+> **현행 기준(실측)**: 현재 로그인 후 전원 `/home`(SCR-HOME) 고정(`LoginPage.completeLogin → navigate('/home')`), AppShell `GROUPS`(ops·design·visitor·finance·work·system) 전원 동일 노출(system 그룹만 `isAdmin` 게이트). 역할 신호: `EventRole = ORGANIZER|EXHIBITOR|CONTRACTOR|HALL_MANAGER`(워크스페이스 `myRole`) + `globalRole`(`app_user.role_code` = ADMIN|MANAGER|VISITOR…). authStore: `globalRole()`·`roleForCurrent()`·`workspaces`. **src·design.md 미수정(frontend·kintex-mobile·designer 후속).**
+
+### 2-3-1. 역할 → 트랙 매핑 확정표 (웹·모바일 공통)
+
+| 트랙 | 구성 역할(§2) | 판정 신호 | 웹 셸 | 모바일 앱 타깃(§2-1-1) |
+|---|---|---|---|---|
+| **visitor**(관람) | 관람객·일반대중·게스트 | **미인증** OR `globalRole=VISITOR` OR 업무 워크스페이스 0개 | 공개/관람객 셸(MDI 비적용, §2.7-6) | **관람객 앱(B2C)** |
+| **business**(비즈니스) | ORGANIZER·EXHIBITOR | `myRole ∈ {ORGANIZER, EXHIBITOR}` 워크스페이스 보유 | AppShell(MDI) | **운영 앱(B2B)** |
+| **agency**(에이전시) | CONTRACTOR·등록/협력업체 | `myRole=CONTRACTOR` 워크스페이스 보유(상위 트랙 없음) | AppShell(MDI) | **운영 앱(B2B)** |
+| **내부-ops** | HALL_MANAGER·킨텍스 운영 | `globalRole=MANAGER` OR `myRole=HALL_MANAGER` 워크스페이스 | AppShell(MDI) | **운영 앱(B2B)** |
+| **내부-admin** | 테넌트/플랫폼 관리자 | `globalRole=ADMIN`(=현행 `isAdmin`) | AppShell(MDI) + 시스템관리 그룹 | 웹 전용(모바일 백오피스 미제공, §2-1-1) |
+
+- **트랙 판정 헬퍼**: `trackOf(EventRole)` → ORGANIZER/EXHIBITOR = business, CONTRACTOR = agency, HALL_MANAGER = 내부-ops. `globalRole` 오버레이 → ADMIN = 내부-admin(system 그룹 부여), MANAGER = 내부-ops, VISITOR/null = visitor(업무 워크스페이스 없을 때).
+
+### 2-3-2. 판정 우선순위 · 멀티역할 · 트랙 스위칭
+
+- **primaryTrack 우선순위(높음→낮음)**: **내부-admin > 내부-ops > business > agency > visitor.** 복수 역할 사용자(계정 통합, §2-1-1)의 **로그인 랜딩**은 이 우선순위로 결정한다. 예: `admin+organizer` → 내부-admin 랜딩(관리 메인, 단 비즈니스 메뉴도 접근), `contractor+exhibitor` → business 랜딩(둘 다 업무 트랙, business 우위).
+- **activeTrack(반응형)**: 좌측 상단 **행사 전환 드롭다운(기존)** 으로 워크스페이스를 바꾸면 `activeTrack = trackOf(roleForCurrent())` 로 따라 바뀌어 메뉴/탭이 재스코프된다 → **별도 트랙 스위처 UI 신설 최소화**(기존 워크스페이스 선택 재사용). `globalRole=ADMIN` 의 시스템관리 오버레이는 activeTrack과 무관하게 항상 유지(현행 `isAdmin` 게이트 보존).
+- **연속성**: 트랙별 메인/메뉴가 달라도 계정은 단일 통합(§2-1-1)이므로 리드·비즈매칭·재방문 이력은 훼손되지 않는다(관람객→바이어/참가 승격 시 business 트랙 접근 권한만 추가).
+
+### 2-3-3. 랜딩(메인페이지) 결정표
+
+**(A) 웹** — `completeLogin` 분기 + 미인증 루트 가드.
+
+| 순서 | 상태·조건(신호) | 트랙 | 메인페이지 | 라우트(권고·안 B) | 화면 |
+|---|---|---|---|---|---|
+| 0 | **미인증(토큰 없음)** | visitor | 공개 관람 랜딩 | `/` (현행 `/login` 리다이렉트 → visitor 랜딩으로) | **SCR-T0(신규)** |
+| 1 | `globalRole=ADMIN` | 내부-admin | 관리 메인(**또는** 비즈니스 메인+시스템관리) | `/admin`(또는 `/home`) | SCR-16(+SCR-T2) |
+| 2 | `globalRole=MANAGER` OR HALL_MANAGER ws | 내부-ops | 운영 메인(비즈니스 메인 운영 변형) | `/home` | SCR-T2(운영 변형) |
+| 3 | ORGANIZER/EXHIBITOR ws | business | 비즈니스 메인 | `/home` | **SCR-T2(신규)** |
+| 4 | CONTRACTOR ws(1~3 없음) | agency | 에이전시 메인(수주·옥션) | `/contractor/dashboard`(또는 `/agency`) | **SCR-T3(신규)** |
+| 5 | `globalRole=VISITOR` OR 업무 ws 0 | visitor | 관람객 메인 | `/visitor` | **SCR-T1(신규)** |
+| — | 판정 불가(폴백) | — | 안전 폴백 | `/home` | SCR-HOME(현행) |
+
+**(B) 모바일 앱** — 동일 로직(진입 시 미인증=관람객, 로그인 후 역할별 홈 탭 네비게이터 스위칭).
+
+| 순서 | 상태·조건 | 트랙/역할 | 모바일 진입(랜딩) | 화면 |
+|---|---|---|---|---|
+| 0 | **미인증(토큰 없음)** | visitor(게스트) | **관람객 진입** — 행사탐색·사전등록·티켓·게스트 예매 | 모바일 관람객 홈(신규) |
+| 1 | ADMIN | (모바일 백오피스 없음) | 보유 업무역할 메인, 없으면 관람 최소 뷰 | — |
+| 2 | MANAGER/HALL_MANAGER | 내부-ops | 운영 홈(승인·현장 상황판) | SCR-M2 계열 + 운영 홈(신규) |
+| 3 | ORGANIZER/EXHIBITOR | business | 업무 홈(주최=대시보드 / 참가=내 부스) | 모바일 비즈니스 홈(신규) |
+| 4 | CONTRACTOR | agency | 수주/옥션 홈 + 현장 | SCR-M1 계열 + 모바일 에이전시 홈(신규) |
+| 5 | VISITOR/게스트 | visitor | 관람객 홈(티켓·배지·wayfinding·매칭) | SCR-M5~M9·M14·M15 계열 |
+
+### 2-3-4. 좌측 메뉴 그룹 매트릭스 (웹 AppShell `GROUPS`)
+
+노출 규칙: `●` 전체 노출 · `◐` 부분(항목 필터) · `✕` 숨김. 현행 `visibleGroups = GROUPS.filter(!g.admin || isAdmin)` 를 **트랙 필터 추가**로 확장.
+
+| `GROUPS.id` (그룹명) | 내부-admin | 내부-ops | business(주최) | business(참가) | agency | visitor |
+|---|---|---|---|---|---|---|
+| `ops` 전시 운영 | ● | ● | ● | ◐(대시보드·참가업체 조회) | ✕ | ✕ |
+| `design` 설계·시공 | ● | ●(검수·홀 부하) | ● | ●(부스설계·유틸·시각화) | ●(수주부스·공사옥션·규정·반입) | ✕ |
+| `visitor` 관람객·마케팅 | ● | ● | ● | ◐(리드) | ✕ | ✕(셸 미노출) |
+| `finance` 정산·분석 | ● | ● | ● | ◐(내 정산) | ✕ | ✕ |
+| `work` 업무 공통 | ● | ● | ● | ● | ● | ✕ |
+| `system` 시스템관리 | ● | ✕ | ✕ | ✕ | ✕ | ✕ |
+
+- **visitor 트랙**은 업무 AppShell(GROUPS) 미노출 — §2-2-3(B)의 공개/관람객 셸(내 티켓·내 행사·방문가이드·알림 최소 메뉴)을 사용한다.
+- **agency `design` 그룹 부분(◐→항목)**: 노출 = `수주 부스`(`/contractor/dashboard`)·`공사 옥션`(`/auctions`)·`서류·마일스톤`(도면제출)·`반입·반출`. 숨김 = 주최자 전용 `플로어플랜 스튜디오` 편집.
+- **내부-ops(홀매니저)**: `design`은 검수·홀 부하 관점(플로어플랜 RO), `system` 제외(테넌트 관리자 아님).
+
+### 2-3-5. 하단 탭바 매트릭스 (모바일, 역할별 결정)
+
+design.md §2-2 모바일 하단 탭 4개(홈/내 부스·승인/알림/더보기)를 **역할별 구성으로 확정**(탭 4~5개).
+
+| 트랙/역할 | 탭1 (홈) | 탭2 | 탭3 | 탭4 | 탭5 |
+|---|---|---|---|---|---|
+| visitor(미인증·관람객) | 행사탐색 | 티켓·배지 | 길찾기(wayfinding) | 알림 | 더보기(게스트→가입) |
+| business 주최자 | 대시보드 | 참가·승인 | 현장 상황판 | 알림 | 더보기 |
+| business 참가업체 | 내 부스 | 체크인·리드스캔 | 시각화 | 알림 | 더보기 |
+| agency 공사·협력 | 수주·옥션 | 현장 체크리스트 | 통행증 | 알림 | 더보기 |
+| 내부-ops 홀매니저 | 홈 | 승인·검수 | 현장(안전·혼잡) | 알림 | 더보기 |
+| 내부-admin | (모바일 백오피스 미제공 — 웹 전용) | | | | |
+
+### 2-3-6. 신규 트랙 메인 화면 목록·스펙 (designer Stitch 의뢰 대상)
+
+> planner는 **화면 목록·용도·핵심 섹션·재사용 위젯**까지 확정한다. **SCR 번호 확정·Stitch 영어 프롬프트 작성·생성·이식은 designer 소관**(MEMORY: 전 화면 Stitch 경유). 아래 가칭 SCR-T0~T3(웹)·모바일 3종은 designer가 design.md에서 정식 번호·프롬프트로 확정한다.
+
+| # | 화면(가칭) | 채널 | 트랙 | 핵심 섹션/위젯 | 셸 | 조합할 기존 위젯 |
+|---|---|---|---|---|---|---|
+| 1 | **SCR-T0 공개 관람 랜딩(루트)** | 웹 | visitor(미인증) | 히어로·행사 카드·사전등록/티켓 CTA·**business/agency 로그인 진입**·방문가이드 허브 | 공개 셸 | §2-2-3(B) visitor 서브홈, SCR-P1 |
+| 2 | **SCR-T1 관람객 메인** | 웹 | visitor(인증) | 내 티켓·모바일 배지·행사탐색·wayfinding·비즈매칭 | 관람객 셸 | SCR-P4·P7·P8 |
+| 3 | **SCR-T2 비즈니스 메인** | 웹 | business/내부-ops | KPI·D-데이 할일·내 행사 — **역할 변형**(주최=마일스톤·홀 SCR-02 / 참가=내 부스·마감 SCR-05) | AppShell | SCR-02·05·HOME |
+| 4 | **SCR-T3 에이전시 메인** | 웹 | agency | 수주 목록·진행 중 옥션·응찰 현황·규정검증 요약·작업신고 | AppShell | SCR-27·28·09·38 |
+| 5 | **모바일 관람객 홈** | 모바일 | visitor | 행사탐색·티켓·배지·wayfinding | 관람객 앱 | SCR-M5~M9 |
+| 6 | **모바일 비즈니스 홈** | 모바일 | business | 대시보드/내 부스·승인·리드(역할 변형) | 운영 앱 | (신규) |
+| 7 | **모바일 에이전시 홈** | 모바일 | agency | 수주·옥션·현장 | 운영 앱 | SCR-M1 |
+
+- **관리 메인**(내부-admin)은 **기존 SCR-16 관리 대시보드 재사용**(신규 아님) — 비즈니스 메인(SCR-T2)+시스템관리 그룹 조합 대안 병기.
+- **신규 Stitch 화면 = 웹 4(SCR-T0~T3) + 모바일 3 = 7종.** 나머지 세부 업무 화면은 전량 기존 SCR 재사용(안 B).
+
+### 2-3-7. 구현 권고 및 담당 (지시서 포인터)
+
+- **웹 (frontend 담당)**: ① `LoginPage.completeLogin` 을 `resolveLandingTrack(user, workspaces)` → `landingPath(track)` 분기로 교체(현행 `/home` 고정 폐기, business/내부는 `/home` 유지 = 회귀 0, agency=`/contractor/dashboard`·visitor=`/visitor` 신설 분기). ② 미인증 루트 `/` = SCR-T0(현행 `/login` 강제 리다이렉트 완화). ③ `AppShell.visibleGroups` 에 **트랙 필터** 추가(`GROUPS`/item에 선택적 `tracks?` 허용목록 필드, `activeTrack` 파생). ④ authStore에 `activeTrack()` 셀렉터 신설. **라우트·화면 재작성 없음(안 B).**
+- **모바일 (kintex-mobile 담당)**: ① 앱 진입 미인증 = 관람객 탭 네비게이터. ② 로그인 후 `resolveLandingTrack` 동일 규칙으로 역할별 탭 네비게이터 스위칭(§2-3-5). ③ 탭 구성은 트랙-스코프. WISE 모바일 레퍼런스 컨벤션 준수.
+- **지시서**: `_workspace/plan_role_routing.md`(웹+모바일 섹션·담당·웨이브 명시). **design.md/designer 후속**: 신규 트랙 메인 7종 Stitch 의뢰 + §2.7-6 역할별 적용·§2-1 사이트맵·§4 모바일 탭 매트릭스에 본 규칙 반영.
+
+---
+
## 3. 현행(As-Is) 프로세스 분석
### 3-1. 주최자 타임라인과 병목
@@ -1091,5 +1196,6 @@ 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.3** | 2026-07-12 | planner | **사용자 구분 → 랜딩(메인페이지)·메뉴/탭 그룹 결정 규칙 확정 — 웹+모바일 공통(소유자 확정 2026-07-12).** 기존 스코프(§2 6역할·§2-2 3-트랙·§2-1 포털·화면 SCR-*·딥 라우트) 전부 보존, 결정 로직·트랙 메인 목록만 순증(안 B). ①헤더 v3.3 규칙 요약 라인 + 버전 상향 ②**§2-3 신설** — (2-3-1) 역할→트랙 매핑 확정표(visitor/business/agency/내부-ops/내부-admin, 판정 신호=EventRole `myRole`+globalRole `role_code`, 웹 셸·모바일 앱 타깃) (2-3-2) primaryTrack 우선순위(내부-admin>내부-ops>business>agency>visitor)·activeTrack 반응형(기존 행사 전환 재사용)·멀티역할 연속성 (2-3-3) 랜딩 결정표 **(A)웹**(미인증=SCR-T0 공개 관람 랜딩, 인증=역할별 전용 메인, completeLogin 분기·현행 `/home` 고정 교체) **(B)모바일**(동일 로직·역할별 홈 탭 스위칭) (2-3-4) 웹 AppShell `GROUPS` 트랙별 노출 매트릭스(●/◐/✕, `visibleGroups` 트랙 필터 확장) (2-3-5) **모바일 하단 탭바 역할별 매트릭스** (2-3-6) **신규 트랙 메인 화면 목록·스펙 7종**(웹 SCR-T0 공개랜딩·T1 관람객·T2 비즈니스(역할변형)·T3 에이전시 + 모바일 관람객/비즈니스/에이전시 홈 — designer Stitch 의뢰 대상, 관리 메인=SCR-16 재사용) (2-3-7) 구현 권고·담당(웹=frontend·모바일=kintex-mobile, 지시서 `_workspace/plan_role_routing.md`). §2-2-3(C) "트랙-스코프 홈" 권고를 소유자 확정으로 정밀화(기존 홈 재사용→전용 신규 메인이 기존 위젯 조합). **src·design.md 미수정** — frontend/kintex-mobile/designer 후속 반영 필요 |
| **v3.2** | 2026-07-12 | planner | **제품 명칭 확정 + 대외 접점 3-트랙 재구성(소유자 확정 2026-07-12).** 기존 스코프(M1~M18·§1A 멀티테넌시·§2 6역할/7페르소나·§2-1 포털 매트릭스·§5B 공통레이어·§6 나노바나나·§8 아키텍처·design.md SCR-*) **전부 보존**, IA 레이어만 순증. ①**제품 공식 명칭 = "KINTEX AI 전시·행사시스템"**(기존 "자동전시시스템/Exhibition Automation Platform" 대체 — 전시+행사/이벤트 포괄) → H1 제목·헤더 명칭 절 갱신, 정본 선언(기존 표기는 문맥 보존상 잔존) ②**`docs/analysis/coex-website.md` 신설** — 코엑스 3-사이트 IA(VISITOR `coex.co.kr`/BUSINESS `business.coex.co.kr`/CYBER 참가신청 `cybercoex.co.kr`) 분석 + 킨텍스(kintex.com) 대비표 11축 + 차용 요소. WebFetch 3사이트 + WebSearch 보조 ③**§2-2 대외 접점 3-트랙 재구성 신설** — visitor(관람객)/business(주최자·참가업체)/agency(공사·장치·협력사) 3-트랙 정의(대상·여정·진입화면·기능 매핑), 코엑스 차용요소, 공개사이트 구조(관문 랜딩 3-분기 + 트랙별 서브홈 3 + **로그인 후 트랙-스코프 홈 권고**), **라우트 체계 3안 비교→하이브리드(트랙 셸 + 기존 딥라우트 유지, 마이그레이션 비용 최소) 권고**, 기존 SCR/라우트→트랙 재배치 매핑표(신규 화면 4종=관문+서브홈3만, 나머지 재사용). 트랙은 역할의 상위 묶음(대체 아님), 내부역할 ops·admin은 트랙 외부 유지. 헤더 v3.2 정체성 라인 추가. **design.md·src 미수정(designer/frontend 후속)** — 구현 지시서 `_workspace/plan_3track.md` 별도 작성 |
| **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/analysis/nifty-design-refs.md b/docs/analysis/nifty-design-refs.md
new file mode 100644
index 0000000..7a585ef
--- /dev/null
+++ b/docs/analysis/nifty-design-refs.md
@@ -0,0 +1,30 @@
+# Nifty 디자인 레퍼런스 (소유자 지정, 2026-07-12)
+
+> **방향 확정(2026-07-12):** 킨텍스 공통 UI의 **디자인 기준을 Nifty(themeon)로 채택**한다. 개별 요소를 하나씩 지정받는 중 — 아래 표에 계속 누적. 적용은 wise-ui 하네스가 요소별 웨이브로 kx 토큰 번역 통일.
+
+킨텍스 UI 정렬의 스타일 레퍼런스. GUARDiA ITSM이 원래 Nifty 다크 테마 기반이라 프로젝트 계보와 정합.
+적용은 kx 토큰으로 번역(하드코딩 금지), 다크/라이트 양 테마, 이모지 금지(선 SVG).
+
+| 영역 | 레퍼런스 URL | 적용 대상 | 상태 |
+|---|---|---|---|
+| 정적 테이블 헤더 | https://preview.themeon.net/nifty/tables/static-tables/ ("Advanced table headers") | `.kx-table` 공통 표준 | 진행 중 |
+| 인터랙티브 그리드 | https://preview.themeon.net/nifty/tables/tabulator/ | 공통 DataGrid(정렬·필터·페이지·CSV export) — 관리자/목록 | 대기 |
+| 상세 페이지 | https://preview.themeon.net/nifty/blog-apps/blog/ | 행사·전시·공지·옥션 등 상세 뷰 | 대기 |
+| 카드 | https://preview.themeon.net/nifty/ui-elements/cards/ | `.kx-card` 공통 카드(헤더·바디·푸터·액션·컬러바) | 대기 |
+| 버튼 | https://preview.themeon.net/nifty/ui-elements/buttons/ | `Button`/`.kx-btn` 공통(변형·크기·상태·아이콘·그룹) | 대기 |
+| 드롭다운 | https://preview.themeon.net/nifty/ui-elements/dropdowns/ | 드롭다운/메뉴/셀렉트(kx 드롭다운) | 대기 |
+| 컴포넌트 전반 | https://preview.themeon.net/nifty/ui-elements/components/ | 뱃지·알럿·탭·모달·툴팁·프로그레스·페이지네이션 등 공통 컴포넌트 | 대기 |
+| 리스트 그룹 | https://preview.themeon.net/nifty/ui-elements/list-group/ | 리스트 그룹(공지·활동·검색결과·목록형 카드) | 대기 |
+| 타이포그래피 | https://preview.themeon.net/nifty/ui-elements/ (typography) | 제목·본문·캡션 위계(kx 타이포 토큰) | 대기 |
+| 모달/메시지박스 | https://preview.themeon.net/nifty/ui-elements/ (modals) | 모달·확인/경고 메시지박스(kx Modal) | 대기 |
+| Offcanvas | https://preview.themeon.net/nifty/ui-elements/ (offcanvas) | 오프캔버스 패널(모바일 드로어·상세 슬라이드) | 대기 |
+| 달력 | https://preview.themeon.net/nifty/app-views/calendar/ | 일정 캘린더(월/주/일)·이벤트 색/드래그·사이드 목록 | 대기(일정 3-뷰는 구조 완료, Nifty 스타일 정합 대상) |
+| **UI-elements 전체** | **https://preview.themeon.net/nifty/ui-elements/** | **Nifty UI-elements 전 페이지를 킨텍스 공통 UI 기준으로 채택**(위 개별 + 나머지 요소 전부) | 기준 |
+| 셸·메뉴(기존) | WISE(UIWS) AppLayout/LeftNav/CalendarView | 셸·아코디언·캘린더 | 완료(구조) |
+| 상단바(top frame) | https://preview.themeon.net/nifty/ (navbar/header) | 상단 헤더 — 로고·검색·알림·설정·사용자 드롭다운·풀스크린·언어 | 대기 |
+| 설정(레이아웃 커스터마이저) | https://preview.themeon.net/nifty/ (설정 아이콘 → 우측 패널) | 상단 설정 아이콘 클릭 → 우측 offcanvas: 테마(라이트/다크)·레이아웃 모드·사이드바·색상 등 실시간 조정 | 대기(공용 Offcanvas 재사용) |
+
+## 원칙
+- 레퍼런스의 **구조·위계·인터랙션**을 차용하되, 색·간격·폰트는 kx 디자인 토큰으로 번역.
+- WISE(UIWS)가 대응물을 가진 영역은 WISE 우선(셸·시스템관리·공통기능). Nifty는 WISE에 없는 표현(고급 테이블·blog형 상세)의 레퍼런스.
+- 신규 npm 의존성(Tabulator 등)은 번들·다크·i18n 정합 확인 후 파일럿→확산.
diff --git a/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayController.java b/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayController.java
new file mode 100644
index 0000000..fc41548
--- /dev/null
+++ b/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayController.java
@@ -0,0 +1,87 @@
+package com.zioinfo.kintex.system.holiday;
+
+import com.zioinfo.kintex.auth.KintexPrincipal;
+import com.zioinfo.kintex.common.ApiResponse;
+import com.zioinfo.kintex.common.audit.Audited;
+import com.zioinfo.kintex.system.SystemAccessGuard;
+import jakarta.validation.Valid;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 공휴일 API — 시스템관리(관리자 CRUD) + 캘린더 공개(인증) 조회.
+ *
+ * - 관리: {@code /api/admin/holidays} — 목록/추가/삭제/휴무토글, ADMIN 게이트({@link SystemAccessGuard}).
+ * - 공개(인증): {@code GET /api/holidays?from=&to=} — 기간 내 실제 휴무만(붉은색 표시용).
+ *
+ * 데이터·시드(2024~2050)는 V41 소유 — 본 API는 그 위 조회·임시공휴일 관리만. 테넌트 스코프.
+ */
+@RestController
+public class HolidayController {
+
+ private final HolidayService service;
+ private final SystemAccessGuard guard;
+
+ public HolidayController(HolidayService service, SystemAccessGuard guard) {
+ this.service = service;
+ this.guard = guard;
+ }
+
+ /** 관리자 — 목록(year 또는 from/to, 휴무 off 포함). */
+ @GetMapping("/api/admin/holidays")
+ public ApiResponse> adminList(@AuthenticationPrincipal KintexPrincipal principal,
+ @RequestParam(value = "year", required = false) Integer year,
+ @RequestParam(value = "from", required = false) String from,
+ @RequestParam(value = "to", required = false) String to) {
+ guard.requireAdmin(principal);
+ return ApiResponse.ok(service.adminList(principal.tenantId(), year, from, to));
+ }
+
+ /** 관리자 — 데이터 존재 연도 목록(년 선택 드롭다운). */
+ @GetMapping("/api/admin/holidays/years")
+ public ApiResponse> years(@AuthenticationPrincipal KintexPrincipal principal) {
+ guard.requireAdmin(principal);
+ return ApiResponse.ok(service.years(principal.tenantId()));
+ }
+
+ /** 관리자 — 임시공휴일 추가(PK 충돌 시 409). */
+ @Audited(action = "HOLIDAY_CREATE", targetType = "holiday")
+ @PostMapping("/api/admin/holidays")
+ public ApiResponse create(@AuthenticationPrincipal KintexPrincipal principal,
+ @Valid @RequestBody HolidayCreateRequest req) {
+ guard.requireAdmin(principal);
+ return ApiResponse.ok(service.create(principal.tenantId(), req));
+ }
+
+ /** 관리자 — 삭제. */
+ @Audited(action = "HOLIDAY_DELETE", targetType = "holiday")
+ @DeleteMapping("/api/admin/holidays/{ymd}")
+ public ApiResponse delete(@AuthenticationPrincipal KintexPrincipal principal,
+ @PathVariable String ymd) {
+ guard.requireAdmin(principal);
+ service.delete(principal.tenantId(), ymd);
+ return ApiResponse.ok(null);
+ }
+
+ /** 관리자 — 휴무 여부 토글(임시휴무 on/off). */
+ @Audited(action = "HOLIDAY_TOGGLE", targetType = "holiday")
+ @PatchMapping("/api/admin/holidays/{ymd}")
+ public ApiResponse toggle(@AuthenticationPrincipal KintexPrincipal principal,
+ @PathVariable String ymd,
+ @Valid @RequestBody HolidayToggleRequest req) {
+ guard.requireAdmin(principal);
+ service.toggle(principal.tenantId(), ymd, req.isHoliday());
+ return ApiResponse.ok(null);
+ }
+
+ /** 공개(인증) — 캘린더 붉은색 표시용. 기간 내 실제 휴무만. */
+ @GetMapping("/api/holidays")
+ public ApiResponse> calendar(@AuthenticationPrincipal KintexPrincipal principal,
+ @RequestParam(value = "from", required = false) String from,
+ @RequestParam(value = "to", required = false) String to) {
+ guard.require(principal);
+ return ApiResponse.ok(service.publicRange(principal.tenantId(), from, to));
+ }
+}
diff --git a/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayCreateRequest.java b/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayCreateRequest.java
new file mode 100644
index 0000000..ede8d0e
--- /dev/null
+++ b/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayCreateRequest.java
@@ -0,0 +1,16 @@
+package com.zioinfo.kintex.system.holiday;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+
+/**
+ * 임시공휴일 추가 요청 — 관리자 전용. PK(tenant_id, ymd) 충돌 시 409.
+ * {@code ymd}는 yyyy-MM-dd. {@code isSubstitute}는 선택(기본 false).
+ */
+public record HolidayCreateRequest(
+ @NotBlank @Pattern(regexp = "\\d{4}-\\d{2}-\\d{2}", message = "날짜는 yyyy-MM-dd 형식이어야 합니다.")
+ String ymd,
+ @NotBlank @Size(max = 60) String name,
+ Boolean isSubstitute) {
+}
diff --git a/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayDto.java b/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayDto.java
new file mode 100644
index 0000000..2a15411
--- /dev/null
+++ b/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayDto.java
@@ -0,0 +1,18 @@
+package com.zioinfo.kintex.system.holiday;
+
+/**
+ * 공휴일 DTO — 관리 화면·캘린더 붉은색 표시 공용(민감정보 없음).
+ *
+ * - {@code ymd} — 날짜(yyyy-MM-dd).
+ * - {@code name} — 공휴일명(예: 신정, 설날, 대체공휴일(추석)).
+ * - {@code isSubstitute} — 대체공휴일 여부(배지 표시용).
+ * - {@code isHoliday} — 휴무 여부(임시휴무 토글, 관리 화면 전용). 공개 조회는 true만 반환.
+ *
+ * 데이터·시드는 V41__holidays_2050.sql(2024~2050) 소유. 본 DTO는 조회·관리 표현만.
+ */
+public record HolidayDto(
+ String ymd,
+ String name,
+ boolean isSubstitute,
+ boolean isHoliday) {
+}
diff --git a/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayMapper.java b/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayMapper.java
new file mode 100644
index 0000000..7671ff8
--- /dev/null
+++ b/src/backend/src/main/java/com/zioinfo/kintex/system/holiday/HolidayMapper.java
@@ -0,0 +1,60 @@
+package com.zioinfo.kintex.system.holiday;
+
+import org.apache.ibatis.annotations.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 공휴일 매퍼 — V41 holiday 테이블(PK tenant_id, ymd) 조회·관리.
+ * 정렬 권위 = ymd ASC. ymd는 to_char로 yyyy-MM-dd 문자열 반환(프론트 셀 매칭 키).
+ * ★MyBatis 어노테이션 규약: 별칭 AS "x" 쌍따옴표, {@code
""")
diff --git a/src/backend/src/main/java/com/zioinfo/kintex/work/search/SearchResultItem.java b/src/backend/src/main/java/com/zioinfo/kintex/work/search/SearchResultItem.java
index 05c9634..1947828 100644
--- a/src/backend/src/main/java/com/zioinfo/kintex/work/search/SearchResultItem.java
+++ b/src/backend/src/main/java/com/zioinfo/kintex/work/search/SearchResultItem.java
@@ -1,5 +1,9 @@
package com.zioinfo.kintex.work.search;
-/** 통합검색 결과 항목. type: WORKLOG|NOTICE|MEETING|REPORT|OPINION. */
-public record SearchResultItem(String type, String id, String title, String createdAt) {
+/**
+ * 통합검색 결과 항목.
+ * type: 업무(WORKLOG|NOTICE|MEETING|REPORT|OPINION) + 도메인(EVENT|COMPANY|AUCTION|BOOTH|LEAD|CMS).
+ * route: type(및 id)로 파생한 프론트 이동 경로(민감정보 미포함).
+ */
+public record SearchResultItem(String type, String id, String title, String createdAt, String route) {
}
diff --git a/src/backend/src/main/java/com/zioinfo/kintex/work/search/SearchService.java b/src/backend/src/main/java/com/zioinfo/kintex/work/search/SearchService.java
index 00bd187..50edeec 100644
--- a/src/backend/src/main/java/com/zioinfo/kintex/work/search/SearchService.java
+++ b/src/backend/src/main/java/com/zioinfo/kintex/work/search/SearchService.java
@@ -26,8 +26,32 @@ public class SearchService {
int size = limit <= 0 ? 30 : Math.min(limit, 100);
boolean seeAll = scope.canSeeAll(principal);
List