[React] 프론트엔드 에러 핸들링 1편 ( with. ErrorBoundary )

HP :) 😃·2022년 12월 9일
0

안녕하세요 HP 입니다 :)

오늘은 React ErrorBoundary공식 문서를 활용하여 프론트엔드에서 에러 핸들링을 하는 방식에 대해서 얘기해보겠습니다.

📚 개념

리액트에서는 ErrorBoundary를 아래와 같이 설명하고 있습니다.

  • 자바스크립트 에러가 전체 어플리케이션을 중단시키는 현상을 막기 위해 React 16 버전부터 도입된 개념
  • ErrorBoundary는 하위 컴포넌트 트리의 어디에서든 자바스크립트 에러를 기록하며 깨진 컴포넌트 트리 대신 fallback UI를 보여주는 React의 컴포넌트

❗️Note
Error boundary는 다음과 같은 에러는 포착하지 않습니다.

  • 이벤트 핸들러
  • 비동기 코드
  • 서버사이드 렌더링
  • 자식에서가 아닌 에러 경계 자체에서 발생하는 에러

ErrorBoundary를 배치할 위치

  1. 서버 사이드 프레임워크가 충돌을 해결하는것 처럼 최상위 경로의 컴포넌트를 감싸서 유저에게 ‘문제가 발생했습니다.’ 라는 메시지를 보여줄 수 있습니다.
  2. 에러 경계의 각 위젯을 에러 경계로 감싸서 어플리케이션의 나머지 부분이 충돌하지 않도록 보호할 수도 있습니다.

⇒ ErrorBoundary의 세분화된 부분은 개발자에게 달려있습니다.

⚒️ 사용법

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

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

  componentDidCatch(error, errorInfo) {
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

리액트에서 ErrorBoundary는 클래스 컴포넌트를 이용하고 있고 클래스 컴포넌트에 있는 생명주기 메서드인 getDerivedStateFromErrorcomponentDidCatch를 사용하고 있습니다.

getDerivedStateFromError

  • 에러가 발생한 뒤에 hasError의 state를 변경시켜 fallback UI를 보일 수 있게 업데이트 합니다.
  • getDerivedStateFromError 메서드는 컴포넌트의 render 단계에서 호출됩니다. 즉, render() 메소드가 실행되기 전에 호출되기 때문에 VDOM에 영향을 끼칠 수 있는 사이드 이펙트가 발생하는 작업을 해서는 안됩니다.

componentDidCatch

  • 에러 리포팅 서비스에 에러를 기록할 수 있습니다.
  • getDerivedStateFromError와 달리 render 이후의 commit 단계에서 호출되기 때문에, 사이드 이펙트가 발생하는 작업을 수행할 수 있습니다.

아래는 일반 컴포넌트로 사용하는 방법입니다.

<ErrorBoundary>
	<MyWidget />
</ErrorBoundary>

위와 같이 MyWidget 컴포넌트를 ErrorBoundary로 감싸면 catch 구문과 유사하게 해당 컴포넌트에서 발생시킨 Error를 catch 합니다.

❗️ 단 , 에러 경계는 트리 내에서 하위에 존재하는 컴포넌트의 에러만을 포착합니다.

👊🏻 실전 사용법

App 컴포넌트

import React, { useEffect, useState } from "react";
import ErrorBoundary from "./ErrorBoundary";

function App() {
  const ErrorTest = () => {
    const [error, setError] = useState(false);

    useEffect(() => {
      if (error) throw new Error("Boom!");
    }, [error]);

    return <button onClick={() => setError(true)}>에러 만들기</button>;
  };

  return (
    <ErrorBoundary>
      <ErrorTest />
    </ErrorBoundary>
  );
}

export default App;

ErrorBoudary 컴포넌트

import React, { ErrorInfo } from "react";

interface ErrorBoundaryProps {
  children: React.ReactNode;
}

interface ErrorBoundaryState {
  hasError: boolean;
}
export default class ErrorBoundary extends React.Component<
  ErrorBoundaryProps,
  ErrorBoundaryState
> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false };
  }

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

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {}

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h2>에러가 발생했습니다. 잠시 후 다시 시도해주세요.</h2>
          <button
            type="button"
            onClick={() => this.setState({ hasError: false })}
          >
            다시 시도
          </button>
        </div>
      );
    }
    return this.props.children;
  }
}

⛔️ 위에 예제는 인위적으로 에러를 발생시키기 위해 App 컴포넌트의 코드가 어색할 수 있습니다!

위 예제를 통해 ErrorTest 컴포넌트에 에러 만들기 버튼을 클릭하면 ErrorTest를 감싸고 있는 ErrorBoundary에서 Error를 Catch 하는 것을 확인할 수 있습니다.

끝까지 읽어주셔서 감사합니다. 😃

다음 포스팅은 프론트엔드 에러 핸들링 2편을 통해 재사용 가능한 ErrorBoundary를 알아보겠습니다.

📌 참고

Error Boundary - React 공식 문서
Error Boundary - Next 공식 문서

profile
끊임없이 노력하는 개발자

0개의 댓글