# KCG AI Monitoring - 아키텍처 문서

> AI 기반 불법조업 탐지 차단 플랫폼 프론트엔드

---

## 기술스택

| 분류 | 라이브러리 | 버전 | 역할 |
|------|-----------|------|------|
| UI 프레임워크 | React | 19.2.4 | 함수형 컴포넌트 + Hooks 기반 SPA |
| 언어 | TypeScript | 5.9 | 정적 타입, strict 모드 |
| 빌드 도구 | Vite | 8.0.3 | Rolldown 기반 ESM 번들링 (~480ms build), HMR |
| CSS 프레임워크 | Tailwind CSS | 4.2.2 | `@tailwindcss/vite` 플러그인 통합 |
| 지도 (베이스맵) | MapLibre GL | 5.22 | CartoDB 래스터 타일 (Dark/Light 자동 전환) |
| 지도 (벡터) | deck.gl | 9.2 | MapboxOverlay 기반 GPU 벡터 렌더링 (40만척+) |
| 차트 | ECharts | 6.0 (core) | `lib/charts` 래퍼를 통한 프리셋 차트 |
| 상태관리 | Zustand | 5.0 | 8개 독립 스토어 (vessel, patrol, event, kpi 등) |
| 라우팅 | react-router-dom | 7.12.0 | BrowserRouter, 중첩 Route |
| 다국어 | react-i18next + i18next | 17.0 + 26.0 | 10 NS, ko/en 네임스페이스 기반 |
| 스타일 변형 | class-variance-authority (CVA) | 0.7 | card/badge/statusDot variants |
| 아이콘 | lucide-react | 0.487.0 | SVG 아이콘 컴포넌트 |
| 린트 | ESLint | 10.2 | Flat Config, typescript-eslint + react-hooks + react-refresh |

---

## 디렉토리 구조

```
src/
├── lib/
│   ├── charts/             # BaseChart + 4 프리셋 (Area, Bar, Pie, Line)
│   │   ├── BaseChart.tsx   # 코어 (init, resize, dispose 자동 관리, 'kcg-dark' 테마)
│   │   ├── theme.ts        # ECharts 테마 등록
│   │   ├── tokens.ts       # 차트 색상 토큰
│   │   └── presets/        # AreaChart, BarChart, PieChart, LineChart
│   ├── map/
│   │   ├── BaseMap.tsx     # MapLibre + deck.gl (forwardRef, memo, overlay 노출)
│   │   ├── hooks/          # useMapLayers, useStoreLayerSync (RAF 기반)
│   │   ├── layers/         # markers, polyline, heatmap, zones, static (STATIC_LAYERS)
│   │   │   ├── markers.ts  # createMarkerLayer (transitions + DataFilterExtension)
│   │   │   ├── static.ts   # EEZ + NLL 싱글턴 (GPU 1회 업로드)
│   │   │   ├── polyline.ts # 경로/트랙 라인
│   │   │   ├── heatmap.ts  # 히트맵 (위험도 시각화)
│   │   │   ├── zones.ts    # 구역 원
│   │   │   └── boundaries.ts # 레거시 GeoJSON (하위 호환)
│   │   ├── controls/       # (예약) 지도 컨트롤 확장
│   │   ├── constants.ts    # EEZ, NLL, 타일 URL, 기본값
│   │   └── types.ts        # MapVessel, MapLayerConfig, HeatPoint
│   ├── i18n/               # 10 NS (common, dashboard, detection, patrol, enforcement, statistics, ai, fieldOps, admin, auth)
│   │   ├── config.ts       # i18next 초기화 (ko 기본, en 폴백)
│   │   └── locales/        # ko/*.json, en/*.json (10파일 x 2언어)
│   └── theme/              # tokens, colors, variants (CVA)
│       ├── tokens.ts       # CSS 변수 매핑 + resolved 색상값
│       ├── colors.ts       # 시맨틱 팔레트 (risk, alert, vessel, status, chartSeries)
│       └── variants.ts     # cardVariants, badgeVariants, statusDotVariants (CVA)
│
├── data/
│   ├── mock/               # 7 공유 mock 모듈
│   │   ├── vessels.ts      # 선박 목록 (한국, 중국, 경비함)
│   │   ├── events.ts       # 탐지/단속 이벤트
│   │   ├── transfers.ts    # 전재(환적) 데이터
│   │   ├── patrols.ts      # 순찰 경로/일정
│   │   ├── gear.ts         # 어구 탐지 데이터
│   │   ├── kpi.ts          # KPI/통계 데이터
│   │   └── enforcement.ts  # 단속 이력 데이터
│   ├── areasCodes.json     # 해역 코드 (52건)
│   ├── speciesCodes.json   # 어종 코드 (578건)
│   ├── fisheryCodes.json   # 어업유형 코드 (59건)
│   ├── vesselTypeCodes.json # 선박유형 코드 (186건)
│   └── commonCodes.ts      # 코드 유틸리티
│
├── stores/                 # 8 Zustand 스토어
│   ├── vesselStore.ts      # 선박 목록, 선택, 필터
│   ├── patrolStore.ts      # 순찰 경로/함정
│   ├── eventStore.ts       # 탐지/경보 이벤트
│   ├── kpiStore.ts         # KPI 메트릭, 추세
│   ├── transferStore.ts    # 전재(환적) 데이터
│   ├── gearStore.ts        # 어구 탐지
│   ├── enforcementStore.ts # 단속 이력
│   └── settingsStore.ts    # theme/language + localStorage 동기화
│
├── services/               # 7 API 서비스 (현재 mock 반환)
│   ├── api.ts              # fetch 래퍼 (향후 Axios 교체 예정)
│   ├── vessel.ts           # getVessels, getSuspects, getVesselDetail
│   ├── event.ts            # getEvents, getAlerts
│   ├── patrol.ts           # getPatrolShips
│   ├── kpi.ts              # getKpiMetrics, getMonthlyTrends, getViolationTypes
│   ├── ws.ts               # connectWs (STOMP 스텁, 미구현)
│   └── index.ts            # 배럴 export
│
├── shared/components/      # 공유 UI 컴포넌트
│   ├── ui/
│   │   ├── card.tsx        # Card(CVA variant), CardHeader, CardTitle, CardContent
│   │   └── badge.tsx       # Badge(CVA intent/size)
│   └── common/
│       ├── DataTable.tsx   # 범용 테이블 (가변너비, 검색, 정렬, 페이징, 엑셀, 출력)
│       ├── Pagination.tsx  # 페이지네이션
│       ├── SearchInput.tsx # 검색 입력
│       ├── ExcelExport.tsx # 엑셀 다운로드
│       ├── FileUpload.tsx  # 파일 업로드
│       ├── PageToolbar.tsx # 페이지 상단 툴바
│       ├── PrintButton.tsx # 인쇄 버튼
│       ├── SaveButton.tsx  # 저장 버튼
│       └── NotificationBanner.tsx # 알림 배너
│
├── features/               # 13 도메인 그룹 (31 페이지)
│   ├── dashboard/          # 종합 대시보드 (Dashboard)
│   ├── monitoring/         # 실시간 모니터링 (MonitoringDashboard)
│   ├── surveillance/       # 감시 (LiveMapView, MapControl)
│   ├── detection/          # 탐지 (DarkVessel, Gear, ChinaFishing)
│   ├── risk-assessment/    # 위험도 평가 (RiskMap, EnforcementPlan)
│   ├── patrol/             # 순찰 (PatrolRoute, FleetOptimization)
│   ├── enforcement/        # 단속 (EnforcementHistory, EventList)
│   ├── statistics/         # 통계 (Statistics, ExternalService, ReportManagement)
│   ├── ai-operations/      # AI 운영 (AIModelManagement, MLOps, AIAssistant)
│   ├── field-ops/          # 현장 대응 (MobileService, ShipAgent, AIAlert)
│   ├── admin/              # 관리 (AccessControl, SystemConfig, Notice, DataHub, AdminPanel)
│   ├── vessel/             # 선박 (VesselDetail, TransferDetection)
│   └── auth/               # 인증 (LoginPage)
│
├── app/                    # 애플리케이션 셸
│   ├── App.tsx             # BrowserRouter + Routes (26 보호 경로 + login)
│   ├── auth/AuthContext.tsx # 인증 컨텍스트 (ProtectedRoute)
│   └── layout/MainLayout.tsx # 사이드바 + 콘텐츠 + i18n 메뉴
│
└── styles/                 # 전역 스타일
    ├── theme.css           # CSS 커스텀 속성 (dark 기본 + .light 오버라이드)
    ├── tailwind.css        # Tailwind 기본
    └── fonts.css           # 웹폰트
```

---

## Path Alias

| Alias | 경로 | 용도 |
|-------|------|------|
| `@/*` | `src/*` | 프로젝트 전체 절대 임포트 |
| `@lib/*` | `src/lib/*` | 공통 라이브러리 (charts, map, i18n, theme) |
| `@shared/*` | `src/shared/*` | 공유 UI 컴포넌트 |
| `@features/*` | `src/features/*` | 도메인 feature 모듈 |
| `@data/*` | `src/data/*` | 기준정보 + mock 데이터 |
| `@stores/*` | `src/stores/*` | Zustand 스토어 |

Vite `resolve.alias`와 TypeScript `compilerOptions.paths`에 동일하게 설정되어 있다.

---

## 의존성

### 프로덕션 (11개)

| 패키지 | 버전 | 용도 |
|--------|------|------|
| react | ^19.2.4 | UI 렌더링 |
| react-dom | ^19.2.4 | DOM 렌더링 |
| react-router-dom | ^7.12.0 | SPA 라우팅 |
| maplibre-gl | ^5.22.0 | 래스터 베이스맵 엔진 |
| deck.gl | ^9.2.11 | GPU 벡터 렌더링 (ScatterplotLayer, PathLayer 등) |
| @deck.gl/mapbox | ^9.2.11 | MapboxOverlay (MapLibre 인터리브) |
| echarts | ^6.0.0 | 차트 라이브러리 |
| zustand | ^5.0.12 | 경량 상태관리 (8개 스토어) |
| class-variance-authority | ^0.7.1 | Tailwind 변형 관리 (CVA) |
| react-i18next | ^17.0.2 | React 다국어 바인딩 |
| i18next | ^26.0.3 | 다국어 코어 |
| lucide-react | 0.487.0 | SVG 아이콘 |

### 개발 (13개)

| 패키지 | 버전 | 용도 |
|--------|------|------|
| vite | ^8.0.3 | 빌드/개발 서버 (Rolldown) |
| @vitejs/plugin-react | ^6.0.1 | React Fast Refresh |
| tailwindcss | ^4.2.2 | 유틸리티 CSS |
| @tailwindcss/vite | ^4.2.2 | Tailwind Vite 플러그인 |
| typescript | 5.9 | 타입 체크 |
| eslint | ^10.2.0 | 린트 (Flat Config) |
| @eslint/js | ^10.0.1 | ESLint JS 설정 |
| typescript-eslint | ^8.58.0 | TS ESLint 파서/규칙 |
| eslint-plugin-react-hooks | ^7.0.1 | Hooks 규칙 |
| eslint-plugin-react-refresh | ^0.5.2 | Fast Refresh 규칙 |
| globals | ^17.4.0 | 전역 변수 정의 |
| @types/react | ^19.2.14 | React 타입 |
| @types/react-dom | ^19.2.3 | ReactDOM 타입 |

---

## 공통 모듈 API 요약

### lib/map — BaseMap + deck.gl 최적화

**BaseMap** — MapLibre + deck.gl 통합 컴포넌트 (`forwardRef`, `memo`)

```typescript
interface BaseMapProps {
  center?: [number, number];       // [lat, lng] 기본값 [35.5, 127.0]
  zoom?: number;                   // 기본값 7
  className?: string;
  style?: React.CSSProperties;
  height?: number | string;        // 기본값 '100%'
  layers?: Layer[];                // @deprecated — useMapLayers hook 사용 권장
  onMapReady?: (map: Map) => void; // 지도 로드 완료 콜백
  onClick?: (info: unknown) => void;
  interactive?: boolean;           // 기본 true
}

// ref를 통해 MapHandle 노출
interface MapHandle {
  overlay: MapboxOverlay | null;   // deck.gl overlay 직접 접근
}
```

- `useImperativeHandle`로 overlay 외부 노출 → hook에서 직접 `setProps()` 호출
- CartoDB Dark/Light 래스터 타일 자동 전환 (`settingsStore.theme` 구독)
- `interactive=false`로 정적 지도 프리뷰 생성 가능

**useMapLayers** — RAF 배치 레이어 업데이트 (React 리렌더 0회)

```typescript
useMapLayers(
  handleRef: RefObject<MapHandle>,  // BaseMap ref
  buildLayers: () => Layer[],       // 레이어 빌드 함수
  deps: unknown[],                  // 변경 감지 (shallow 비교)
)
```

**useStoreLayerSync** — Zustand store.subscribe + RAF 기반 레이어 동기화

```typescript
useStoreLayerSync<T>(
  handleRef: RefObject<MapHandle>,
  subscribe: (cb: (state: T) => void) => () => void,
  buildLayers: (state: T) => Layer[],
)
```

**STATIC_LAYERS** — EEZ + NLL 싱글턴 (모듈 로드 시 1회 생성, GPU 재전송 없음)

**createMarkerLayer** — ScatterplotLayer 생성 (transitions 보간 + DataFilterExtension)

### lib/charts — ECharts 래퍼

**BaseChart** — ECharts 코어 래퍼 컴포넌트

```typescript
interface BaseChartProps {
  option: EChartsOption;
  className?: string;
  style?: React.CSSProperties;
  height?: number;       // 기본값 200
  notMerge?: boolean;
  onEvents?: Record<string, (params: unknown) => void>;
}
```

- `echarts.init(container, 'kcg-dark')` 프로젝트 다크 테마 자동 적용
- `ResizeObserver` 자동 리사이즈, unmount 시 `dispose` 자동 정리

**프리셋 차트**

| 컴포넌트 | 주요 Props | 설명 |
|----------|-----------|------|
| `AreaChart` | `data`, `xKey`, `series`, `yAxisDomain?` | smooth line + 반투명 영역 |
| `BarChart` | `data`, `xKey`, `series`, `horizontal?`, `itemColors?` | 수직/수평 막대, 항목별 색상 |
| `PieChart` | `data: {name, value, color?}[]`, `innerRadius?`, `outerRadius?` | 파이/도넛 (기본 도넛) |
| `LineChart` | `data`, `xKey`, `series` | smooth 라인, 원형 심볼 |

### lib/theme — CVA 변형 + 디자인 토큰

| 모듈 | 내용 |
|------|------|
| `variants.ts` | `cardVariants` (default/elevated/inner/transparent), `badgeVariants` (8 intent x 4 size), `statusDotVariants` (4 status x 3 size) |
| `tokens.ts` | `cssVars` (CSS 변수 참조), `resolvedColors` (ECharts/MapLibre용 하드코딩 값) |
| `colors.ts` | `riskColors` (5단계), `alertStyles`, `vesselColors`, `statusColors`, `chartSeriesColors` (8색 팔레트) |

### lib/i18n — 10 네임스페이스 다국어

- `i18next` + `react-i18next` 기반
- 기본 언어: `ko`, 폴백: `ko`, 지원: `ko` / `en`
- 10 네임스페이스: `common`, `dashboard`, `detection`, `patrol`, `enforcement`, `statistics`, `ai`, `fieldOps`, `admin`, `auth`
- 사용: `useTranslation('namespace')` → `t('key')`
- 언어 전환: `settingsStore.toggleLanguage()` + `localStorage` 동기화

---

## 렌더링 최적화 아키텍처

```
store 변경 → useStoreLayerSync → RAF → overlay.setProps() (React 리렌더 0회)
deps 변경 → useMapLayers        → RAF → overlay.setProps() (React 리렌더 0회)
정적 레이어 → STATIC_LAYERS 싱글턴 (GPU 1회 업로드, 모든 페이지 공유)
동적 레이어 → transitions 보간 + DataFilterExtension (GPU 필터링)
시계/타이머 → useRef + DOM textContent 직접 조작 (setState 0회)
```

핵심 원칙: **React render cycle 완전 우회**. deck.gl overlay에 직접 `setProps()`를 호출하여 40만척+ 실시간 렌더링 시에도 React 리렌더가 발생하지 않는다.

---

## 테마 시스템

- **기본값**: `:root` = dark 테마, `.light` 클래스 오버라이드
- **시맨틱 CSS 변수**: `surface-raised`, `surface-overlay`, `text-heading`, `text-label`, `text-hint`, `border`
- **Tailwind 통합**: `@theme inline`으로 CSS 변수를 Tailwind 유틸리티에 매핑
- **settingsStore**: `theme` / `language` + `localStorage` 자동 동기화
- **지도 타일**: CartoDB Dark Matter ↔ CartoDB Positron 자동 전환 (`settingsStore.theme` 구독)
- **CVA 변형**: `cardVariants`, `badgeVariants`, `statusDotVariants`가 CSS 변수 참조 → 테마 자동 반응

---

## 라우팅 구조 (26 보호 경로 + login)

`App.tsx`에서 `BrowserRouter` > `AuthProvider` > `Routes`로 구성된다.

- `/login` — 비보호 라우트 (LoginPage)
- `/` — `ProtectedRoute` > `MainLayout` (사이드바 + Outlet)
  - `/` → `/dashboard` 리다이렉트
  - `/dashboard` — 종합 대시보드 (SFR-12)
  - `/monitoring` — 실시간 모니터링
  - `/risk-map` — 위험도 평가 (SFR-05)
  - `/enforcement-plan` — 단속계획 (SFR-06)
  - `/dark-vessel` — 무등화 선박 탐지 (SFR-09)
  - `/gear-detection` — 어구 탐지 (SFR-10)
  - `/china-fishing` — 중국어선 탐지
  - `/patrol-route` — 순찰경로 (SFR-07)
  - `/fleet-optimization` — 함대 최적화 (SFR-08)
  - `/enforcement-history` — 단속 이력 (SFR-11)
  - `/event-list` — 이벤트 목록
  - `/mobile-service` — 현장 모바일 (SFR-15)
  - `/ship-agent` — 함정 에이전트 (SFR-16)
  - `/ai-alert` — AI 경보 (SFR-17)
  - `/statistics` — 통계 (SFR-13)
  - `/external-service` — 외부연계 (SFR-14)
  - `/reports` — 보고서 관리
  - `/ai-model` — AI 모델 관리 (SFR-04)
  - `/mlops` — MLOps (SFR-18~19)
  - `/ai-assistant` — AI 어시스턴트 (SFR-20)
  - `/data-hub` — 데이터허브 (SFR-03)
  - `/system-config` — 환경설정 (SFR-02)
  - `/notices` — 공지사항
  - `/access-control` — 접근 권한 (SFR-01)
  - `/admin` — 시스템 관리
  - `/events` — 감시 (LiveMapView)
  - `/map-control` — 지도 컨트롤
  - `/vessel/:id` — 선박 상세

인증은 `AuthContext`의 `useAuth().user` 존재 여부로 판단하며, 미인증 시 `/login`으로 리다이렉트한다.

---

## 빌드 설정

- **TypeScript**: `target: ES2020`, `module: ESNext`, `moduleResolution: bundler`, `strict: true`
- **Vite**: `react()` + `tailwindcss()` 플러그인, 6개 path alias (`@`, `@lib`, `@shared`, `@features`, `@data`, `@stores`)
- **ESLint 10 Flat Config**: `typescript-eslint` + `react-hooks` + `react-refresh` 규칙
- **빌드 속도**: Rolldown 기반 ~480ms

---

## 현재 아키텍처 특성

1. **Zustand 8개 스토어**: vessel, patrol, event, kpi, transfer, gear, enforcement, settings. `settingsStore`는 theme/language + localStorage 동기화 담당.
2. **deck.gl GPU 렌더링**: MapLibre(래스터 베이스맵) + deck.gl(벡터). React 리렌더 완전 분리, RAF 기반 `overlay.setProps()` 직접 호출.
3. **CVA 스타일 시스템**: `cardVariants`, `badgeVariants`, `statusDotVariants`로 Tailwind 패턴 통합. CSS 변수 기반 테마 반응.
4. **mock 기반 서비스 계층**: 7개 API 서비스가 `data/mock/` 모듈에서 데이터 반환. 향후 Axios + 실제 API로 교체 예정.
5. **i18n 10 NS 구조 완성**: 리소스 파일 완비, MainLayout 메뉴 + 페이지 제목 + LoginPage 적용 완료. 페이지 내부 텍스트는 대부분 한국어 하드코딩 잔존.
6. **Dark/Light 테마**: CSS 변수 기반 양방향 테마. 지도 타일 자동 전환. 일부 alert 색상(`red-500/20` 등) 하드코딩 잔존.
7. **단일 번들**: 코드 스플리팅 미적용 (~3.2MB), React.lazy 미사용. 모든 feature가 단일 번들로 빌드.
8. **WebSocket 미구현**: `connectWs` 스텁만 존재, STOMP.js + SockJS 미설치.
