React | Error Boundary로 에러 처리하기

이동욱·2024년 1월 16일
0
post-thumbnail

프로그래밍을 하면서 에러가 없게하는것은 매우 중요하다. 하지만 에러는 예상치 못하게 생길수도 있고 지금은 괜찮아도 나중에는 에러가 발생할수도 있다. 그렇기 때문에 에러를 방지하는것 만큼 내결함성도 중요하다.

react에서는 이런 내결함성을 error boundary를 통해 구현 할 수 있다.

error boundary는 React 16에 도입된 개념으로 애플리케이션 렌더링중 오류가 발생하면 해당 UI를 지우는 것이 아닌 fallback UI를 그리며 자바스크립트 에러를 기록할 수 있게 해준다.


ErrorBoundary 구현하기

ErrorBoundary를 구현하는데는 static getDerivedStateFromError() 와 componentDidCatch() 두 생명주기에 대한 이해가 필요하다.

  • static getDerivedStateFromError(error) 하위 자손 컴포넌트에서 오류가 발생했을 때 호출되는 생명주기. 오류를 파라미터로 전달 받고 갱신된 state를 반드시 반환해야한다.
  • componentDidCatch(error,info) 자식 컴포넌트에서 오류가 발생 했을때 호출되며 커밋단계에서 호출함으로 부수효과를 발생시켜도 된다.
    • error : 발생한 오류
    • info : 오류 발생시킨 컴포넌트 정보포함 객체 (componentStack 에 담겨있다.)

위의 두 생명주기는 class컴포넌트에서만 사용이 가능하다. 함수형으로 구현하고 싶다면 react-error-boundary를 사용하면 구현할 수 있다.

import React from "react";

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

  static getDerivedStateFromError(error) {
    console.log("---------------  getDerivedStateFromError   ---------------");
    console.log(error);
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    console.log("---------------  componentDidCatch   ---------------");
    console.log(error, info.componentStack);
  }

  render() {
		//hasError값이 true로 바뀌면 fallback를 렌더한다
    if (this.state.hasError) {
      return this.props.fallback;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

위와 같이 생성한 ErrorBoundary는 아래와 같이 자식 컴포넌트를 랩핑해 사용한다.

function App() {
  return (
    <>
      {["1", "11"].map((userId) => {
        return (
          <ErrorBoundary key={`user_${userId}`} fallback={<h2>Error...</h2>}>
            <Suspense fallback={<h2>Loading...</h2>}>
              <User resource={fetchUser(userId, 3000)} />
            </Suspense>
          </ErrorBoundary>
        );
      })}
    </>
  );
}

export default App;


ErrorBoundary를 활용하면 Suspense와 같이 선언적 프로그래밍이 가능해 더 리액트 스러운 코드 관리가 가능하다


ErrorBoundary 실습코드

React-Suspense에서 사용한 코드에 이어 작업했습니다.(전체코드)

function fetchUser(userId, delay = 0) {
  let user = null;
  let isError = false;

  const suspender = fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
    .then((response) => {
      if (!response.ok) {
        throw new Error("Failed to fetch data");
      }
      return response.json();
    })
    .then((data) => {
      setTimeout(() => {
        user = data;
      }, delay);
    })
    .catch((error) => {
      setTimeout(() => {
        return (isError = true);
      }, delay);
    });

  return {
    read() {
      if (isError) {
        throw new Error("에러발생");
      }
      if (user === null) {
        throw suspender;
      }

      return user;
    },
  };
}

//--------------------------

function User({ resource, idx }) {
  const user = resource.read();

  return (
    <div>
      <p>
        {user.name}({user.email}) 님이 작성한 글
      </p>
    </div>
  );
}

//--------------------------

function App() {
  return (
    <>
      {["1", "11"].map((userId) => {
        return (
          <ErrorBoundary key={`user_${userId}`} fallback={<h2>Error...</h2>}>
            <Suspense fallback={<h2>Loading...</h2>}>
              <User resource={fetchUser(userId, 3000)} />
            </Suspense>
          </ErrorBoundary>
        );
      })}
    </>
  );
}

export default App;

구현화면

ErrorBoundary Error Log


💡 ErrorBoundary가 포착하지 않는 에러

  • 이벤트 핸들러
  • 비동기적 코드 (예: setTimeout 혹은 requestAnimationFrame 콜백)
  • 서버 사이드 렌더링
  • 자식에서가 아닌 에러 경계 자체에서 발생하는 에러


참고
fault-tolerance-react
react-doc-error-boundary
react.dev-Catching rendering errors with an error boundary

profile
프론트엔드

0개의 댓글