error.js
파일 컨벤션은 중첩 라우트에서 발생하는 예상치 못한 런타임 에러를 우아하게 처리할 수 있도록 도와줍니다.
error.js
파일을 라우트 세그먼트에 위치하고 리액트 컴포넌트를 export 하면 에러 UI를 생성할 수 있습니다.
'use client' // 에러 컴포넌트는 반드시 클라이언트 컴포넌트여야 합니다.
import { useEffect } from 'react'
export default function Error({
error,
reset,
}: {
error: Error
reset: () => void
}) {
useEffect(() => {
// 에러 리포트 서비스에 에러 로그를 남깁니다.
console.error(error)
}, [error])
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// 세그먼트를 다시 리렌더링 하여 에러를 핸들링 합니다.
() => reset()
}
>
Try again
</button>
</div>
)
}
error.js
파일은 page.js
컴포넌트나 중첩 된 자식 세그먼트를 감싸는 리액트 에러 바운더리를 자동으로 생성합니다.error.js
파일에서 export 되는 리액트 컴포넌트는 리액트 에러 바운더리에 fallback 컴포넌트로 사용 됩니다.에러의 발생 원인은 때로는 일시적일 수 있습니다.
이런 경우엔 다시 시도해보는게 이슈를 해결할 방법이 될 수 있습니다.
에러 컴포넌트는 reset()
함수를 사용하여 유저가 에러를 핸들링 할 수 있도록 처리합니다.
함수가 실행 되면 에러 바운더리 내의 컨텐츠를 리렌더링 합니다.
만약 성공했다면 fallback 에러 컴포넌트는 성공한 렌더링 결과물과 스왑하게 됩니다.
'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>
)
}
특수 파일로 만들어진 리액트 컴포넌트는 특별한 중첩 계층 구조를 가진채 렌더링 됩니다.
예를 들어 layout.js
파일과 error.js
파일을 가지고 있는 중첩 된 두 라우트 세그먼트는 다음의 간단한 컴포넌트 계층 구조를 가지게 됩니다.
중첩 컴포넌트 계층 구조는 중첩 라우트 간 다음의 error.js
파일의 행동을 내포하고 있습니다.
error.js
파일은 중첩 되어 있는 자식 세그먼트의 에러도 핸들링 할 수 있습니다. 에러 UI의 많고 적은 입자감은 중첩 폴더 혹은 라우트에서 error.js
를 어느 위치에 놓는지로 성취할 수 있습니다.error.js
경계는 동일 세그먼트에 선언 된 layout.js
에서 발생 되는 에러를 핸들링 하지 못합니다. 왜냐면 에러 바운더리는 레이아웃 컴포넌트 내부에 위치하기 때문입니다.error.js
경계는 동일 세그먼트의 layout.js
, template.js
컴포넌트의 에러를 잡아내지 못합니다.
이런 의도 된 계층 구조 원칙은 네비게이션 같은 형제 라우트간 공유 되는 중요 UI를 에러가 발생하더라도 볼 수 있도록 유지합니다.
특정 레이아웃, 템플릿에서 에러를 핸들링하고 싶다면 레이아웃의 부모 세그먼트에 error.js
파일을 생성하면 됩니다.
루트 레이아웃, 템플릿에서 에러를 핸들링하기 위해선 error.js
의 변형인 global-error.js
컴포넌트를 사용하면 됩니다.
app/error.js
루트 바운더리는 app/layout.js
, app/template.js
같은 루트 컴포넌트의 에러를 잡아내지 못합니다.
특별히 루트 컴포넌트에서 에러를 핸들링 하기 위해 error.js
의 변형인 app/global-error.js
파일을 app
디렉토리에 위치하여 해결할 수 있습니다.
루트 error.js
컴포넌트와 다르게 global-error.js
에러 바운더리는 어플리케이션 전체를 감싸고 fallback UI가 활성화 되면 루트 레이아웃을 대체하게 됩니다.
이런 이유 때문에 global-error.js
컴포넌트는 자신만의 <html>
, <body>
태그를 반드시 포함해야 합니다.
global-error.js
컴포넌트는 최소한의 파편(granular) 에러 UI이고 어플리케이션 전체에서 "catch-all" 에러 핸들링 컴포넌트라고 간주할 수 있습니다.
내부의 다른 error.js
바운더리가 대부분의 에러를 핸들링 하기도 하고, 루트 컴포넌트는 대부분 다이나믹하지 않기 때문에 에러 바운더리가 트리거 될 일은 흔하지 않습니다.
global-error.js
컴포넌트가 정의 되었다고 해도 글로벌하게 공유 되는 UI와 브랜딩을 가지고 있는 루트 레이아웃 내부에서 fallback 컴포넌트가 렌더링 되는 루트 error.js
컴포넌트를 정의하는 것을 추천합니다.(global-error.js 컴포넌트는 fallback UI가 렌더링 되면 전체 레이아웃도 같이 사라지기 때문입니다.)
'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>
)
}
서버 컴포넌트 내에서 에러가 발생한다면 Next.js는 Error
객체를 프로덕션 모드인 경우 민감한 정보는 제거한 이후 가장 가까운 error.js
파일의 error
프롭으로 전달합니다.
프로덕션 모드에서 클라이언트로 보내진 Error
객체는 message
와 digest
속성만 가지게 됩니다.
이는 보안 예방책으로 잠재적으로 민감한 정보가 담긴 에러가 클라이언트로 전송 되는 과정에서 유출 되는 것을 막기 위함입니다.
message
속성은 에러에 대한 일반적인 설명을 담고 있고 digest
속성은 서버쪽 로그와 맞춰 볼 수 있는 자동으로 생성 된 해시 값을 담고 있습니다.
디벨롭 모드에선 클라이언트로 전해지는 Error
객체에는 쉬운 디버깅을 위해 원본 에러 객체에 message
속성을 붙여 전달 됩니다.