import { useState, useEffect } from 'react'; import { getRecentObservation, OBS_STATION_CODES, API_KEY } from '../services/khoaApi'; interface WeatherStation { id: string; name: string; location: { lat: number; lon: number }; } export interface EnrichedWeatherStation extends WeatherStation { wind: { speed: number; direction: number; speed_1k: number; speed_3k: number; }; wave: { height: number; period: number; }; temperature: { current: number; feelsLike: number; }; pressure: number; visibility: number; salinity?: number; } /** * 기상 데이터 가져오기 훅 (KHOA 조위관측소 API 사용) */ export function useWeatherData(stations: WeatherStation[]) { const [weatherStations, setWeatherStations] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [lastUpdate, setLastUpdate] = useState(null); useEffect(() => { let isMounted = true; // 관측소별 fallback 데이터 생성 function generateFallbackStation(station: WeatherStation): EnrichedWeatherStation { const seed = station.location.lat * 100 + station.location.lon; const r = (n: number) => Math.round(n * 10) / 10; // 소수 첫째자리 const windSpeed = r(6 + (seed % 7)); const waveHeight = r(0.8 + (seed % 20) / 10); const temp = r(5 + (seed % 8)); const windDir = [0, 45, 90, 135, 180, 225, 270, 315][Math.floor(seed) % 8]; return { ...station, wind: { speed: windSpeed, direction: windDir, speed_1k: r(windSpeed * 0.8), speed_3k: r(windSpeed * 1.2), }, wave: { height: waveHeight, period: 4 + (Math.floor(seed) % 3) }, temperature: { current: temp, feelsLike: r(temp - windSpeed * 0.3) }, pressure: 1010 + (Math.floor(seed) % 12), visibility: 12 + (Math.floor(seed) % 10), }; } async function fetchWeatherData() { try { setLoading(true); setError(null); if (!API_KEY) { console.warn('KHOA API 키 미설정 — fallback 데이터를 사용합니다.'); if (isMounted) { setWeatherStations(stations.map(generateFallbackStation)); setLastUpdate(new Date()); setLoading(false); } return; } const enrichedStations = await Promise.all( stations.map(async (station): Promise => { try { const obsCode = OBS_STATION_CODES[station.id]; if (!obsCode) return generateFallbackStation(station); const obs = await getRecentObservation(obsCode); if (obs) { const r = (n: number) => Math.round(n * 10) / 10; const windSpeed = r(obs.wind_speed ?? 8.5); const windDir = obs.wind_dir ?? 315; const waterTemp = r(obs.water_temp ?? 8.0); const airPres = Math.round(obs.air_pres ?? 1016); return { ...station, wind: { speed: windSpeed, direction: windDir, speed_1k: r(windSpeed * 0.8), speed_3k: r(windSpeed * 1.2), }, wave: { height: r(1.0 + windSpeed * 0.1), period: Math.floor(4 + windSpeed * 0.3), }, temperature: { current: waterTemp, feelsLike: r((obs.air_temp ?? waterTemp) - windSpeed * 0.3), }, pressure: airPres, visibility: airPres > 1010 ? 15 + Math.floor(Math.random() * 5) : 10, }; } return generateFallbackStation(station); } catch (stationError) { console.warn(`관측소 ${station.id} fallback 처리:`, stationError); return generateFallbackStation(station); } }) ); if (isMounted) { setWeatherStations(enrichedStations); setLastUpdate(new Date()); setLoading(false); } } catch (err) { console.error('기상 데이터 가져오기 오류:', err); if (isMounted) { setError(err instanceof Error ? err.message : '알 수 없는 오류'); setLoading(false); } } } fetchWeatherData(); // 10분마다 데이터 갱신 const interval = setInterval(fetchWeatherData, 10 * 60 * 1000); return () => { isMounted = false; clearInterval(interval); }; }, [stations]); return { weatherStations, loading, error, lastUpdate }; }