GPS 스푸핑 등 비정상 운항 패턴의 강화학습 분류기 고도화를 위해,
비정상 궤적을 정상 테이블(5min/hourly/daily)과 캐시(L1/L2)에도
포함 저장하는 설정 플래그 추가.
- vessel.batch.track.include-abnormal-in-tracks 플래그 (기본 false)
- 5min: isAbnormal 시에도 filteredTracks에 포함 (플래그 true)
- Hourly/Daily: correctedTrack null 시에도 originalTrack 포함 (플래그 true)
- 비정상 검출 + t_abnormal_tracks 기록은 플래그와 무관하게 항상 유지
- prod 환경 true 설정 (강화학습 데이터 수집)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 토글 활성 상태 시각적 구분 강화 (bg-secondary + font-medium)
- IP 모드 "-" 표시 수정 — 백엔드 client 필드명 매핑 보정
- ID 데이터 없을 때 섹션 사라지는 대신 안내 메시지 표시
- 쿼리 이력(ApiMetrics)에 client_id 컬럼 추가
- history SQL에 client_id 컬럼 포함
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GC_SESSION JWT 쿠키에서 인증된 사용자 email을 추출하여 쿼리 메트릭에 기록.
대시보드 Top 클라이언트를 IP 기준 또는 사용자 ID 기준으로 전환 가능.
백엔드:
- WebSocket 핸드셰이크에서 GC_SESSION 쿠키 JWT payload → email 추출
- QueryMetric에 clientId 필드 추가, t_query_metrics에 client_id 컬럼 자동 생성
- timeseries API에 groupBy=ip|id 파라미터 추가
프론트엔드:
- Dashboard Top 클라이언트 섹션에 IP/ID 세그먼트 토글 추가
- 토글 전환 시 즉시 재조회
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WebSocket 리플레이 쿼리가 캐시 범위 내 조회에도 100% DB 경로를 사용하던 문제 수정.
- HOURLY/FIVE_MINUTE 전략에 L1(FiveMinTrackCache)/L2(HourlyTrackCache) 캐시 직접 조회 적용
- currentHourStart 기준 L1/L2 자동 라우팅 (현재시간 정각 이후→L1, 이전→L2)
- 뷰포트 필터를 캐시 데이터에서 직접 수행 (경량 WKT 파싱, JTS 불필요)
- vessel info SQL 컬럼명 오류 수정 (ship_nm → name)
- QueryBenchmark에 cacheHourlyRanges/cacheFiveMinRanges 추가, determinePath 3레벨 캐시 반영
- collectViewportVesselIds에서 HOURLY/5MIN DB 쿼리 제거 (캐시에서 처리)
당일 3시간 쿼리: DB 100% → CACHE 100%, 14일 쿼리: CACHE 100% (L3 범위 내)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AreaSearch/VesselContact 동시성·메모리 관리 통합 (ActiveQueryManager + MemoryBudget)
- 순차 통과 SQL 동적 N-구역(2~10) 확장
- 성능 최적화: ArrayList 사전 할당, Coordinate 재사용, equirectangular 근사
- 3개 API에 chnPrmShipOnly 파라미터 추가 (~1,400 MMSI 필터링)
- 대시보드 DataPipeline 차트 개선
- parseTimestamp 실패 로깅 추가 (AreaSearchService)
- isNightTimeContact 야간 판정 로직 단순화 (VesselContactService)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase A: 버그 수정
- isQueryCancelled: queryCancelFlags 전체 순회 → queryId 직접 조회 (O(n)→O(1))
한 쿼리 취소 시 서버 전체 쿼리 조기 종료되던 치명적 버그 해결
- QueryContext 내부 클래스 추출: 싱글턴 인스턴스 변수 5개를 쿼리별 로컬로 전환
동시 쿼리 간 상태 교차 오염 원천 차단
- 대기열 타임아웃: 하드코딩 120초 → ActiveQueryManager 설정값 사용
Phase B: 쿼리 메트릭 DB 저장
- QueryMetricsService: signal.t_query_metrics 비동기 INSERT
- QueryMetricsController: GET /api/monitoring/query-metrics, /stats
- streamChunkedTracks finally 블록에서 자동 저장 (QueryBenchmark 데이터 연동)
Phase C: N+1 해소 + 데드코드 제거
- VesselInfo 배치 프리로드: viewportVesselIds 수집 후 1회 배치 조회
- 미사용 코드 ~400줄 삭제: simplificationStrategy, executorService, processQueryInChunks,
batchGetVesselInfo, processChunk, selectTableByTimeRange, groupRangesByDate 등
Phase D: 코드 품질
- WKTReader: 싱글턴 공유 → ThreadLocal (스레드 안전성)
- avgSpeed 계산: 4곳 중복 → calculateAvgSpeed() 헬퍼 추출
2,984줄 → 2,575줄 (409줄 삭감)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SignalKindCode 매핑 변경: aton→DEFAULT, tug/tender→DEFAULT,
Vessel+towing/dredging/diving→DEFAULT, Vessel+leisure→DEFAULT
- shipName 기반 BUOY 검출: '.' '_' 문자 2개 이상 → BUOY
- 캐시 저장 시 1회 치환, API 응답 시 DB/캐시 값 직접 사용
- 응답 경로 6곳 resolve() 재계산 제거
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 1: L1/L2/L3 캐시 키 기반 직접 O(1) 조회 (전체 스캔 대체)
Phase 2: 64GB JVM 메모리 예산 논리적 파티셔닝 (캐시 35GB/쿼리 20GB)
Phase 3: L2 HourlyTrackCache 6시간 경과 엔트리 Nth-point 간소화
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- application.yml: gzip 압축 활성화 (1KB 이상 JSON 자동 압축, 70~85% 감소)
- JacksonConfig: NON_NULL 전역 설정 (null 필드 직렬화 제거, 5~15% 감소)
- VesselPositionService: sog/cog 소수점 1자리, lon/lat 6자리 제한 (3~5% 감소)
- MapTileController: @Tag, @Operation, @Parameter Swagger 문서 추가
- LockMonitorController: @Tag, @Operation Swagger 문서 추가
- BatchAdminController daily-stats: @ApiResponse 응답 스키마 예시 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PostGIS ST_AsText()는 "LINESTRING M (...)" (공백 포함) 반환하지만
Java 코드는 "LINESTRING M(...)" (공백 없음) 형식만 매칭하여
앱 재시작 후 DB fallback/warmup 데이터가 전량 필터되는 버그 수정.
- DB 읽기 시 WKT 정규화 (CacheWarmup, CacheBasedReader 4곳)
- merge processor regex에 \s* 방어적 처리 (Daily/Hourly)
- countWktPoints indexOf('(') 기반으로 개선
- BatchAdmin: dailyJob 수동 실행 시 timeBucket 파라미터 추가
- DataPipeline: L3 표시를 cachedDays → totalVessels로 변경
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hourly Job에 적용된 캐시 기반 병합 패턴을 Daily Job에 동일 적용:
- CacheBasedDailyTrackReader: L2(HourlyTrackCache)에서 읽기 + DB fallback
- DailyTrackMergeProcessor: Java 인메모리 WKT 병합 + 통계 집계
- 비정상 검출: MMSI별 개별 쿼리 → 1회 bulk prefetch
- SQL ~20,000건 → ~3건 (99.98% 감소), 24분 → 30~60초 예상
삭제: DailyTrackProcessor, DailyTrackProcessorWithAbnormalDetection,
BaseTrackProcessorWithAbnormalDetection (N+1 SQL 프로세서)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ShipImageService @PostConstruct로 85K IMO 인메모리 캐시 로드
- RecentVesselPositionDto에 shipImagePath, shipImageCount 필드 추가
- VesselPositionService에서 imo 기반 O(1) lookup으로 사진 정보 주입
- ShipImageRepository에 이미지 수 포함 쿼리 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- RecentVesselPositionDto에 imo 필드 추가 (0이면 null 처리)
- GET /api/v2/shipimg/valid-list: 선박사진 보유 IMO 전체 목록 + 썸네일 경로
- 초기 로딩 시 선박 썸네일 즉시 표시 용도 (85K+ IMO)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DateTimeParseUtil: zdt.toLocalDateTime() → withZoneSameInstant(systemDefault) 변환
클라이언트 UTC 시각("Z" 접미사)이 KST로 변환되지 않아 9시간 밀리는 버그 수정
- GisServiceV2 queryWithCache: Daily 캐시 HIT 후 요청 MMSI 미존재 시
DB fallback(hourly/5min 계층 조회) 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GET /api/v2/vessels/chnprmship/recent-positions?minutes=60
ChnPrmShipCacheManager에서 지정 시간 내 신호 수신된 선박 조회.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
L1/L2 캐시에 다른 선박 데이터가 있어 HIT로 판정되지만
요청한 MMSI가 캐시에 없을 때 DB fallback이 누락되는 버그 수정.
filterByMmsi() 후 누락된 MMSI에 대해 부분 DB fallback 실행.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- t_chnprmship_positions 월별 파티션 테이블 (PartitionManager 관리)
- ChnPrmShipPositionSyncStep: 5분 Job 편승 캐시→DB 이중 적재
- ChnPrmShip 캐시 TTL 2→7일, 워밍업 소스 전용 DB + t_ais_position 이중화
- tracks/vessels API: includeChnPrmShip=true 시 ChnPrmShipInfo enrichment
- ShipImageControllerV2: /api/v2/shipimg/{imo} 추가
- SwaggerConfig: V2 경로 분리 + shipimg V2 그룹 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SwaggerConfig: 서버 URL을 현행 환경(192.168.1.18:18090)으로 업데이트
- AbnormalTrackResponse: 13개 필드에 @Schema 어노테이션 추가
- AbnormalTrackStatsResponse: 7개 필드에 @Schema 어노테이션 추가
- GisController V1: minutes 파라미터 4곳에 @Parameter 추가
- BatchAdminController: step/details 엔드포인트에 @Operation 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
application-prod.yml의 five-min-track/hourly-track가 app.ais-api 아래에
잘못 중첩되어 적용되지 않았음. application.yml의 app.cache 아래가 실제
@Value가 읽는 경로이므로 해당 파일을 수정.
- L1: 700K→1.5M, L2: 1.4M→3.5M (application.yml)
- application-prod.yml: 잘못된 중첩 제거
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
백엔드:
- haegu/realtime: DB 공간 JOIN(12s) → 인메모리 캐시 순회(~50ms)
- batch/statistics: N+1 JobExplorer(1.1s) → 단일 SQL 집계(~100ms)
- batch/daily-stats: N+1×7일(9s) → 직접 SQL 2쿼리(~200ms)
- throughput: pg_total_relation_size 매번 호출(1.4s) → Caffeine 5분 캐시
- quality: 풀스캔(0.6s) → 24시간 범위 제한
프론트엔드:
- Promise.allSettled 차단 → 개별 .then() 점진적 렌더링
- useCachedState 훅: 페이지 전환 시 이전 데이터 즉시 표시
- AreaStats: 해구 폴리곤 choropleth 지도 + 선박수 범례 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GREATEST() 반환값이 double precision이라 ROUND(dp, int) 호출 시
PostgreSQL에서 미지원 함수 에러 발생 → 전체 나눗셈 결과를 ::numeric 캐스팅
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- JdbcTemplate이 timestamptz를 java.sql.Timestamp로 반환하는 케이스 처리
- toLocalDateTime() 유틸 메서드로 Timestamp/OffsetDateTime/LocalDateTime 통합 변환
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CacheMonitoringController: @ConditionalOnProperty 제거, 5개 캐시 통합 집계
- MonitoringController: delay API에서 delayMinutes/status 항상 반환
- FiveMinTrackCache/HourlyTrackCache: getStatsMap() 메서드 추가
- App.tsx: Suspense fallback + ErrorBoundary 추가
- Dashboard.tsx: nullable 필드 방어 (?? 연산자)
- formatters.ts: formatNumber null 안전 처리
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>