🌱 ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [WizSched] QueryErrorResetBoundary 적용, 에러 처리
    Front-End/Project 2024. 4. 2. 22:46
    🍀 목차
    설계
    QueryErrorResetBoundary?
    구현

     

     

    설계

     개발하며 마주친 401, 403 등의 에러들을 어떻게 처리할지 고민했다. 사용자 입장에서 어떤 에러가 발생했는지 알 수 있게 최대한 상수화해 보자는 생각이 들었다.

     

    현재의 에러 메시지. 정확히 어떤 에러가 발생했는지는 알 수없다.
    정확히 어떤 에러가 발생했는지 보여주고 싶었다.

     

     작성은 Google Calendar 가이드 API 오류 처리  항목을 참고했다. 401, 403 에러인 경우 재로그인을 해야 하므로 redirect 변수를 true로 주었다.

     

    Google Calendar에서 제공하는 API 오류 처리 가이드
    API 오류 처리 가이드

     

    const ERRORS: ErrorMap = {
      400: {
        message: '잘못된 요청입니다.💦',
        redirectState: {
          redirect: false,
        },
      },
      401: {
        message: '잘못된 인증 정보입니다. 재로그인 해주세요.💦',
        redirectState: {
          redirect: true,
          path: RoutePath.LOGIN,
        },
      },
      
      // ...
      
        0: {
        message:
          '예상치 못한 오류가 발생했습니다. 계속해서 같은 문제 발생 시, 개발자에게 문의해주세요.🐛',
        redirectState: {
          redirect: false,
        },
      },
    } as const;

     

     그 외 예상치 못한 오류는 key값 0으로 매치시켰다.

     

     

     

    QueryErrorResetBoundary?

    문서

     

     TanStack Query의 QueryErrorResetBoundary는 쿼리에 suspense를 사용하는 경우 오류 발생 후 렌더링 시 쿼리를 다시 시도할 수 있다. 사용법은 아래와 같다.

    import { QueryErrorResetBoundary } from '@tanstack/react-query'
    import { ErrorBoundary } from 'react-error-boundary'
    
    const App: React.FC = () => (
      <QueryErrorResetBoundary>
        {({ reset }) => (
          <ErrorBoundary
            onReset={reset}
            fallbackRender={({ resetErrorBoundary }) => (
              <div>
                There was an error!
                <Button onClick={() => resetErrorBoundary()}>Try again</Button>
              </div>
            )}
          >
            <Page />
          </ErrorBoundary>
        )}
      </QueryErrorResetBoundary>
    )

     

    ErrorFallback 컴포넌트가 있기 때문에 위 예시코드의 fallbackRender 프로퍼티를 FallbackComponent로 바꿔주었다. 

     

    구현

     기존 CalendarSelector에 QueryErrorResetBoundary를 적용시킨다.

    import { QueryErrorResetBoundary } from '@tanstack/react-query';
    import { Suspense } from 'react';
    import { ErrorBoundary } from 'react-error-boundary';
    import ErrorFallback from '@/components/fallback/ErrorFallback';
    import LoadingFallback from '@/components/fallback/LoadingFallback';
    
    // ...
    
    const CalendarSelector = () => {
      return (
        <div className="py-4">
          <QueryErrorResetBoundary>
            {({ reset }) => (
              <ErrorBoundary onReset={reset} FallbackComponent={ErrorFallback}>
                <Suspense fallback={<LoadingFallback />}>
                  <CalendarDropDown />
                </Suspense>
              </ErrorBoundary>
            )}
          </QueryErrorResetBoundary>
        </div>
      );
    };
    
    export default CalendarSelector;

     

     

     에러 텍스트만 띄우던 ErrorFallback도 개선한다.

    props로 전달받은 error를 만들어둔 에러 상수 객체와 연결, resetErrorBoundary를 재시도 버튼 클릭 시 onClick 이벤트로 연결해 준다.

     

    import { FallbackProps } from 'react-error-boundary';
    import { useNavigate } from 'react-router-dom';
    import Button from '@/components/button/Button';
    import ERRORS from '@/constants/errors';
    import useSupabaseAuth from '@/hooks/useSupabaseAuth';
    
    const ErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => {
      const navigate = useNavigate();
      const { logOutWithGoogle } = useSupabaseAuth();
    
      const statusCode = error.response.status || 0;
      const errorMessage = ERRORS[statusCode].message;
      const redirectState = ERRORS[statusCode].redirectState;
    
      const handleLogoutAndRedirect = (path: string) => {
        logOutWithGoogle();
        navigate(`/${path}`);
      };
    
      return (
        <>
          <p>{errorMessage}</p>
          {redirectState.redirect ? (
            <Button
              variant="contained"
              color="secondary"
              onClick={() => handleLogoutAndRedirect(redirectState.path)}
            >
              페이지 이동
            </Button>
          ) : (
            <Button variant="contained" color="error" onClick={resetErrorBoundary}>
              재시도
            </Button>
          )}
        </>
      );
    };
    
    export default ErrorFallback;

     

     

    404 에러의 경우
    404 에러의 경우

     

    401 or 403 에러의 경우
    401 or 403 에러의 경우

     

     const handleLogoutAndRedirect = (path: string) => {
        logOutWithGoogle();
        navigate(`/${path}`);
      };

     

    재로그인이 필요한 경우는 버튼 클릭 시에 로그아웃 후 로그인 페이지로 이동시켰다.

     문서에서는 데이터에 대한 응답일 경우 redirect 사용을 권장하지만, 해당되지 않는다고 생각하여 useNavigate hook을 사용하였다. 함수 이름을 handleLogoutAndNavigate로 해야 하나 했지만 상수에서 redirectState로 관리해서... Redirect를 붙이기로 결정했다.

     

     

    참고자료 

    https://developers.google.com/calendar/api/guides/errors?hl=ko

    https://reactrouter.com/en/main/hooks/use-navigate

    https://tanstack.com/query/latest/docs/framework/react/reference/QueryErrorResetBoundary

    https://www.npmjs.com/package/react-error-boundary

    댓글

🍀 Y0ungZ dev blog.
스크롤 버튼