iran 백엔드 프록시 잔재 제거:
- IranBackendClient dead class 삭제, AppProperties/application.yml iran-backend 블록 제거
- Frontend UI 라벨/주석/system-flow manifest deprecated 마킹
- CLAUDE.md 시스템 구성 다이어그램 최신화
백엔드 계층 분리:
- AlertController/MasterDataController/AdminStatsController 에서 repository/JdbcTemplate 직접 주입 제거
- AlertService/MasterDataService/AdminStatsService 신규 계층 도입 + @Transactional(readOnly=true)
- Proxy controller 의 @PostConstruct RestClient 생성 → RestClientConfig @Bean 으로 통합
감사 로그 보강:
- EnforcementService createRecord/updateRecord/createPlan 에 @Auditable 추가
- VesselAnalysisGroupService.resolveParent 에 PARENT_RESOLVE 액션 기록
카탈로그 정합성:
- performanceStatus 를 catalogRegistry 에 등록 (쇼케이스 자동 노출)
- alertLevels 확장: isValidAlertLevel / isHighSeverity / getAlertLevelOrder
- LiveMapView/DarkVesselDetection 시각 매핑(opacity/radius/tier score) 상수로 추출
- GearIdentification/vesselAnomaly 직접 분기를 타입 가드/헬퍼로 치환
vessel_type 카탈로그
- shared/constants/vesselTypes.ts 신규 — TRAWL/PURSE/GILLNET/LONGLINE/
TRAP/CARGO/UNKNOWN 7종 + getVesselTypeLabel / getVesselTypeIntent
헬퍼. 기존 alertLevels 카탈로그 패턴 답습
- catalogRegistry 에 VESSEL_TYPES 등록 — design-system 쇼케이스에 자동
노출
RealVesselAnalysis 필터 props 확장
- Props 에 mmsiPrefix / minRiskScore / size 추가 (all·spoofing mode)
- 선박 유형 컬럼을 한글 라벨로 렌더
- RealAllVessels 편의 export 를 mmsiPrefix='412' 로 고정 + 제목을
'중국 선박 전체 분석 결과 (실시간)' 로 변경
효과
- Tab 1 상단 그리드가 중국 선박만 표시해 페이지 성격과 일치
- 선박 유형 '저인망/선망/유자망/연승/통발/운반선/미분류' 한글 표시
- 55점 HIGH 같은 중국 선박이 상단/하단 양쪽에 일관되게 노출
- DarkVesselDetection: 판정 상세 사이드 패널(점수 산출 내역 P1~P11,
GAP 상세, 7일 이력 차트), 선박 위치 gap_start_lat/lon fallback,
클릭 시 지도 하이라이트
- TransferDetection: 5단계 필터 기반 환적 운영 화면 재구성
(KPI, 쌍 목록, 쌍 상세, 감시영역 지도, 탐지 조건 시각화)
- GearDetection: 모선 추론 상태(DIRECT_MATCH/AUTO_PROMOTED/REVIEW_REQUIRED),
추정 모선 MMSI, 후보 수 3개 컬럼 추가
- EnforcementPlan: CRITICAL 이벤트를 카테고리별(다크베셀/환적/EEZ침범/고위험)
아이콘+라벨로 "탐지 기반 단속 대상" 통합 표시
- darkVesselPatterns: prediction P1~P11 전 패턴 한국어 카탈로그 +
buildScoreBreakdown() 점수 산출 유틸
- ScoreBreakdown: 가점/감점 분리 점수 내역 시각화 공통 컴포넌트
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
이슈: "Select element must have an accessible name" — 스크린 리더가 용도를
인지할 수 없어 WCAG 2.1 Level A 위반.
수정:
- Select 공통 컴포넌트 타입을 union으로 강제
- aria-label | aria-labelledby | title 중 하나는 TypeScript 컴파일 타임에 필수
- 누락 시 tsc 단계에서 즉시 실패 → 회귀 방지
- 네이티브 <select> 5곳 aria-label 추가:
- admin/SystemConfig: 대분류 필터
- detection/RealVesselAnalysis: 해역 필터
- detection/RealGearGroups: 그룹 유형 필터
- detection/ChinaFishing: 관심영역 선택
- detection/GearIdentification: SelectField에 label prop 추가
- 쇼케이스 FormSection Select 샘플에 aria-label 추가
이제 모든 Select 사용처가 접근 이름을 가지며,
향후 신규 Select 사용 시 tsc가 누락을 차단함.
Phase A: 쇼케이스의 카탈로그/variant 정보를 중앙 상수로 끌어올림
- shared/constants/catalogRegistry.ts 신규
- 19+ 카탈로그의 id/showcaseId/titleKo/titleEn/description/source/items를
단일 레지스트리로 통합 관리
- 새 카탈로그 추가 = 레지스트리에 1줄 추가로 쇼케이스 자동 노출
- CATALOG_REGISTRY + getCatalogById()
- lib/theme/variantMeta.ts 신규
- BADGE_INTENT_META: 8 intent의 titleKo/titleEn/description
- BUTTON_VARIANT_META: 5 variant의 titleKo/titleEn/description
- BADGE_INTENT_ORDER/SIZE_ORDER, BUTTON_VARIANT_ORDER/SIZE_ORDER
- 쇼케이스 섹션 리팩토링 — 하드코딩 제거
- CatalogSection: CATALOG_REGISTRY 자동 열거 (CATALOGS 배열 삭제)
- BadgeSection: BADGE_INTENT_META에서 의미 가이드 + titleKo 참조
- ButtonSection: BUTTON_VARIANT_META에서 의미 가이드 + titleKo 참조
효과:
- 카탈로그의 라벨/색상/intent 변경 시 쇼케이스와 실 페이지 동시 반영
- Badge/Button의 variant 의미가 variantMeta 한 곳에서 관리됨
- 쇼케이스 섹션에 분산돼 있던 하드코딩 제거 (INTENT_USAGE, VARIANT_USAGE 등)
다음 단계: 실 페이지를 PageContainer/PageHeader/Button/Input으로 마이그레이션
변경:
- badgeVariants 8 intent 모두 라이트/다크 팔레트 분리
- 다크: 밝은 솔리드 배경(-400) + slate-900 글자 + 강한 보더(-600)
- 라이트: 파스텔 배경(-100) + 진한 글자(-900) + 소프트 보더(-300)
- base에서 text-on-bright 제거 (intent별로 관리)
- classes 기반 카탈로그 4종에 dark: 변형 추가로 라이트 모드 대응:
- eventStatuses: bg-red-100 text-red-800 dark:bg-red-500/20 dark:text-red-400
- enforcementResults: 동일 패턴 (red/purple/yellow/green)
- patrolStatuses: border 포함 (7 상태)
- enforcementActions: classes 필드 신규 추가 (기존에 없어서 fallback grey로 떨어져
라이트 모드에서 글자 안 보이던 원인)
- CatalogSection fallback classes도 dark: 변형 추가 (안전장치)
- enforcementActions에 getEnforcementActionClasses() 헬퍼 신규
빌드 검증:
- tsc ✅, vite build ✅
- CSS 확인: .dark\:bg-red-400:is(.dark *) 컴파일 정상
## 시간 표시 KST 통일
- shared/utils/dateFormat.ts 공통 유틸 신규 (formatDateTime/formatDate/formatTime/toDateParam)
- 14개 파일에서 인라인 toLocaleString → 공통 유틸 교체
## i18n 'group.parentInference' 사이드바 미번역 수정
- ko/en common.json의 'group' 키 중복 정의를 병합
(95행 두번째 group 객체가 35행을 덮어써서 parentInference 누락)
## Dashboard/MonitoringDashboard/Statistics 더미→실 API
- 백엔드 GET /api/stats/hourly 신규 (PredictionStatsHourly 엔티티/리포지토리)
- Dashboard: HOURLY_DETECTION/VESSEL_TYPE/AREA_RISK 하드코딩 제거 →
getHourlyStats(24) + getDailyStats(today) 결과로 useMemo 변환
- MonitoringDashboard: TREND Math.random() 제거 → getHourlyStats 기반
위험도 가중평균 + 경보 카운트
- Statistics: KPI_DATA 하드코딩 제거 → getKpiMetrics() 결과를 표 행으로
## Store mock 의존성 제거
- eventStore.alerts/MOCK_ALERTS 제거 (MobileService는 events에서 직접 추출)
- enforcementStore.plans 제거 (EnforcementPlan은 이미 직접 API 호출)
- transferStore + MOCK_TRANSFERS 완전 제거
(ChinaFishing/TransferDetection은 RealTransshipSuspects 컴포넌트 사용)
- mock/events.ts, mock/enforcement.ts, mock/transfers.ts 파일 삭제
## RiskMap 랜덤 격자 제거
- generateGrid() Math.random() 제거 → 빈 배열 + 'AI 분석 데이터 수집 중' 안내
- MTIS 외부 통계 5개 탭에 [MTIS 외부 통계] 배지 추가
## 12개 mock 화면에 '데모 데이터' 노란색 배지 추가
- patrol/PatrolRoute, FleetOptimization
- admin/AdminPanel, DataHub, NoticeManagement, SystemConfig
- ai-operations/AIModelManagement, MLOpsPage
- field-ops/ShipAgent
- statistics/ReportManagement, ExternalService
- surveillance/MapControl
## 백엔드 NUMERIC precision 동기화
- PredictionKpi.deltaPct: 5,2 → 12,2
- PredictionStatsDaily/Monthly.aiAccuracyPct: 5,2 → 12,2
- (V015 마이그레이션과 동기화)
44 files changed, +346 / -787
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PermissionsPanel UI 수정:
- 같은 노드의 effective READ가 거부되면 C/U/D/E도 forced-denied
(READ가 안 되면 그 페이지 자체에 접근 못 하므로 다른 작업도 의미 없음)
→ 사용자가 Read를 N으로 바꾸는 즉시 같은 행의 CUDE도 회색 비활성
DataTable EXPORT 권한 가드:
- exportResource prop 추가
- useAuth().hasPermission(resource, 'EXPORT')로 export 버튼 표시 여부 결정
- AccessControl의 사용자 관리 / 감사 로그 DataTable에 적용
- exportResource="admin:user-management"
- exportResource="admin:audit-logs"
Operation 의미 명확화:
- ParentExclusion release 엔드포인트를 UPDATE → DELETE 로 재분류
(제외 항목을 "삭제(해제)"하는 의미가 더 정확)
V007 마이그레이션: 권한 트리 명칭을 사이드바 i18n 라벨과 일치
- Level 0 13개 + Level 1 32개 노드의 rsrc_nm을 nav.* / group.* 라벨에 맞춤
- 예: "어구탐지" → "어구 탐지", "Dark Vessel" → "다크베셀 탐지"
- 권한 관리 트리를 운영자가 사이드바와 동일한 명칭으로 이해 가능
API의 RCUDE 적용 현황 (참고):
- READ 19건, UPDATE 8건, CREATE 4건, DELETE 1→2건
- EXPORT는 백엔드 엔드포인트 별도 없음 → 프론트 EXPORT 가드로 처리
- 향후 백엔드 CSV/Excel 생성 API 추가 시 EXPORT operation으로 가드
검증:
- V007 마이그레이션 자동 적용 + Started in 3.272s
- Level 0 13개 모두 사이드바 라벨로 변경됨 확인
- 프론트 빌드 통과 (599ms)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 1: 모노레포 디렉토리 구조 구축
- 기존 React 프로젝트를 frontend/ 디렉토리로 이동 (git mv)
- backend/ 디렉토리 생성 (Phase 2에서 Spring Boot 초기화)
- database/migration/ 디렉토리 생성 (Phase 2에서 Flyway 마이그레이션)
- 루트 .gitignore에 frontend/, backend/ 경로 반영
- 루트 CLAUDE.md를 모노레포 가이드로 갱신
- Makefile 추가 (dev/build/lint 통합 명령)
- frontend/vite.config.ts에 /api → :8080 백엔드 proxy 설정
- .githooks/pre-commit을 모노레포 구조에 맞게 갱신
(frontend/ 변경 시 frontend/ 내부에서 검증)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>