ErrorBoundary 실 사용 예시

Sheryl Yun·2023년 12월 23일
0

React.js

목록 보기
22/24

참고 자료

React의 Error Boundary를 이용하여 효과적으로 에러 처리하기 (22.11 작성됨)

개념과 특징

  • React 16에서 도입
  • 쉽고 효과적인 에러 처리에 사용됨
  • 특히 React에서 가장 흔한 상황 중 하나인 비동기 호출에 대한 에러 처리가 용이

카카오페이지에서 사용하는 방식

  • Next.js 12를 쓰고 있어서 React 18로 업데이트하지 못함
    • 대신 Fetcher 컴포넌트 사용

구현된 2단계

  • Fetcher 컴포넌트로 API를 호출하여 데이터를 가져옴
  • Error Boundary를 사용하여 Fetcher 컴포넌트에서 던진 에러를 처리

Fetcher 컴포넌트를 만든 이유
Suspense 컴포넌트에서 힌트를 얻었고, API 호출 및 상태를 관리하는 로직을 선언형으로 사용하기 위함

기존에 사용했던 에러 처리 방식

  • /products API를 호출하여 상품 리스트를 보여주는 페이지
  • 에러 상황: 예상치 못한 API 서버 에러 상황 또는 클라이언트에서 API를 잘못 호출한 경우
    => 페이지를 제대로 그리지 못하게 됨
  • 위 에러 상황을 해결하기 위한 코드
function ProductListPage() {
  return (
    <ProductListFetcher>
      <ProductListContainer />
    </ProductListFetcher>
  );
}

// Fetcher 컴포넌트에서 dispatch와 selector(redux 코드)를 처리
function ProductListFetcher({ children }) {
  const dispatch = useDispatch();
  const { error } = useSelector(state => state.productList);

  useEffect(() => {
    // fetchProductList:
    // GET /products 요청 후 pending/success/error 액션을 dispatch하는 함수
    dispatch(fetchProductList);
  }, []);

  // redux store에서 데이터를 가져오는 데 문제가 발생하면 렌더링
  if (error) {
    return <div>문제가 발생했습니다.</div>;
  }
  
  // 에러 없이 성공했을 때 렌더링
  return children;
}

function ProductListContainer() {
  const { data } = useSelector(state => state.productList);

  return (
    <div>
      {data?.map(item => (
        <Product title={item.title} rate={item.rate.toFixed(1)} />
      ))}
    </div>
  );
}

위 방식에서 고민되었던 문제

에러가 발생할 경우 에러 메시지를 보여주는 컴포넌트를 렌더링하는 식으로 에러 관리

고민 4가지

  • React에서 선언적으로 에러 처리를 하는 방법은 없을까?
    • cf. 비동기 동작(API 호출)을 선언적으로 제어하기 위한 Fetcher 컴포넌트
  • 공통 처리할 수 있는 에러를 한 곳에서 처리할 수 있지 않을까?
    • 서버 점검, 네트워크 에러 등
  • 에러 발생 시 보여주는 컴포넌트에서 유저가 API 호출을 재시도하여 에러를 리셋할 수 있는 트리거 장치를 두는 건?
    • for 더 나은 사용자 경험
  • 렌더링 중에 TypeError와 같이 예상치 못한 런타임 에러가 발생했을 때
    • 예: 위 예제 코드에서 API 응답 값에 rate의 값은 non-nullable이지만 알 수 없는 이유로 서버에서 nullable한 값을 주고 있음
      => React 트리 전체가 마운트가 해제되어 유저가 빈 화면을 볼 수 있음

Error Boundary를 도입하여 에러 처리하기

React의 Error Boundary 개념을 이용하여 위에서 고민한 내용을 해결할 수 있음
=> 하위 컴포넌트 트리에서 발생한 에러를 잡아서 선언적으로 처리하는 컴포넌트

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    // 에러 발생 시 fallback UI 렌더링
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

<ErrorBoundary>
  <ProductListFetcher>
    <ProductListContainer />
  </ProductListFetcher>
</ErrorBoundary>;
  • Error Boundary는 렌더링 중에 하위 트리에서 throw한 error를 catch
  • 해당 Error Boundary에서 에러 처리를 실패하여 re-throw될 경우 가장 가까운 Error Boundary에서 catch

=> Error Boundary를 이용하여 에러를 선언적으로 해결하고 렌더링(런타임) 중에 발생한 TypeError도 처리

결과

  • Fetcher가 에러를 throw만 하면, Error Boundary로 제한된 에러 영역에서 유저에게 ‘다시 시도’ 버튼을 노출
  • 이 버튼으로 error가 리셋되고 Fetcher가 다시 마운트되어 API 요청을 다시 진행
    => 이 과정을 통해 사용자는 에러 발생 시에도 에러 외의 영역의 컨텐츠를 볼 수 있고 에러 영역은 '다시 시도' 버튼을 눌러 컨텐츠를 다시 요청할 수 있음
profile
데이터 분석가 준비 중입니다 (티스토리에 기록: https://cherylog.tistory.com/)

0개의 댓글