본문으로 건너뛰기
개발 머꼬
개발 노트Nuxt.js
hohyeon.dev19

createError를 불렀는데 404 화면이 안 뜬 이유

  • #Common Pitfall
  • #Engineering Note
  • #Nuxt.js

문제 발생

없는 글에 접근했을 때 404를 보여주려고 오류를 던졌는데, 서버에서는 전체 화면 오류 페이지가 뜨고 클라이언트 이동에서는 아무 일도 일어나지 않았습니다.

<script setup>
const post = await findPost(route.params.slug);
if (!post) {
  createError({ statusCode: 404 });   // ❌ throw 하지 않음
}
</script>

error.vue를 만들고 나서는 그 화면에서 빠져나올 수가 없었습니다.

원인 분석

createError는 오류 객체를 만들기만 합니다. 던져야 동작합니다.

그리고 던졌을 때의 동작이 실행 위치에 따라 다릅니다. Nuxt 공식 문서가 나눠서 설명합니다.

  • 서버에서 던지면 전체 화면 오류 페이지가 뜨고, clearError로 지울 수 있습니다.
  • 클라이언트에서 던지면 치명적이지 않은(non-fatal) 오류가 던져져 우리가 직접 처리하게 됩니다. 전체 화면 오류 페이지가 필요하면 fatal: true 를 설정합니다.

error.vue에서 못 빠져나온 이유도 문서에 있습니다 — 이 컴포넌트는 앱 루트에 두는 오류 경계이며(pages/가 아닙니다), 정상 렌더링으로 돌아가려면 clearError()를 호출해야 합니다. 그리고 오류 페이지를 렌더하는 것은 완전히 별개의 페이지 로드라서, 등록된 미들웨어가 다시 실행됩니다.

해결 방안

  1. throw를 붙이고 상태 코드를 명시합니다.
<script setup>
const post = await findPost(route.params.slug);
if (!post) {
  throw createError({
    statusCode: 404,
    statusMessage: "글을 찾을 수 없습니다",
    fatal: true,
  });
}
</script>
  1. error.vue는 앱 루트에 둡니다. pages/ 안에 넣으면 그냥 페이지가 됩니다.
<!-- error.vue -->
<script setup>
const props = defineProps({ error: Object });
</script>

<template>
  <main>
    <h1>{{ error.statusCode === 404 ? "없는 페이지입니다" : "오류가 발생했습니다" }}</h1>
    <button @click="clearError({ redirect: '/' })">홈으로</button>
  </main>
</template>
  1. clearErrorredirect를 줍니다. 오류 상태만 지우면 같은 자리에 남습니다. 안전한 곳으로 함께 보냅니다.

  2. 추가 정보는 data에 담습니다. statusCode, statusText, message, data, cause, stack을 넘길 수 있습니다. 화면에서 분기해야 하는 값은 data에 둡니다.

throw createError({ statusCode: 403, data: { reason: "not-owner" } });
  1. 운영에서는 원인이 노출되지 않습니다. 문서가 명시합니다 — 개발 환경에서는 오류의 cause가 보존되어 오류 페이지에 노출되므로 원본 오류를 추적할 수 있지만, 운영에서는 cause가 오류 응답이나 오류 페이지 payload에 절대 포함되지 않습니다. 그래서 화면에 원인을 그리는 코드는 로컬에서만 동작하는 것처럼 보입니다. 진단은 서버 로그로 합니다.

  2. showError보다 throw createError()를 씁니다. 문서의 권장 형태입니다. showError도 전체 화면 오류 페이지를 띄우지만, 던지는 쪽이 흐름을 그 자리에서 끊어 의도가 분명합니다.

  3. 오류 페이지에서 데이터를 다시 부르지 않습니다. 별개의 페이지 로드라 미들웨어가 다시 도는데, 그 미들웨어가 또 실패하면 순환합니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.