본문 바로가기

programming/React

리액트 에러 바운더리를 사용하여 에러 핸들링하기 (+ react-query)

에러 케이스

에러가 발생하는 원인 관점에서 두가지 케이스가 있다.

1. 프론트 에러 : 주로 Type Error, 참조 에러 등

2. 백엔드 API 에러 : HTTP 케이스와 웹사이트 로직에 따라서 세부로 나뉨 (Get / Post / Put / Delete .. 등)

 

처리 방안

1. 프론트 에러

a) axios 요청 전부터 에러 발생 (accessToken 없음) 👉 토큰 없음 메시지 Toast UI 표출 & 로그인 페이지로 이동

b) 참조에러 👉 발생하는 컴포넌트를 ErrorBoundary 로 감싸고 fallBackComponent 처리 + Uncaught Error 메시지 제공

 

2. 백엔드 API 에러

a) GET 조회 결과 빈 데이터 👉 에러로 처리하지 않고, 200 성공에 데이터가 없는 경우 보여줄 컴포넌트로 처리

b) GET 조회 결과 에러 👉 에러 코드 & 메시지와 함께 Retry 버튼 제공

c) GET 조회 할 자원이 없는 데이터 👉 404 페이지

d) PUT, DELETE, POST 실패 👉 에러 코드 & 메시지 Toast UI 표출 및 요청 내용 처리 안함, 페이지 유지

e) 401 에러 👉 접근 권한 없음 문구 노출 & 로그인 페이지로 이동 버튼 제공

f) 404 에러 👉 404 페이지

g) 500 에러 👉 500 페이지

 

코드

(* ErrorBoundary 관련 케이스만)

    • 전체 구조

 

  • page/category.tsx
    • React-Query 의 useQueryErrorResetBoundary 를 사용하여 error 가 발생한 queryFn 을 재실행 트리거 할 수 있는 reset 함수 제공
export default function Category() {
	const { reset } = useQueryErrorResetBoundary();

	 return (
         <ErrorBoundary
            onReset={reset}
            fallbackRender={({ error, resetErrorBoundary }) => (
              <APIErrorFallback error={error} onClickHandler={resetErrorBoundary} />
            )}
          >
            <CategoryListTemplate/>
          </ErrorBoundary>
      )
  }

 

 

  • template/CategoryListTemplate.tsx
export default function CategoryListTemplate({
    const { isLoading, error, data, refetch } = useQuery({
        queryKey: ["category", "list"],
        queryFn: getCategoryList,
        useErrorBoundary: true,
      });

    if (isLoading) return <LoadingSpinner />;

    if (error) throw error; // error boundary 에서 처리

    return (
        <ErrorBoundary FallbackComponent={UncaughtErrorFallback}>
          <CategoryList data={categoryListData} />
        </ErrorBoundary>
    );
}

 

 

  • errors/UncaughtErrorFallback.tsx
import { Card } from "react-bootstrap";
import Flex from "../Flex";

export default function UncaughtErrorFallback({ error }) {
  return (
    <div role="alert">
      <Card className="mt-3 bg-light p-4" style={{ boxShadow: "none" }}>
        <Flex direction="column" alignItems="center">
          <h5 className="mb-2">오류가 발생했습니다.</h5>
          <div className="fs--1 mb-3">관리자에게 문의해주세요.</div>
          <pre className="text-danger">[Client 에러] : {error.message}</pre>
        </Flex>
      </Card>
    </div>
  );
}

 

 

  • errors/APIErrorFallback.tsx
import { Button, Card } from "react-bootstrap";
import Flex from "../Flex";

export default function APIErrorFallback({ error, onClickHandler }) {
  return (
    <div role="alert">
      <Card className="mt-3 bg-light p-5" style={{ boxShadow: "none" }}>
        <Flex direction="column" alignItems="center">
          <h5 className="mb-2">오류가 발생했습니다.</h5>
          <div className="fs--1">잠시 후 다시 시도해주세요.</div>
          <div className="fs--1 mb-2">동일한 문제가 반복될 경우 관리자에게 문의해주세요</div>
          <pre className="text-danger mb-4">
            API 요청 에러 [{error.code}] : {error.message}
          </pre>
          <Button variant="falcon-default" onClick={onClickHandler}>
            Retry
          </Button>
        </Flex>
      </Card>
    </div>
  );
}