Error Handling
error.js 파일 컨벤션을 사용하면 중첩된 route에서 런타임 오류를 적절하게 처리할 수 있다.
- route segment와 중첩된 자식들의 React Error Boundary로 자동으로 래핑한다.
- 파일 시스템 계층을 사용하여 특정 segment에 맞게 조정된 error UI를 만들어 세세하게 조정한다.
- 영향을 받는 segment에 대한 error를 격리하고, app의 나머지 부분을 작동하는 상태로 유지한다.
- 전체 페이지를 다시 로드하지 않고 error 복구를 시도하는 기능을 추가한다.
route segment에 error.js를 추가함으로써 error UI를 만들고 export하여 React 컴포넌트로 사용한다.
// app/dashboard/error.tsx
'use client' // Error components must be Client Components
import { useEffect } from 'react'
export default function Error({
error,
reset,
}: {
error: Error
reset: () => void
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error)
}, [error])
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>
Try again
</button>
</div>
)
}
How error.js Works
- error.js는 중첩된 하위 segment나 page.js 컴포넌트를 감싸는 React Error Boundary를 자동으로 만든다.
- error.js에서 export된 React 컴포넌트는 fallback(대체) 컴포넌트로 사용된다.
- error boundary안에서 error가 발생하면 error가 포함되고 fallback 컴포넌트가 렌더링된다.
- fallback 에러 컴포넌트가 활성화되면 error boundary 위의 레이아웃은 상태를 유지하고 여전히 interactive하며, 에러 컴포넌트는 에러 복구 기능을 표시할 수 있다.
Recovering From Errors
에러는 때때로 일시적일 수도 있다. 이러한 상황에서는 다시 시도하기만 해도 문제가 해결될 수 있다.
에러 컴포넌트는 reset() 함수를 사용해서 사용자에게 에러 복구를 시도하라는 메시지를 보낼 수 있다. 실행되면, error boundary의 내용을 다시 렌더링하려고 시도한다. 성공하면 fallback 에러 컴포넌트가 리렌더링의 결과로 바뀐다.
// app/dashboard/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
)
}
Nested Routes
특수한 파일을 통해 작성된 React 컴포넌트는 특별한 중첩 계층 구조로 렌더링된다.
예를 들어 layout.js 및 error.js 파일을 모두 포함하는 두 개의 segment가 있는 중첩 route는 다음과 같은 단순화된 컴포넌트 계층 구조로 렌더링된다. :
중첩된 컴포넌트 계층은 중첩된 route에서 error.js 파일의 동작에 영향을 미친다 :
- 에러는 가장 가까운 부모 error boundary로 올라간다. 즉, error.js 파일은 중첩된 모든 하위 segment에 대한 오류를 처리한다. route의 중첩된 폴더에 error.js 파일을 서로 다른 level에 배치하면 더 혹은 덜 세부적인 error UI를 만들 수 있다.
- error boundary는 레이아웃 컴포넌트 내부에 중첩되어 있기 때문에 error.js boundary는 동일한 segment의 layout.js 컴포넌트안에서 발생한 에러는 처리하지 않는다.
Handling Errors in Layouts
error.js boundary는 동일한 segment에 있는 layout.js 컴포넌트나 template.js 컴포넌트에서 발생한 에러는 처리하지 않는다. 이러한 의도된 계층 구조는 에러가 발생할 때 형제 route(navigation 같은) 간에 공유되는 중요한 UI를 표시하고 작동하도록 유지한다.
특정 레이아웃 또는 템플릿 내의 에러를 처리하려면 레이아웃 상위 segment에 error.js파일을 배치한다.
루트 레이아웃 또는 템플릿 내의 오류를 처리하려면 global-error.js라는 error.js의 변형을 사용한다.
Handling Errors in Root Layouts
루트 app/error.js boundary는 루트 app/layout.js 또는 app/template.js 컴포넌트에 발생한 오류를 처리하지 않는다.
이러한 루트 컴포넌트의 에러를 처리하려면 루트 app 디렉토리에 있는 app/global-error.js라는 error.js의 변형을 사용한다.
루트 error.js와 다르게 global-error.js 에러 boundary는 전체 애플리케이션을 감싸고, 활성화된 경우 해당 fallback 컴포넌트가 루트 레이아웃을 대체한다. 따라서 global-error.js는 자체 <html> 및 <body> 태그를 정의해야 한다.
global-error.js는 가장 세분화되지 않은 error UI이며, 전체 애플리케이션에 대한 'catch-all' 에러 처리로 생각할 수 있다. 루트 컴포넌트는 일반적으로 동적이지 않고 다른 error.js boundary가 대부분의 에러를 잡아내기 때문에 자주 동작하지는 않는다.
global-error.js를 정의하더라도 global UI 및 branding을 포함하는 루트 레이아웃 내에서 fallback 컴포넌트를 렌더링할 루트 error.js를 정의하는 것이 좋다.
// app/global-error.tsx
'use client'
export default function GlobalError({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</body>
</html>
)
}
Handling Server Errors
데이터를 fetch하는 동안 혹은 서버 컴포넌트 내부에서 에러가 발생하면 Next.js는 결과 에러객체를 error prop으로 가장 가까운 error.js 파일에 전달한다.
next dev를 실행중일 때 에러가 serialize(직렬화)되어 서버 컴포넌트에서 클라이언트 error.js로 전달된다. 프로덕션에서 next start를 실행할 때 보안을 보장하기 위해 generic 에러 메시지가 에러 메시지의 해시가 포함된 .digest와 함께 에러로 전달된다. 이 해시는 서버 로그에서 사용될 수 있다.
Reference
- https://nextjs.org/docs/app/building-your-application/routing/error-handling
Routing: Error Handling | Next.js
Using App Router Features available in /app
nextjs.org
'개인공부' 카테고리의 다른 글
Next.js Docs (10) Intercepting Routes (0) | 2023.07.05 |
---|---|
Next.js Docs (9) Parallel Routes (0) | 2023.07.02 |
Next.js Docs (7) Loading UI and Streaming (0) | 2023.06.24 |
Next.js Docs (6) Dynamic Routes (0) | 2023.06.20 |
Next.js Docs (5) Route Groups (0) | 2023.06.16 |
댓글