프로젝트

문제해결_ 공공 데이터의 배신 ( 숫자여야만 하는데…)

warrior.p 2024. 3. 12. 16:59

 

  • 간혹 실시간 데이터나 과거 데이터의 일부지역 의 미세먼지 값이 null 또는 ‘ - ‘로 들어올때가 있어서 그래프와 계산 로직에 악영향을 준다. 그래서 실시간 데이터 값이 숫자가 아닌 경우는 다른 지역의 데이터로 대체해주는 것을 피할 수 없었다.
  • 실시간 미세먼지 수치에서는 일부지역에서 숫자가 아닌 값이 들어와서 그럴때는 강릉의 value를 넣어줬다.
const { data, isError, isLoading, refetch } = useQuery(
    'dustData',
    fetchDustData,
    {
      // onSuccess ->useQuery에서 사용함. 비동기 요청 성공시 실행되는 콜백함수 정의!
      onSuccess: data => {
        // 데이터 fetch 성공 시, Recoil Atom 업데이트, 5개 지역 로컬스토리지 저장
        if (data && data.length > 0) {
          const fiveSpot = filterGangwonDustData(data);

          // 데이터가 제대로 나지 않을 경우 강릉 데이터로 교체
          const gangneungData = fiveSpot.find(([name]) => name === '주문진읍');
          // 강릉 데이터가 없을 수 있으므로 없으면 0을
          const gangneungValue = gangneungData ? gangneungData.value : '0';

          // 각각의 recoil atom 업데이트
          fiveSpot.forEach(([name, value]) => {
            // 데이터의 value가 숫자가 아닐 경우
            const replaceValue = isNaN(parseInt(value, 10))
              ? gangneungValue
              : parseInt(value, 10);

            switch (name) {
              case '고성(DMZ)':
                setGoseongCurrent([name, replaceValue]);
                break;
              case '양양읍':
                setYangyangCurrent([name, replaceValue]);
                break;
              case '평창읍':
                setPyeongchangCurrent([name, replaceValue]);
                break;
              case '주문진읍':
                setGangneungCurrent([name, replaceValue]);
                break;
              case '금호동':
                setSokchoCurrent([name, replaceValue]);
                break;
              default:
                break;
            }
          });
          const bestSpot = findGoodSpot(fiveSpot);
          setCurrentSpot(bestSpot);
  • 실시간의 경우 과거 데이터를 찾기가 애매하여 다른 지역의 데이터로 대체를 하였는데, 과거 데이터의 경우 다른지역의 데이터가 아닌 이전의 숫자값으로 제대로 들어온 해당 지역의 데이터로 대체 해주었다.
// 데이터가 숫자가 아닐 경우 정상적인 pm10Value의 값을 찾는 로직
  const findLastValue = data => {
    for (let i = data.length - 1; i >= 0; i--) {
      const value = parseInt(data[i].pm10Value, 10);
      if (!isNaN(value)) {
        return data[i].pm10Value;
      }
    }
    // 도저히 안되면 그냥 0...
    return '0';
  };

const fetchDataQueries = Object.entries(locationAtoms).map(([msrstnName]) => {
    return {
      queryKey: ['pastDustData', msrstnName],
      queryFn: async () => {
        // 로컬 스토리지에서 데이터 검증
        const cachedData = loadDataWithDailyCheck(msrstnName);
        // 유효한 캐시된 데이터 반환, API 호출 없이 해당 데이터 반환
        if (cachedData.isValid) {
          return cachedData.data;
        } else {
          // API 호출하여 새로운 데이터 가져오기
          try {
            const newData = await fetchPastDustData(
              getSevenDaysBeforeDate(new Date()),
              getYesterdaysStringDate(new Date()),
              msrstnName
            );
            // console.log(newData);
            return newData;
          } catch (error) {
            console.error(`Error fetching data for ${msrstnName}:`, error);
            // 에러를 다시 throw하여 React Query가 캐치할 수 있도록 함
            throw error;
          }
        }
      },
      onSuccess: data => {
        //  데이터 fetch 성공 시, Recoil Atom 업데이트
        if (data && data.length > 0) {
          let pastDustHistory = data.map(item => {
            const pm10Value = parseInt(item.pm10Value, 10);
            return {
              ...item,
              pm10Value: isNaN(pm10Value)
                ? findLastValue(data)
                : item.pm10Value,
            };
          });
          pastDustHistory =filterLocationInfo(pastDustHistory) 
          //로컬에 저장
          saveToLocalStorage(msrstnName, pastDustHistory);

          console.log(pastDustHistory);
          // 각각의 recoil atom 업데이트
          switch (msrstnName) {
            case '고성(DMZ)':
              setGoseongPastState(pastDustHistory);
              break;
            case '양양읍':
              setYangyangPastState(pastDustHistory);
              break;
            case '평창읍':
              setPyeongchangPastState(pastDustHistory);
              break;
            case '주문진읍':
              setGangneungPastState(pastDustHistory);
              break;
            case '금호동':
              setSokchoPastState(pastDustHistory);
              break;
            default:
              break;
          }
        }
      },
    };
  });

  useQueries(fetchDataQueries);