[Next.js] Not Found Errors / Unexpected Errors 처리하기 - app router

문지은·2024년 1월 6일
2

Next.js - App Router

목록 보기
5/20
post-thumbnail

Not Found Errors 처리

  • 요청한 페이지가 존재하지 않을 때, 오류를 처리하는 방법에 대해 알아보자.
  • Next.js 는 기본적으로 페이지가 존재하지 않는 경우, 다음과 같은 404 페이지가 출력된다.

  • 하지만 상황에 따라 페이지를 직접 정의할 수 있다.
    • Next.js에서 명시한 규칙의 파일명 not-found.tsx 를 사용하면 된다.

not-found.tsx

  • Global not-found
    • 앱 전체 범위에 대한 Not Found 파일
    • app/not-found.tsx
  • Local not-found
    • 원하는 라우터에 대한 Not Found 파일
    • ex) app/users/[id]/not-found.tsx

Global not-found

  • app 폴더에 not-found.tsx 를 추가하고 컴포넌트를 작성해보자.
    • 존재하지 않는 페이지에 접근했을 때 해당 컴포넌트가 렌더링된다.

app/not-found.tsx

import React from "react";

const NotFound = () => {
  return <div>요청하신 페이지는 존재하지 않습니다.</div>;
};

export default NotFound;
  • 이제 존재하지 않은 페이지에 접근했을 때, 작성한 오류메시지가 출력되는 것을 볼 수 있다.

Local not-found

  • 앱 전체 범위 뿐만 아니라 원하는 라우터에 모두 not-found.tsx 파일을 사용할 수 있다.
  • 예를 들어, 존재하지 않는 사용자를 보려고 할 때 해당 사용자가 존재하지 않음을 오류 메시지로 출력할 수 있다.

한 번 구현해보자!

  • 이를 구현하기 위해서는 Dynamic Routes를 사용해야 한다.
    • Dynamic Routes는 기본 URL이 존재하고, 그 뒤로는 동적으로 변하는 값이 오는 라우터를 처리하는 방식으로 동작한다.
  • 동적 라우트 폴더 내에 not-found.tsx 파일을 생성하고 출력할 오류 컴포넌트를 작성한다.

app/users/[id]/not-found.tsx

import React from "react";

const UserNotFound = () => {
  return <div>사용자가 존재하지 않습니다.</div>;
};

export default UserNotFound;
  • next/navigation 모듈을 사용하면 다음과 같이 not-found, loading 등의 next.js에서 지정한 파일을 마치 함수처럼 호출할 수 있다.
import { notFound } from "next/navigation";
  • id 가 10보다 큰 경우, 사용자가 존재하지 않는다고 가정하고, 해당 경우에 not-found 페이지로 이동하도록 수정해보자.

app/users/[id]/page.tsx

import React from "react";
import { notFound } from "next/navigation";

interface Props {
  params: { id: number };
}
const UserDetailPage = ({ params: { id } }: Props) => {

  if (id > 10) notFound();

  return <div>UserDetailPage {id}</div>;
};

export default UserDetailPage;
  • 브라우저에서 id 가 10보다 큰 사용자 페이지에 접근하려고 하면, 작성한 오류 페이지가 잘 출력되는 것을 볼 수 있다.

http://localhost:3000/users/11

Unexpected Errors

  • 이번에는 컴포넌트에서 발생한 예상치 못한 오류에 대해 처리하는 방법에 대해 알아보자.
  • 한번 user 정보를 불러오는 API 호출 함수에 고의적으로 잘못된 주소를 작성해보자.

app/users/UserTable.tsx

import React from "react";
import Link from "next/link";
import { sort } from "fast-sort";

interface User {
  id: number;
  name: string;
  email: string;
}

interface Props {
  sortOrder: string;
}

const UserTable = async ({ sortOrder }: Props) => {
	// 잘못된 주소 작성
  const res = await fetch("https://jsonplaceholder.typicode.com/users-error");
  const users: User[] = await res.json();

  const sortedUsers = sort(users).asc(
    sortOrder === "email" ? (user) => user.email : (user) => user.name
  );

  return (
    <table>
      <thead>
        <tr>
          <th>
            <Link href="/users?sortOrder=name">Name</Link>
          </th>
          <th>
            <Link href="/users?sortOrder=email">Email</Link>
          </th>
        </tr>
      </thead>
      <tbody>
        {sortedUsers.map((user) => (
          <tr key={user.id}>
            <td>{user.name}</td>
            <td>{user.email}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};

export default UserTable;
  • 브라우저에 들어가 실행해보면, 잘못된 URL에 요청을 보냈기 때문에 데이터를 받아오지 못해 오류가 발생한 것을 볼 수 있다.

http://localhost:3000/users/11

  • 오류 발생과 함께 디버깅 도구는 오류 세부 정보, 오류 메시지 및 오류 발생 위치를 알려준다.
    • 이는 개발 모드에서만 발생하며, Production 모드에서는 이렇게 상세한 페이지가 출력되지는 않는다.
    • 이를 확인하기 위해 다음 명령어를 통해 앱을 빌드하고 실행해보자.
npm run build
npm start
  • 개발 모드에서 오류 페이지에 대한 자세한 정보를 제공하지 않는 것을 확인할 수 있다.
    • 보안상의 목적으로 개발자만 봐야하는 정보이기 때문

  • 위와 같은 페이지도 not-found.tsx 처럼 오류 메시지를 원하는 방식으로 작성하기 위해서는 어떻게 해야할까?
    • Next.js에서 명시한 규칙의 파일명 error.tsx 를 사용하면 된다.

error.tsx

  • 앱 내부적으로 예상치 못한 문제가 발생했을 때, 파일 내부에 정의된 컴포넌트가 렌더링된다.
  • 새로 고침을 하는 등 브라우저 API와 상호작용하는 경우가 많기 때문에, 반드시 클라이언트 컴포넌트로 만들어야 한다.
    • 따라서 use client 지시문을 정의해야한다.
      • 정의하지 않으면 컴파일 에러 발생!

  • error.tsx 파일을 생성하고, 오류 메시지를 정의해보자.

app/error.tsx

"use client";

import React from "react";

const ErrorPage = () => {
  return <div>예상치 못한 오류가 발생했습니다.</div>;
};

export default ErrorPage;
  • 다시 브라우저에서 확인해보면, 정상적으로 앞서 생성한 오류 메시지가 출력되는 것을 볼 수 있다.

  • 개발 모드에서는 좌측 하단 알림창을 통해 오류가 발생한 위치를 확인할 수 있다.

  • error.tsx 파일은 발생한 오류에 대한 상세 정보를 컴포넌트의 인자 값 props로 전달받는다.
    • Props 인터페이스를 정의하고 콘솔창에 error 정보를 출력해보자.

app/error.tsx

"use client";

import React from "react";

interface Props {
  error: Error;
}

const ErrorPage = ({ error }: Props) => {
  console.log("Error: ", error);

  return <div>예상치 못한 오류가 발생했습니다.</div>;
};

export default ErrorPage;

  • 일시적인 오류인 경우, 사용자가 오류가 출력된 페이지를 재로드할 수 있도록 설정할 수도 있다.
    • error.tsx 파일이 props로 전달받는 reset 메소드를 사용하면 된다.
    • 사용자가 클릭하면 reset 메소드를 실행하는 버튼을 추가해보자.

error.tsx

"use client";

import React from "react";

interface Props {
  error: Error;
  reset: () => void;
}

const ErrorPage = ({ error, reset }: Props) => {
  console.log("Error: ", error);

  return (
    <>
      <div>예상치 못한 오류가 발생했습니다.</div>
      <button className="btn" onClick={() => reset()}>
        Retry
      </button>
    </>
  );
};

export default ErrorPage;
  • 브라우저에서 reset 버튼을 클릭하면, 페이지가 재로드되는 것을 확인할 수 있다.

  • 하지만 reset 메소드를 과도하게 사용하면 불필요한 오류 로그가 여러 번 기록될 수 있다.
    • 이는 사용자 경험에도 부정적인 영향을 미칠 수 있으므로, 꼭 필요한 경우에만 사용해야 한다.

global-error.tsx

  • error.tsx 파일도 not-found.tsx 파일과 동일하게 app 폴더에 파일을 추가해 Global Unexpected Error 파일을 작성할 수도 있고, 각 라우터 폴더에 추가하면 개별적으로 오류를 처리할 수도 있다.
    • app/error.tsx
    • ex) app/users/error.tsx
  • 하지만, error.tsx 는 루트 레이아웃(app/layout.tsx) 에서 발생한 오류는 캐치하지 못한다.
    • 만약 루트 레이아웃에서 발생한 오류를 캐치하고 싶은 경우, global-error.tsx 파일을 사용해야 한다.
    • 이 또한 next.js 에서 명시한 파일명이기 때문에 반드시 이름을 지켜야 한다.
profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글