'use client'; /** * 날씨 위젯 컴포넌트 * - 실시간 날씨 정보를 표시 */ import React, { useEffect, useState } from 'react'; import { getWeather, WeatherData } from '@/lib/api/openApi'; import { Cloud, CloudRain, Sun, CloudSnow, Wind, Droplets, Gauge, RefreshCw, Check, ChevronsUpDown, Settings, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'; import { cn } from '@/lib/utils'; interface WeatherWidgetProps { city?: string; refreshInterval?: number; // 새로고침 간격 (ms), 기본값: 600000 (10분) } export default function WeatherWidget({ city = '서울', refreshInterval = 600000, }: WeatherWidgetProps) { const [open, setOpen] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false); const [selectedCity, setSelectedCity] = useState(city); const [weather, setWeather] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [lastUpdated, setLastUpdated] = useState(null); // 표시할 날씨 정보 선택 const [selectedItems, setSelectedItems] = useState([ 'temperature', 'feelsLike', 'humidity', 'windSpeed', 'pressure', ]); // 날씨 항목 정의 const weatherItems = [ { id: 'temperature', label: '기온', icon: Sun }, { id: 'feelsLike', label: '체감온도', icon: Sun }, { id: 'humidity', label: '습도', icon: Droplets }, { id: 'windSpeed', label: '풍속', icon: Wind }, { id: 'pressure', label: '기압', icon: Gauge }, ]; // 항목 토글 const toggleItem = (itemId: string) => { setSelectedItems((prev) => prev.includes(itemId) ? prev.filter((id) => id !== itemId) : [...prev, itemId] ); }; // 도시 목록 (전국 시/군/구 단위) const cities = [ // 서울특별시 (25개 구) { value: '서울', label: '서울' }, { value: '종로구', label: '서울 종로구' }, { value: '중구', label: '서울 중구' }, { value: '용산구', label: '서울 용산구' }, { value: '성동구', label: '서울 성동구' }, { value: '광진구', label: '서울 광진구' }, { value: '동대문구', label: '서울 동대문구' }, { value: '중랑구', label: '서울 중랑구' }, { value: '성북구', label: '서울 성북구' }, { value: '강북구', label: '서울 강북구' }, { value: '도봉구', label: '서울 도봉구' }, { value: '노원구', label: '서울 노원구' }, { value: '은평구', label: '서울 은평구' }, { value: '서대문구', label: '서울 서대문구' }, { value: '마포구', label: '서울 마포구' }, { value: '양천구', label: '서울 양천구' }, { value: '강서구', label: '서울 강서구' }, { value: '구로구', label: '서울 구로구' }, { value: '금천구', label: '서울 금천구' }, { value: '영등포구', label: '서울 영등포구' }, { value: '동작구', label: '서울 동작구' }, { value: '관악구', label: '서울 관악구' }, { value: '서초구', label: '서울 서초구' }, { value: '강남구', label: '서울 강남구' }, { value: '송파구', label: '서울 송파구' }, { value: '강동구', label: '서울 강동구' }, // 부산광역시 { value: '부산', label: '부산' }, { value: '해운대구', label: '부산 해운대구' }, { value: '부산진구', label: '부산 부산진구' }, { value: '동래구', label: '부산 동래구' }, { value: '사하구', label: '부산 사하구' }, { value: '금정구', label: '부산 금정구' }, { value: '사상구', label: '부산 사상구' }, // 인천광역시 { value: '인천', label: '인천' }, { value: '부평구', label: '인천 부평구' }, { value: '계양구', label: '인천 계양구' }, { value: '남동구', label: '인천 남동구' }, // 대구광역시 { value: '대구', label: '대구' }, { value: '수성구', label: '대구 수성구' }, { value: '달서구', label: '대구 달서구' }, // 광주광역시 { value: '광주', label: '광주' }, { value: '광산구', label: '광주 광산구' }, // 대전광역시 { value: '대전', label: '대전' }, { value: '유성구', label: '대전 유성구' }, // 울산광역시 { value: '울산', label: '울산' }, // 세종특별자치시 { value: '세종', label: '세종' }, // 경기도 (주요 도시) { value: '수원', label: '수원' }, { value: '성남', label: '성남' }, { value: '고양', label: '고양' }, { value: '용인', label: '용인' }, { value: '부천', label: '부천' }, { value: '안산', label: '안산' }, { value: '안양', label: '안양' }, { value: '남양주', label: '남양주' }, { value: '화성', label: '화성' }, { value: '평택', label: '평택' }, { value: '의정부', label: '의정부' }, { value: '시흥', label: '시흥' }, { value: '파주', label: '파주' }, { value: '김포', label: '김포' }, { value: '광명', label: '광명' }, // 강원도 { value: '춘천', label: '춘천' }, { value: '원주', label: '원주' }, { value: '강릉', label: '강릉' }, { value: '속초', label: '속초' }, { value: '동해', label: '동해' }, { value: '태백', label: '태백' }, { value: '삼척', label: '삼척' }, // 충청북도 { value: '청주', label: '청주' }, { value: '충주', label: '충주' }, { value: '제천', label: '제천' }, // 충청남도 { value: '천안', label: '천안' }, { value: '공주', label: '공주' }, { value: '보령', label: '보령' }, { value: '아산', label: '아산' }, { value: '서산', label: '서산' }, { value: '논산', label: '논산' }, { value: '당진', label: '당진' }, // 전라북도 { value: '전주', label: '전주' }, { value: '군산', label: '군산' }, { value: '익산', label: '익산' }, { value: '정읍', label: '정읍' }, { value: '남원', label: '남원' }, { value: '김제', label: '김제' }, // 전라남도 { value: '목포', label: '목포' }, { value: '여수', label: '여수' }, { value: '순천', label: '순천' }, { value: '나주', label: '나주' }, { value: '광양', label: '광양' }, // 경상북도 { value: '포항', label: '포항' }, { value: '경주', label: '경주' }, { value: '김천', label: '김천' }, { value: '안동', label: '안동' }, { value: '구미', label: '구미' }, { value: '영주', label: '영주' }, { value: '영천', label: '영천' }, { value: '상주', label: '상주' }, { value: '문경', label: '문경' }, { value: '경산', label: '경산' }, { value: '울릉도', label: '울릉도' }, // 경상남도 { value: '창원', label: '창원' }, { value: '진주', label: '진주' }, { value: '통영', label: '통영' }, { value: '사천', label: '사천' }, { value: '김해', label: '김해' }, { value: '밀양', label: '밀양' }, { value: '거제', label: '거제' }, { value: '양산', label: '양산' }, // 제주특별자치도 { value: '제주', label: '제주' }, { value: '서귀포', label: '서귀포' }, ]; // 날씨 정보 가져오기 const fetchWeather = async () => { try { setLoading(true); setError(null); const data = await getWeather(selectedCity, 'metric', 'kr'); setWeather(data); setLastUpdated(new Date()); } catch (err: any) { console.error('날씨 조회 실패:', err); // 에러 메시지 추출 let errorMessage = '날씨 정보를 가져오는 중 오류가 발생했습니다.'; if (err.response?.status === 503) { errorMessage = 'API 키가 설정되지 않았습니다. 관리자에게 문의하세요.'; } else if (err.response?.status === 401) { errorMessage = 'API 키가 유효하지 않습니다.'; } else if (err.response?.status === 404) { errorMessage = `도시를 찾을 수 없습니다: ${city}`; } else if (err.response?.data?.message) { errorMessage = err.response.data.message; } setError(errorMessage); } finally { setLoading(false); } }; // 초기 로딩 및 자동 새로고침 useEffect(() => { fetchWeather(); const interval = setInterval(fetchWeather, refreshInterval); return () => clearInterval(interval); }, [selectedCity, refreshInterval]); // 도시 변경 핸들러 const handleCityChange = (newCity: string) => { setSelectedCity(newCity); }; // 날씨 아이콘 선택 const getWeatherIcon = (weatherMain: string) => { switch (weatherMain.toLowerCase()) { case 'clear': return ; case 'clouds': return ; case 'rain': case 'drizzle': return ; case 'snow': return ; default: return ; } }; // 로딩 상태 if (loading && !weather) { return (

날씨 정보 불러오는 중...

); } // 에러 상태 if (error || !weather) { return (

{error || '날씨 정보를 불러올 수 없습니다.'}

); } return (
{/* 헤더 */}
도시를 찾을 수 없습니다. {cities.map((city) => ( { handleCityChange(currentValue === selectedCity ? selectedCity : currentValue); setOpen(false); }} > {city.label} ))}

{lastUpdated ? `업데이트: ${lastUpdated.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', })}` : ''}

표시 항목

{weatherItems.map((item) => { const Icon = item.icon; return ( ); })}
{/* 반응형 그리드 레이아웃 - 자동 조정 */}
{/* 날씨 아이콘 및 온도 */}
{(() => { const iconClass = "h-5 w-5"; switch (weather.weatherMain.toLowerCase()) { case 'clear': return ; case 'clouds': return ; case 'rain': case 'drizzle': return ; case 'snow': return ; default: return ; } })()}
{weather.temperature}°C

{weather.weatherDescription}

{/* 기온 - 선택 가능 */} {selectedItems.includes('temperature') && (

기온

{weather.temperature}°C

)} {/* 체감 온도 */} {selectedItems.includes('feelsLike') && (

체감온도

{weather.feelsLike}°C

)} {/* 습도 */} {selectedItems.includes('humidity') && (

습도

{weather.humidity}%

)} {/* 풍속 */} {selectedItems.includes('windSpeed') && (

풍속

{weather.windSpeed} m/s

)} {/* 기압 */} {selectedItems.includes('pressure') && (

기압

{weather.pressure} hPa

)}
); }