Next.js 15에서 error.js 파일로 에러 처리하는 방법 정리

Next.js 15에서 error.js 파일로 에러 처리하는 방법 정리
TILPosted On Apr 22, 20258 min read

error.js 파일 이해하기: 예기치 않은 에러 처리하기

프로그래밍 하다 보면 갑자기 예상하지 못한 에러가 발생할 때가 있어요. 이럴 때 사용자에게 깔끔한 ‘에러 메시지’를 보여주고, 앱이 갑자기 죽지 않도록 안전하게 처리하는 게 중요하죠. 이번에 소개할 error.js 파일은 그런 에러를 잡아내고 사용자에게 알림 UI(화면)를 보여주는 역할을 해요.


핵심 코드 정리

'use client' // Error boundaries는 Client 측 컴포넌트여야 해요

import { useEffect } from 'react'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  useEffect(() => {
    // 에러가 발생하면 콘솔과 같은 에러 리포팅 서비스에 기록하세요
    console.error(error)
  }, [error])

  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={() => reset()}>
        Try again
      </button>
    </div>
  )
}

주요 포인트

항목설명
'use client'에러 경계(Error Boundary)는 클라이언트 컴포넌트여야 한다는 점! 서버 컴포넌트에서는 에러 경계를 사용할 수 없어요.
useEffect에러가 발생하면 로깅(log)하는 부분이에요. 보통 여기에 Sentry, LogRocket 같은 에러 수집 서비스 코드를 넣기도 하죠.
reset 함수사용자가 “Try again” 버튼을 누르면 이 reset 함수가 실행되면서, 문제가 발생한 부분을 다시 렌더링하는 시도를 합니다.

제가 더 알려드리고 싶은 점!

  • 에러 경계란?
    리액트에서는 ‘에러 경계’라는 개념이 있는데, 컴포넌트 트리 어느 한 부분에서 에러가 나더라도, 해당 부분만 격리해서 에러 UI를 보여주고 전체 앱이 멈추지 않도록 도와줍니다.

  • 에러 처리할 때 어디에 쓰면 좋을까?
    보통 사용자가 조작하는 주요 화면 컴포넌트 주변에 적용하면 좋아요. 예를 들어, 특정 API 호출이 실패하거나, 컴포넌트 내부 상태가 꼬였을 때 앱이 멈추는 걸 방지하죠.

  • 추가 아이디어
    에러 메시지를 조금 더 사용자 친화적으로 바꾸거나, 에러 상세 내용을 개발자용 로그에만 남기고 사용자는 간단하게 안내하는 것도 좋은 UX 방식입니다.

  • 실무 꿀팁!
    에러 발생 시 단순히 console.error만 하기보단, 별도의 에러 모니터링 툴(Sentry, Bugsnag 등)과 연동해두면 실시간으로 문제를 감지하고 빠르게 대응할 수 있어서 정말 좋아요.


요약하면, error.js 파일은 “앱이 갑자기 멈추지 않고 사용자에게 안내 메시지를 띄우면서, 다시 시도할 수 있도록 도와주는 작은 안전망” 같은 역할을 하는 컴포넌트입니다. 다음 프로젝트에 적용해보면 에러 대응이 한결 수월해질 거예요! 😊

error.js는 라우트 세그먼트와 그 안에 중첩된 자식들을 React의 에러 경계(Error Boundary)로 감싸주는 역할을 해요. 만약 이 경계 내에서 에러가 발생하면, 에러 컴포넌트가 대체 UI(fallback UI)로 보여지게 되죠.

Error Boundary 예시

알아두면 좋은 팁! React DevTools에서는 에러 경계를 토글해가며 실제 에러 상태를 테스트할 수 있어요. 만약 하위 에러 경계에서 발생한 에러가 부모 에러 경계까지 전달되길 원한다면, 에러 컴포넌트 렌더링 시점에 에러를 다시 던지(throw)면 됩니다.

에러 경계(Error Boundary)는 React에서 UI가 예기치 않게 깨지는 상황을 방지하고 사용자에게도 친절한 메시지를 보여줄 수 있는 좋은 방법인데요, React 16 버전부터 도입된 기능이랍니다. 에러 경계 안에서만 렌더링되는 컴포넌트가 문제가 생기면 그 부분만 대체 UI로 교체되고, 전체 앱이 다운되는 걸 막아줘요.

만약 이 기능을 제대로 활용하고싶다면, 한번 직접 React DevTools에서 에러 경계 토글 기능을 사용해 보면서 에러 상태 테스트를 해보는 걸 추천드려요. 실제 서비스에서 에러 상황을 어떻게 커버할지 고민할 때 큰 도움이 될 거예요!

필요하면 이걸로 사용자에게 에러 메시지를 커스텀하거나, 에러 로그를 서버로 보내는 등의 작업도 할 수 있으니 참고하시고요.

참고 자료

Props

error

errorerror.js 클라이언트 컴포넌트로 전달되는 Error 객체의 인스턴스입니다.

알아두면 좋은 점: 개발 환경에서는 클라이언트로 전달되는 Error 객체가 직렬화되어 원래 에러 메시지를 포함해 디버깅이 더 쉽도록 도와줍니다. 하지만 프로덕션 환경에서는 잠재적으로 민감한 정보가 클라이언트로 유출되는 것을 막기 위해 이 동작이 달라진다는 점 참고하세요.

추가로, 이런 에러 핸들링 방식을 통해 개발할 때에는 에러 메시지를 좀 더 자유롭게 확인할 수 있지만, 실제 운영 환경에서는 안전을 최우선으로 생각하기 때문에 에러 내용이 제한적으로 전달된다는 점 기억해두시면 좋아요. 에러 관련 코드를 작성할 때는 이 점을 고려해 로깅이나 모니터링도 함께 신경 써주세요!

error.message

  • 클라이언트 컴포넌트에서 전달된 에러는 원래의 에러 메시지를 그대로 보여줘요.
  • 반면, 서버 컴포넌트에서 온 에러는 예민한 정보를 노출하지 않기 위해 일반적인 메시지와 식별자(identifier)를 보여준답니다.
    이 식별자, 즉 errors.digest 값을 사용하면 서버 로그에서 해당 에러를 쉽게 찾아낼 수 있어요.

error.digest

이건 자동으로 생성된 에러 해시 값이에요.
서버 쪽 로그와 에러를 매칭할 때 아주 유용하죠.
즉, 사용자에게는 세세한 정보 대신 이 해시값만 보여주고, 개발자는 이 해시값을 통해 문제를 추적할 수 있는 거예요.


조금 더 알아볼까요?

서버 컴포넌트에서 에러 메시지에 상세한 내용을 숨기는 이유는 보안 때문이에요.
만약 민감한 코드 구조나 데이터가 클라이언트에 노출되면 해커들이 공격에 악용할 수 있거든요.
그래서 '어떤 문제가 발생했는지'는 알려주되, '어떤 코드에서, 왜'에 관한 자세한 건 서버 쪽에만 기록해서 관리하는 거죠.

이런 구조 덕분에 사용자 경험은 방해받지 않으면서, 개발자는 충분한 디버깅 정보를 확보할 수 있답니다.

혹시 서버 로그에서 에러를 찾으려면 error.digest 값을 복사해서 검색해보세요!
이게 생각보다 꽤 쏙쏙 잘 맞아 떨어진답니다. 🙂

리셋 (reset)

가끔 에러가 일시적인 경우가 있어요. 이런 경우에는 다시 시도하면 문제를 해결할 수 있죠.

에러 컴포넌트에서는 reset() 함수를 사용해서 사용자에게 에러에서 복구할 기회를 줄 수 있어요. 이 함수가 호출되면, 에러 경계(error boundary)에 감싸져 있는 컴포넌트를 다시 렌더링하려고 시도합니다. 만약 다시 렌더링에 성공하면 기존의 에러 화면(fallback)이 정상 화면으로 바뀌게 되죠.

사용법은 아주 간단해요. 예를 들어:

'use client' // 에러 경계는 반드시 클라이언트 컴포넌트여야 합니다

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div>
      <h2>문제가 발생했습니다!</h2>
      <button onClick={() => reset()}>다시 시도하기</button>
    </div>
  )
}

위 예제에서 reset() 함수가 버튼 클릭 시 호출되고, 이로 인해 오류 상태가 초기화되어 정상 화면을 다시 시도하게 되는 거죠.


추가 팁!

  • reset()은 React의 에러 경계 기능과 연동되어 동작하기 때문에, Next.js 13의 app 디렉토리 기반에서 클라이언트 컴포넌트로 작성해야 합니다.
  • reset()을 사용할 땐 오류 발생 시 사용자에게 친절하게 안내하는 UI를 보여주는 게 좋아요. 예를 들어, 에러 내용과 함께 재시도 버튼을 제공하면 사용자 경험이 훨씬 좋아집니다.
  • 일시적인 네트워크 문제나 API 호출 실패 같은 상황에서 특히 유용하니, 네트워크 상태 변화 이벤트와도 같이 활용하면 효과적이에요.

예시

전역 에러 처리(Global Error)

전역 에러 처리는 조금 덜 흔하지만, 루트 앱 디렉토리에 global-error.js 파일을 만들어서 루트 레이아웃(root layout)이나 템플릿(root template)에서 발생하는 에러를 잡을 수 있어요. 특히 다국어 지원(i18n)을 사용할 때도 문제없이 동작합니다. 여기서 중요한 점! 전역 에러 UI는 반드시 htmlbody 태그를 직접 정의해야 해요. 이 파일이 활성화되면 해당 루트 레이아웃이나 템플릿을 대체하게 된답니다.

'use client' // 에러 바운더리는 클라이언트 컴포넌트여야 해요

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <html>
      <body>
        <h2>문제가 발생했어요!</h2>
        <button onClick={() => reset()}>다시 시도하기</button>
      </body>
    </html>
  )
}

추가 팁!

  • reset 함수는 에러 상태를 초기화해서 컴포넌트를 리프레시하는 역할을 해요. 사용자 경험을 위해 꼭 넣어주세요.
  • 전역 에러 컴포넌트가 활성화되면 기존의 레이아웃이나 템플릿이 완전히 대체되니, 디자인 요소나 공통 레이아웃을 포함시키고 싶으면 직접 만들어야 한다는 점 기억하세요.
  • **에러 경계(Error Boundaries)**는 클라이언트에서만 동작하기 때문에 꼭 'use client'를 선언해야 하는 점도 꼭 체크!

이렇게 전역에서 에러를 처리하면 예상치 못한 문제가 발생해도 깔끔하게 사용자에게 안내할 수 있어서 앱 안정성에 큰 도움이 됩니다.

버전 히스토리

버전변경 사항
v15.2.0개발 환경에서 global-error도 표시되도록 변경됨.
v13.1.0global-error 기능 추가됨.
v13.0.0error 기능 추가됨.

여기서 잠깐!

  • global-error는 전역적으로 발생하는 에러를 한눈에 확인할 수 있게 해주는 기능이에요. 특히 개발할 때 어디서 문제가 생겼는지 빠르게 파악할 수 있어서 무척 유용하죠.
  • 버전 히스토리를 잘 확인해두면 새로 추가된 기능이나 변경된 점을 놓치지 않을 수 있으니 개발하면서 꼭 참고하시길 추천해요!