같은 폴더 layout.tsx의 오류를 error.tsx가 잡지 못한 이유
문제 발생
app/dashboard/error.tsx를 만들어 뒀는데, 같은 폴더의 layout.tsx에서 난 오류는 이 화면이 잡지 못하고 앱 전체가 흰 화면이 됐습니다.
루트 레이아웃에서 오류가 났을 때는 error.tsx가 아예 소용이 없었습니다.
원인 분석
error.js는 라우트 세그먼트와 그 중첩된 자식을 React Error Boundary로 감쌉니다. 공식 문서가 범위를 명확히 적습니다 — 컴포넌트 계층에서 error.js는 loading.js, not-found.js, page.js, 그리고 중첩된 layout.js를 감싸지만, 같은 세그먼트의 layout.js나 template.js는 감싸지 않습니다.
경계가 레이아웃 안쪽에 있기 때문입니다. 그 레이아웃 자체가 실패하면 경계도 함께 실패합니다. 그래서 같은 세그먼트 레이아웃의 오류는 부모 쪽 경계로 올라갑니다.
루트 레이아웃에는 부모가 없습니다. 그래서 문서는 루트 레이아웃의 오류를 다루려면 global-error.js를 쓰라고 안내합니다.
해결 방안
error.tsx는 Client Component여야 합니다. 이건 선택이 아닙니다.
"use client"; // Error boundaries must be Client Components
export default function Error({
error,
retry,
}: {
error: Error & { digest?: string };
retry: () => void;
}) {
return (
<div>
<h2>문제가 발생했습니다</h2>
<button onClick={() => retry()}>다시 시도</button>
</div>
);
}-
복구는
retry()입니다. Next.js 16.3에서 안정화된 prop으로, 실행하면 경계 자식의 데이터를 다시 가져오고 다시 렌더합니다. 성공하면 오류 화면이 결과로 교체됩니다. 예전의reset()도 있지만 문서는 대부분의 경우retry()를 쓰라고 안내합니다 —reset()은 다시 가져오지 않고 오류 상태만 지웁니다. -
루트 레이아웃은
global-error.tsx가 맡습니다. 활성화되면 루트 레이아웃을 대체하므로 자기<html>과<body>를 직접 가져야 합니다.
"use client";
export default function GlobalError({ error, retry }) {
return (
<html>
<body>
<h2>문제가 발생했습니다</h2>
<button onClick={() => retry()}>다시 시도</button>
</body>
</html>
);
}-
global-error에는 전역 스타일이 닿지 않습니다. 문서가 명시합니다 —global-error와 기본 500 페이지는 자체 문서를 렌더하며 전역 스타일을 포함하지 않습니다. 앱 수준의 테마 클래스나data-theme속성이 전달되지 않으므로, 필요한 스타일은 이 컴포넌트 안에서 직접 넣습니다. -
metadata를 export할 수 없습니다. Client Component이기 때문입니다. 제목이 필요하면 React의<title>컴포넌트를 씁니다. -
운영에서는 오류 메시지가 다릅니다. 개발 중에는 원본
message가 전달되지만, 문서의 설명대로 운영에서는 민감한 정보 유출을 막기 위해 동작이 다릅니다. Server Component에서 올라온 오류는 식별자가 붙은 일반 메시지로 바뀌고, 그 식별자가error.digest입니다. 서버 로그와 대조할 때 이 값을 씁니다.
useEffect(() => {
reportError({ digest: error.digest, message: error.message });
}, [error]);-
더 위로 올리고 싶으면 다시 던집니다. 문서의 안내대로
error컴포넌트를 렌더하는 중에throw하면 부모 경계로 올라갑니다. -
not-found.tsx와 혼동하지 않습니다. 이쪽은 예상된 부재(notFound()호출)를 다루고,error.tsx는 예상하지 못한 런타임 오류를 다룹니다. 없는 글에 500을 주지 않습니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.