React의 선언형 에러처리(Error Boundary)

이수빈·2023년 3월 7일
1
post-thumbnail
  • 프로젝트에서 Suspense와 Error Boundary를 이용한 선언적 Error, Loading을 처리한 부분과

  • React.lazy를 사용해 초기 페이지 로드시간을 감소시킨 글을 작성하려고 한다.

  • 이번글에서는 Errorboundary에 대해 중점적으로 다루고자 한다.

React의 선언형 프로그래밍

  • 선언형 프로그래밍이란, 프로그램이 어떤 방법으로 해야 하는지를 나타내기보다 무엇과 같은지를 설명하는 경우에 맞춰 코드를 작성하는 방법이다. (위키백과 출처)

  • React는 기본적으로 선언형 프로그래밍 방식을 사용한다. 즉 컴포넌트별 코드의 재사용, UI를 화면에 그리는(render)로직을 분리함으로써, 코드가 간결해지고 한눈에 컴포넌트 구성을 알아볼 수 있다.


명령형 프로그래밍 방식 에러처리와 로딩처리

  • Suspense와 Error Boundary를 적용하기 전에는 명령형 프로그래밍 방식으로 loading 상태와 error 상태를 처리했다.
const FoodDetail = () => {
  const scrollRef = useRef<HTMLElement>(null);
  const shopId = Number(useParams().id);

  const { isCommentLoading, isCommentError, commentState } = useCommentQuery(shopId);
  const { isMenuLoading, isMenuError, menuState } = useMenuQuery(shopId);
  const { isShopLoading, isShopError, shopState } = useShopQuery(shopId);

  if (isCommentLoading || isMenuLoading || isShopLoading) {
    return <S.CommentContainer>로딩중</S.CommentContainer>;
  }

  if (isCommentError || isMenuError || isShopError) {
    return <S.CommentContainer>Error 발생</S.CommentContainer>;
  }
  
  return ...
  • 이는 몇가지 단점이 존재했는데, 명령형으로 loading과 Error 상태를 처리하게 되면 컴포넌트별로 각각 모든 loading과 error처리를 해줘야했다.

  • 또한 이는 React가 추구하는 선언형 프로그래밍에도 적합하지 않은 방식이다.

  • Loading과 Error 상태를 선언적으로 처리하는데 React에서는 Suspense와 Error Boundary라는 기능을 제공한다.

Error Boundary란?

  • React 공식문서에 따르면, Error Boundary는 하위 컴포넌트 트리의 어디에서든 자바스크립트 에러를 기록하며 깨진 컴포넌트 트리 대신 폴백 UI를 보여주는 React 컴포넌트이다.

  • 즉, Error Boundary로 감싼 하위 컴포넌트에서 try.. catch 처럼 error를 감지할 수 있으며, 만약 에러가 발생한다면 깨진 컴포넌트 대신
    Error Boundary에서 fallback으로 넘겨준 UI를 보여주는 render 하는 방식이다.

  • 여기서 ErrorBoundary 컴포넌트는 state에 error가 발생했는지를 식별 할 수 있는 값을 필요로 한다.

  • 생명주기 메소드인 getDerivedStateFromError나 componentDidCatch 중 하나를 정의하면 클래스형 컴포넌트를 Error를 catch할 수 있는 Error Boundary로 사용 할 수 있다.

  • DerivedStateFromError가 Error를 catch하면, 컴포넌트 내 state의 hasError 프로퍼티가 true로 변한다.

  • Error가 존재한다면 fallback UI를, 아니라면 child component를 렌더링한다.

import React, { Component, ErrorInfo, ReactNode } from 'react';
import { FlexContainer } from '../styles/GlobalStyle';

interface Props {
  children?: ReactNode;
}

interface State {
  hasError: boolean;
}

class ErrorBoundary extends Component<Props, State> {
  public state: State = {
    hasError: false,
  };

  public static getDerivedStateFromError(_: Error): State {
    return { hasError: true };
  }

  public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Uncaught error:', error, errorInfo);
  }

  public render() {
    if (this.state.hasError) {
      return <FlexContainer>ErrorBoundary로 에러처리하기</FlexContainer>;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;
  • ErrorBoundary는 Suspense와 함께 선언적으로 컴포넌트 상태를 처리 할 수 있다. 컴포넌트별 loading과 Error 처리 로직을 한곳에 모을 수 있기 때문이다.

  • react Query를 사용할 때, Suspense, ErrorBoundary로 비동기처리 상태 처리에 대한 작업을 하려면 쿼리 옵션을 변경해주어야 한다.

//Router.tsx


const Router = () => {
  return (
    <BrowserRouter>
      <ErrorBoundary>
        <Suspense fallback={<Spinner />}>
          <Routes>
            <Route path="/" element={<MainPage />} />
            <Route path="/login" element={<Login />} />
            <Route path="/register" element={<Register />} />
            <Route path="/foodList" element={<FoodList />} />
            <Route path="/foodList/:id" element={<FoodDetail />} />
            <Route path="/admin" element={<Admin />} />
            <Route path="/mypage" element={<MyPage />} />
            <Route path="*" element={<NotFound />} />
          </Routes>
        </Suspense>
      </ErrorBoundary>
    </BrowserRouter>
  );
};

export default Router;
export const useCommentQuery = (shopId: number) => {
  const {
    isLoading: isCommentLoading,
    isError: isCommentError,
    data: commentState,
    isSuccess,
  } = useQuery<Tcomment[], AxiosError>(['comment', shopId], () => fetchComments(shopId), {
    suspense: true,
    useErrorBoundary: true,
  });

  return { isCommentLoading, isCommentError, commentState, isSuccess };
};

Error Boundary 특이점

  • Error Boundary는 이벤트 핸들러 내에서 발생한 Error는 포착하지 못한다. 따라서 이벤트 핸들러 내에서는 try.. catch문을 통해 error를 따로 handling 해줘야 한다.

  • Error boundary 일시적인 api호출 관련 에러를 처리할때나 url의 path variable이나 query parameter를 찾을 수 없는 에러를 공통적으로 처리할 때 유용한 것 같다.

  • 아직 에러 상태에 따른 상황별 에러처리나, Error가 발생했을때 reset과정을 거쳐서 api를 재호출하는 것 등 에러상태에 따른 에러핸들링은 구현하지 못했다.

ref) 공식문서 : https://ko.reactjs.org/docs/error-boundaries.html

profile
응애 나 애기 개발자

0개의 댓글