## Summary - 기상 맵 컨트롤 컴포넌트 추가 및 KHOA API 연동 개선 - KHOA API 엔드포인트 교체 및 해양예측 오버레이 Canvas 렌더링 전환 ## 변경 파일 - OceanForecastOverlay.tsx - WeatherMapOverlay.tsx - WeatherView.tsx - useOceanForecast.ts - khoaApi.ts - vite.config.ts ## Test plan - [ ] 기상정보 -> 기상 레이어 -> 해황 예보도 클릭 -> 이미지 렌더링 확인 - [ ] 기상정보 -> 기상 레이어 -> 백터 바람 클릭 -> 백터 이미지 렌더링 확인 Co-authored-by: Nan Kyung Lee <nankyunglee@Nanui-Macmini.local> Reviewed-on: #78 Co-authored-by: leedano <dnlee@gcsc.co.kr> Co-committed-by: leedano <dnlee@gcsc.co.kr>
114 lines
3.1 KiB
TypeScript
Executable File
114 lines
3.1 KiB
TypeScript
Executable File
import { useState, useEffect } from 'react'
|
|
import {
|
|
getOceanForecast,
|
|
getLatestForecast,
|
|
getForecastByTime,
|
|
type OceanForecastData
|
|
} from '../services/khoaApi'
|
|
|
|
interface UseOceanForecastReturn {
|
|
forecasts: OceanForecastData[]
|
|
latestForecast: OceanForecastData | null
|
|
selectedForecast: OceanForecastData | null
|
|
loading: boolean
|
|
error: string | null
|
|
lastUpdate: Date | null
|
|
refetch: () => Promise<void>
|
|
selectForecast: (day: string, hour: string) => void
|
|
availableTimes: Array<{ day: string; hour: string; label: string }>
|
|
}
|
|
|
|
/**
|
|
* 해황예보도 데이터 가져오기 훅
|
|
* @param regionType 지역 유형 (KOREA, INCHEON 등)
|
|
* @param autoRefresh 자동 갱신 여부 (기본: true, 30분마다)
|
|
*/
|
|
export function useOceanForecast(
|
|
regionType: string = 'KOREA',
|
|
autoRefresh: boolean = true
|
|
): UseOceanForecastReturn {
|
|
const [forecasts, setForecasts] = useState<OceanForecastData[]>([])
|
|
const [latestForecast, setLatestForecast] = useState<OceanForecastData | null>(null)
|
|
const [selectedForecast, setSelectedForecast] = useState<OceanForecastData | null>(null)
|
|
const [loading, setLoading] = useState(true)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const [lastUpdate, setLastUpdate] = useState<Date | null>(null)
|
|
|
|
const fetchForecastData = async () => {
|
|
try {
|
|
setLoading(true)
|
|
setError(null)
|
|
|
|
const data = await getOceanForecast(regionType)
|
|
|
|
setForecasts(data)
|
|
const latest = getLatestForecast(data)
|
|
setLatestForecast(latest)
|
|
|
|
// 선택된 예보가 없으면 최신 예보를 선택
|
|
if (!selectedForecast && latest) {
|
|
setSelectedForecast(latest)
|
|
}
|
|
|
|
setLastUpdate(new Date())
|
|
} catch (err) {
|
|
console.error('해황예보도 데이터 가져오기 오류:', err)
|
|
setError(err instanceof Error ? err.message : '알 수 없는 오류')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
// 특정 시간대의 예보 선택
|
|
const selectForecast = (day: string, hour: string) => {
|
|
const forecast = getForecastByTime(forecasts, day, hour)
|
|
if (forecast) {
|
|
setSelectedForecast(forecast)
|
|
}
|
|
}
|
|
|
|
// 사용 가능한 시간대 목록 생성
|
|
const availableTimes = forecasts
|
|
.map((f) => ({
|
|
day: f.ofcFrcstYmd,
|
|
hour: f.ofcFrcstTm,
|
|
label: `${f.ofcFrcstYmd.slice(4, 6)}/${f.ofcFrcstYmd.slice(6, 8)} ${f.ofcFrcstTm}:00`
|
|
}))
|
|
.sort((a, b) => `${a.day}${a.hour}`.localeCompare(`${b.day}${b.hour}`))
|
|
|
|
useEffect(() => {
|
|
let isMounted = true
|
|
let interval: NodeJS.Timeout | null = null
|
|
|
|
const loadData = async () => {
|
|
if (isMounted) {
|
|
await fetchForecastData()
|
|
}
|
|
}
|
|
|
|
loadData()
|
|
|
|
// 자동 갱신 설정 (30분마다)
|
|
if (autoRefresh) {
|
|
interval = setInterval(loadData, 30 * 60 * 1000)
|
|
}
|
|
|
|
return () => {
|
|
isMounted = false
|
|
if (interval) clearInterval(interval)
|
|
}
|
|
}, [regionType, autoRefresh])
|
|
|
|
return {
|
|
forecasts,
|
|
latestForecast,
|
|
selectedForecast,
|
|
loading,
|
|
error,
|
|
lastUpdate,
|
|
refetch: fetchForecastData,
|
|
selectForecast,
|
|
availableTimes
|
|
}
|
|
}
|