kcg-ai-monitoring/docs/architecture.md
htlee b37e18d952 docs: prediction-analysis 신규 + 루트/SFR 문서 drift 해소
- docs/prediction-analysis.md 신설 — opus 4.7 독립 리뷰 기반 prediction 구조/방향 심층 분석
  (9개 섹션: 아키텍처·5분 사이클·17 알고리즘·4대 도메인 커버리지·6축 구조 평가·개선 제안 P1~P4·임계값 전수표)
- AGENTS.md / README.md — V001~V016→V030, Python 3.9→3.11+, 14→17 알고리즘 모듈
- docs/architecture.md — /gear-collision 라우트 추가 (26→27 보호 경로)
- docs/sfr-traceability.md — V029→V030, 48→51 테이블, SFR-10 에 GEAR_IDENTITY_COLLISION 추가
- docs/sfr-user-guide.md — 어구 정체성 충돌 페이지 섹션 신설
- docs/system-flow-guide.md — 노드 수 102→115, V030 manifest 미반영 경고
- backend/README.md — "Phase 2 예정" 상태 → 실제 운영 구성 + PR #79 hotfix 요구사항 전면 재작성
2026-04-17 11:20:53 +09:00

19 KiB

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언어)
│   │                       # 2026-04-17: common.json 에 aria(36)/error(7)/dialog(4)/success(2)/message(5) 네임스페이스 추가
│   └── 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 컴포넌트 (design-system.html SSOT)
│   ├── ui/                 # 9개 공통 컴포넌트 (2026-04-17 모든 화면 SSOT 준수 완료)
│   │   ├── card.tsx        # Card(CVA variant), CardHeader, CardTitle, CardContent (4 variant)
│   │   ├── badge.tsx       # Badge(CVA intent 8종 × size 4단계, LEGACY_MAP 변형 호환)
│   │   ├── button.tsx      # Button (variant 5종 × size 3단계, icon/trailingIcon prop)
│   │   ├── input.tsx       # Input (size/state, forwardRef)
│   │   ├── select.tsx      # Select (aria-label|aria-labelledby|title TS union 강제)
│   │   ├── textarea.tsx    # Textarea
│   │   ├── checkbox.tsx    # Checkbox (native input 래퍼)
│   │   ├── radio.tsx       # Radio
│   │   └── tabs.tsx        # TabBar + TabButton (underline/pill/segmented 3 variant)
│   ├── layout/             # PageContainer / PageHeader / Section (표준 페이지 루트)
│   └── common/
│       ├── DataTable.tsx   # 범용 테이블 (가변너비, 검색, 정렬, 페이징, 엑셀, 출력)
│       ├── Pagination.tsx  # 페이지네이션
│       ├── SearchInput.tsx # 검색 입력 (i18n 통합)
│       ├── ExcelExport.tsx # 엑셀 다운로드
│       ├── FileUpload.tsx  # 파일 업로드
│       ├── PageToolbar.tsx # 페이지 상단 툴바
│       ├── PrintButton.tsx # 인쇄 버튼
│       ├── SaveButton.tsx  # 저장 버튼
│       └── NotificationBanner.tsx # 알림 배너 (common.aria.closeNotification)
│
├── 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)

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회)

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

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

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 코어 래퍼 컴포넌트

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 변수 참조 → 테마 자동 반응

라우팅 구조 (27 보호 경로 + 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)
    • /gear-collision — 어구 정체성 충돌 (SFR-10, V030 — 동일 어구 이름 × 복수 MMSI 공존 감지)
    • /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 — 선박 상세

인증은 AuthContextuseAuth().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 미설치.