1. 개요


1-1) 렌더링 과정에서 발생한 에러만 잡는다

React의 에러 바운더리는 자식 컴포넌트의 렌더링, 생명주기 메서드, 생성자(constructor) 내부에서 던져진 오류만 잡아낸다.

비동기 로직(fetch() 콜백, setTimeout 등) 내부에서 발생한 예외는 기본적으로 잡히지 않으니, 별도로 try/catch.catch() 로직을 써서 throw 해주거나, 상태를 세팅해서 렌더 단계에서 예외를 던지도록 해야 에러 바운더리가 이를 포착할 수 있다.

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

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

  render() {
    if (this.state.hasError) {
      // 폴백 UI를 커스텀하여 렌더링할 수 있습니다.
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

<ErrorBoundary>
  <ProductListFetcher>
    <ProductListContainer />
  </ProductListFetcher>
</ErrorBoundary>;

Error Boundary는 렌더링 중에 throw 된 error를 catch 하도록 동작한다. 좀 더 친숙한 try-catch 를 생각해보면 된다. 이 동작과 비슷하게 하위 트리 에서 발생하는 에러를 catch 한다. 만약 해당 Error Boundary에서 에러 처리를 실패하는 등을 이유로 에러를 re-throw 될 경우 가장 가까운 Error Boundary에서 catch 된다.

1-2) UI 폴백(fallback) 컴포넌트

에러가 잡혔을 때 사용자에게 보여줄 화면(“네트워크 오류입니다”, “점검 중입니다” 등)은 별도의 컴포넌트로 만들어 두는 것이 일반적이다. (<NetworkError/>, <Maintenance/>, <UnknownError/> 같은 것들.)

2. GlobalErrorBoundary


class GlobalErrorBoundary extends React.Component {
  state = { shouldHandleError: false };

  static getDerivedStateFromError(error) {
    // 에러가 발생하면 state.shouldHandleError = true 로 변경
    return { shouldHandleError: true };
  }

  render() {
    if (!this.state.shouldHandleError) {
      // 에러 없을 땐 자식 컴포넌트 그대로 렌더
      return this.props.children;
    }

    // 에러 종류에 따라 서로 다른 UI 보여주기
    if (/* 네트워크 에러인지 검사 */) {
      return (
        <NetworkError onClickRetry={() => this.setState({ shouldHandleError: false })}/>
      );
    }
    if (/* 서버 점검 에러인지 검사 */) {
      return <Maintenance />;
    }

    // 그 외 알 수 없는 에러
    return (
      <UnknownError onClickRetry={() => this.setState({ shouldHandleError: false })} />
    );
  }
}