"use client"; import React, { useEffect, useState } from "react"; import dynamic from "next/dynamic"; import { DashboardElement } from "@/components/admin/dashboard/types"; import { getMultipleWeather, WeatherData } from "@/lib/api/openApi"; import { Cloud, CloudRain, CloudSnow, Sun, Wind } from "lucide-react"; import "leaflet/dist/leaflet.css"; // Leaflet 아이콘 경로 설정 (엑박 방지) if (typeof window !== "undefined") { const L = require("leaflet"); delete (L.Icon.Default.prototype as any)._getIconUrl; L.Icon.Default.mergeOptions({ iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png", iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png", shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png", }); } // Leaflet 동적 import (SSR 방지) const MapContainer = dynamic(() => import("react-leaflet").then((mod) => mod.MapContainer), { ssr: false }); const TileLayer = dynamic(() => import("react-leaflet").then((mod) => mod.TileLayer), { ssr: false }); const Marker = dynamic(() => import("react-leaflet").then((mod) => mod.Marker), { ssr: false }); const Popup = dynamic(() => import("react-leaflet").then((mod) => mod.Popup), { ssr: false }); // 브이월드 API 키 const VWORLD_API_KEY = "97AD30D5-FDC4-3481-99C3-158E36422033"; interface WeatherMapWidgetProps { element: DashboardElement; cities?: string[]; } /** * 날씨 아이콘 반환 */ const getWeatherIcon = (weatherMain: string) => { switch (weatherMain.toLowerCase()) { case "clear": return ; case "rain": return ; case "snow": return ; case "clouds": return ; default: return ; } }; /** * 날씨 지도 위젯 * - 여러 도시의 날씨를 지도에 표시 * - 실시간 날씨 정보 (온도, 습도, 풍속 등) * - Leaflet + 브이월드 지도 사용 */ export default function WeatherMapWidget({ element, cities }: WeatherMapWidgetProps) { const [weatherData, setWeatherData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // 기본 도시 목록 (사용자가 지정하지 않은 경우) const defaultCities = [ "서울", "부산", "인천", "대구", "광주", "대전", "울산", "세종", "제주", ]; const targetCities = cities || defaultCities; useEffect(() => { loadWeatherData(); // 자동 새로고침 (5분마다) const interval = setInterval(() => { loadWeatherData(); }, 300000); return () => clearInterval(interval); }, []); const loadWeatherData = async () => { try { setLoading(true); setError(null); const data = await getMultipleWeather(targetCities); // 위도경도가 있는 데이터만 필터링 const validData = data.filter((item) => item.lat && item.lng); setWeatherData(validData); } catch (err: any) { console.error("날씨 데이터 로드 실패:", err); setError(err.message || "날씨 데이터를 불러오는데 실패했습니다."); } finally { setLoading(false); } }; if (loading && weatherData.length === 0) { return ( 날씨 정보 로딩 중... ); } if (error) { return ( {error} ); } if (weatherData.length === 0) { return ( 날씨 데이터가 없습니다. ); } // 지도 중심 (대한민국 중심) const center: [number, number] = [36.5, 127.5]; return ( {/* 브이월드 Base Map */} {/* 날씨 마커 */} {weatherData.map((weather, index) => { if (!weather.lat || !weather.lng) return null; return ( {/* 도시명 */} {weather.city} {getWeatherIcon(weather.weatherMain)} {/* 날씨 설명 */} {weather.weatherDescription} {/* 날씨 정보 */} 온도 {weather.temperature}°C 체감온도 {weather.feelsLike}°C 습도 {weather.humidity}% 풍속 {weather.windSpeed} m/s {/* 타임스탬프 */} {new Date(weather.timestamp).toLocaleString("ko-KR", { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", })} ); })} ); }
날씨 정보 로딩 중...
{error}
날씨 데이터가 없습니다.
{weather.weatherDescription}