wing-gis/frontend/wing-gis-web/README.md
htlee b9d924e81e chore: 팀 워크플로우 기반 초기 프로젝트 구성
WING-GIS 해양경찰 통합 GIS 위치정보시스템.
모노레포: frontend(React 19 + MapLibre + deck.gl) + services(Spring Boot + Gradle).

- npm + Nexus 프록시 레지스트리 설정
- 팀 워크플로우 v1.6.1 부트스트랩 파일 배치
- .githooks (commit-msg, post-checkout)
- custom_pre_commit: true (모노레포 pre-commit 별도 관리)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 12:36:38 +09:00

200 lines
8.0 KiB
Markdown

# 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<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 타일 서버 |