refactor(weather): KHOA API 병렬 요청 및 지도 컨트롤 위치 개선

This commit is contained in:
jeonghyo.k 2026-04-20 14:13:03 +09:00
부모 70fe23e40b
커밋 1825bcbb5f
5개의 변경된 파일59개의 추가작업 그리고 63개의 파일을 삭제

파일 보기

@ -19,7 +19,7 @@
```bash
cd frontend && npm install # Frontend
npm run dev # Vite dev (localhost:5173)
npm run dev # Vite dev (localhost:5174)
npm run build # tsc -b && vite build
npm run lint # ESLint

파일 보기

@ -11,6 +11,9 @@
### 변경
- InfoLayerSection을 공통 컴포넌트로 이동 (prediction → common/layer)
### 수정
- 기상정보 탭 로딩 지연 개선: KHOA API 관측소 요청을 병렬 처리로 전환 및 API 키 미설정 시 즉시 fallback 처리
### 기타
- DB migration 033: SPIL_QTY NUMERIC(22,10) 확장 (대용량 HNS 유출량 지원)

파일 보기

@ -139,8 +139,8 @@ function MapOverlayControls({
return (
<>
{/* 측 컨트롤 컬럼 */}
<div className="absolute top-[80px] left-[10px] z-10 flex flex-col gap-1">
{/* 측 컨트롤 컬럼 */}
<div className="absolute top-[10px] right-[10px] z-10 flex flex-col gap-1">
{/* 줌 */}
<button title="줌 인" onClick={() => map?.zoomIn()} className={btn}>
+

파일 보기

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { getRecentObservation, OBS_STATION_CODES } from '../services/khoaApi';
import { getRecentObservation, OBS_STATION_CODES, API_KEY } from '../services/khoaApi';
interface WeatherStation {
id: string;
@ -68,71 +68,64 @@ export function useWeatherData(stations: WeatherStation[]) {
setLoading(true);
setError(null);
const enrichedStations: EnrichedWeatherStation[] = [];
let apiFailed = false;
for (const station of stations) {
if (apiFailed) {
enrichedStations.push(generateFallbackStation(station));
continue;
}
try {
const obsCode = OBS_STATION_CODES[station.id];
if (!obsCode) {
enrichedStations.push(generateFallbackStation(station));
continue;
}
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);
enrichedStations.push({
...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,
});
} else {
enrichedStations.push(generateFallbackStation(station));
}
await new Promise((resolve) => setTimeout(resolve, 100));
} catch (stationError) {
if (!apiFailed) {
console.warn('KHOA API 연결 실패, fallback 데이터를 사용합니다:', stationError);
apiFailed = true;
}
enrichedStations.push(generateFallbackStation(station));
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<EnrichedWeatherStation> => {
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);
if (apiFailed) {
setError('KHOA API 연결 실패 — fallback 데이터 사용 중');
}
}
} catch (err) {
console.error('기상 데이터 가져오기 오류:', err);

파일 보기

@ -1,7 +1,7 @@
// KHOA (국립해양조사원) API 서비스
// API Key를 환경변수에서 로드 (소스코드 노출 방지)
const API_KEY = import.meta.env.VITE_DATA_GO_KR_API_KEY || '';
export const API_KEY = import.meta.env.VITE_DATA_GO_KR_API_KEY || '';
const BASE_URL = 'https://apis.data.go.kr/1192136/oceanCondition/GetOceanConditionApiService';
const RECENT_OBS_URL = 'https://apis.data.go.kr/1192136/dtRecent/GetDTRecentApiService';