Next.js에서는 서버 및 클라이언트에서 발생하는 에러를 효과적으로 처리할 수 있도록 다양한 에러 핸들링 방식을 제공한다.
Next.js의 App Router를 사용할 경우, 특정 경로에서 발생하는 에러를 전역적으로 처리하려면 error.tsx 파일을 추가하면 된다.
app/some-route/error.tsx를 만들면, 해당 경로 아래의 모든 페이지와 레이아웃에서 발생하는 에러를 처리할 수 있다.
"use client"; // 반드시 클라이언트 컴포넌트여야 함
import { useEffect } from "react";
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
useEffect(() => {
console.error("에러 발생:", error);
}, [error]);
return (
<div>
<h2>⚠️ 에러가 발생했습니다!</h2>
<p>{error.message}</p>
<button onClick={() => reset()}>다시 시도</button>
</div>
);
}
error.tsx는 클라이언트 컴포넌트여야 하므로 "use client"를 추가해야 한다.error 객체를 통해 에러 메시지를 표시할 수 있다.reset 함수를 호출하면, 현재 경로를 초기화하여 다시 시도할 수 있다.
error.tsx의reset함수는 클라이언트 컴포넌트를 다시 렌더링하지만, 서버 컴포넌트는 다시 실행되지 않으므로 클라이언트에서 발생한 오류만 복구할 수 있다.
모든 경우의 에러를 복구하려면router.refresh()를 사용해 서버 컴포넌트까지 다시 불러와야 하며, React 18부터 제공되는startTransition()내부에서router.refresh()와reset()을 함께 호출하면 UI의 끊김 없이 동기적으로 전체 렌더링을 갱신할 수 있다.
<button
onClick={() => {
startTransition(() => {
// 함수 하나를 인수로 받아서, 해당 함수 내부의 코드를 동기적으로 실행
router.refresh(); // 현재 페이지에 필요한 서버 컴포넌트들을 다시 불러옴
reset(); // 에러 상태를 초기화, 컴포넌트들을 다시 렌더링
});
}}
>
다시 시도
</button>
서버 컴포넌트나 클라이언트 컴포넌트에서 직접 try-catch 문을 사용하여 에러를 처리할 수도 있다.
async function fetchData() {
try {
const res = await fetch("https://api.example.com/data");
if (!res.ok) {
throw new Error("데이터 불러오기에 실패했습니다.")
};
return await res.json();
} catch (error) {
console.error("데이터 로딩 중 에러 발생:", error);
return null; // 에러 발생 시 기본값 반환
}
}
export default async function Page() {
const data = await fetchData();
if (!data) return <p>⚠️ 데이터를 불러오지 못했습니다.</p>;
return <div>✅ 데이터: {JSON.stringify(data)}</div>;
}
try-catch를 사용하여 예외를 처리null 반환 → 페이지에서 에러 메시지를 출력