# KCG AI Monitoring - 다음 단계 리팩토링 TODO > 프론트엔드 UI 스캐폴딩 + 기반 인프라(상태관리, 지도 GPU, mock 데이터, CVA) 완료 상태. 백엔드 연동 및 운영 품질 확보를 위해 남은 항목을 순차적으로 진행한다. --- ## 1. ✅ 상태관리 도입 (Zustand 5.0) — COMPLETED `zustand` 5.0.12 설치, `src/stores/`에 8개 독립 스토어 구현 완료. - `vesselStore` — 선박 목록, 선택, 필터 - `patrolStore` — 순찰 경로/함정 - `eventStore` — 탐지/경보 이벤트 - `kpiStore` — KPI 메트릭, 추세 - `transferStore` — 전재(환적) - `gearStore` — 어구 탐지 - `enforcementStore` — 단속 이력 - `settingsStore` — theme/language + localStorage 동기화, 지도 타일 자동 전환 > `AuthContext`는 유지 (인증은 Context API가 적합, 마이그레이션 불필요로 결정) --- ## 2. API 서비스 계층 (Axios 1.14) — 구조 완성, 실제 연동 대기 ### 현재 상태 - `src/services/`에 7개 서비스 모듈 구현 (api, vessel, event, patrol, kpi, ws, index) - `api.ts`: fetch 래퍼 (`apiGet`, `apiPost`) — 향후 Axios 교체 예정 - 각 서비스가 `data/mock/` 모듈에서 mock 데이터 반환 (실제 HTTP 호출 0건) - `ws.ts`: STOMP WebSocket 스텁 존재, 미구현 ### 남은 작업 - [ ] `axios` 1.14 설치 → `api.ts`의 fetch 래퍼를 Axios 인스턴스로 교체 - [ ] Axios 인터셉터: - Request: Authorization 헤더 자동 주입 - Response: 401 → 로그인 리다이렉트, 500 → 에러 토스트 - [ ] `@tanstack/react-query` 5.x 설치 → TanStack Query Provider 추가 - [ ] 각 서비스의 mock 반환을 실제 API 호출로 교체 - [ ] 로딩 스켈레톤, 에러 바운더리 공통 컴포넌트 --- ## 3. 실시간 인프라 (STOMP.js + SockJS) — 스텁 구조만 존재 ### 현재 상태 - `services/ws.ts`에 `connectWs` 스텁 함수 존재 (인터페이스 정의 완료) - STOMP.js, SockJS 미설치 — 실제 WebSocket 연결 없음 - `useStoreLayerSync` hook으로 store→지도 실시간 파이프라인 준비 완료 ### 남은 작업 - [ ] `@stomp/stompjs` + `sockjs-client` 설치 - [ ] `ws.ts` 스텁을 실제 STOMP 클라이언트로 구현 - [ ] 구독 채널 설계: - `/topic/ais-positions` — 실시간 AIS 위치 - `/topic/alerts` — 경보/이벤트 - `/topic/detections` — 탐지 결과 - `/user/queue/notifications` — 개인 알림 - [ ] 재연결 로직 (지수 백오프) - [ ] store → `useStoreLayerSync` → 지도 마커 실시간 업데이트 연결 - [ ] `eventStore`와 연동하여 알림 배너/뱃지 카운트 업데이트 --- ## 4. ✅ 고급 지도 레이어 (deck.gl 9.2) — COMPLETED `deck.gl` 9.2.11 + `@deck.gl/mapbox` 설치, MapLibre + deck.gl 인터리브 아키텍처 구현 완료. - **BaseMap**: `forwardRef` + `memo`, `MapboxOverlay`를 `useImperativeHandle`로 외부 노출 - **useMapLayers**: RAF 배치 레이어 업데이트, React 리렌더 0회 - **useStoreLayerSync**: Zustand store.subscribe → RAF → overlay.setProps (React 우회) - **STATIC_LAYERS**: EEZ + NLL PathLayer 싱글턴 (GPU 1회 업로드) - **createMarkerLayer**: ScatterplotLayer + transitions 보간 + DataFilterExtension - **createRadiusLayer**: 반경 원 표시용 ScatterplotLayer - 레거시 GeoJSON 레이어(`boundaries.ts`)는 하위 호환으로 유지 > 성능 목표 40만척+ GPU 렌더링 달성. TripsLayer/HexagonLayer/IconLayer는 실데이터 확보 후 추가 예정. --- ## 5. ✅ 더미 데이터 통합 — COMPLETED `src/data/mock/`에 7개 공유 mock 모듈 구현 완료. TypeScript 인터페이스 정의 포함. ``` data/mock/ ├── vessels.ts # VesselData — 선박 목록 (한국, 중국, 경비함) ├── events.ts # EventRecord, AlertRecord — 탐지/단속 이벤트 ├── transfers.ts # 전재(환적) 데이터 ├── patrols.ts # PatrolShip — 순찰 경로/함정 ├── gear.ts # 어구 탐지 데이터 ├── kpi.ts # KpiMetric, MonthlyTrend, ViolationType └── enforcement.ts # 단속 이력 데이터 ``` - `services/` 계층이 mock 모듈을 import하여 반환 → 향후 API 교체 시 서비스만 수정 - 인터페이스가 API 응답 타입 계약 역할 수행 --- ## 6. i18n 실적용 — 구조 완성, 내부 텍스트 미적용 ### 현재 상태 - 10 네임스페이스 리소스 완비: common, dashboard, detection, patrol, enforcement, statistics, ai, fieldOps, admin, auth - ko/en 각 10파일 (총 20 JSON) - `settingsStore.toggleLanguage()` + `localStorage` 동기화 구현 완료 - **적용 완료**: MainLayout 사이드바 메뉴명, 24개 페이지 제목, LoginPage - **미적용**: 각 페이지 내부 텍스트 (카드 레이블, 테이블 헤더, 상태 텍스트 등) — 대부분 한국어 하드코딩 잔존 ### 남은 작업 - [ ] 각 feature 페이지 내부 텍스트를 `useTranslation('namespace')` + `t()` 로 교체 - [ ] 날짜/숫자 포맷 로컬라이즈 (`Intl.DateTimeFormat`, `Intl.NumberFormat`) - [ ] 누락 키 감지 자동화 (i18next missing key handler 또는 lint 규칙) --- ## 7. ✅ Tailwind 공통 스타일 모듈화 (CVA) — COMPLETED `class-variance-authority` 0.7.1 설치, `src/lib/theme/variants.ts`에 3개 CVA 변형 구현 완료. - **cardVariants**: default / elevated / inner / transparent — CSS 변수 기반 테마 반응 - **badgeVariants**: 8 intent (critical~cyan) x 4 size (xs~lg) — 150회+ 반복 패턴 통합 - **statusDotVariants**: 4 status (online/warning/danger/offline) x 3 size (sm/md/lg) - `shared/components/ui/card.tsx`, `badge.tsx`에 CVA 적용 완료 - CSS 변수(`surface-raised`, `surface-overlay`, `border`) 참조로 Dark/Light 자동 반응 --- ## 8. 코드 스플리팅 — 미착수 ### 현재 상태 - **단일 번들 ~3.2MB** (모든 feature + deck.gl + MapLibre + ECharts 포함) - `React.lazy` 미적용, 모든 31개 페이지가 동기 import - 초기 로딩 시 사용하지 않는 페이지 코드까지 전부 다운로드 ### 필요한 이유 - 초기 로딩 성능 개선 (FCP, LCP) - 현장 모바일 환경 (LTE/3G)에서의 사용성 확보 - 번들 캐싱 효율 향상 (변경된 chunk만 재다운로드) ### 구현 계획 - [ ] `React.lazy` + `Suspense`로 feature 단위 동적 임포트: ```typescript const Dashboard = lazy(() => import('@features/dashboard/Dashboard')); const RiskMap = lazy(() => import('@features/risk-assessment/RiskMap')); ``` - [ ] `App.tsx` 라우트 전체를 lazy 컴포넌트로 교체 - [ ] 로딩 폴백 컴포넌트 (스켈레톤 또는 스피너) 공통화 - [ ] Vite `build.rollupOptions.output.manualChunks` 설정: ```typescript manualChunks: { 'vendor-react': ['react', 'react-dom', 'react-router-dom'], 'vendor-map': ['maplibre-gl', 'deck.gl', '@deck.gl/mapbox'], 'vendor-chart': ['echarts'], } ``` - [ ] 목표: 초기 번들 < 300KB (gzip), 각 feature chunk < 100KB - [ ] `vite-plugin-compression`으로 gzip/brotli 사전 압축 검토 --- ## 9. Light 테마 하드코딩 정리 ### 현재 상태 - Dark/Light 테마 전환 구조 완성 (CSS 변수 + `.light` 클래스 + settingsStore) - 시맨틱 변수(`surface-raised`, `text-heading` 등) + CVA 변형은 정상 작동 - **문제**: 일부 alert/status 색상이 Tailwind 하드코딩 (`bg-red-500/20`, `text-red-400`, `border-red-500/30` 등) - Dark에서는 자연스러우나, Light 전환 시 대비/가독성 부족 ### 구현 계획 - [ ] 하드코딩 alert 색상을 CSS 변수 또는 CVA intent로 교체 - [ ] `badgeVariants`의 intent 색상도 CSS 변수 기반으로 전환 검토 - [ ] Light 모드 전용 대비 테스트 (WCAG AA 기준) --- ## 우선순위 및 의존관계 ``` ✅ 완료 ───────────────────────────────────── [1. Zustand] [4. deck.gl] [5. mock 데이터] [7. CVA] 진행 중 / 남은 작업 ────────────────────────── [6. i18n 내부 텍스트] ──┐ ├──▶ [2. API 실제 연동] ──▶ [3. 실시간 STOMP] [9. Light 테마 정리] ───┘ [8. 코드 스플리팅] ← 독립 작업, 언제든 착수 가능 (~3.2MB → 목표 <300KB) ``` ### 권장 진행 순서 1. **Phase A (품질)**: i18n 내부 텍스트 적용 (6) + Light 테마 하드코딩 정리 (9) + 코드 스플리팅 (8) 2. **Phase B (연동)**: Axios 설치 + API 실제 연동 (2) 3. **Phase C (실시간)**: STOMP.js + SockJS 실시간 인프라 (3)