ErrorBoundary가 비동기 에러를 감지하지 못하는 이유

dodog·2024년 9월 1일

얼마 전 왜 ErrorBoundary는 비동기 코드에서 발생하는 에러를 캐치하지 못하는지 의문이 생겨 동작 방식을 살펴보았다.

왜 ErrorBoundary는 비동기 함수의 에러를 잡아주지 못할까?

React는 자신이 직접 실행하는 렌더링과 생명주기 처리 중 에러를 포착하고, 컴포넌트 트리의 부모 관계를 따라 가장 가까운 ErrorBoundary에 전달한다.

반면 API 요청의 Promise 콜백은 React의 렌더링 과정 밖에서 실행되므로, 콜백에서 발생한 에러는 ErrorBoundary에 전달되지 않는다.

그럼 ErrorBoundary가 비동기 에러를 처리하게 하는 방법은 없을까?

그럼에도 풀리지 않는 의문은, 분명히 다른 아티클에서는
API 호출에서 발생한 에러를 ErrorBoundary가 처리하도록 하는 것을 봤다는 것이다.

아래처럼 비동기 에러를 state에 저장한 뒤 렌더링 중 다시 throw하면
ErrorBoundary가 감지할 수 있다.

const [error, setError] = useState();

useEffect(() => {
  const fetchTodos = async () => {
    try {
      await getTodos();
    } catch (e) {
      setError(e);
    }
  };

  fetchTodos();
}, []);

if (error) {
  throw error;
}

위 코드가 App 컴포넌트 내부 코드라고 가정하고
App 컴포넌트는 ErrorBoundary에 의해 감싸져있고
App 컴포넌트 내부의 getTodos api 함수에서 에러가 발생했다고 하자.

  1. React가 ErrorBoundary와 App을 렌더링하고 화면에 반영한다.
  2. 화면에 반영된 후 App 컴포넌트의 useEffect 콜백이 실행되어 getTodos를 호출한다.
  3. getTodos의 Promise가 reject되면 catch 블록이 실행된다.
  4. catch에서 잡은 에러를 setError에 전달한다.
  5. 상태 변경으로 App 컴포넌트의 리렌더링이 예약된다.
  6. App이 다시 렌더링되면서 저장된 error를 동기적으로 throw 한다.
  7. React가 에러를 포착하고 컴포넌트 트리의 부모 관계를 따라 가장 가까운 ErrorBoundary를 찾는다.
  8. ErrorBoundary의 복구 렌더링 과정에서 getDerivedStateFromError()가 호출되고 변경된 상태에 따라 fallback UI가 렌더링된다.

이런 순차적인 단계를 통해서 비동기 컨텍스트에서 발생한 에러를
렌더링 중 다시 throw하여 ErrorBoundary가 감지할 수 있게 하는 것이다.

마무리

이번 계기를 통해 React가 렌더링 과정에서 발생한 에러를 컴포넌트 트리를 따라 ErrorBoundary에 전달한다는 것을 알게 되었다.

앞으로도 React의 기능을 사용법만 외우기보다 실제 동작 범위를 이해하며 사용하도록 노력해야겠다.

profile
심리학, 사회문제해결에 관심이 많은 프론트엔드 개발자

0개의 댓글