커밋 그래프

25 커밋

작성자 SHA1 메시지 날짜
c1cc36b134 refactor(design-system): 하드코딩 색상 라이트/다크 대응 + raw button/input 공통 컴포넌트 치환
30개 파일 전 영역에 동일한 패턴으로 SSOT 준수:

**StatBox 재설계 (2파일)**:
- RealGearGroups, RealVesselAnalysis 의 `color: string` prop 제거
- `intent: BadgeIntent` prop + `INTENT_TEXT_CLASS` 매핑 도입

**raw `<button>` → Button 컴포넌트 (다수)**:
- `bg-blue-600 hover:bg-blue-500 text-on-vivid ...` → `<Button variant="primary">`
- `bg-orange-600 ...` / `bg-green-600 ...` → `<Button variant="primary">`
- `bg-red-600 ...` → `<Button variant="destructive">`
- 아이콘 전용 → `<Button variant="ghost" aria-label=".." icon={...} />`
- detection/enforcement/admin/parent-inference/statistics/ai-operations/auth 전영역

**raw `<input>` → Input 컴포넌트**:
- parent-inference (ParentReview, ParentExclusion, LabelSession)
- admin (PermissionsPanel, UserRoleAssignDialog)
- ai-operations (AIAssistant)
- auth (LoginPage)

**raw `<select>` → Select 컴포넌트**:
- detection (RealGearGroups, RealVesselAnalysis, ChinaFishing)

**커스텀 탭 → TabBar/TabButton (segmented/underline)**:
- ChinaFishing: 모드 탭 + 선박 탭 + 통계 탭

**raw `<input type="checkbox">` → Checkbox**:
- GearDetection FilterCheckGroup

**하드코딩 Tailwind 색상 라이트/다크 쌍 변환 (전영역)**:
- `text-red-400` → `text-red-600 dark:text-red-400`
- `text-green-400` → `text-green-600 dark:text-green-400`
- blue/cyan/orange/yellow/purple/amber 동일 패턴
- `text-*-500` 아이콘도 `text-*-600 dark:text-*-500` 로 라이트 모드 대응
- 상태 dot (bg-red-500 animate-pulse 등)은 의도적 시각 구분이므로 유지

**에러 메시지 한글 → t('error.errorPrefix') 통일**:
- detection/parent-inference/admin 에서 `에러: {error}` 패턴 → `t('error.errorPrefix', { msg: error })`

**결과**: tsc 0 errors / eslint 0 errors (84 warnings 기존)
2026-04-16 17:09:14 +09:00
8af693a2df refactor(i18n): alert/confirm/aria-label 하드코딩 한글 제거
공통 번역 리소스 확장:
- common.json 에 aria / error / dialog / success / message 네임스페이스 추가
- ko/en 양쪽 동일 구조 유지 (aria 36 키 + error 7 키 + dialog 4 키 + message 5 키)

alert/confirm 11건 → t() 치환:
- parent-inference: ParentReview / LabelSession / ParentExclusion
- admin: PermissionsPanel / UserRoleAssignDialog / AccessControl

aria-label 한글 40+건 → t() 치환:
- parent-inference (group_key/sub_cluster/정답 parent MMSI/스코프 필터 등)
- admin (역할 코드/이름, 알림 제목/내용, 시작일/종료일, 코드 검색, 대분류 필터, 수신 현황 기준일)
- detection (그룹 유형/해역 필터, 관심영역, 필터 설정/초기화, 멤버 수, 미니맵/재생 닫기)
- enforcement (확인/선박 상세/단속 등록/오탐 처리)
- vessel/statistics/ai-operations (조회 시작/종료 시각, 업로드 패널 닫기, 전송, 예시 URL 복사)
- 공통 컴포넌트 (SearchInput, NotificationBanner)

MainLayout 언어 토글:
- title 삼항분기 → t('message.switchToEnglish'/'switchToKorean')
- aria-label="페이지 내 검색" → t('aria.searchInPage')
- 토글 버튼 자체에 aria-label={t('aria.languageToggle')} 추가
2026-04-16 16:32:37 +09:00
234169d540 refactor(frontend): admin 11개 페이지 디자인 시스템 하드코딩 색상 제거 (Phase 1-B)
129건 하드코딩 Tailwind 색상 → 시맨틱 토큰 치환:
- text-cyan-400 (45건) → text-label
- text-green-400/500 (51건) → text-label + Badge intent="success"
- text-red-400/500 (31건) → text-heading + Badge intent="critical"
- text-blue-400 (33건) → text-label + Badge intent="info"
- text-purple-400 (20건) → text-heading
- text-yellow/orange/amber (32건) → text-heading + Badge intent="warning"

raw <button> → <Button> 컴포넌트 교체 (DataHub/NoticeManagement/SystemConfig 등)
미사용 import 정리 (SaveButton/DataTable/lucide 아이콘)

대상: AIAgentSecurityPage, AISecurityPage, AccessControl, AccessLogs,
AdminPanel, AuditLogs, DataHub, LoginHistoryView, NoticeManagement,
PermissionsPanel, SystemConfig

검증: tsc 0 errors, eslint 0 errors, 하드코딩 색상 잔여 0건
2026-04-16 11:25:51 +09:00
69b97d33f6 refactor(admin): 3개 신규 페이지 디자인 시스템 준수 + RBAC skeleton (Phase 1-A)
- performanceStatus.ts 카탈로그 신설 (status→intent/hex/label)
- 자체 탭 네비 3건 → TabBar/TabButton (underline variant)
- raw <button> 3건 → TabButton
- PerformanceMonitoring hex 9건 → getPerformanceStatusHex 카탈로그
- statusIntent/barColor 로컬 함수 → 카탈로그 getPerformanceStatusIntent + utilizationStatus
- 3개 페이지 useAuth().hasPermission() skeleton 배치 (Phase 3 action guard 대비)
- 남은 hex 10건(DAR-10/11 KPI)은 Phase 1-B 전역 admin 시맨틱 색상 통일에서 처리

검증: tsc + ESLint + vite build 모두 통과
2026-04-16 08:39:34 +09:00
Nan Kyung Lee
755f3919ba feat: 시스템관리 > 감사·보안에 성능 모니터링(PER-01~06) 메뉴 추가
- V028 Flyway 마이그레이션: admin:performance-monitoring 권한 트리 + RBAC
- PerformanceMonitoring.tsx: 5 탭 구조(성능 현황·응답성·처리용량·AI 모델·가용성/확장성)
- PER-01~06 전체 커버: 3,000명 규모·상황실 100명 24/7 SLO·S&P 글로벌 AIS 영향 최소화 8대 전략
- 6개 AI 모델 성능 지표(정확도·정밀도·재현율·F1·ROC-AUC) 표시
- 디자인 시스템 준수: PageContainer/PageHeader/Card/Badge intent 기반

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:12:53 +09:00
Nan Kyung Lee
256152f7fc feat: 시스템관리 > 감사·보안에 데이터 모델 검증(DAR-11) 메뉴 추가
- 5탭 구성: 검증 현황 / 논리 모델 검증 / 물리 모델 검증 / 중복·정합성 점검 / 검증 결과 이력
- 4단계 검증 절차 (계획 수립→논리 검증→물리 검증→결과 보고)
- 논리 모델 8항목 (완전성·정합성·정규화·표준), 물리 모델 10항목 (구조·타입·인덱스·제약·성능)
- 중복·정합성 점검 6항목 + 8개 주제영역 48테이블 매핑
- V027 마이그레이션: admin:data-model-verification 권한 트리 + ADMIN 역할 권한

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:55:05 +09:00
Nan Kyung Lee
77f39497e5 feat: 시스템관리 > 감사·보안에 데이터 보관·파기 정책(DAR-10) 메뉴 추가
- 5탭 구성: 보관 현황 / 유형별 보관기간 / 파기 절차 / 예외·연장 / 파기 감사 대장
- 6종 데이터 유형별 보관기간 기준표 (법적 근거 포함)
- 4단계 파기 승인 절차 워크플로우 (선별→신청→승인→기록)
- 보존 연장 예외 관리 (수사·소송·감사·재난 4가지 사유)
- 파기 감사 대장 (대상·일시·담당자·방식·용량 기록)
- V026 마이그레이션: admin:data-retention 권한 트리 + ADMIN 역할 권한

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:55:05 +09:00
99d72e3622 refactor(frontend): LGCNS 3개 페이지 디자인 시스템 공통 구조 전환
- 커스텀 탭 → TabBar/TabButton 공통 컴포넌트 교체 (3개 파일)
- hex 색상 맵 → Tailwind 클래스 토큰 전환, style={{ }} 인라인 제거
- 인라인 Badge intent 삼항 → 카탈로그 함수 교체 (getAgentPermTypeIntent 등)
- 신규 카탈로그: mlopsJobStatuses (4종), aiSecurityStatuses (위협3+권한5+결과3)
- catalogRegistry에 4건 등록 → design-system.html 쇼케이스 자동 노출
- statusIntent.ts에 '허용', '위험', '관리자', '중지', '실행' 매핑 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:41:49 +09:00
7e37b5b680 Merge remote-tracking branch 'origin/develop' into feature/lgcns-mlops-ai-security-menu 2026-04-13 11:10:53 +09:00
45371315ba feat: prediction 알고리즘 재설계 + 프론트 CRUD 권한 가드 보완 (#29) 2026-04-13 11:08:11 +09:00
Nan Kyung Lee
1244f07de6 feat: LGCNS MLOps + AI 보안(SER-10) + AI Agent 보안(SER-11) 메뉴 추가
- V025 마이그레이션: admin 그룹 하위 3개 메뉴 등록
  - LGCNS MLOps (AI 플랫폼, nav_sort=350)
  - AI 보안 (감사·보안, nav_sort=1800)
  - AI Agent 보안 (감사·보안, nav_sort=1900)
- 페이지 컴포넌트 3개 신규 생성
- componentRegistry, i18n(ko/en) 반영

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 10:51:05 +09:00
6fe7a7daf4 feat: 메뉴 DB SSOT 구조화 — auth_perm_tree 기반 메뉴·권한·i18n 통합
## 핵심 변경
- auth_perm_tree를 메뉴 SSOT로 확장 (V020~V024)
  - url_path, label_key, component_key, nav_group, nav_sub_group, nav_sort 컬럼
  - labels JSONB (다국어: {"ko":"...", "en":"..."})
- 보이지 않는 도메인 그룹 8개 삭제 (surveillance, detection, risk-assessment 등)
  - 권한 트리 = 메뉴 트리 완전 동기화
  - 그룹 레벨 권한 → 개별 자식 권한으로 확장 후 그룹 삭제
- 패널 노드 parent_cd를 실제 소속 페이지로 수정
  (어구식별→어구탐지, 전역제외→후보제외, 역할관리→권한관리)
- vessel:vessel-detail 권한 노드 제거 (드릴다운 전용, 인증만 체크)

## 백엔드
- MenuConfigService: auth_perm_tree에서 menuConfig DTO 생성
- /api/auth/me 응답에 menuConfig 포함 (로그인 시 프리로드)
- @RequirePermission 12곳 수정 (삭제된 그룹명 → 구체적 자식 리소스)
- Caffeine 캐시 menuConfig 추가

## 프론트엔드
- NAV_ENTRIES 하드코딩 제거 → menuStore(Zustand) 동적 렌더링
- PATH_TO_RESOURCE 하드코딩 제거 → DB 기반 longest-match
- App.tsx 36개 정적 import/33개 Route → DynamicRoutes + componentRegistry
- PermissionsPanel: DB labels JSONB 기반 표시명 + 페이지/패널 아이콘 구분
- DB migration README.md 전면 재작성 (V001~V024, 49테이블, 149인덱스)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:54:04 +09:00
f4d56ea891 fix(frontend): 아이콘 전용 버튼 접근 이름 누락 7곳 보완
이전 스캐너가 놓친 패턴 — 모달 닫기 X 버튼과 토글 스위치 등:

- NoticeManagement: 모달 헤더 X → '닫기'
- ReportManagement: 업로드 패널 X → '업로드 패널 닫기'
- AIModelManagement: 규칙 토글 → role=switch + aria-checked + aria-label
                     API 예시 복사 → '예시 URL 복사'
- FileUpload: 파일 제거 X → '{파일명} 제거'
- NotificationBanner: 알림 닫기 X → '알림 닫기'
- SearchInput: 입력 aria-label (placeholder), 지우기 버튼 → '검색어 지우기'

검증:
- 개선된 스캐너로 remaining=0 확인 (JSX tag 중첩 파싱)
- tsc 
2026-04-08 13:16:20 +09:00
c51873ab85 fix(frontend): a11y/호환성 — backdrop-filter webkit prefix + Button/Input/Select 접근 이름
axe/forms/backdrop 에러 3종 모두 해결:

1) CSS: backdrop-filter Safari 호환성
   - design-system CSS에 -webkit-backdrop-filter 추가
   - trk-pulse 애니메이션을 outline-color → opacity로 변경
     (composite만 트리거, paint/layout 없음 → 더 나은 성능)

2) 아이콘 전용 <button> aria-label 추가 (9곳):
   - MainLayout 알림 버튼 → '알림'
   - UserRoleAssignDialog 닫기 → '닫기'
   - AIAssistant/MLOpsPage 전송 → '전송'
   - ChinaFishing 좌/우 네비 → '이전'/'다음'
   - 공통 컴포넌트 (PrintButton/ExcelExport/SaveButton) type=button 누락 보정

3) <input>/<textarea> 접근 이름 27곳 추가:
   - 로그인 폼, ParentReview/LabelSession/ParentExclusion 폼 (10)
   - NoticeManagement 제목/내용/시작일/종료일 (4)
   - SystemConfig/DataHub/PermissionsPanel 검색·역할 입력 (5)
   - VesselDetail 조회 시작/종료/MMSI (3)
   - GearIdentification InputField에 label prop 추가
   - AIAssistant/MLOpsPage 질의 input/textarea
   - MainLayout 페이지 내 검색
   - 공통 placeholder → aria-label 자동 복제 (3)

Button 컴포넌트에는 접근성 정책 JSDoc 명시 (타입 강제는 API 복잡도 대비
이득 낮아 문서 가이드 + 코드 리뷰로 대응).

검증:
- 실제 위반 지표: inaccessible button 0, inaccessible input 0, textarea 0
- tsc , eslint , vite build 
- dist CSS에 -webkit-backdrop-filter 확인됨
2026-04-08 13:04:23 +09:00
9dfa8f5422 fix(frontend): Select 접근성 — aria-label 필수 + 네이티브 <select> 보완
이슈: "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가 누락을 차단함.
2026-04-08 12:50:51 +09:00
da4dc86e90 refactor(frontend): 인라인 버튼/하드코딩 색상 전수 제거
Phase C-2 (인라인 <button>):
- TabBar/TabButton 공통 컴포넌트 신규 (underline/pill/segmented 3종)
- DataHub: 메인 탭 → TabBar + TabButton 전환, 필터 pill 전환,
  CTA 버튼 (작업 등록/스토리지 관리/새로고침) → Button variant
- PermissionsPanel: 역할 생성/저장 → Button variant, icon 버튼 유지
- Python 일괄 치환: 51개 inline <button>에 type="button" 추가
- 남은 <button> type 누락 0건 (multi-line 포함)

Phase C-3 (하드코딩 색상):
- AdminPanel SERVER_STATUS 뱃지: getStatusIntent() 사용으로 통일
- bg-X-500/20 text-X-400 패턴 0건

Phase C-4 (인라인 style):
- LiveMapView BaseMap minHeight → className="min-h-[400px]"
- 나머지 89건 style={{}}은 모두 dynamic value
  (progress width, toggle left, 데이터 기반 color 등)로 정당함

4개 catalog (eventStatuses/enforcementResults/enforcementActions/
patrolStatuses)에 intent 필드 추가, statusIntent.ts 공통 유틸 신규.
이제 모든 Badge가 쇼케이스 팔레트 자동 적용됨.

빌드 검증:
- tsc , eslint , vite build 
- 남은 위반 지표: Badge className 0, button-type-missing 0, 하드코딩 색상 0
2026-04-08 12:36:07 +09:00
2483174081 refactor(frontend): Badge className 위반 37건 전수 제거
- 4개 catalog(eventStatuses/enforcementResults/enforcementActions/patrolStatuses)에
  intent 필드 추가 + getXxxIntent() 헬퍼 신규
- statusIntent.ts 공통 유틸: 한글/영문 상태 문자열 → BadgeIntent 매핑
  + getRiskIntent(0-100) 점수 기반 매핑
- 모든 Badge className="..." 패턴을 intent prop으로 치환:
  - admin (AuditLogs/AccessControl/SystemConfig/NoticeManagement/DataHub)
  - ai-operations (AIModelManagement/MLOpsPage)
  - enforcement (EventList/EnforcementHistory)
  - field-ops (AIAlert)
  - detection (GearIdentification)
  - patrol (PatrolRoute/FleetOptimization)
  - parent-inference (ParentExclusion)
  - statistics (ExternalService/ReportManagement)
  - surveillance (MapControl)
  - risk-assessment (EnforcementPlan)
  - monitoring (SystemStatusPanel — ServiceCard statusColor → statusIntent 리팩토)
  - dashboard (Dashboard PatrolStatusBadge)

이제 Badge의 테마별 팔레트(라이트 파스텔 + 다크 translucent)가 자동 적용되며,
쇼케이스에서 palette 조정 시 모든 Badge 사용처에 일관되게 반영됨.
2026-04-08 12:28:23 +09:00
4ee8f05dfd refactor(frontend): admin 계열 PageContainer/PageHeader 적용
- AdminPanel: PageContainer + PageHeader(demo)
- AuditLogs/AccessLogs/LoginHistoryView: size=lg + primary Button
- AccessControl: size=lg + 우측 stats 유지 + ghost 새로고침 Button
- DataHub: PageContainer + demo 배지 + secondary 새로고침
- NoticeManagement: primary '새 알림 등록' Button
- SystemConfig: secondary 2개 액션 Button

인라인 <button>/<div className="p-5 space-y-4"> 패턴을 쇼케이스 공통 컴포넌트로
치환. admin 계열 9개 파일 중 7개 완료 (PermissionsPanel은 서브 컴포넌트라 제외).
UserRoleAssignDialog는 dialog라 제외.
2026-04-08 11:48:41 +09:00
a07c745cbc feat(frontend): 40+ 페이지 Badge/시맨틱 토큰 마이그레이션
- 모든 feature 페이지의 Badge className 패턴을 intent/size prop으로 변환
- 컬러풀 액션 버튼 (bg-*-500/600/700 + text-heading) -> text-on-vivid
- 검색/필터 버튼 배경 bg-blue-400 + text-on-bright (밝은 배경 위 검정)
- ROLE_COLORS 4곳 중복 제거 (MainLayout/UserRoleAssignDialog/
  PermissionsPanel/AccessControl) -> getRoleBadgeStyle 공통 호출
- PermissionsPanel 역할 생성/수정에 ColorPicker 통합
- MainLayout: PagePagination + scroll page state 제거 (데이터 페이지네이션 혼동)
- Dashboard RiskBar 단위 버그 수정 (0~100 정수 처리)
- ReportManagement, TransferDetection p-5 space-y-4 padding 복구
- EnforcementHistory 그리드 minmax 적용으로 컬럼 잘림 해소
- timeline 시간 formatDateTime 적용 (ISO T 구분자 처리)
- 각 feature 페이지가 공통 카탈로그 API (getXxxIntent/Label/Classes) 사용
2026-04-08 10:53:58 +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
f545aeafac fix: 권한 트리 UX 개선 + 라벨 사이드바 일치 + EXPORT 가드
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>
2026-04-07 10:33:29 +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
fc1a686700 feat: 시스템 관리 페이지 백엔드 연결 + 메트릭 카드
백엔드 API 추가:
- UserManagementController (admin:user-management)
  - GET /api/admin/users : 사용자 목록 + 역할 코드
  - GET /api/admin/users/stats : 상태별/역할별/인증방식별 카운트
  - POST /api/admin/users/{id}/unlock : 잠금 해제 (@Auditable USER_UNLOCK)
  - PUT /api/admin/users/{id}/status : 상태 변경 (@Auditable USER_STATUS_CHANGE)
  - 권한 캐시 evict 자동 호출
- AdminStatsController (admin:audit-logs/access-logs/login-history READ)
  - GET /api/admin/stats/audit : 전체/24시간/실패/액션별/시간별 통계
  - GET /api/admin/stats/access : 전체/24시간/4xx/5xx/평균응답/인기경로
  - GET /api/admin/stats/login : 성공률/사용자별/일별 추세

프론트엔드 연결:
- adminApi.ts 확장: AdminUser/UserStats/AuditStats/AccessStats/LoginStats
  타입 정의 + 사용자/통계 fetch 함수
- AccessControl.tsx (시스템 관리 > 권한 관리):
  - 4개 탭 모두 백엔드 연결
  - 역할 관리: GET /api/roles + 사용자별 카운트 표시
  - 사용자 관리: GET /api/admin/users + DataTable + 잠금 해제 버튼
    + 통계 카드 4개 (총/활성/잠금/비활성)
  - 감사 로그: GET /api/admin/audit-logs + GET /api/admin/stats/audit
    + 액션별 분포 Badge + 통계 카드
  - 보안 정책: 실제 백엔드 동작과 일치하도록 갱신
- AuditLogs.tsx: 메트릭 카드 4개 + 액션별 분포
- AccessLogs.tsx: 메트릭 카드 5개 (전체/24시간/4xx/5xx/평균) + Top 10 경로 테이블
- LoginHistoryView.tsx: 메트릭 카드 5개 + 사용자별 + 일별 추세

검증:
- /api/admin/users → 5명 (admin/operator/analyst/field/viewer)
- /api/admin/users/stats → byRole, byStatus, byProvider 카운트
- /api/admin/stats/audit → total 15, 액션 6종, hourly 추세
- /api/admin/stats/login → success 80%, byUser top, daily 추세
- 프론트엔드 빌드 통과 (493ms)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:57:59 +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