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 된다.
에러가 잡혔을 때 사용자에게 보여줄 화면(“네트워크 오류입니다”, “점검 중입니다” 등)은 별도의 컴포넌트로 만들어 두는 것이 일반적이다. (<NetworkError/>
, <Maintenance/>
, <UnknownError/>
같은 것들.)
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 })} />
);
}
}
state.shouldHandleError
getDerivedStateFromError
가 호출되면 true
로 설정되어, 렌더 단계에서는 fallback UI
를 띄운다.
에러 분류
네트워크 문제, 서버 점검, 그 외(Unknown) 등 조건문으로 분기해 적절한 컴포넌트를 렌더한다.
Retry 처리
“다시 시도” 버튼 등을 눌렀을 때 상태를 초기화하여(→ 자식 컴포넌트 재시도) 에러를 리셋한다.