Error Boundary: 렌더링 에러를 우아하게 처리하기
JavaScript에서 에러가 발생하면 try/catch로 잡습니다. 하지만 React 컴포넌트 렌더링 중 발생하는 에러는 try/catch로 잡을 수 없습니다. 렌더링은 React가 호출하기 때문입니다. 이 에러를 잡는 것이 Error Boundary입니다.
Error Boundary 없으면?
function UserProfile({ userId }) {
const user = getUser(userId); // userId가 없으면 에러 발생
return <div>{user.name}</div>; // user가 null이면 에러 발생
}이런 에러가 발생하면 React는 컴포넌트 트리 전체를 언마운트합니다. 사용자는 빈 화면을 보게 됩니다. Error Boundary를 사용하면 에러가 발생한 부분만 fallback UI로 교체하고 나머지는 정상 동작합니다.
Error Boundary 구현
Error Boundary는 클래스 컴포넌트로만 만들 수 있습니다. getDerivedStateFromError와 componentDidCatch라는 클래스 컴포넌트 전용 생명주기 메서드를 사용하기 때문입니다.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
// 에러 발생 시 state 업데이트 (렌더링 단계)
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
// 에러 로깅 (커밋 단계, 사이드 이펙트 가능)
componentDidCatch(error, info) {
console.error('렌더링 에러:', error);
console.error('컴포넌트 스택:', info.componentStack);
// Sentry 같은 에러 추적 서비스에 보내기
// logErrorToService(error, info);
}
render() {
if (this.state.hasError) {
return this.props.fallback ?? <div>문제가 발생했습니다.</div>;
}
return this.props.children;
}
}사용법
function App() {
return (
<ErrorBoundary fallback={<ErrorPage />}>
<Header />
<ErrorBoundary fallback={<p>피드를 불러올 수 없습니다.</p>}>
<NewsFeed />
</ErrorBoundary>
<ErrorBoundary fallback={<p>추천 콘텐츠를 불러올 수 없습니다.</p>}>
<Recommendations />
</ErrorBoundary>
</ErrorBoundary>
);
}NewsFeed에서 에러가 나도 Header와 Recommendations는 정상적으로 보입니다. Error Boundary를 세밀하게 배치할수록 에러의 영향 범위가 줄어듭니다.
react-error-boundary 라이브러리
클래스 컴포넌트를 직접 작성하는 대신, react-error-boundary 라이브러리를 쓰면 더 간편합니다.
npm install react-error-boundaryimport { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div>
<p>오류가 발생했습니다: {error.message}</p>
<button onClick={resetErrorBoundary}>다시 시도</button>
</div>
);
}
function App() {
return (
<ErrorBoundary
FallbackComponent={ErrorFallback}
onError={(error, info) => logError(error, info)}
onReset={() => { /* 에러 상태 초기화 */ }}
>
<SomeComponent />
</ErrorBoundary>
);
}resetErrorBoundary를 호출하면 에러 상태를 초기화하고 자식 컴포넌트를 다시 렌더링합니다.
Error Boundary가 잡지 못하는 에러
Error Boundary는 렌더링 중 발생하는 에러만 잡습니다. 다음은 잡지 못합니다.
// 이벤트 핸들러 안 에러 (try/catch로 직접 처리)
<button onClick={() => {
try {
doSomething();
} catch (e) {
setError(e);
}
}}>
// 비동기 코드
useEffect(() => {
fetch('/api').then(/* ... */); // Promise 에러는 잡지 못함
}, []);
// 서버 사이드 렌더링 중 에러Suspense와 함께 사용
function App() {
return (
<ErrorBoundary fallback={<ErrorPage />}>
<Suspense fallback={<LoadingSpinner />}>
<AsyncComponent />
</Suspense>
</ErrorBoundary>
);
}Suspense는 로딩 중 상태를, ErrorBoundary는 에러 상태를 담당합니다.
정리
- Error Boundary는 자식 컴포넌트 트리의 렌더링 에러를 잡습니다
- 에러 발생 시 전체 화면 대신 지정한 fallback UI를 보여줍니다
- 세밀하게 배치할수록 에러 영향 범위가 줄어듭니다
- 클래스 컴포넌트로 구현하거나,
react-error-boundary라이브러리를 사용합니다 - 이벤트 핸들러와 비동기 에러는 잡지 못하므로 별도 처리가 필요합니다