- frontend: React 19 + Vite 7 + Leaflet + Tailwind + Zustand - backend: Express + better-sqlite3 + TypeScript - database: PostgreSQL 초기화 스크립트 - .gitignore: 대용량 참고자료(scat, 참고용) 및 바이너리 파일 제외 - .env.example: API 키 템플릿 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
199 lines
3.8 KiB
Markdown
Executable File
199 lines
3.8 KiB
Markdown
Executable File
# Backend API 문서
|
|
|
|
## 시뮬레이션 API
|
|
|
|
### POST /api/simulation/run
|
|
|
|
오일 확산 시뮬레이션을 실행합니다.
|
|
|
|
#### 요청
|
|
|
|
```json
|
|
{
|
|
"model": "OpenDrift", // "KOSPS" | "POSEIDON" | "OpenDrift" | "앙상블"
|
|
"lat": 34.7312, // 사고 위도
|
|
"lon": 127.6845, // 사고 경도
|
|
"duration_hours": 48, // 예측 시간 (시간)
|
|
"oil_type": "벙커C유", // 유종
|
|
"spill_amount": 100, // 유출량 (kL)
|
|
"spill_type": "연속" // 유출 형태
|
|
}
|
|
```
|
|
|
|
#### 응답
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"model": "OpenDrift",
|
|
"parameters": {
|
|
"lat": 34.7312,
|
|
"lon": 127.6845,
|
|
"duration_hours": 48,
|
|
"oil_type": "벙커C유",
|
|
"spill_amount": 100,
|
|
"spill_type": "연속"
|
|
},
|
|
"trajectory": [
|
|
{
|
|
"lat": 34.7312,
|
|
"lon": 127.6845,
|
|
"time": 0,
|
|
"particle": 0
|
|
},
|
|
// ... more points
|
|
],
|
|
"metadata": {
|
|
"particle_count": 20,
|
|
"time_steps": 49,
|
|
"generated_at": "2026-02-16T16:45:00.000Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 오류 응답
|
|
|
|
```json
|
|
{
|
|
"error": "Missing required parameters",
|
|
"required": ["model", "lat", "lon", "duration_hours"]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/simulation/status/:jobId
|
|
|
|
시뮬레이션 작업 상태를 확인합니다 (향후 비동기 작업용).
|
|
|
|
#### 응답
|
|
|
|
```json
|
|
{
|
|
"jobId": "abc123",
|
|
"status": "completed", // "pending" | "running" | "completed" | "failed"
|
|
"progress": 100, // 0-100
|
|
"message": "Simulation completed"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## OpenDrift 통합 계획
|
|
|
|
### 1단계: 환경 설정
|
|
|
|
```bash
|
|
# Python 가상 환경 생성
|
|
python3 -m venv venv
|
|
source venv/bin/activate # Linux/Mac
|
|
# venv\Scripts\activate # Windows
|
|
|
|
# OpenDrift 설치
|
|
pip install opendrift
|
|
|
|
# 필요한 기상/해양 데이터 라이브러리
|
|
pip install netCDF4 xarray
|
|
```
|
|
|
|
### 2단계: 데이터 소스 연동
|
|
|
|
OpenDrift는 다음 데이터 소스가 필요합니다:
|
|
|
|
- **바람 데이터**: WindSpeed, WindDirection
|
|
- 소스: ERA5, ECMWF, NCEP 등
|
|
- 형식: NetCDF (CF-compliant)
|
|
|
|
- **해류 데이터**: CurrentSpeed, CurrentDirection
|
|
- 소스: CMEMS, HYCOM, RTOFS 등
|
|
- 형식: NetCDF
|
|
|
|
- **파고 데이터**: WaveHeight (선택)
|
|
- 소스: WaveWatch III
|
|
|
|
### 3단계: TypeScript에서 Python 호출
|
|
|
|
```typescript
|
|
// backend/src/routes/simulation.ts
|
|
import { spawn } from 'child_process'
|
|
|
|
function runOpenDriftPython(params: SimulationRequest): Promise<ParticlePoint[]> {
|
|
return new Promise((resolve, reject) => {
|
|
const python = spawn('python3', [
|
|
'./src/utils/opendrift_runner.py',
|
|
JSON.stringify(params)
|
|
])
|
|
|
|
let stdout = ''
|
|
let stderr = ''
|
|
|
|
python.stdout.on('data', (data) => {
|
|
stdout += data.toString()
|
|
})
|
|
|
|
python.stderr.on('data', (data) => {
|
|
stderr += data.toString()
|
|
console.error('OpenDrift stderr:', data.toString())
|
|
})
|
|
|
|
python.on('close', (code) => {
|
|
if (code === 0) {
|
|
const result = JSON.parse(stdout)
|
|
resolve(result.trajectory)
|
|
} else {
|
|
reject(new Error(`OpenDrift failed with code ${code}: ${stderr}`))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
```
|
|
|
|
### 4단계: 비동기 작업 큐 (선택)
|
|
|
|
장시간 시뮬레이션의 경우 작업 큐 사용:
|
|
|
|
```bash
|
|
npm install bull redis
|
|
```
|
|
|
|
```typescript
|
|
import Queue from 'bull'
|
|
|
|
const simulationQueue = new Queue('simulation', {
|
|
redis: { host: 'localhost', port: 6379 }
|
|
})
|
|
|
|
simulationQueue.process(async (job) => {
|
|
const result = await runOpenDriftPython(job.data)
|
|
return result
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## 레이어 API
|
|
|
|
### GET /api/layers/tree/all
|
|
|
|
전체 레이어 트리 구조를 반환합니다.
|
|
|
|
#### 응답
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": "해도",
|
|
"name": "해도",
|
|
"type": "group",
|
|
"children": [
|
|
{
|
|
"id": "기본해도",
|
|
"name": "기본해도",
|
|
"type": "layer",
|
|
"wmsLayer": "khoa:base_chart"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
```
|