Todo 페이지에서는 가장 먼저 'weather Box' 부분을 작업하였다.
기상청 open API
기상청 open API
먼저 기상청 open API( 기상청단기예보 ((구)동네예보) 활용신청을 했다.
신청하자마자 자동 승인이 되고 해당 데이터를 활용하기 위한 End Point와 인증키를 발급받았다.
기상청 API 는 '초단기실황', '초단기예보', '단기예보', '예보버전'으로 4가지 형식이 있었다.
이 중에서 '날씨 / 강수형태'가 적절하게 나누어져 있다고 판단한 '단기예보'를 활용하기로 했다.
기상청 API는 활용 문서를 다운받아 확인하면서 작업하였다.
serviceKey, base_date, base_time 등 필수로 요청해야하는 데이터들과 나는 데이터를 json으로 받고자 하기 때문에 dataType을 쿼리 문으로 작성하였고 이 후 변동이 생길 시 적용이 편리하도록 쿼리 내용은 모두 변수 처리했다.
useEffect(() => {
fetch(
`${apiUrl}?serviceKey=${serviceKey}&pageNo=${pageNo}&numOfRows=${numOfRows}&dataType=${dataType}&base_date=${baseDate}&base_time=${baseTime}&nx=${nx}&ny=${ny}`
)
.then((response) => response.json())
.then((result) => setWeather(result.response.body.items.item));
}, []);
base_date
와 base_time
base_date
와 base_time
은 조회 시점에 어느 데이터를 활용할 지를 결정을 위한 것이다.
단기예보는 '0200', '0500', '0800', '1100', '1400', '1700', '2000', '2300'으로 1일 총 8회, 3시간 마다 데이터가 업데이트 되고 이 데이터는 10분 후 제공된다.
이 때 base_date
는 '20231121'의 형태로 8자의 string 형식이다.
base_time
은 위에 작성된 8개 중 선택해 조회하고, '0000'부터 '0200' 사이에 데이터를 조회할 경우 하루 전의 '2300' 데이터를 조회해야 한다.
const today = new Date();
const year = today.getFullYear();
const date = today.getDate();
const currentMonth = today.getMonth();
// month: currentMonth가 한자리 숫자일 경우 '0'을 더해주어 포맷과 맞춤
const month = (currentMonth: number) => {
if (currentMonth < 10) {
return "0" + (currentMonth + 1);
} else return currentMonth + 1;
};
const currentHour = today.getHours();
const currentMin = today.getMinutes();
// makeMin: currentMin이 한자리 일 경우 그냥 시간과 더해졌을 때,
// 11시 1분이 '1101'이 아닌 '111'이 되므로 '0'을 더해줌
const makeMin = (currentMin: number) => {
if (currentMin < 10) {
return "0" + `${currentMin}`;
} else {
return currentMin;
}
};
let currentTime = `${currentHour}${makeMin(currentMin)}`;
// 코드 블럭에서 나와서 활용할 수 있도록 미리 선언 !
let getBaseTime: string = "";
const timeInform = (currentTime: string) => {
// 기상청 API 데이터 업데이트 시간
const timeArr = [
"0200",
"0500",
"0800",
"1100",
"1400",
"1700",
"2000",
"2300",
];
const time = parseInt(currentTime);
// timeArr(기상청 데이터 업데이트 시간) 과 현재 시간 비교
for (let i: number = 0; i < timeArr.length; i++) {
// 업데이트 10분 후 조회 가능하므로 +10이 더 되어있다 !
const startTime = i * 300 + 210;
const endTime = startTime + 300;
if (startTime <= time && time < endTime) {
getBaseTime = timeArr[i];
break;
}
getBaseTime = timeArr[7];
}
return getBaseTime;
};
// 0000 시가 지나고 getBaseTime '2300'을 활용하는 경우 전 날의 데이터를 활용
const checkDate = () => {
if (getBaseTime === "2300" && currentTime < 0210) {
return date - 1;
} else return date;
};
const todayInform = `${year}${month(currentMonth)}${checkDate()}`;
❗️ error code '01'
APPLICATION_ERROR( error code: 01 )은 base_date와 base_time 요청 파라미터가 잘못되었을 때 응답 받는 에러이다. 처음에는 이 에러에 대해 정확한 의미를 알지 못해서 엄청 많이 헤매다가.. 구글링을 통해 알게되었다. 어느 지점이 문제인지 알고 나니 빠르게 해결할 수 있었다.
category
카테고리는 처음에 정확한 형태를 이해하지 못해서 매우 헤맸다가, 형태를 이해하면서 그래도 금방 해결했다... 처음에 data가 제대로 넘어오는지만 단순히 확인할 때, 'pageNo'과 'numOfRows'를 모두 '1'로 작성해두었는데 이 'numOfRows' 가 문제가 되었었다.. 'numOfRows'가 늘어나면 category 값들도 전부 넘어오는데, 1만 지정해 놓고 랜덤한 순서로 category가 출력되다 보니 계속 하나의 카테고리만 랜덤하게 넘어오는 형식이 되었다. 그래서 category={category} 형식으로 쿼리도 넘겨보고 하다가 결국 numOfRows 값을 변경하여 전부 받아올 수 있도록 했다.
{ ... category: 'SKY', fcstValue: '1' ...} 형태로 해당 날짜, 해당 시간의 데이터가 각각 카테고리 별로 각각의 객체 형태로 넘어온다.
그런 뒤 weather
(api response가 담겨있는 객체 배열)가 있을 경우,
weather 내의 'category' 키에 사용하고자 하는 'SKY', 'PTY', 'POP', 'TMP' 가 들어 있는 객체를 찾고, 해당하는 객체의 fcstValue 값을 각각의 state에 저장하도록 했다.
const weatherCategory = (weather: [{ [key: string]: string }]) => {
if (weather) {
for (let i = 0; i < weather.length; i++) {
// 하늘상태 (맑음(1), 구름많음(3), 흐림(4))
if (weather && Object.values(weather[i]).includes("SKY")) {
setSky(weather[i]["fcstValue"]);
}
// 강수형태 (없음(0), 비(1), 비/눈(2), 눈(3), 소나기(4))
if (weather && Object.values(weather[i]).includes("PTY")) {
setPty(weather[i]["fcstValue"]);
}
// 강수확률 %
if (weather && Object.values(weather[i]).includes("POP")) {
setPop(weather[i]["fcstValue"]);
}
// 1시간 기온
if (weather && Object.values(weather[i]).includes("TMP")) {
setTmp(weather[i]["fcstValue"]);
}
}
}
};
icons
마지막으로 아이콘은 위에서 저장된 sky와 pty를 활용하여 렌더링 되도록 했다.
PTY( 강수형태 )를 확인하고 '강수형태 없음' 일 경우 SKY를 확인해 맑음, 구름, 흐림 중에 띄울 수 있도록 하고 강수형태가 있을 경우 해당하는 icon을 띄울 수 있도록 했다.
const weatherIcon = (sky: string, pty: string) => {
if (pty === "0") {
if (sky === "1") {
// 맑음
setIcon("/images/icon/weather/clear.svg");
}
if (sky === "3") {
// 구름많음
setIcon("images/icon/weather/cloudy.svg");
}
// ...codes