React Error Boundary
[React] Error Boundary: 우리 앱이 ‘흰 화면’으로 죽지 않게 만드는 법
React 애플리케이션을 개발하다 보면 예상치 못한 에러로 인해 전체 앱이 멈추거나 하얀 화면만 남는 현상을 겪곤 합니다.
작은 컴포넌트 하나에서 발생한 에러가 전체 애플리케이션을 중단시킨다면 사용자 경험(UX)에 치명적일 수밖에 없습니다.
Get Youngho’s stories in your inbox
Join Medium for free to get updates from this writer.
오늘은 이러한 문제를 해결해 주는 React의 Error Boundary(에러 경계) 에 대해 자세히 알아보겠습니다.
1. Error Boundary란 무엇인가요?
- Error Boundary는 하위 컴포넌트 트리의 어디에서든 자바스크립트 에러를 감지(Catch) 하고, 이 에러를 기록(Log) 하며, 깨진 컴포넌트 트리 대신 대체 UI(Fallback UI) 를 보여주는 React 컴포넌트입니다.
- 쉽게 말해, 자바스크립트의
try...catch구문이 컴포넌트 레벨로 확장된 것이라고 이해하면 됩니다.
핵심: Error Boundary는 렌더링 도중, 생명주기 메서드, 그리고 그 아래에 있는 모든 생성자(constructor) 내의 에러를 잡아냅니다.
2. 왜 사용해야 하나요? (The “Why”)
1) 전체 애플리케이션 붕괴 방지
- Error Boundary가 없다면, React는 컴포넌트 트리 내부의 에러로 인해 내부 상태가 손상되었을 때 오염된 UI를 보여주는 것보다 아예 아무것도 보여주지 않는 것(Unmount)을 택합니다.
- 하지만 사이드바의 작은 아이콘 에러 때문에 메인 대시보드 전체가 안 보인다면 곤란하겠죠?
- Error Boundary를 사용하면 에러가 발생한 부분만 대체 UI로 처리하고, 나머지 기능은 정상적으로 유지할 수 있습니다.
2) 사용자 경험(UX) 개선
- 사용자에게 “알 수 없는 에러가 발생했습니다”라는 친절한 메시지나 “다시 시도” 버튼을 제공함으로써, 갑작스러운 흰 화면보다 훨씬 나은 경험을 제공합니다.
3) 에러 로깅의 중앙화
componentDidCatch 생명주기 메서드를 통해 에러 정보를 Sentry, LogRocket 같은 외부 로깅 서비스로 전송하는 로직을 한곳에서 관리할 수 있습니다.
3. 어떻게 구현하나요?
- Error Boundary는 반드시 클래스 컴포넌트(Class Component) 로 작성해야 합니다. (React 18 기준, 아직 함수형 컴포넌트와 Hook으로는 이 기능을 완벽히 대체할 수 없습니다.)
다음 두 가지 생명주기 메서드 중 하나라도 정의하면 Error Boundary
static getDerivedStateFromError(): 에러가 발생했을 때 state를 업데이트하여 대체 UI를 렌더링하기 위해 사용합니다.componentDidCatch(): 에러 정보를 기록(logging) 하기 위해 사용합니다.
기본 구현 코드
JavaScript
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
// 1. 에러가 발생하면 state를 업데이트하여 Fallback UI를 보여줍니다.
static getDerivedStateFromError(error) {
return { hasError: true };
}
// 2. 에러 리포팅 서비스에 에러를 기록합니다.
componentDidCatch(error, errorInfo) {
console.error("Uncaught error:", error, errorInfo);
// 예: logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 커스텀 Fallback UI를 렌더링합니다.
return <h1>무언가 잘못되었습니다.</h1>;
}
// 에러가 없다면 자식 컴포넌트를 그대로 렌더링합니다.
return this.props.children;
}
}
export default ErrorBoundary;사용 방법
JavaScript
<ErrorBoundary>
<MyProfile />
</ErrorBoundary>4. Error Boundary의 한계 (주의사항)
Error Boundary는 만능이 아닙니다. 다음과 같은 경우에는 에러를 포착하지 못합니다.
- 이벤트 핸들러 (예:
onClick,onChange내부의 에러) - 해결책: 이벤트 핸들러 내부는 일반적인
try...catch문을 사용 - 비동기 코드 (예:
setTimeout,requestAnimationFrame콜백) - 서버 사이드 렌더링 (SSR)
- Error Boundary 자체에서 발생한 에러 (자식이 아닌 본인의 에러)
5. 더 나은 방법: react-error-boundary 라이브러리
클래스 컴포넌트를 직접 작성하는 것이 번거롭거나, 에러 발생 후 “다시 시도(Reset)” 기능을 쉽게 구현하고 싶다면 react-error-boundary 라이브러리 사용을 추천합니다.
사용 예시
JavaScript
import { ErrorBoundary } from 'react-error-boundary';
function FallbackComponent({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>에러가 발생했습니다:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>다시 시도</button>
</div>
);
}
function App() {
return (
<ErrorBoundary
FallbackComponent={FallbackComponent}
onReset={() => {
// 여기서 에러 발생 원인이 된 상태를 초기화합니다.
}}
>
<MyComponent />
</ErrorBoundary>
);
}이 라이브러리를 사용하면 함수형 컴포넌트 환경에서도 우아하게 Error Boundary를 선언적으로 처리할 수 있습니다.
마치며
Error Boundary는 완벽한 앱을 만들기 위한 필수적인 안전장치입니다. 모든 컴포넌트를 하나로 감싸는 것보다, 위젯 단위나 페이지 단위로 적절히 나누어 감싸는 것이 사용자 경험 측면에서 훨씬 유리합니다.
지금 여러분의 프로젝트에도 이 안전벨트를 채워보는 건 어떨까요?





