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 |
실시간 물표 수신 (예정) |
시작하기
# 의존성 설치
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<mmsi, AisTarget>)
│
▼
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 타일 서버 |