[번역] Next.js13 App Router - Routing - Error Handling

최영호·2023년 7월 29일
1

On this page

error.js 파일 컨벤션은 중첩 라우트에서 발생하는 예상치 못한 런타임 에러를 우아하게 처리할 수 있도록 도와줍니다.

  • 자동으로 라우트 세그먼트와 자식 세그먼트를 리액트 에러 바운더리로 감싸줍니다.
  • 입자감(granularity)을 조절하기 위해 파일 시스템 계층 구조를 사용하여 특정 세그먼트에 최적화 된 에러 UI를 생성할 수 있습니다.
  • 나머지 기능은 그대로 유지한 채로 에러가 발생한 세그먼트만 분리할 수 있습니다.
  • 전체 페이지 로딩을 하지 않고 에러를 핸들링 할 수 있는 기능을 추가할 수 있습니다.

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>
  )
}

How error.js Works

  • error.js 파일은 page.js 컴포넌트나 중첩 된 자식 세그먼트를 감싸는 리액트 에러 바운더리를 자동으로 생성합니다.
  • error.js 파일에서 export 되는 리액트 컴포넌트는 리액트 에러 바운더리에 fallback 컴포넌트로 사용 됩니다.
  • 에러 바운더리 내에서 에러가 발생하면 에러 객체가 포함 된 fallback 컴포넌트는 렌더링 됩니다.
  • fallback 에러 컴포넌트가 활성화 됬을 때 에러 바운더리 컴포넌트의 상위 컴포넌트로 자리 잡은 레이아웃은 스테이트를 유지하고 인터랙션 능력을 유지합니다. 그리고 에러 컴포넌트는 에러를 핸들링 할 수 있는 기능을 UI로 보여줄 수 있습니다.

Recovering From Errors

에러의 발생 원인은 때로는 일시적일 수 있습니다.
이런 경우엔 다시 시도해보는게 이슈를 해결할 방법이 될 수 있습니다.

에러 컴포넌트는 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>
  )
}

Nested Routes

특수 파일로 만들어진 리액트 컴포넌트는 특별한 중첩 계층 구조를 가진채 렌더링 됩니다.

예를 들어 layout.js 파일과 error.js 파일을 가지고 있는 중첩 된 두 라우트 세그먼트는 다음의 간단한 컴포넌트 계층 구조를 가지게 됩니다.

중첩 컴포넌트 계층 구조는 중첩 라우트 간 다음의 error.js 파일의 행동을 내포하고 있습니다.

  • 에러는 가장 가까운 부모 에러 바운더리로 버블링 됩니다. 따라서 error.js 파일은 중첩 되어 있는 자식 세그먼트의 에러도 핸들링 할 수 있습니다. 에러 UI의 많고 적은 입자감은 중첩 폴더 혹은 라우트에서 error.js 를 어느 위치에 놓는지로 성취할 수 있습니다.
  • error.js 경계는 동일 세그먼트에 선언 된 layout.js 에서 발생 되는 에러를 핸들링 하지 못합니다. 왜냐면 에러 바운더리는 레이아웃 컴포넌트 내부에 위치하기 때문입니다.

Handling Errors in Layouts

error.js 경계는 동일 세그먼트의 layout.js, template.js 컴포넌트의 에러를 잡아내지 못합니다.
이런 의도 된 계층 구조 원칙은 네비게이션 같은 형제 라우트간 공유 되는 중요 UI를 에러가 발생하더라도 볼 수 있도록 유지합니다.

특정 레이아웃, 템플릿에서 에러를 핸들링하고 싶다면 레이아웃의 부모 세그먼트에 error.js 파일을 생성하면 됩니다.

루트 레이아웃, 템플릿에서 에러를 핸들링하기 위해선 error.js 의 변형인 global-error.js 컴포넌트를 사용하면 됩니다.

Handling Errors in Root Layouts

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>
  )
}

Handling Server Errors

서버 컴포넌트 내에서 에러가 발생한다면 Next.js는 Error 객체를 프로덕션 모드인 경우 민감한 정보는 제거한 이후 가장 가까운 error.js 파일의 error 프롭으로 전달합니다.

Securing Sensitive Error Information

프로덕션 모드에서 클라이언트로 보내진 Error 객체는 messagedigest 속성만 가지게 됩니다.

이는 보안 예방책으로 잠재적으로 민감한 정보가 담긴 에러가 클라이언트로 전송 되는 과정에서 유출 되는 것을 막기 위함입니다.

message 속성은 에러에 대한 일반적인 설명을 담고 있고 digest 속성은 서버쪽 로그와 맞춰 볼 수 있는 자동으로 생성 된 해시 값을 담고 있습니다.

디벨롭 모드에선 클라이언트로 전해지는 Error 객체에는 쉬운 디버깅을 위해 원본 에러 객체에 message 속성을 붙여 전달 됩니다.

profile
무친 프론트엔드 개발자를 꿈꾸며...

0개의 댓글