Todo Weather

Yeonn·2023년 11월 21일
0

Project_Todo

목록 보기
3/4
post-thumbnail

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_datebase_time

base_datebase_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

0개의 댓글