행복한 개발자
517
2021-12-31 15:02:29 작성 2021-12-31 15:23:01 수정됨
4
161

리액트 초기값이 없는 reduce에 대한 에러


안녕하세요.

제목처럼 초기값이 없는 배열에 대한 reduce에러가 발생하여 도움 요청 드립니다.

코드는 아래와 같습니다.

봐주셔야 할 부분은 

제 생각에 let srtWeather0200Arr = []; 이 부분 혹은 

let weather10PopAmArr = [];

let weather10PopPmArr = [];

이 부분에서 에러가 나는 것 같은데 찾아봐도 이유를 모르겠습니다.

어떻게 하면 해결 할 수 있을 까요? 도움 부탁 드리겠습니다.

에러메시지는 TypeError: reduce of empty array with no initial value 이것이고,

이런 에러가 항상 나는 것이 아니라 똑같은 코드로도 잘 될 때가 있고, 이렇게 에러가 나는 때가 있습니다.

아직 에러가 어떤 경우에 나는지 정확히 파악하지 못했는데, 

하루를 기준으로 그날 처음 앱을 실행했을 때 나는 것 같아 보입니다.


Weather.js

async function Srt10Weather(apikey, nx, ny) {
  let currentTime = moment().format("HHmm"); //현재 시간분 (HH:24h / hh:12h)
  const baseTime = "0200";
  let baseDate = moment().format("YYYYMMDD");

  if (moment(currentTime).isBetween("0000", "0212", undefined, "[)")) {
    baseDate = moment().subtract(1, "days").format("YYYYMMDD");
  }

  const srt10Url = `http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?serviceKey=${apikey}&numOfRows=1000&pageNo=1&dataType=JSON&base_date=${baseDate}&base_time=${baseTime}&nx=${nx}&ny=${ny}`;
  let weather10Data;
  let srtWeather0200Arr = [];

  console.log("10일 예보용 단기예보 url : " + srt10Url);
  await axios
    .get(srt10Url)
    .then(function (response) {
      weather10Data = response.data.response.body.items.item; //필요한 정보만 받아오기 전부 다 받아 오려면 response.data 까지만 적는다.

      const weather10TmnArr = weather10Data.filter((ele) => {
        return ele.category == "TMN";
      }); // 최저 기온 [tmn]

      const weather10TmxArr = weather10Data.filter((ele) => {
        return ele.category == "TMX";
      }); // 최고 기온 [tmx]

      const weather10SkyOrgArr = weather10Data.filter((ele) => {
        return ele.category == "SKY";
      }); // 날씨 아이콘 [sky]

      let weather10SkyArr = [];
      for (let i = 0; i < 3; i++) {
        weather10SkyArr[i] = getModeValue(getSepArrByDate(weather10SkyOrgArr, baseDate, i));
      }

      const weather10PopOrgArr = weather10Data.filter((ele) => {
        return ele.category == "POP" && Number(ele.fcstValue) >= 40;
      }); // 오전, 오후 강수확률 [popAm, popPm]

      let weather10PopAmArr = [];
      let weather10PopPmArr = [];
      for (let i = 0; i < 3; i++) {
        weather10PopAmArr[i] = Math.max.apply(null, getSepArrByDate(weather10PopOrgArr, baseDate, i, "popAm"));
        weather10PopPmArr[i] = Math.max.apply(null, getSepArrByDate(weather10PopOrgArr, baseDate, i, "popPm"));
      }

      // POP가 중간중간 끼여 있으면 TMN TMX SKY 를 순서대로 쓰기에 불편하기 때문에 배열 뒤로 보내는 코드
      // weather10Data = weather10Data.filter((ele) => ele.category != "POP").concat(weather10Data.filter((ele) => ele.category == "POP"));

      const weather10DataLth = 3; // 단기예보는 3일간의 날씨를 불러오는 데 1일에 TMX, TMN, SKY 는 각각 하나씩 있으므로 3일x3개는 9개. 뒤에 POP가 있을 수 있으므로 배열 길이로 체크하면 안됨.
      for (let i = 0; i < weather10DataLth; i++) {
        srtWeather0200Arr[i] = {
          tmn: Math.round(weather10TmnArr[i].fcstValue),
          tmx: Math.round(weather10TmxArr[i].fcstValue),
          sky: Number(weather10SkyArr[i]),
          popAm: Number(weather10PopAmArr[i]),
          popPm: Number(weather10PopPmArr[i]),
        };
      }

      // console.log("strWeather10Obj = " + JSON.stringify(weather10Data));
    })
    .catch(function (error) {
      srtWeather0200Arr = "";
      console.log("10일 예보용 단기예보 실패 : " + error);
    });
  return { srtWeather0200Arr, baseDate };
}


질문을 다시 보다가 아래 코드에서 에러가 날 수도 있겠다 싶어 추가로 올립니다.

에러가 찍히는 위치는 위 코드 (Weather.js) 입니다.

function WeatherComponent(props) {
  const gridX = props.gridX;
  const gridY = props.gridY;
  const gridXYStr = String(gridX) + String(gridY);
  // const gridYStr = String(gridY);

  const initialState = {
    // [초단기예보 조회용 변수]
    ultSrtWeatherArr: null,

    // [단기 예보 2시 조회용 변수]
    srtWeather0200Arr: null,
  };

  function reducer(state, action) {
    switch (action.type) {
      case "SET_ISLOADING":
        return { ...state, isLoading: false };
      case "SET_LOCATION":
        return {
          ...state,
          latitude: action.latitude,
          longitude: action.longitude,
          gridX: action.gridX,
          gridY: action.gridY,
        };
      case "SET_ADDR_OBJ":
        return { ...state, addrObj: action.addrObj };
      case "SET_ULT_SRT_WEATHER":
        return {
          ...state,
          ultSrtWeatherArr: action.ultSrtWeatherArr,
        };
      case "SET_WEATHER10":
        return {
          ...state,
          srtWeather0200Arr: action.srtWeather0200Arr,
        };
      default:
        throw new Error();
    }
  }

  const [state, dispatch] = useReducer(reducer, initialState);

  /**
   * 초단기 예보 API
   * 사용 컴포넌트 : <WeatherNowComponent>, <WeatherDetailComponent>
   * 사용 데이터 :
   *  <WeatherNowComponent> : SKY (하늘 상태 : 날씨 아이콘), T1H (현재 기온)
   *  <WeatherDetailComponent> : T1H (현재 기온 : 체감온도 구할 때), REH (현재 습도), VEC (풍향), WSD (풍속), SKY (하늘 상태 : 날씨 아이콘),
   *                             PTY (강수 형태 : 날씨 아이콘), LGT (낙뢰 : 날씨 아이콘)
   */
  getUltSrtWeather = async () => {
    let ultSrtWeatherArr;

    try {
      const weatherItem = await AsyncStorage.getItem("@ultSrtWeather5");
      const dateItem = await AsyncStorage.getItem("@ultSrtBaseDate");
      const timeItem = await AsyncStorage.getItem("@ultSrtBaseTime");
      const gridXYItem = await AsyncStorage.getItem("@ultSrtgridXY");
      console.log("초단기 예보 쿠키 baseDate, Time", dateItem, ", ", timeItem);
      console.log("초단기 예보 현재 ultSrtBaseDate, ultSrtBaseTime", ultSrtBaseDate, ", ", ultSrtBaseTime);
      console.log("초단기 예보 쿠키 gridXYItem : ", gridXYItem);
      console.log("초단기 예보 현재 gridXYStr : ", gridXYStr);
      if (ultSrtBaseDate == dateItem && ultSrtBaseTime == timeItem && gridXYStr == gridXYItem && weatherItem !== null) {
        ultSrtWeatherArr = JSON.parse(weatherItem);
      } else {
        ultSrtWeatherArr = await UltSrtWeather(API_KEY, ultSrtBaseDate, ultSrtBaseTime, gridX, gridY);
        if (ultSrtWeatherArr.length > 0) {
          const ultSrtWeather = ["@ultSrtWeather5", JSON.stringify(ultSrtWeatherArr)];
          const ultSrtDate = ["@ultSrtBaseDate", ultSrtBaseDate];
          const ultSrtTime = ["@ultSrtBaseTime", ultSrtBaseTime];
          const gridXY = ["@ultSrtgridXY", gridXYStr];

          await AsyncStorage.multiSet([ultSrtWeather, ultSrtDate, ultSrtTime, gridXY]);
        }
      }
    } catch (e) {
      // error reading value
    }

    dispatch({
      type: "SET_ULT_SRT_WEATHER",
      ultSrtWeatherArr: ultSrtWeatherArr,
    });
  };

  /**
   * 단기 예보 2시 API (최고, 최저기온 불러오기 위해 BaseTime 0200으로 조회)
   * 사용 컴포넌트 : <WeatherNowComponent>, <Weather10Component>, <WeatherClothesComponent>
   * 사용 데이터 :
   *  <WeatherNowComponent> : TMX (최고기온), TMN (최저기온)
   *  <Weather10Component> : TMX (최고기온), TMN (최저기온), SKY (하늘상태)
   *  <WeatherClothesComponent> : TMX (최고기온), TMN (최저기온) : 오늘의 평균 기온 구해서 기온에 알맞은 옷차림 알림
   */
  getSrtWeather0200 = async () => {
    let srtWeather0200Arr, baseDate;

    /**
     * 오전 12시~2시 11분은 날짜가 하루 넘어갔지만 여전히 전날의 데이터를 받아와야 하는 상황이다.
     * 당일 오전 2시 데이터가 오전 2시 11분 이후에 생성되기 때문이다.
     * 하여 해당 시간 사이에는 오늘날짜가 아닌 어제 날짜로 세팅을 바꿔주어야 한다.
     */
    const currentTime = moment().format("HHmm");
    let crntBaseDate = moment().format("YYYYMMDD");
    if (moment(currentTime).isBetween("0000", "0212", undefined, "[)")) {
      crntBaseDate = moment().subtract(1, "days").format("YYYYMMDD");
    }

    try {
      const weatherItem = await AsyncStorage.getItem("@srtWeather02005");
      const dateItem = await AsyncStorage.getItem("@srt0200BaseDate2");
      const gridXYItem = await AsyncStorage.getItem("@srt0200gridXY");
      console.log("10일 예보 쿠키 baseDate", dateItem);
      console.log("10일 예보 현재 crntBaseDate", crntBaseDate);
      console.log("10일 예보 쿠키 gridXYItem : ", gridXYItem);
      console.log("10일 예보 현재 gridXYStr : ", gridXYStr);
      if (crntBaseDate == dateItem && gridXYStr == gridXYItem && weatherItem !== null) {
        srtWeather0200Arr = JSON.parse(weatherItem);
      } else {
        ({ srtWeather0200Arr, baseDate } = await Srt10Weather(API_KEY, gridX, gridY));
        if (checkNotNull(srtWeather0200Arr)) {
          const srtWeather0200 = ["@srtWeather02005", JSON.stringify(srtWeather0200Arr)];
          const srt0200BaseDate1 = ["@srt0200BaseDate2", baseDate];
          const gridXY = ["@srt0200gridXY", gridXYStr];

          await AsyncStorage.multiSet([srtWeather0200, srt0200BaseDate1, gridXY]);
        }
      }
    } catch (e) {
      // error reading value
    }
    dispatch({
      type: "SET_WEATHER10",
      srtWeather0200Arr: srtWeather0200Arr,
    });
  };

  getWeather = async () => {
    getUltSrtWeather();
    getSrtWeather0200();
  };

  useEffect(() => {
    getWeather();
  }, []);

이 코드에서 ultSrtWeatherArr: null, srtWeather0200Arr: null,

두 가지 초기값을 null로 설정한 이유는 자식 컴포넌트에서 ultSrtWeatherArr, srtWeather0200Arr 값을 체크하여 에러 처리를 하게 되는 데, null인 경우는 값이 아직 조회 되기 전인 경우, ""인 경우는 값을 조회하던 중 에러가 난 경우로 로직을 짰습니다. 로직을 조금더 간결하게 하기 위해 초기값을 null로 할당했습니다. 


0
  • 답변 4

  • 페코옹
    1k
    2021-12-31 16:54:48

    나중에 코드 보시고 알아보실 수 있겠어요?;;

    지금 에러 나는거 한 두군데 고민하시는 것 보다

    전체적으로 네이밍 스타일이나 플로우 다시 한 번 살펴보시는게 어떨까 합니다.

    눈에 잘 안 들어오네요 코드가;

  • 행복한 개발자
    517
    2022-01-01 00:50:13

    네 알아볼 수 있습니다.

    제가 에러가 나는 코드 부분을 올리지 않아 이해가 되지 않는 게 당연합니다.

    에러는 잘 해결했습니다

  • Rn개발
    8
    2022-01-01 18:44:22

    null값의 경우 예외를 발생시켜서 값을 불러오지 못해서 오류가 난건지 다른 오류가 났는지 구분을 잘 못해서 저는 사용하지않는걸로 하고있습니다. 빈 스트링과 빈 배열을 초기값으로 주고 길이를 체크하는걸로 로딩을 구분하시는게 낫지않을까요?

  • 행복한 개발자
    517
    2022-01-02 17:11:50

    @Rn개발 님 답변 감사드립니다.

    처음에 그렇게 생각하다가 null값을 사용하는 것으로 로직을 짰는데

    답변을 보고 다시 생각해보니 말씀대로 해보는 것도 괜찮겠다는 생각이 듭니다.

    다시 한번 확인해봐야 되겠네요

  • 로그인을 하시면 답변을 등록할 수 있습니다.