하위 컴포넌트 트리의 어디에서든 JS 에러를 기록하며, 깨진 컴포넌트 트리 대신 fallback대비책 UI를 보여주는 React 컴포넌트. 렌더링 도중 생명주기 메서드 및 그 아래에 있는 전체 트리에서 에러를 잡아낸다.
즉, 에러가 발생했을 때 전체 애플리케이션이 중단되는 것을 방지한다.
공식홈페이지의 errorBoundary 안내는 클래스형이지만, react-error-boundary 라이브러리를 사용하면 함수형으로 errorBoundary를 사용할 수 있다.
# npm
npm install react-error-boundary
# pnpm
pnpm add react-error-boundary
# yarn
yarn add react-error-boundary
FallbackComponent : 에러 발생 시 보여줄 컴포넌트onError : 오류 로깅onReset : 에러 상태 초기화할 때 실행되는 콜백resetKeys : key가 바뀌면 자동으로 리셋을 한다.// 메인 컴포넌트
import { ErrorBoundary } from 'react-error-boundary';
import { ErrorInfo } from "react";
const App = () => {
return (
<ErrorBoundary
FallbackComponent={FallbackComponent}
onError={(error: Error, info: ErrorInfo) => {
console.log("에러 발생:", error);
console.log("컴포넌트 스택:", info.componentStack);
}}
onReset={() => {
console.log('에러 상태 초기화됨');
}}
resetKeys={['someKey']}
>
<ComponentThatMayError /> {/* 실제 사용할 컴포넌트 */}
</ErrorBoundary>
);
};
// FallbackComponent 예시
const FallbackComponent = ({ error, resetErrorBoundary }: FallbackProps) => {
return (
<div role="alert">
<p>문제가 발생했습니다:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>다시 시도</button>
</div>
);
};
hook을 사용하여 컴포넌트 내부에서 에러 상태를 직접 관리할 수 있다.
ErrorBoundary가 자동으로 잡지 못하는 에러들비동기 작업 이벤트 핸들러을 수동으로 ErrorBoundary에 전달하고 싶은 경우 사용한다. -> 애플리케이션 전체에서 일관된 에러 처리와 UI 제공
import { useErrorBoundary } from "react-error-boundary";
고차 컴포넌트로도 사용할 수 있다.
여러 컴포넌트에 동일한 에러 처리를 적용해야 할 경우나 컴포넌트를 수정하지 않고 에러 처리를 추가하고 싶을 경우 사용한다.
import {withErrorBoundary} from 'react-error-boundary';
const ComponentWithErrorBoundary = withErrorBoundary(ExampleComponent, {
fallback: <div>Something went wrong</div>,
onError(error, info) {
// Do something with the error
// E.g. log to an error logging client here
},
})
Error Boundary가 잡는 에러는 에러를 throw한 것만 잡는다.
React-Query 에러는 에러를 throw하지 않고 error 상태로 관리하기 때문에 Error Boundary로 잡히지 않는다.
대부분은 React Query 레벨예상 가능한 에러과 Error Boundary예상 못한 치명적 에러를 나눠서 처리한다.
-> Suspense + Error Boundary 조합 방법 사용
✔️ React-Query 옵션
throwOnError: true : 에러 throw (로딩은 컴포넌트에서 처리)
Error Boundary와 일반 try-catch 모두에서 에러를 받을 수 있다.useErrorBoundary: true : 에러 throw
suspense: true(기본값) : 로딩 throw
// ❌ throwOnError만 사용 - 로딩 처리는 여전히 컴포넌트에서
const UserProfile = () => {
const { data, isLoading } = useQuery({
queryKey: ['user'],
queryFn: fetchUser,
throwOnError: true
});
if (isLoading) return <div>로딩중...</div>; // 여전히 필요
return <div>{data.name}</div>;
};
// ✅ suspense + useErrorBoundary 사용 - 완전히 깔끔
const UserProfile = () => {
const { data } = useQuery({
queryKey: ['user'],
queryFn: fetchUser,
suspense: true,
useErrorBoundary: true
});
return <div>{data.name}</div>; // 로딩, 에러 처리 완전 제거!
};
// 사용할 때
<ErrorBoundary FallbackComponent={UserErrorFallback}>
<Suspense fallback={<UserSkeleton />}>
<UserProfile />
</Suspense>
</ErrorBoundary>
데이터를 불러올 때 서버에서 HTTP ErrorCode 500, Error Response 메시지가 “CRITICAL_ERROR”인 경우 “화면 전체를 덮는 에러 화면”을 표시한다.
Suspense 하위에 비동기 데이터 불러오기가 여러 개 있을 경우
Suspense는 마치 Promise.all처럼 동작한다.
명령형으로 UI를 구성할 경우 > 2번의 삼항연산자 사용
선언형으로 UI를 구성할 경우 > 단순히 화면에 무엇을 그려줄지에 대한 관심만..