[React] 음식 주문 앱 연습: handling of loading and error

summereuna🐥·2023년 5월 29일
0

React JS

목록 보기
61/69

로딩

const AvailableMeals = () => {
  const [meals, setMeals] = useState([]);
  //❗️ 로딩 state 추가
  const [isLoading, setIsLoading] = useState(true);
  
  //...
  
  //❗️ 로딩 중인 경우
  //mealsList 매핑하기 전에 확인하여 로딩중일 경우 아래 섹션 리턴하기
  if (isLoading) {
    return (
      <section className={classes.mealsLoading}>
        <p>Loading...</p>
      </section>
    );
  }

  const mealsList = meals.map((meal) => (
    <MealItem
      id={meal.id}
      key={meal.id}
      name={meal.name}
      description={meal.description}
      price={meal.price}
    />
  ));

  return (
    //...

오류 처리

const AvailableMeals = () => {
  const [meals, setMeals] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  //❗️ error state 추가
  //초기값 null 세팅하여 처음에 값을 갖지 않는다는 목적을 더 분명히 할 수 있음
  //혹은 그냥 비워둬도 됨 (undefined)
  const [httpError, setHttpError] = useState(null);

  useEffect(() => {
    const fetchMeals = async () => {
      const response = await fetch(
        "https://react-http-35c4a-default-rtdb.firebaseio.com/meals.json"
      );

      //❗️ GET요청 후 응답에 대해 오류 여부 확인
      if (!response.ok) {
        throw new Error("Something went wrong!");
        //throw new Error() 는 줄이 실행되지 않았다는 것을 의미함
      }
      const responseData = await response.json();
      
      const loadedMeals = [];

      for (const key in responseData) {
        loadedMeals.push({
          id: key,
          key: key,
          name: responseData[key].name,
          description: responseData[key].description,
          price: responseData[key].price,
        });
      }
      setMeals(loadedMeals);
      setIsLoading(false);
    };

    //❗️ try-catch는 호출하는 곳에서
    try {
      fetchMeals();
    } catch {
      setIsLoading(false);
      setHttpError(error.message);
    };
  }, []);
    
    //...

  if (httpError) {
    return (
      <section className={classes.mealsError}>
        <p>{httpError}</p>
      </section>
    );
  }

 //...

이렇게 작성한 후 엔드포인트를 살짝 바꿔서 일부러 오류가 나게 해보았는데 콘솔에 에러만 뜨고 에러 메시지가 렌더링되지 않는다.
그 이유는 fetchMeals이 async 함수이기 때문에 프로미스를 반환하는데, 프로미스 내부에서 오류가 발생하면 프로미스가 거부되기 때문이다.

따라서 try-catch로 그것을 래핑할 수 없다.
그러면 아래처럼 하면 되지 않을까?

 useEffect( async () => {
    const fetchMeals = async () => {
      //...
    };

    //❗️ async-await 으로 감싸기..
    try {
      await fetchMeals();
    } catch {
      setIsLoading(false);
      setHttpError(error.message);
    };
  }, []);

async로 useEffect함수 전체를 감싸고 await fetchMeals();을 적용하여, useEffect 함수를 async 함수로 전환하여 사용하면 되지 않을까 생각할 수도 있지만 그렇게 못한다 ^ㅇ^~
이펙트 함수는 자체는 async로 감쌀수 없다. 그래서 fetchMeals도 따로 함수를 만들어서 호출해서 사용하고 있다..

📝 함수로 만들어 호출하여 사용

fetchMeals 함수 따로 만들어 호출해서 사용한 것처럼 별도의 함수를 만들어 호출하여 try-catch 구문과 async-await을 함께 입력하는 방법이 있다.

//...
  const errorHandle = async () => {
    try {
      await fetchMeals();
    } catch (error) {
      setIsLoading(false);
      etHttpError(error.message);
    }
  };

  errorHandle();
}, []);

이런식으로 말이다. 이렇게 하면 잘 작동한다.

또는 프로미스 내부에서 발생하는 오류를 처리하기 위해 프로미스에 catch()메서드를 추가하여 사용할 수도 있다.

📝 프로미스 내부에서 발생하는 오류를 처리하는 방법

//...
 fetchMeals().catch((error) => {
   setIsLoading(false);
   setHttpError(error.message);
  });
}, []);
profile
Always have hope🍀 & constant passion🔥

0개의 댓글