Skip to content

오류 처리

기본적으로 로더에서 throw된 모든 오류는 예상하지 못한 오류 로 간주됩니다. 이는 내비게이션 가드와 마찬가지로 내비게이션을 중단시킵니다. 내비게이션을 중단시키므로 로더의 error 속성에는 나타나지 않습니다. 대신 Vue Router의 router.onError() 오류 처리에서 가로채집니다.

하지만 로더가 내비게이션 인식형이 아니라면, 오류는 Vue Router가 가로챌 수 없고 로더의 error 속성에 유지됩니다. 이는 lazy loader데이터 다시 불러오기의 경우에 해당합니다.

예상된 오류 정의하기

non-lazy loader에서 오류를 가로챌 수 있도록 하려면 예상된 오류 로 간주할 오류 클래스 목록을 지정할 수 있습니다. 이렇게 하면 blocking loader가 내비게이션을 중단하지 않고, 대신 로더의 error 속성에 오류를 유지하여 페이지가 로컬에서 오류 상태를 표시할 수 있습니다.

ts
import { defineBasicLoader } from 'vue-router/experimental'
// 사용자 정의 오류 클래스
class MyError extends Error {
  // override는 TS에서만 필요합니다
  override name = 'MyError' // Displays in logs instead of 'Error'
  // 생성자 정의는 선택 사항입니다
  constructor(message: string) {
    super(message)
  }
}

export const useUserData = defineBasicLoader(
  async (to) => {
    throw new MyError('Something went wrong')
    // ...
    // ---cut-start---
    return { name: 'John' }
    // ---cut-end---
  },
  {
    errors: [MyError],
  }
)

DataLoaderPluginerrors 옵션을 제공하여 모든 로더에 대해 전역적으로 예상된 오류 를 지정할 수도 있습니다.

ts
import { createApp } from 'vue'
import type { Router } from 'vue-router'
import { DataLoaderPlugin } from 'vue-router/experimental'
const app = createApp({})
const router = {} as Router
class MyError extends Error {
  name = 'MyError'
  constructor(message: string) {
    super(message)
  }
}
// @errors: 2769
// ---cut---
app.use(DataLoaderPlugin, {
  router,
  // `instanceof MyError`로 검사합니다
  errors: [MyError],
})

그런 다음 로더의 errors 옵션을 true로 설정해 opt-in해야 로더의 error 속성에 오류가 유지됩니다.

ts
import { defineBasicLoader } from 'vue-router/experimental'
// ---cut---
export const useUserData = defineBasicLoader(
  async (to) => {
    throw new Error('Something went wrong')
    // ...
    // ---cut-start---
    return { name: 'John' }
    // ---cut-end---
  },
  {
    errors: true,
  }
)
Why is errors: true needed?

Data Loaders의 장점 중 하나는 컴포넌트가 렌더링되기 전에 data가 준비되도록 보장한다는 점입니다. 하지만 예상된 오류를 허용하면 더 이상 그렇지 않아서 dataundefined일 수 있습니다:

ts
import { defineBasicLoader } from 'vue-router/experimental'
// ---cut---
export const useDataWithErrors = defineBasicLoader(
  async (to) => {
    // ...
    // ---cut-start---
    return { name: 'John' }
    // ---cut-end---
  },
  {
    errors: true,
  }
)

const { data } = useDataWithErrors()
data.value // `data` can be `undefined`

사용자 정의 오류 처리

오류 처리를 더 세밀하게 제어해야 한다면 errors 옵션에 함수를 제공할 수 있습니다. 이 옵션은 DataLoaderPlugin과 로더 정의 양쪽에서 모두 사용할 수 있습니다.

ts
// @errors: 2769
import { createApp } from 'vue'
import { DataLoaderPlugin } from 'vue-router/experimental'
const app = createApp({})
const router = {} as any
// ---cut---
app.use(DataLoaderPlugin, {
  router,
  errors: (error) => {
    // 사용자 정의 오류에 대한 관례
    if (error instanceof Error && error.name?.startsWith('My')) {
      return true
    }
    return false // 예상하지 못한 오류
  },
})

로컬 및 전역 오류를 함께 처리하기

TODO: 아직 구현되지 않았습니다

오류 처리 우선순위

전역 오류 처리와 로컬 오류 처리를 함께 사용하는 경우, 로컬 오류 처리가 더 높은 우선순위를 가지며 전역 오류 처리를 덮어씁니다. 로컬 및 전역 오류는 다음과 같이 확인됩니다:

  • 로컬 errorsfalse이면: 내비게이션 중단 -> dataundefined가 아님
  • 로컬 errorstrue이면: 전역으로 정의된 errors 옵션 사용 -> dataundefined일 수 있음
  • 그 외에는: 로컬 errors 옵션 사용 -> dataundefined일 수 있음

TypeScript

errors 옵션을 지정해도 error의 타입이 Error | null인 것을 볼 수 있습니다. 이는 reload() 메서드를 호출할 때(즉, 내비게이션 바깥에 있을 때) 오류가 버려지지 않고 errors 옵션에 의해 필터링되지 않은 채로 error 속성에 나타나기 때문입니다.

실제로는 오류를 처리하는 방식에 따라, 오류를 표시하는 컴포넌트 안이나 템플릿의 v-if 안에 직접 타입 가드를 추가하게 됩니다.

template
<template>
  <!-- ... -->
  <p v-if="isMyError(error)">{{ error.message }}</p>
</template>

더 엄격하게 하고 싶다면 TypesConfig 인터페이스를 보강하여 기본 Error 타입을 unknown(또는 다른 타입)으로 덮어쓸 수 있습니다.

ts
// types-extension.d.ts
import 'vue-router'
export {}

declare module 'vue-router' {
  interface TypesConfig {
    Error: unknown
  }
}

모두를 위한 문서 한글화