커밋 그래프

9 커밋

작성자 SHA1 메시지 날짜
52749638ef refactor(frontend): 쇼케이스 SSOT 구조 — 카탈로그 레지스트리 + variant 메타
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으로 마이그레이션
2026-04-08 11:42:43 +09:00
d7f8db88ee fix(frontend): Badge 다크 팔레트를 translucent로 통일 + 쇼케이스 한/영 병기
- badgeVariants 다크 팔레트를 classes 기반 4종 카탈로그와 동일 패턴으로 통일
  - 이전: dark:bg-X-400 dark:text-slate-900 dark:border-X-600 (솔리드)
  - 이후: dark:bg-X-500/20 dark:text-X-400 dark:border-X-500/30 (translucent)
  - 라이트 팔레트는 그대로 유지 (이미 통일되어 있음)
- CatalogSection: 각 카탈로그 항목을 [code / 한글 배지 / 영문 배지] 3열 행으로 렌더
  - 한글/영문 라벨 두 버전을 한눈에 비교 검토 가능
  - 추적 ID Trk는 행 전체를 감싸서 호버/복사 동작
2026-04-08 11:28:02 +09:00
f589cb0f94 fix(frontend): 카탈로그 배지 테마 분리 + 단속 조치 가독성 수정
변경:
- 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 *) 컴파일 정상
2026-04-08 11:23:38 +09:00
e0b51efc54 feat(frontend): 디자인 시스템 쇼케이스 페이지 + 신규 공통 컴포넌트
쇼케이스 (/design-system.html):
- 별도 Vite entry (System Flow 패턴 재사용, 메인 SPA 분리)
- 10개 섹션: Intro / Token / Typography / Badge / Button / Form /
  Card / Layout / Catalog (19+) / Guide
- 추적 ID 체계 (TRK-CATEGORY-SLUG):
  - hover 시 툴팁 + "ID 복사 모드"에서 클릭 시 클립보드 복사
  - URL hash 딥링크 (#trk=TRK-BADGE-critical-sm) 스크롤+하이라이트
  - 산출문서/논의에서 특정 변형 정확히 참조 가능
- Dark/Light 테마 토글로 양쪽 시각 검증

신규 공통 컴포넌트:
- Button (@shared/components/ui/button.tsx)
  - 5 variant × 3 size = 15 변형
  - primary/secondary/ghost/outline/destructive × sm/md/lg
- Input / Select / Textarea / Checkbox / Radio
  - Input · Select 공통 inputVariants 공유 (sm/md/lg × default/error/success)
- PageContainer / PageHeader / Section (shared/components/layout/)
  - PageContainer: size sm/md/lg + fullBleed (지도/풀화면 예외)
  - PageHeader: title + description + icon + demo 배지 + actions 슬롯
  - Section: Card + CardHeader + CardTitle + CardContent 단축

variants.ts 확장:
- buttonVariants / inputVariants / pageContainerVariants CVA 정의
- Button/Input/Select는 variants.ts에서 import하여 fast-refresh 경고 회피

빌드 검증 완료:
- TypeScript 타입 체크 통과
- ESLint 통과 (경고 0)
- vite build: designSystem-*.js 54KB (메인 SPA와 분리)

이 쇼케이스가 확정된 후 실제 40+ 페이지 마이그레이션 진행 예정.
2026-04-08 11:09:36 +09:00
5812d9dea3 feat(frontend): UI 공통 인프라 + 19개 분류 카탈로그 구축
- cn() 유틸 신규 (clsx + tailwind-merge, 시맨틱 토큰 classGroup 등록)
- theme.css @layer utilities로 직접 정의 (Tailwind v4 복합 이름 매핑 실패 대응):
  text-heading/label/hint/on-vivid/on-bright, bg-surface-raised/overlay
- badgeVariants (CVA) 재구축: 8 intent x 4 size, rem 기반, !important 제거
- Badge 컴포넌트가 cn(badgeVariants, className)로 override 허용
- DataTable width 의미 변경: 고정 -> 선호 최소 너비 (minWidth), truncate + title 툴팁
- dateFormat.ts sv-SE 로케일로 YYYY-MM-DD HH:mm:ss 일관된 KST 출력
- ColorPicker 신규 (팔레트 + native color + hex 입력)
- shared/constants/ 19개 카탈로그: violation/alert/event/enforcement/patrol/
  engine/userRole/device/parentResolution/modelDeployment/gearGroup/darkVessel/
  httpStatus/userAccount/loginResult/permission/vesselAnalysis/connection/trainingZone
  + kpiUiMap. 백엔드 enum/code_master 기반 SSOT
- i18n ko/en common.json에 카테고리 섹션 추가
- adminApi.fetchRoles()가 updateRoleColorCache 자동 호출
- 공통 컴포넌트 (ExcelExport/NotificationBanner/Pagination/SaveButton) 시맨틱 토큰 적용
2026-04-08 10:53:40 +09:00
19b1613157 feat: 프론트 전수 mock 정리 + UTC→KST 통일 + i18n 수정 + stats hourly API
## 시간 표시 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>
2026-04-07 15:36:38 +09:00
febfb2cbe8 feat: Phase 5 - 권한 관리 UI 고도화 (트리 RBAC PermissionsPanel)
버그 수정:
- AccessControl 무한 새로고침 (loadRoles의 userStats 의존성 → setUserStats 호출 → 무한 루프)
  loadRoles에서 항상 fetchUserStats를 같이 호출하도록 변경

백엔드 API 추가:
- RoleManagementService (역할/권한 매트릭스 CRUD)
  - createRole / updateRole / deleteRole (built-in 보호)
  - updatePermissions (Y/N upsert + null 시 명시 권한 제거)
  - assignUserRoles (전체 교체 방식, 권한 캐시 evict)
  - 모든 액션에 @Auditable 자동 기록
- PermTreeController 확장:
  - POST /api/roles (admin:role-management:CREATE)
  - PUT /api/roles/{sn} (admin:role-management:UPDATE)
  - DELETE /api/roles/{sn} (admin:role-management:DELETE)
  - PUT /api/roles/{sn}/permissions (admin:permission-management:UPDATE)
  - PUT /api/admin/users/{id}/roles (admin:user-management:UPDATE)
- DTO: RoleCreateRequest, RoleUpdateRequest, PermissionUpdateRequest, UserRoleAssignRequest
- GlobalExceptionHandler:
  - IllegalArgumentException → 400 BAD_REQUEST
  - IllegalStateException → 409 CONFLICT
  - AccessDeniedException → 403 FORBIDDEN

프론트엔드:
- lib/permission/permResolver.ts (TypeScript 미러)
  - resolveSingleRoleEffective: 백엔드 PermResolver와 동일 알고리즘
  - 4가지 셀 상태 계산 (explicit-granted/inherited-granted/explicit-denied/forced-denied)
- PermissionsPanel.tsx (트리 + R/C/U/D/E 매트릭스)
  - 좌측: 역할 목록 + 신규 생성 + 삭제 (built-in 보호)
  - 우측: 트리 표 + 셀 클릭 (Y → N → 미지정 순환)
  - 부모 READ 게이팅 시각화 (강제 거부 회색 비활성)
  - 변경된 셀만 일괄 저장 (dirty 추적)
- UserRoleAssignDialog.tsx
  - 사용자에게 역할 다중 선택 배정 (체크박스)
- adminApi.ts 확장: createRole/updateRole/deleteRole/updateRolePermissions/assignUserRoles
- AccessControl.tsx 갱신:
  - 역할 관리 탭 → PermissionsPanel 통합
  - 사용자 관리 탭 → 역할 배정 버튼 추가 (UserCog 아이콘)

검증:
- 역할 생성 → TESTROLE 6번으로 추가
- 권한 매트릭스 갱신 → dashboard/monitoring READ 부여 (changed: 2)
- 역할 삭제 → built-in이 아니면 OK
- built-in ADMIN 삭제 시도 → 400 BAD_REQUEST (BUILTIN_ROLE_CANNOT_DELETE)
- viewer에게 OPERATOR + ANALYST 다중 배정 → roles=[OPERATOR, ANALYST]
  → 재로그인 시 detection READ 등 자동 상속 확인
- 권한 캐시 evictAllPermissions 즉시 반영
- 프론트 빌드 통과 (533ms)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:11:27 +09:00
bae2f33b86 feat: Phase 4 - 모선 워크플로우 + 관리자 화면 + 권한 라우트 가드
Phase 4-1: 운영자 워크플로우 백엔드 (자체 DB)
- ParentResolution / ParentReviewLog / CandidateExclusion / LabelSession 엔티티
- Repository 4종 + DTO 5종
- ParentInferenceWorkflowService (HYBRID 패턴):
  - review (CONFIRM/REJECT/RESET) - parent-inference-workflow:parent-review (UPDATE)
  - excludeForGroup - parent-inference-workflow:parent-exclusion (CREATE)
  - excludeGlobal - parent-inference-workflow:exclusion-management (CREATE) [admin]
  - releaseExclusion (UPDATE)
  - createLabelSession / cancelLabelSession (CREATE/UPDATE)
- ParentInferenceWorkflowController: @RequirePermission으로 권한 강제
- 모든 액션에 @Auditable AOP → audit_log + review_log 동시 기록

Phase 4-2: PermTreeController + AdminLogController
- GET /api/perm-tree (모든 사용자) - 메뉴/사이드바 구성용
- GET /api/roles (admin:role-management) - 역할+권한 매트릭스
- GET /api/admin/audit-logs / access-logs / login-history

Phase 4-3: iran 백엔드 프록시 (stub)
- IranBackendClient: RestClient 기반, 호출 실패 시 null 반환 (graceful)
- VesselAnalysisProxyController: serviceAvailable=false 응답
- PredictionProxyController: DISCONNECTED 응답
- Phase 5에서 iran 백엔드 실 연결 시 코드 변경 최소

Phase 4-4: 프론트엔드 services
- parentInferenceApi.ts: 모선 워크플로우 22개 함수
- adminApi.ts: 감사로그/접근이력/로그인이력/권한트리/역할 조회

Phase 4-5: 사이드바 권한 필터링 + ProtectedRoute 권한 가드
- AuthContext.PATH_TO_RESOURCE에 신규 경로 매핑 추가
- ProtectedRoute에 resource/operation prop 추가
  → 권한 거부 시 403 페이지 표시
- 모든 라우트에 권한 리소스 명시
- MainLayout 사이드바: parent-inference-workflow + admin 로그 메뉴 추가
- 사이드바 hasAccess 필터링 (이전부터 구현됨, 신규 메뉴에도 자동 적용)

Phase 4-6: 신규 페이지 3종
- ParentReview.tsx: 모선 확정/거부/리셋 + 신규 등록 폼
- ParentExclusion.tsx: GROUP/GLOBAL 제외 등록 + 해제
- LabelSession.tsx: 학습 세션 생성/취소
- AuditLogs.tsx: 감사 로그 조회
- AccessLogs.tsx: 접근 이력 조회
- LoginHistoryView.tsx: 로그인 이력 조회

Phase 4-7: i18n 키 + 라우터 등록
- 한국어/영어 nav.* + group.* 키 추가
- App.tsx에 12개 신규 라우트 등록 + 권한 가드 적용

Phase 4-8: 검증 완료
- 백엔드 컴파일/기동 성공
- 프론트엔드 빌드 성공 (475ms)
- E2E 시나리오:
  - operator 로그인 → CONFIRM 확정 → MANUAL_CONFIRMED 갱신
  - operator GROUP 제외 → 성공
  - operator GLOBAL 제외 → 403 FORBIDDEN (권한 없음)
  - operator 학습 세션 생성 → ACTIVE
  - admin GLOBAL 제외 → 성공
  - 감사 로그 자동 기록: REVIEW_PARENT/EXCLUDE_CANDIDATE_GROUP/
    LABEL_PARENT_CREATE/EXCLUDE_CANDIDATE_GLOBAL 등 14건
  - 권한 트리 RBAC + AOP 정상 동작 확인

설계 핵심:
- 운영자 의사결정만 자체 DB에 저장 (HYBRID)
- iran 백엔드 데이터는 향후 Phase 5에서 합쳐서 표시
- @RequirePermission + @Auditable로 모든 액션 권한 + 감사 자동화
- 데모 계정으로 완전한 워크플로우 시연 가능

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:44:43 +09:00
e6319a571c refactor: 모노레포 구조로 전환 (frontend/ + backend/ + database/)
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>
2026-04-07 08:47:24 +09:00