-
[NextJS] 튜토리얼 챕터 13 - 에러 핸들링Web 2024. 6. 1. 23:24
들어가기 전에
이 글은 Next.js - Chapter 13 : Handling Errors 강의를 번역한 글입니다.
에러 핸들링
이전 챕터에서, Server Action을 이용한 데이터 변형 방법을 배웠습니다. 이제 자바스크립트
try/catch
문과 Next.js API를 이용해서 우아하게(gracefully) 에러를 처리하는법을 배웁시다.이번 챕터에서...
여기 우리가 다룰 주제들이 있습니다.
error.tsx
파일을 사용하여 라우트 경로의 에러를 잡는 방법과 유저에게 대체 UI를 보여주는 방법- 404 에러(경로를 찾을 수 없습니다.)를 처리하기 위한
notFound
함수와not-found
파일을 사용하는 방법
Server Action에
try/catch
추가하기우선, 우리가 만든 Server Action에 자바스크립트의
try/catch
구문을 추가해서 에러를 다뤄봅시다.이미 어떻게 할 지 알 것 같으면, 시간을 조금 들여 Server Actions를 업데이트 해보세요, 아니면 아래의 코드를 복사해도 좋습니다.
/app/lib/actions.ts
export async function createInvoice(formData: FormData) { const { customerId, amount, status } = CreateInvoice.parse({ customerId: formData.get('customerId'), amount: formData.get('amount'), status: formData.get('status'), }); const amountInCents = amount * 100; const date = new Date().toISOString().split('T')[0]; try { await sql` INSERT INTO invoices (customer_id, amount, status, date) VALUES (${customerId}, ${amountInCents}, ${status}, ${date}) `; } catch (error) { return { message: 'Database Error: Failed to Create Invoice.', }; } revalidatePath('/dashboard/invoices'); redirect('/dashboard/invoices'); }
/app/lib/actions.ts
export async function updateInvoice(id: string, formData: FormData) { const { customerId, amount, status } = UpdateInvoice.parse({ customerId: formData.get('customerId'), amount: formData.get('amount'), status: formData.get('status'), }); const amountInCents = amount * 100; try { await sql` UPDATE invoices SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status} WHERE id = ${id} `; } catch (error) { return { message: 'Database Error: Failed to Update Invoice.' }; } revalidatePath('/dashboard/invoices'); redirect('/dashboard/invoices'); }
/app/lib/actions.ts
export async function deleteInvoice(id: string) { try { await sql`DELETE FROM invoices WHERE id = ${id}`; revalidatePath('/dashboard/invoices'); return { message: 'Deleted Invoice.' }; } catch (error) { return { message: 'Database Error: Failed to Delete Invoice.' }; } }
try/catch 블록 바깥에서
redirect
가 어떻게 불리는지 확인해보세요. error를 던지고 나서 리다이렉트가 동작하는걸 피하기 위해 우리는redirect
을try/catch
후에 호출했습니다.redirect
는 오직try
가 성공한 후에 불립니다.이제 Server Action에서 에러가 발생할 때 무엇이 일어나는지 확인해볼겁니다.
deleteInvoice
액션 내 함수 최상단에 에러를 던져봅시다./app/lib/actions.ts
export async function deleteInvoice(id: string) { throw new Error('Failed to Delete Invoice'); // Unreachable code block try { await sql`DELETE FROM invoices WHERE id = ${id}`; revalidatePath('/dashboard/invoices'); return { message: 'Deleted Invoice' }; } catch (error) { return { message: 'Database Error: Failed to Delete Invoice' }; } }
송장을 지우려고 시도할때, 로컬호스트에서 에러를 볼 수 있습니다.
개발하는 동안 이러한 에러를 보는 것은 잠재적인 문제를 미리 볼 수 있으므로 도움이 됩니다. 하지만 실제 유저에게는 갑작스러운 실패 로그보다는 어플리케이션이 계속 동작하는 모습을 보여주고 싶을겁니다.
여기서 Next.js의
error.tsx
가 필요한 부분입니다.error.tsx
로 에러 다루기error.tsx
파일은 라우트 경로에 UI 바운더리를 정의하기 위해 쓰일 수 있습니다. 이것은 예상하지 못한 에러를 위한 광범위한 에러 페이지로서(catch-all) 동작하며 대체 UI를 보여줄 수 있습니다./dashboard/invoice
폴더 내에,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(() => { // Optionally log the error to an error reporting service console.error(error); }, [error]); return ( <main className="flex h-full flex-col items-center justify-center"> <h2 className="text-center">Something went wrong!</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={ // Attempt to recover by trying to re-render the invoices route () => reset() } > Try again </button> </main> ); }
위의 코드에서 주목할게 몇가지 있습니다.
`- "use client" -
error.tsx
는 클라이언트 컴퍼넌트 입니다. - 2가지 props를 받습니다
error
: 자바스크립트의 네이비트Error
오브젝트 입니다.reset
: 이 함수는 에러 바운더리를 리셋하는데 쓰입니다. 실행이되면, 함수는 해당 결로를 다시 렌더링 합니다.
송장을 지우려고 다시 시도해보면, 다음과 같은 UI를 볼 수 있습니다.
notFound
함수를 이용한 404 에러 핸들링에러를 우아하게(gracefully) 다룰 다른 한가지 방법은
notFound
함수를 쓰는 겁니다.error.tsx
가 모든 에러를 잡는다면,notFound
는 존재하지 않는 리소스를 가져오려 할때 사용할 수 있습니다.예를들어 http://localhost:3000/dashboard/invoices/2e94d1ed-d220-449f-9f11-f0bbceed9645/edit 를 방문해보세요.
이것은 가짜 UUID로, 데이터베이스내에 존재하지 않습니다.
error.tsx
를 볼 겁니다. 왜냐하면error.tsx
가 정의된/invoices
의 하위 경로이기 때문이죠.만약 더 구체적인 오류 표시가 되길 원한다면, 404 에러를 보여줘서 유저에게 지금 접근하려는 경로가 존재하지 않는다고 알려줄 수 있습니다.
data.ts
파일 내에fetchInvoiceById
함수로 가서 데이터가 존재하지 않는지 확인할 수 있고invoice
를 리턴하는 로그도 추가할 수 있습니다./app/lib/data.ts
export async function fetchInvoiceById(id: string) { noStore(); try { // ... console.log(invoice); // Invoice is an empty array [] return invoice[0]; } catch (error) { console.error('Database Error:', error); throw new Error('Failed to fetch invoice.'); } }
이제 데이터베이스 내에 송장이 존재하지 않는걸 알았으니,
notFound
를 사용해서 에러를 처리합시다./dashboard/invoices/[id]/edit/page.tsx
로 이동해서,'next/navigation'
에서notFound
를 불러(import)오세요./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를 보여주기 위해not-found.tsx
파일을/edit
폴더 내에 생성합시다.그리고나서,
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>Could not find the requested invoice.</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" > Go Back </Link> </main> ); }
이제 해당 페이지를 새로고침하세요. 다음과 같은 UI를 볼 수 있습니다.
notFound
는error.tsx
보다 우선 적용 된다는걸 명심하세요 그래서 더 구체적인 오류를 처리하고 싶을때 이것에 접근할 수 있습니다.퀴즈할 시간입니다!
익힌걸 테스트해보고 무엇을 배웠는지 봅시다.
라우트 경로에서 예상치 못한 에러를 전반적으로 다루는 Next.js의 파일은 무엇일까요?
A 404.tsxB not-found.tsxC error.tsxD catch-all.tsx읽을거리
Next.js의 에러 핸들링에 대해 더 배우고싶다면, 다음의 문서들을 확인하세요.
챕터 13을 완료했습니다.
이제 어플리케이션에서 우아하게 에러를 다룰 수 있습니다.
Ref
'Web' 카테고리의 다른 글
[NextJS] 튜토리얼 챕터 15 - 인증 추가하기 (2) 2024.06.07 [NextJS] 튜토리얼 챕터 14 - 접근성 향상 (2) 2024.06.05 웹 개발 용어사전 (0) 2024.06.01 [NextJS] 튜토리얼 챕터 12 - 데이터 변형(Mutating) (0) 2024.06.01 [NextJS] 튜토리얼 챕터 11 - 검색과 페이지네이션 추가하기 (0) 2024.05.30