KCG AI 기반 불법조업 탐지·차단 플랫폼 프론트엔드. React 19 + TypeScript 5.9 + Vite 8 + MapLibre + deck.gl + Zustand + Tailwind CSS. SFR 20개 전체 UI 구현 완료, 백엔드 연동 대기. - npm + Nexus 프록시 레지스트리 설정 - 팀 워크플로우 v1.6.1 부트스트랩 파일 배치 - .githooks (commit-msg, post-checkout) - package.json name: kcg-ai-monitoring v0.1.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
195 lines
8.5 KiB
Markdown
195 lines
8.5 KiB
Markdown
# 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)
|