ErrorBoundary를 감쌌는데 버튼 클릭 에러는 못 잡은 이유
문제 발생
컴포넌트 트리를 ErrorBoundary로 감싸서 에러 발생 시 대체 UI를 보여주도록 만들었는데, 버튼 클릭 핸들러 안에서 발생한 에러는 전혀 잡히지 않고 콘솔에 처리되지 않은 에러로만 남았습니다.
<ErrorBoundary fallback={<ErrorMessage />}>
<SubmitButton />
</ErrorBoundary>
function SubmitButton() {
const handleClick = () => {
throw new Error("실패"); // ErrorBoundary가 잡지 못함
};
return <button onClick={handleClick}>제출</button>;
}원인 분석
React의 Error Boundary는 렌더링 과정에서 발생한 에러만 잡도록 설계되어 있습니다 — 컴포넌트 함수 본문이 실행되는 동안(즉 React가 그 컴포넌트를 화면에 그리려고 시도하는 동안) 던져진 에러를 감지합니다.
React 공식 문서는 Error Boundary가 잡지 못하는 경우를 명시적으로 나열합니다.
- 이벤트 핸들러(
onClick,onChange등) — 이건 렌더링 과정이 아니라 사용자 상호작용에 반응해 실행되는 별도의 코드 경로입니다. - 비동기 코드(
setTimeout, Promise 콜백,async함수 안) — 에러가 발생하는 시점에 React의 렌더링 스택이 이미 끝나 있습니다. - 서버 사이드 렌더링 중 발생한 에러
- Error Boundary 자기 자신의 코드에서 발생한 에러
이런 경우들은 React의 렌더링 트리 바깥에서 발생하므로, React가 "이 하위 트리에서 에러가 났으니 fallback을 보여줘야겠다"고 판단할 방법이 없습니다.
해결 방안
- 이벤트 핸들러/비동기 코드는 일반 JavaScript의
try/catch로 직접 처리합니다.
function SubmitButton() {
const [error, setError] = useState<string | null>(null);
const handleClick = async () => {
try {
await submit();
} catch (e) {
setError("제출에 실패했습니다"); // 상태로 관리해 UI에 반영
}
};
return (
<>
<button onClick={handleClick}>제출</button>
{error && <p role="alert">{error}</p>}
</>
);
}- 이벤트 핸들러 안에서 잡은 에러를 렌더링 중 에러로 다시 전환하고 싶다면(즉 Error Boundary가 처리하게 만들고 싶다면),
catch블록에서 상태를 업데이트해 그 상태 변화가 다음 렌더링에서 실제로throw되도록 만드는 패턴을 쓸 수 있습니다(예: 상태에 에러를 저장했다가 렌더링 함수 본문에서 그 상태가 있으면throw). - React 19+에서는
createRoot/hydrateRoot의onUncaughtError/onCaughtError콜백으로 Error Boundary 밖의 처리되지 않은 에러까지 중앙에서 로깅할 수 있는 훅이 추가되었습니다 — Error Boundary가 못 잡는 에러를 완전히 대체하지는 않지만, 최소한 관측(로깅) 목적으로는 보완할 수 있습니다. - 비동기 데이터 페칭에서 발생하는 에러를 UI 트리 차원에서 선언적으로 다루고 싶다면,
Suspense+ 에러를 던지는 방식으로 설계된 데이터 페칭 라이브러리(예: React Query의useSuspenseQuery)와 Error Boundary를 조합하는 패턴도 있습니다 — 이 경우는 데이터 페칭이 렌더링 과정의 일부로 통합되어 있어 Error Boundary가 정상적으로 개입할 수 있습니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.