본문 바로가기
웹 프로그래밍/Next.js

[Next.js 공식문서 정리] Routing : 03. Error Handling

by 청량리 물냉면 2025. 3. 29.
반응형

3. 에러 핸들링


에러는 예상된 에러(expected errors)예기치 않은 에러(uncaught exceptions) 두 가지 범주로 나눌 수 있다.

✅ 1. 예상된 에러(Expected Errors)는 반환 값으로 처리해야 한다.

  • 서버 액션(Server Actions)에서 예상된 에러를 처리할 때는 try/catch를 사용하지 않는 것이 좋다.
  • 대신, useActionState를 사용하여 에러를 관리하고 클라이언트로 반환하는 것이 바람직하다.

✅ 2. 예기치 않은 에러(uncaught exceptions)는 에러 경계(error boundaries)를 사용해야 한다.

  • 예기치 않은 에러를 처리하려면 error.tsxglobal-error.tsx 파일을 활용하여 에러 경계(error boundaries) 를 구현해야 한다.
  • 이를 통해 애플리케이션이 갑자기 중단되지 않도록 하고, 사용자에게 적절한 대체 UI (fallback UI)를 제공할 수 있다.

1. 예상된 에러(Expected Errors) 처리

예상된 에러는 애플리케이션의 정상적인 동작 중에 발생할 수 있는 에러로, 예를 들면 서버 측 폼 검증 오류API 요청 실패 등이 포함된다. 이러한 에러는 명시적으로 처리하여 클라이언트에 반환해야 한다.

✅ 서버 액션(Server Actions)에서의 예상된 에러 처리

"use server";

import { redirect } from "next/navigation";

export async function createUser(prevState: any, formData: FormData) {
  const res = await fetch("https://...");
  const json = await res.json();

  if (!res.ok) {
    return { message: "Please enter a valid email" }; // 에러 메시지 반환
  }

  redirect("/dashboard"); // 성공 시 대시보드로 리디렉션
}
  • 위의 예제처럼, useActionState 훅을 사용하여 서버 액션의 상태를 관리하고 에러를 처리한다.
  • 예상된 에러는 try/catch로 감싸기보다는 반환 값(return value)으로 처리하는 것이 좋다.
"use client";

import { useActionState } from "react";
import { createUser } from "@/app/actions";

const initialState = {
  message: "",
};

export function Signup() {
  const [state, formAction, pending] = useActionState(createUser, initialState);

  return (
    <form action={formAction}>
      <label htmlFor="email">Email</label>
      <input type="text" id="email" name="email" required />
      {/* 에러 메시지 출력 */}
      <p aria-live="polite">{state?.message}</p>​<button disabled={pending}>Sign up</button>
    </form>
  );
}

❗ useActionState 훅의 사용법

const [state, formAction, pending] = useActionState(createUser, initialState);
// useActionState(action, initialState, permalink?)
  • useActionState 훅을 사용하여 createUser의 상태를 관리한다.
  • 위의 코드에서는, state.message를 활용하여 에러 메시지를 사용자에게 표시할 수 있다.(state.message는 예시 코드로, 서버 액션에서 반환하는 데이터 구조에 따라 달라질 수 있다.)
  • pending 값이 true일 경우 버튼을 비활성화하여 중복 요청을 방지할 수 있다.

✅ 서버 컴포넌트(Server Components)에서의 예상된 에러 처리

  • 서버 컴포넌트 내부에서 데이터를 가져올 때 응답 상태를 기반으로 에러 메시지를 조건부 렌더링하거나 리디렉션할 수 있다.
export default async function Page() {
  const res = await fetch(`https://...`);
  const data = await res.json();

  if (!res.ok) {
    return "There was an error."; // 에러 발생 시 메시지 출력
  }

  return "..."; // 정상적인 데이터 렌더링
}

2. 예기치 않은 예외(Uncaught Exceptions) 처리

예기치 않은 예외는 애플리케이션의 정상적인 흐름에서 발생하지 말아야 하는 오류로, 코드의 버그나 예측하지 못한 오류가 포함된다.
이러한 오류는 에러 경계(Error Boundaries) 를 사용하여 처리해야 한다.

✅ 에러 경계(Error Boundaries) 사용

  • Next.js에서는 에러 경계 컴포넌트를 사용하여 예기치 않은 에러를 처리한다.
  • 에러 경계는 자식 컴포넌트에서 발생한 에러를 감지하고, 애플리케이션이 완전히 중단되지 않도록 대체 UI (fallback UI)를 제공한다.
  • route 세그먼트 내부에 error.tsx 파일을 추가하고, React Component를 export 하여 에러 경계 컴포넌트를 생성한다.

app/dashboard/error.tsx (특정 경로에서 에러 경계 적용)

"use client"; // 에러 경계는 반드시 클라이언트 컴포넌트여야 함

import { useEffect } from "react";

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  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>
  );
}
  • error.tsx 파일은 특정 경로(app/dashboard)에서 발생하는 에러를 감지하고 처리하는 에러 경계 역할을 한다.
  • reset() 함수를 호출하면 해당 페이지를 다시 렌더링하여 에러를 해결한다.
  • useEffect를 사용하여 에러를 콘솔 또는 로깅 서비스에 기록할 수 있다.

✅ 중첩된 에러 경계(Nested Routes Error Handling)

Next.js에서는 에러가 발생한 위치에서 가장 가까운 부모 에러 경계로 버블링(전파) 된다.
즉, 중첩된 라우트에 각각 error.tsx를 배치하면 더 세밀한 에러 처리가 가능하다.

✅ 글로벌 에러(Global Errors) 처리

전역 에러 처리는 일반적으로 필요하지 않지만, 국제화(i18n)를 사용하는 경우 등에서 유용할 수 있다.
Next.js에서는 app/global-error.tsx 파일을 사용하여 애플리케이션 전체에 대한 에러 처리를 수행할 수 있다.

app/global-error.tsx (전역 에러 경계)

"use client"; // Error boundaries must be Client Components

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    // global-error must include html and body tags
    <html>
      <body>
        <h2>Something went wrong!</h2>
        <button onClick={() => reset()}>Try again</button>
      </body>
    </html>
  );
}
  • 글로벌 에러 경계는 애플리케이션 전체에서 발생하는 오류를 감지한다.
  • 주의할 점: global-error.tsx는 이 컴포넌트는 전체 레이아웃을 대체하기 때문에, <html><body> 태그를 직접 포함해야 한다.

📝 정리

유형처리 방식

유형 설명
예상된 에러 useActionState 또는 응답 상태를 기반으로 처리
예기치 않은 예외 error.tsx(에러 경계) 사용
중첩된 에러 경계 error.tsx를 다양한 경로에 배치하여 처리
전역 에러 global-error.tsx를 활용하여 애플리케이션 전체 에러 처리
  • 예상된 에러는 반환 값으로 모델링해야 하며, try/catch 사용을 지양해야 함
  • 예기치 않은 예외는 에러 경계를 활용하여 처리해야 하며, error.tsx 또는 global-error.tsx를 적절히 배치하는 것이 중요

 


출처

https://nextjs.org/docs/app/building-your-application/routing/error-handling

 

Routing: Error Handling | Next.js

Learn how to display expected errors and handle uncaught exceptions.

nextjs.org

 

반응형