# WING-GIS Web Frontend 해양경찰청 통합 GIS 위치정보시스템 프론트엔드 ## 기술 스택 | 분류 | 기술 | 버전 | 용도 | |------|------|------|------| | Framework | React | 19.x | SPA UI | | Language | TypeScript | 5.9 | 타입 안전성 | | Build | Vite | 8.x | 빌드 + HMR | | Map Engine | MapLibre GL JS | 5.18 | ENC 전자해도 렌더링 | | Map Overlay | deck.gl (MapboxOverlay) | 9.2 | 선박 아이콘 WebGL 렌더링 | | State | Zustand | 5.x | 경량 상태 관리 | | HTTP | Axios / fetch | - | REST API 통신 | | WebSocket | STOMP.js | 7.x | 실시간 물표 수신 (예정) | ## 시작하기 ```bash # 의존성 설치 yarn install # 개발 서버 (http://localhost:5174) yarn dev # 프로덕션 빌드 yarn build # 빌드 결과물 프리뷰 yarn preview ``` ## 프로젝트 구조 ``` src/ ├── App.tsx # 루트 컴포넌트 (레이아웃 + AIS 폴링) ├── main.tsx # 엔트리 포인트 ├── App.css # 디자인 토큰 (CSS 변수) │ ├── components/ │ ├── layout/ # 5-패널 레이아웃 │ │ ├── TitleBar.tsx # 상단 헤더 (로고, 네비, 시계, 사용자) │ │ ├── SubToolbar.tsx # 컨텍스트별 도구 모음 │ │ ├── Sidebar.tsx # 좌측 (검색, 레이어 트리) │ │ ├── RightPanel.tsx # 우측 (물표현황/경보/해양정보) │ │ └── BottomBar.tsx # 하단 상태바 │ ├── map/ # 지도 오버레이 컴포넌트 │ │ ├── MapViewML.tsx # MapLibre + deck.gl 통합 컨테이너 │ │ ├── MapTools.tsx # 지도 도구 (확대/축소/초기화) │ │ ├── BaseMapSelector.tsx # 배경지도 탭 │ │ ├── ScaleBar.tsx # 축척바 │ │ ├── MapLegend.tsx # 물표 범례 (실시간 AIS 카운트) │ │ └── CoordStatusBar.tsx # 좌표/줌 상태바 │ └── vessel/ # 선박 관련 UI │ ├── VesselPopup.tsx # 선박 미니 팝업 │ ├── VesselSearch.tsx # 선박 통합 검색 │ └── VesselSignalToggle.tsx # 신호 소스 ON/OFF 토글 │ ├── features/ │ ├── vesselLayer/ # 선박 렌더링 파이프라인 │ │ ├── hooks/ │ │ │ └── useVesselDeckLayer.ts # deck.gl 레이어 통합 훅 │ │ └── lib/ │ │ ├── aisAdapter.ts # AisTarget → AisFeature 변환 │ │ ├── aisDeckLayers.ts # AIS deck.gl IconLayer 빌더 │ │ ├── aisIcons.ts # SignalKindCode별 SVG 아이콘 (8종) │ │ ├── shipTooltip.ts # 호버 툴팁 HTML 생성 │ │ ├── ShipBatchRenderer.ts # 뷰포트 컬링 + 밀도 제어 │ │ ├── vesselAdapter.ts # 비AIS 소스 어댑터 │ │ ├── vesselDeckLayers.ts # 비AIS deck.gl 레이어 │ │ └── vesselIcons.ts # 비AIS 소스별 SVG 아이콘 │ ├── nauticalChart/ # ENC 전자해도 오버레이 │ │ ├── hooks/ │ │ │ └── useNauticalChartOverlay.ts │ │ ├── lib/ │ │ │ ├── fetchNauticalStyle.ts # gcnautical.com 스타일 fetch │ │ │ └── nauticalLayerManager.ts # 레이어 주입/제거/S-52 테마 │ │ ├── model/ │ │ │ └── types.ts # S52Theme, NauticalChartSettings │ │ └── ui/ │ │ └── NauticalChartToggle.tsx # ENC on/off + 주간/황혼/야간 │ └── shipImage/ # 선박 사진 │ ├── api/ │ │ └── shipImageApi.ts # 이미지 API + 썸네일/고화질 URL │ └── ui/ │ └── ShipImageModal.tsx # 사진 뷰어 모달 │ ├── hooks/ │ ├── useMap.ts # MapLibre 맵 + MapboxOverlay 초기화 │ ├── useDeckLayers.ts # deck.gl 레이어 + 콜백 전달 │ ├── useStore.ts # Zustand 글로벌 스토어 │ ├── useAisPolling.ts # AIS REST 폴링 (60분 초기, 1분 간격) │ └── useVesselStream.ts # STOMP WebSocket (예정) │ ├── services/ │ ├── aisApi.ts # AIS recent-positions API │ ├── api.ts # Axios 인스턴스 │ └── vesselApi.ts # 물표 CRUD API │ ├── types/ │ ├── ais.ts # SignalKindCode, AisTarget, DTO │ ├── vessel.ts # Vessel, VesselSource, 색상/형태 맵 │ └── layer.ts # LayerGroup, S100_PRODUCTS │ ├── utils/ │ ├── coordinate.ts # DMS 변환, 축척 계산 │ ├── s52Colors.ts # S-52 수심 색상 팔레트 │ └── vesselIcon.ts # 범용 SVG 아이콘 생성 │ ├── lib/map/ │ ├── mapCore.ts # kickRepaint, onMapStyleReady │ ├── mapConstants.ts # 기본 좌표, 줌, OSM 폴백 스타일 │ └── MaplibreDeckCustomLayer.ts # Globe 모드용 (현재 미사용) │ ├── context/ │ └── MapContext.tsx # mapRef, overlayRef 전달 │ └── data/ └── mockVessels.ts # 비AIS 더미 선박 데이터 ``` ## 핵심 데이터 흐름 ### AIS 선박 위치 ``` API: GET /signal-batch/api/v2/vessels/recent-positions?minutes=N │ ▼ useAisPolling (60분 초기 → 60초 간격 2분 데이터 → 2시간 프루닝) │ ▼ Zustand Store: aisTargets (Map) │ ▼ useVesselDeckLayer → ShipBatchRenderer (뷰포트 컬링 + 밀도 제어) │ ▼ deck.gl IconLayer (signalKindCode별 색상 아이콘) │ ├── 호버 → getTooltip (이름, MMSI, SOG/COG, 사진 썸네일) └── 클릭 → ShipImageModal (사진 있는 선박만) ``` ### ENC 전자해도 ``` gcnautical.com/styles/nautical.json → fetchEncStyle (폰트 패칭) │ ▼ MapLibre map.setStyle() (초기 로드 시 OSM → ENC 전환) │ ├── ENC 토글 → useNauticalChartOverlay (레이어 주입/제거) └── S-52 테마 → applyS52Theme (주간/황혼/야간 색상) ``` ## 선박 아이콘 체계 ### AIS 선박 (SignalKindCode 기반, 실시간 데이터) | 코드 | 종류 | 색상 | 형태 | |------|------|------|------| | 000020 | 어선 | #00C853 (녹색) | 항해: 화살표 32x48 / 정박: 원 16x16 | | 000021 | 경비함정 | #FF5722 (주황) | 〃 | | 000022 | 여객선 | #2196F3 (파랑) | 〃 | | 000023 | 화물선 | #9C27B0 (보라) | 〃 | | 000024 | 유조선 | #F44336 (빨강) | 〃 | | 000025 | 관공선 | #FF9800 (오렌지) | 〃 | | 000027 | 일반 | #607D8B (회색) | 〃 | | 000028 | 부이 | #795548 (갈색) | 폴+몸체 32x44 (회전 없음) | ### 비AIS 소스 (VesselSource 기반, 더미 데이터) | 소스 | 색상 | 형태 | |------|------|------| | V-Pass | #1a8a3a | 삼각형 | | E-NAV | #8833cc | 삼각형 | | VTS | #cc6600 | 사각형 | | VTS-RADAR | #cc3300 | 다이아몬드 | | AIR-AIS | #0099aa | 비행기 | | VHF-DSC | #d63030 | 삼각형 (점멸) | ## 환경 변수 | 변수 | 기본값 | 설명 | |------|--------|------| | VITE_API_BASE | `http://localhost:8080` | 백엔드 API 기본 URL | | VITE_WS_URL | `http://localhost:8080/ws/vessels` | WebSocket 엔드포인트 | ## Vite 프록시 (개발 환경) | 경로 | 타겟 | 용도 | |------|------|------| | `/api` | `http://localhost:8080` | 백엔드 API | | `/signal-batch` | `https://wing.gc-si.dev` | AIS 선박 위치 API | | `/shipimg` | `https://wing.gc-si.dev` | 선박 사진 정적 파일 | | `/martin` | `http://192.168.1.18:3030` | Martin ENC 타일 서버 |