Web Devlopment/NextJs
#19. Error Handling 실전
키모형
2025. 12. 28. 11:42
반응형
1) “예상 가능한 에러”는 validation으로 네비게이션 처리
예:
- 권한 없음 → redirect('/login')
- 데이터 없음 → notFound()
- 파라미터 이상 → redirect() 또는 notFound()
이건 UX도 좋고, 에러 바운더리에 불필요한 500을 만들지 않아서 좋아요.
2) “예상 불가한 진짜 예외”만 공통 에러로 처리
이때 공통 처리는 아래처럼:
- app/error.tsx : 대부분의 런타임 예외 공통 처리
- app/global-error.tsx : 루트 레이아웃까지 깨지는 경우 대비
파일 예시 (전역 공통 에러)
app/error.tsx (공통 에러 UI: 대부분 여기서 처리됨)
"use client";
import { useEffect } from "react";
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("App error:", error);
// TODO: Sentry/로그 서버 전송
}, [error]);
return (
<div style={{ padding: 24 }}>
<h2 style={{ fontSize: 18, fontWeight: 700 }}>문제가 발생했습니다.</h2>
<p style={{ marginTop: 8, opacity: 0.8 }}>
잠시 후 다시 시도해 주세요.
</p>
{/* 개발환경에서는 message가 보이지만, 프로덕션에서는 digest만 남는 경우가 많음 */}
{error.digest && (
<p style={{ marginTop: 8, fontSize: 12, opacity: 0.6 }}>
digest: {error.digest}
</p>
)}
<button
onClick={() => reset()}
style={{ marginTop: 12, padding: "10px 14px", borderRadius: 8 }}
>
다시 시도
</button>
</div>
);
}
app/global-error.tsx (루트 레이아웃까지 터질 때 최후의 안전망)
"use client";
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<html lang="ko">
<body style={{ padding: 24 }}>
<h1 style={{ fontSize: 20, fontWeight: 800 }}>
치명적인 오류가 발생했습니다.
</h1>
{error.digest && (
<p style={{ marginTop: 8, fontSize: 12, opacity: 0.6 }}>
digest: {error.digest}
</p>
)}
<button onClick={() => reset()} style={{ marginTop: 12 }}>
다시 시도
</button>
</body>
</html>
);
}
실무 운영 팁
- “특정 페이지에서만 특별한 에러 UI”가 필요 없고 전역 공통만 원하면
→ 세그먼트별 error.tsx는 만들지 말고, app/error.tsx 하나로 통일하면 됩니다. - 대신, 예상 가능한 실패(권한/검증/없음) 는 redirect/notFound로 처리하는 지금 방식이 딱 좋습니다.
반응형