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>
14 KiB
Mock 데이터 공유 현황 분석 및 통합 결과
최초 작성일: 2026-04-06
마지막 업데이트: 2026-04-06
대상:kcg-ai-monitoring프론트엔드 코드베이스 전체 (31개 페이지)
상태: 통합 완료
1. 선박 데이터 교차참조
현재 동일한 선박 데이터가 여러 컴포넌트에 독립적으로 하드코딩되어 있다. 각 파일마다 동일 선박의 속성(위험도, 위치, 상태 등)이 서로 다른 형식과 값으로 중복 정의되어 있어 데이터 일관성 문제가 발생한다.
| 선박명 | 등장 파일 수 | 파일 목록 |
|---|---|---|
| 鲁荣渔56555 | 7+ | Dashboard, MobileService, LiveMapView, MonitoringDashboard, EventList, EnforcementHistory, ChinaFishing |
| 浙甬渔60651 | 4 | Dashboard, LiveMapView, EventList, DarkVesselDetection |
| 冀黄港渔05001 | 6 | MobileService, LiveMapView, Dashboard, TransferDetection, EventList, GearDetection |
| 3001함 | 6+ | ShipAgent, MobileService, LiveMapView, Dashboard, PatrolRoute, FleetOptimization |
| 3009함 | 6+ | ShipAgent, MobileService, Dashboard, PatrolRoute, FleetOptimization, AIAlert |
| 미상선박-A | 5 | MobileService, Dashboard, LiveMapView, MonitoringDashboard, EventList |
문제점
- 하나의 선박이 평균 5~7개 파일에 중복 정의됨
- 선박 속성(이름, MMSI, 위치, 위험도, 상태)이 파일마다 미세하게 다를 수 있음
- 새 선박 추가/수정 시 모든 관련 파일을 일일이 찾아 수정해야 함
2. 위험도 스케일 불일치
동일한 선박의 위험도가 페이지마다 서로 다른 스케일로 표현되고 있다.
| 선박명 | Dashboard (risk) | DarkVesselDetection (risk) | MonitoringDashboard |
|---|---|---|---|
| 鲁荣渔56555 | 0.96 (0~1 스케일) | - | CRITICAL (레벨 문자열) |
| 浙甬渔60651 | 0.85 (0~1 스케일) | 94 (0~100 정수) | - |
| 미상선박-A | 0.94 (0~1 스케일) | 96 (0~100 정수) | - |
원인 분석
- Dashboard는
risk: 0.96형식 (0~1 소수) - DarkVesselDetection은
risk: 96형식 (0~100 정수) - MonitoringDashboard는
'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW'레벨 문자열 - LiveMapView는
risk: 0.94형식 (0~1 소수) - EventList는 레벨 문자열 (
AlertLevel)
통합 방안
위험도를 0~100 정수 스케일로 통일하되, 레벨 문자열은 구간별 자동 매핑 유틸로 변환한다.
0~30: LOW | 31~60: MEDIUM | 61~85: HIGH | 86~100: CRITICAL
3. KPI 수치 중복
Dashboard와 MonitoringDashboard가 완전히 동일한 KPI 수치를 독립적으로 정의하고 있다.
| 지표 | Dashboard KPI_DATA |
MonitoringDashboard KPI |
|---|---|---|
| 실시간 탐지 | 47 | 47 |
| EEZ 침범 | 18 | 18 |
| 다크베셀 | 12 | 12 |
| 불법환적 의심 | 8 | 8 |
| 추적 중 | 15 | 15 |
| 나포/검문(금일 단속) | 3 | 3 |
문제점
- 6개 KPI 수치가 두 파일에 100% 동일하게 하드코딩
- 수치 변경 시 양쪽 모두 수정해야 함
- Dashboard에는
prev필드(전일 비교)가 추가로 있으나, Monitoring에는 없음
4. 이벤트 타임라인 중복
08:47~06:12 시계열 이벤트가 최소 4개 파일에 각각 정의되어 있다.
| 시각 | Dashboard | Monitoring | MobileService | EventList |
|---|---|---|---|---|
| 08:47 | EEZ 침범 (鲁荣渔56555) | EEZ 침범 (鲁荣渔56555 외 2척) | [긴급] EEZ 침범 탐지 | EVT-0001 EEZ 침범 |
| 08:32 | 다크베셀 출현 | 다크베셀 출현 | 다크베셀 출현 | EVT-0002 다크베셀 |
| 08:15 | 선단 밀집 경보 | 선단 밀집 경보 | - | EVT-0003 선단밀집 |
| 07:58 | 불법환적 의심 | 불법환적 의심 | 환적 의심 | EVT-0004 불법환적 |
| 07:41 | MMSI 변조 탐지 | MMSI 변조 탐지 | - | EVT-0005 MMSI 변조 |
| 07:23 | 함정 검문 완료 | 함정 검문 완료 | - | EVT-0006 검문 완료 |
| 06:12 | 속력 이상 탐지 | - | - | EVT-0010 속력 이상 |
문제점
- 동일 이벤트의 description이 파일마다 미세하게 다름 (예: "鲁荣渔56555" vs "鲁荣渔56555 외 2척")
- EventList에는 ID가 있으나(EVT-xxxx), 다른 파일에는 없음
- Dashboard에는 10개, Monitoring에는 6개, EventList에는 15개로 건수도 불일치
5. 환적 데이터 100% 중복
TransferDetection.tsx와 ChinaFishing.tsx에 TR-001~TR-003 환적 데이터가 완전히 동일하게 정의되어 있다.
TransferDetection.tsx:
const transferData = [
{ id: 'TR-001', time: '2026-01-20 13:42:11', a: {name:'장저우8호'}, b: {name:'黑江9호'}, ... },
{ id: 'TR-002', time: '2026-01-20 11:15:33', ... },
{ id: 'TR-003', time: '2026-01-20 09:23:45', ... },
];
ChinaFishing.tsx:
const TRANSFER_DATA = [
{ id: 'TR-001', time: '2026-01-20 13:42:11', a: {name:'장저우8호'}, b: {name:'黑江9호'}, ... },
{ id: 'TR-002', time: '2026-01-20 11:15:33', ... },
{ id: 'TR-003', time: '2026-01-20 09:23:45', ... },
];
문제점
- 변수명만 다르고 (
transferDatavsTRANSFER_DATA) 데이터 구조와 값이 100% 동일 - 한쪽만 수정하면 다른 쪽과 불일치 발생
6. 함정 상태 불일치
동일 함정의 상태가 페이지마다 모순되는 경우가 확인되었다.
| 함정 | ShipAgent | Dashboard | PatrolRoute | FleetOptimization |
|---|---|---|---|---|
| 5001함 | 오프라인 (status: '오프라인') |
가용 (PATROL_SHIPS에 대기로 표시) | 가용 (status: '가용') |
가용 (status: '가용') |
| 3009함 | 온라인 (동기화 중) | 검문 중 | 출동중 | 출동중 |
| 1503함 | 미배포 | - | - | 정비중 |
문제점
- 5001함이 ShipAgent에서는 오프라인이지만, Dashboard/PatrolRoute/FleetOptimization에서는 가용으로 표시됨 -- 직접적 모순
- 3009함의 상태가 "온라인", "검문 중", "출동중"으로 파일마다 다름
- 실제 운영 시 혼란을 초래할 수 있는 시나리오 불일치
7. 현재 상태: 통합 완료
아래 분석에서 식별한 모든 중복/불일치 문제를 해소하기 위해, 7개 공유 Mock 모듈 + 7개 Zustand 스토어 체계로 통합이 완료되었다.
7.1 완료된 아키텍처: mock -> store -> page
┌─────────────────────────────────────────────────────────────────────────┐
│ src/data/mock/ (7개 공유 모듈) │
├───────────┬──────────┬──────────┬────────┬───────────┬────────┬────────┤
│ vessels │ patrols │ events │ kpi │ transfers │ gear │enforce-│
│ .ts │ .ts │ .ts │ .ts │ .ts │ .ts │ment.ts │
└─────┬─────┴─────┬────┴─────┬────┴───┬────┴─────┬────┴───┬────┴───┬────┘
│ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ src/stores/ (7개 Zustand 스토어 + settingsStore) │
├───────────┬──────────┬──────────┬────────┬───────────┬────────┬────────┤
│ vessel │ patrol │ event │ kpi │ transfer │ gear │enforce-│
│ Store │ Store │ Store │ Store │ Store │ Store │mentStr │
└─────┬─────┴─────┬────┴─────┬────┴───┬────┴─────┬────┴───┬────┴───┬────┘
│ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ src/features/*/ (페이지 컴포넌트) │
│ store.load() 호출 -> store에서 데이터 구독 -> 뷰 변환은 페이지 책임 │
└─────────────────────────────────────────────────────────────────────────┘
7.2 스토어별 소비 현황 (16개 페이지가 스토어 사용)
| 스토어 | 소비 페이지 |
|---|---|
useVesselStore |
Dashboard, LiveMapView, DarkVesselDetection, VesselDetail |
usePatrolStore |
Dashboard, PatrolRoute, FleetOptimization |
useEventStore |
Dashboard, MonitoringDashboard, LiveMapView, EventList, MobileService, AIAlert |
useKpiStore |
Dashboard, MonitoringDashboard, Statistics |
useTransferStore |
TransferDetection, ChinaFishing |
useGearStore |
GearDetection |
useEnforcementStore |
EnforcementPlan, EnforcementHistory |
7.3 페이지 전용 인라인 데이터 (미통합)
아래 페이지들은 도메인 특성상 공유 mock에 포함하지 않고 페이지 전용 인라인 데이터를 유지한다.
| 페이지 | 인라인 데이터 | 사유 |
|---|---|---|
| ChinaFishing | COUNTERS_ROW1/2, VESSEL_LIST, MONTHLY_DATA, VTS_ITEMS |
중국어선 전용 센서 카운터/통계 (다른 페이지에서 미사용) |
| VesselDetail | VESSELS: VesselTrack[] |
항적 데이터 구조가 VesselData와 다름 (주석으로 명시) |
| MLOpsPage | 실험/배포 데이터 | MLOps 전용 도메인 데이터 |
| MapControl | 훈련구역 데이터 | 해상사격 훈련구역 전용 |
| DataHub | 수신현황 데이터 | 데이터 허브 전용 모니터링 |
| AIModelManagement | 모델/규칙 데이터 | AI 모델 관리 전용 |
| AIAssistant | SAMPLE_CONVERSATIONS |
챗봇 샘플 대화 |
| LoginPage | DEMO_ACCOUNTS |
데모 인증 정보 |
| 기타 (AdminPanel, SystemConfig 등) | 각 페이지 전용 설정/관리 데이터 | 관리 도메인 특화 |
7.4 설계 원칙 (구현 완료)
- 위험도 0~100 통일: 모든 선박의 위험도를 0~100 정수로 통일. 레벨 문자열은 유틸 함수로 변환.
- 단일 원천(Single Source of Truth): 각 데이터는 하나의 mock 모듈에서만 정의하고, 스토어를 통해 접근.
- Lazy Loading: 스토어의
load()메서드가 최초 호출 시import()로 mock 데이터를 동적 로딩 (loaded 플래그로 중복 방지). - 뷰 변환은 페이지 책임: mock 모듈/스토어는 원본 데이터만 제공하고, 화면별 가공(필터, 정렬, 포맷)은 각 페이지에서 수행.
7.5 Mock 모듈 상세 (참고용)
참고: 초기 분석에서 계획했던 areas.ts는 최종 구현 시 enforcement.ts(단속 이력 데이터)로 대체되었다.
해역/구역 데이터는 RiskMap, MapControl 등 각 페이지에서 전용 데이터로 관리한다.
| # | 모듈 파일 | 스토어 | 내용 |
|---|---|---|---|
| 1 | data/mock/vessels.ts |
vesselStore |
중국어선 + 한국어선 + 미상선박 마스터 (MOCK_VESSELS, MOCK_SUSPECTS) |
| 2 | data/mock/patrols.ts |
patrolStore |
경비함정 마스터 + 경로/시나리오/커버리지 |
| 3 | data/mock/events.ts |
eventStore |
이벤트 타임라인 + 알림 데이터 |
| 4 | data/mock/kpi.ts |
kpiStore |
KPI 수치 + 월별 추이 |
| 5 | data/mock/transfers.ts |
transferStore |
환적 데이터 (TR-001~003) |
| 6 | data/mock/gear.ts |
gearStore |
어구 데이터 (불법어구 목록) |
| 7 | data/mock/enforcement.ts |
enforcementStore |
단속 이력 + 단속 계획 데이터 |
8. 작업 완료 요약
| 모듈 | 상태 | 스토어 소비 페이지 수 |
|---|---|---|
vessels.ts |
완료 | 4개 (useVesselStore) |
events.ts |
완료 | 6개 (useEventStore) |
patrols.ts |
완료 | 3개 (usePatrolStore) |
kpi.ts |
완료 | 3개 (useKpiStore) |
transfers.ts |
완료 | 2개 (useTransferStore) |
gear.ts |
완료 | 1개 (useGearStore) |
enforcement.ts |
완료 | 2개 (useEnforcementStore) |
실제 작업 결과
- Mock 모듈 생성: 7개 파일 (
src/data/mock/) - Zustand 스토어 생성: 7개 + 1개 설정용 (
src/stores/) - 기존 페이지 리팩토링: 16개 페이지에서 스토어 소비로 전환
- 나머지 15개 페이지: 도메인 특화 인라인 데이터 유지 (공유 필요성 없음)
9. 결론
위 1~6절에서 분석한 6개의 심각한 중복/불일치 문제(위험도 스케일, 함정 상태 모순, KPI 중복, 이벤트 불일치, 환적 100% 중복, 선박 교차참조)는 7개 공유 mock 모듈 + 7개 Zustand 스토어 도입으로 모두 해소되었다.
달성한 효과:
- 데이터 일관성: Single Source of Truth로 불일치 원천 차단
- 유지보수성: 데이터 변경 시 mock 모듈 1곳만 수정
- 확장성: 신규 페이지 추가 시 기존 store import로 즉시 사용
- 코드 품질: 중복 인라인 데이터 제거, 16개 페이지가 스토어 기반으로 전환
- 성능: Zustand lazy loading으로 최초 접근 시에만 mock 데이터 로딩
1~6절의 분석 내용은 통합 전 문제 식별 기록으로 보존한다.