
기상청 공공 API 쓰는 법
- 행정안전부 공공데이터 포털 접속
https://www.data.go.kr/ - 기상청 단기예보 조회서비스 검색, 오픈 API 목록에 있다
https://www.data.go.kr/data/15084084/openapi.do - 활용 신청 및 승인
- 로그인 / 회원가입
- API 활용 사유만 쓰면 활용신청 가능, 조금 기다리면 바로 승인됨
- 엔드 포인트와 인증키 메모
- API 호출 함수 생성
fetchWeather() 함수를 만들었다 - 날씨 데이터를 표시할 모달 컴포넌트 생성
- 아이콘 클릭시 API 호출 및 모달 표시
현재 위치에 따라 날씨 데이터 가져오기
아래는 내 프로젝트에서 사용된 WeatherModal 컴포넌트 코드로 리액트, 타입스크립트, 테일윈드를 사용하고 있다
주석에 설명을 추가하였다
import React, { useEffect, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faTimes,
faMapMarkerAlt,
faThermometerHalf,
faTint,
faWind,
} from "@fortawesome/free-solid-svg-icons";
interface WeatherModalProps {
isOpen: boolean;
onClose: () => void;
}
const WeatherModal: React.FC<WeatherModalProps> = ({ isOpen, onClose }) => {
const [weatherData, setWeatherData] = useState<any>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const [location, setLocation] = useState<{ nx: number; ny: number } | null>(
null
);
useEffect(() => {
if (isOpen) {
navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
const { nx, ny } = convertToGridCoords(latitude, longitude);
setLocation({ nx, ny });
},
(error) => {
console.error("Error getting location:", error);
setError("위치를 가져올 수 없습니다.");
}
);
}
}, [isOpen]);
useEffect(() => {
if (isOpen && location) {
fetchWeather(location.nx, location.ny);
}
}, [isOpen, location]);
const fetchWeather = async (nx: number, ny: number) => {
setIsLoading(true);
setError(null);
try {
const apiKey =
"vk4aWLTlPOiSCrGPCqW9+RPDwCEbd2qZroyXnqkjhfKAQM15ca8W2DHua28iBIE0OaNWNEPaaAR2suzllAcotg==";
const encodedKey = encodeURIComponent(apiKey);
const today = new Date(); // 오늘의 날짜 객체 생성
const hour = today.getHours(); // 현재 시각 불러오기
let formattedTime = hour.toString().padStart(2, "0") + "00"; // API에서 요구하는 HH00 형식으로 변환
const formattedDate = today.toISOString().slice(0, 10).replace(/-/g, "");
const baseUrl =
"https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst";
const queryParams = `numOfRows=10&pageNo=1&base_date=${formattedDate}&base_time=${formattedTime}&nx=${nx}&ny=${ny}&dataType=JSON`;
const url = `${baseUrl}?serviceKey=${encodedKey}&${queryParams}`;
const response = await fetch(url);
const data = await response.json();
if (data.response && data.response.body && data.response.body.items) {
const items = data.response.body.items.item;
const processedData = processWeatherData(items);
setWeatherData(processedData);
} else {
throw new Error("예상치 못한 API 응답 구조");
}
} catch (error) {
console.error("날씨 데이터를 가져오는 중 오류 발생:", error);
setError(
error instanceof Error
? error.message
: "알 수 없는 오류가 발생했습니다."
);
} finally {
setIsLoading(false);
}
};
const processWeatherData = (items: any[]) => {
const processed: any = {};
items.forEach((item) => {
switch (item.category) {
case "T1H": // 기온
processed.temperature = parseFloat(item.obsrValue);
break;
case "RN1": // 강수량
processed.rainfall = parseFloat(item.obsrValue);
break;
case "REH": // 습도
processed.humidity = parseFloat(item.obsrValue);
break;
case "WSD": // 풍속
processed.windSpeed = parseFloat(item.obsrValue);
break;
}
});
return processed;
};
// 위치 변환 함수 : 사용자의 현재 위치(위도 및 경도)를 기상청 API가 요구하는 격자 좌표(nx, ny)로 변환하는 함수
const convertToGridCoords = (lat: number, lon: number) => {
return { nx: 55, ny: 127 }; // 격자 좌표로 변환하는 예시 좌표 반환
};
if (!isOpen) {
return null;
}
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white p-6 rounded-lg max-w-sm w-full">
<div className="flex justify-between items-center">
<h2 className="text-xl font-bold">현재 날씨</h2>
<button
onClick={onClose}
className="text-gray-500 hover:text-gray-700"
>
<FontAwesomeIcon icon={faTimes} />
</button>
</div>
<FontAwesomeIcon icon={faMapMarkerAlt} className="mr-2" />
<span>현재 위치</span>
{isLoading ? (
<p>날씨 데이터를 불러오는 중...</p>
) : error ? (
<p className="text-red-500">오류: {error}</p>
) : weatherData ? (
<div>
<div className="flex justify-between gap-2 mt-2">
<div className="flex-grow basis-0 bg-blue-100 p-3 rounded-lg">
<FontAwesomeIcon
icon={faThermometerHalf}
className="mr-2 text-blue-500"
/>
<span className="font-bold">{weatherData.temperature}°C</span>
</div>
<div className="flex-grow basis-0 bg-blue-100 p-3 rounded-lg">
<FontAwesomeIcon icon={faTint} className="mr-2 text-blue-500" />
<span className="font-bold">{weatherData.humidity}%</span>
</div>
<div className="flex-grow basis-0 bg-blue-100 p-3 rounded-lg">
<FontAwesomeIcon icon={faWind} className="mr-2 text-blue-500" />
<span className="font-bold">{weatherData.windSpeed}m/s</span>
</div>
{weatherData.rainfall > 0 && (
<div className="bg-blue-100 p-3 rounded-lg">
<FontAwesomeIcon
icon={faTint}
className="mr-2 text-blue-500"
/>
<span className="font-bold">{weatherData.rainfall} mm</span>
</div>
)}
</div>
</div>
) : (
<p>날씨 데이터가 없습니다.</p>
)}
</div>
</div>
);
};
export default WeatherModal;
근데 이렇게 하면 날씨가 개떡같이 나올 것이다
현재 위치를 00시 00구 00동으로 나타내려면 역지오코딩(Reverse Geocoding)이라는 기술을 사용해야한다
그건 다음 포스팅에서...