export async function deleteInvoice(id: string) {
try {
await sql`DELETE FROM invoices WHERE id = ${id}`;
revalidatePath('/dashboard/invoices');
return { message: '인보이스가 삭제되었습니다.' };
} catch (error) {
return { message: '데이터베이스 오류: 인보이스 삭제에 실패했습니다.' };
}
}
redirect가 try/catch 블록 외부에서 호출되는 것을 주목하세요. 이는 redirect가 에러를 던지는 방식으로 작동하기 때문에 catch 블록에서 잡힐 것입니다. 이를 피하기 위해 try/catch 이후에 redirect를 호출할 수 있습니다. redirect는 try가 성공했을 때에만 도달 가능합니다.
이제 서버 액션에서 에러가 발생했을 때의 동작을 확인해보겠습니다. 이를 위해 이전보다 더 일찍 에러를 던져서 확인할 수 있습니다. 예를 들어, deleteInvoice 액션에서 함수 상단에 에러를 던져보세요:
/app/lib/actions.ts
export async function deleteInvoice(id: string) {
throw new Error('인보이스 삭제에 실패했습니다.');
// 도달할 수 없는 코드 블록
try {
await sql`DELETE FROM invoices WHERE id = ${id}`;
revalidatePath('/dashboard/invoices');
return { message: '인보이스가 삭제되었습니다.' };
} catch (error) {
return { message: '데이터베이스 오류: 인보이스 삭제에 실패했습니다.' };
}
}
인보이스를 삭제하려고 시도하면 로컬호스트에서 에러가 표시될 것입니다.
이러한 에러는 개발 중에 잠재적인 문제를 조기에 발견할 수 있어 도움이 됩니다. 하지만 갑작스러운 실패를 피하고 애플리케이션을 계속 실행할 수 있도록 사용자에게 에러를 표시하고 싶을 것입니다.
error.tsx로 모든 에러 처리하기
error.tsx 파일은 라우트 세그먼트에 대한 UI 경계를 정의하는 데 사용될 수 있습니다. 예기치 않은 에러에 대한 전체 캐치 역할을 하며 사용자에게 대체 UI를 표시할 수 있습니다.
/dashboard/invoices 폴더 내부에 새로운 파일 error.tsx를 만들고 다음 코드를 붙여넣어보세요:
/dashboard/invoices/error.tsx
'use client';
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// 선택적으로 에러를 에러 보고 서비스에 기록할 수 있습니다.
console.error(error);
}, [error]);
return (
<main className="flex h-full flex-col items-center justify-center">
<h2 className="text-center">문제가 발생했습니다!</h2>
<button
className="mt-4 rounded-md bg-blue-500 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-400"
onClick={
// 다시 시도하여 인보이스 라우트를 다시 렌더링하는 시도
() => reset()
}
>
다시 시도
</button>
</main>
);
}
위 코드에 대해 몇 가지 주목할 점이 있습니다:
"use client" - error.tsx는 클라이언트 컴포넌트여야 합니다.
두 가지 프롭을 받습니다:
reset: 이는 에러 경계를 재설정하기 위한 함수입니다. 실행되면 함수가 라우트 세그먼트를 다시 렌더링하려고 시도합니다.
인보이스를 다시 삭제하려고 하면 다음과 같은 UI가 표시될 것입니다:
notFound 함수로 404 에러 처리하기
에러를 우아하게 처리하는 다른 방법으로 notFound 함수를 사용하는 것이 있습니다. error.tsx가 모든 에러를 잡는 데 유용하다면, notFound는 존재하지 않는 리소스를 가져오려고 할 때 사용할 수 있습니다.
이것은 데이터베이스에 존재하지 않는 가짜 UUID입니다.
이것은 error.tsx가 정의되어있는 /invoices의 자식 라우트이기 때문에 error.tsx가 바로 튀어나오는 것을 바로 확인할 수 있습니다.
그러나 좀 더 구체적으로 처리하고 싶다면, 사용자에게 접근하려는 리소스가 찾을 수 없음을 알려주기 위해 404 에러를 표시할 수 있습니다.
data.ts의 fetchInvoiceById 함수로 이동하고 반환된 송장을 콘솔에서 기록하여 리소스를 찾지 못했음을 확인할 수 있습니다.:
/app/lib/data.ts
export async function fetchInvoiceById(id: string) {
noStore();
try {
// ...
console.log(invoice); // 인보이스는 빈 배열입니다 []
return invoice[0];
} catch (error) {
console.error('데이터베이스 오류:', error);
throw new Error('인보이스를 가져오는 데 실패했습니다.');
}
}
이제 데이터베이스에 인보이스가 존재하지 않음을 알게 되었습니다. 이를 처리하기 위해 notFound를 사용해봅시다. /dashboard/invoices/[id]/edit/page.tsx로 이동하고 'next/navigation'에서 { notFound }를 import하세요.
그런 다음, 인보이스가 존재하지 않는 경우 notFound를 호출할 수 있는 조건문을 사용할 수 있습니다:
/dashboard/invoices/[id]/edit/page.tsx
import { fetchInvoiceById, fetchCustomers } from '@/app/lib/data';
import { updateInvoice } from '@/app/lib/actions';
import { notFound } from 'next/navigation';
export default async function Page({ params }: { params: { id: string } }) {
const id = params.id;
const [invoice, customers] = await Promise.all([
fetchInvoiceById(id),
fetchCustomers(),
]);
if (!invoice) {
notFound();
}
// ...
}
좋습니다! <Page>는 이제 특정 인보이스가 없는 경우 에러를 던질 것입니다. 사용자에게 에러 UI를 표시하기 위해 /edit 폴더 내에 not-found.tsx 파일을 생성하세요.
그런 다음, not-found.tsx 파일 내에 다음 코드를 붙여넣어보세요:
/dashboard/invoices/[id]/edit/not-found.tsx
import Link from 'next/link';
import { FaceFrownIcon } from '@heroicons/react/24/outline';
export default function NotFound() {
return (
<main className="flex h-full flex-col items-center justify-center gap-2">
<FaceFrownIcon className="w-10 text-gray-400" />
<h2 className="text-xl font-semibold">404 Not Found</h2>
<p>요청한 인보이스를 찾을 수 없습니다.</p>
<Link
href="/dashboard/invoices"
className="mt-4 rounded-md bg-blue-500 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-400"
>
뒤로 가기
</Link>
</main>
);
}
라우트를 새로고침하면 다음의 UI가 표시됩니다:
이것은 기억해야 할 중요한 점입니다. notFound는 error.tsx보다 우선순위가 있으므로 더 구체적인 에러를 처리하려고 할 때 notFound를 활용할 수 있습니다!
퀴즈 시간입니다!
지금까지 배운 내용을 테스트해보세요.
Next.js에서 라우트 세그먼트의 예기치 않은 에러를 캐치하는 데 사용되는 파일은 무엇인가요?
A: 404.tsx
B: not-found.tsx
C: error.tsx
D: catch-all.tsx
정답 확인
C: error.tsx
error.ts 파일은 예상치 못한 오류를 모두 캐치하고 사용자에게 대체 UI를 표시할 수 있는 역할을 합니다.