14. vue-router version 4 beforeEach, beforeResolve, beforeEnter

전역: beforeEach 가드

const router = createRouter({
    // ...
})

router.beforeEach((to, from) => {
    // ...
    // 탐색을 취소하려면 명시적으로 false를 반환해야 함.
    return false
})

탐색이 트리거될 때마다 등록 순서대로 전역 비포 가드 가 호출된다.

가드는 비동기식으로 해결될 수 있으며, 탐색은 모든 훅이 해결되기 전까지 대기 중(pending)으로 간주한다.

모든 가드 함수는 2개의 인자를 받는다.

  • to: 탐색될 경로 위치 객체
  • from: 탐색 전 현재 경로 위치 객체

그리고 선택적으로 다음 값 중 하나를 반환할 수 있다.

  • false: 현재 탐색을 취소한다. URL이 변경된 경우, from 경로로 URL이 재설정된다.
  • 경로 위치 정보: router.push()를 사용할 때처럼 경로 위치(문자열 또는 객체)를 전달한다.
    현재 탐색이 중단되고, 기존 from 위치에서 새로운 탐색 동작이 생성된다.
router.beforeEach(async (to, from) => {
  if (
    // 유저 로그인 인증여부 확인
    !isAuthenticated &&
    // ❗ 무한 리디렉션 방지
    to.name !== 'Login'
  ) {
    // 유저를 로그인 페이지로 리디렉션
    return { name: 'Login' }
  }
})

예외 상황 시 Error를 던질 수도 있다.

이 경우에도 탐색은 취소되고 router.onError() 메서드에 전달돼, 등록된 모든 콜백 함수를 호출한다.

undefined, true 또는 아무것도 반환되지 않으면, 탐색이 유효하다고 판단하고 다음 탐색 가드가 호출된다.

위의 모든 설명은 async 함수 및 Promise에서 동일한 방식으로 작동한다.

router.beforeEach(async (to, from) => {
  // canUserAccess()는 `true` 또는 `false` 중 하나를 리턴함
  const canAccess = await canUserAccess(to)
  if (!canAccess) return '/login'
})

선택적 세번째 인자 next

Vue Router의 이전 버전에서는 세번째 인자 next를 사용할 수도 있었는데, 이는 일반적인 실수의 원인이었으며, RFCopen in new window를 이유로 제거했다.

그러나 여전히 지원되므로 탐색 가드에서 세번째 인자를 전달받을 수 있다.

이 경우, 트리거되는 탐색 가드별 정확히 한 번만 next를 호출해야 한다.

그렇지 않으면 영원히 훅이 해결되지 않거나 오류가 생긴다.

다음은 인증되지 않은 사용자를 /login으로 리디렉션하는 나쁜 예제이다.

// BAD
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  // 사용자가 인증되지 않은 경우, `next`가 두 번 호출됨
  next()
})

올바른 예제:

// GOOD
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

전역: beforeResolve 가드

router.beforeResolve로 전역 가드를 등록할 수 있다.

이것은 모든 탐색에서 트리거되기 때문에 router.beforeEach와 유사하지만, 컴포넌트 내 가드 및 비동기 경로 컴포넌트가 모두 해결된 후, 최종적으로 탐색을 진행할 것인지 결정하기 위한 목적으로 호출한다.

다음은 사용자 정의 meta를 정의한open in new window 속성 requiresCamera가 있는 경로에 대해,

사용자가 카메라에 접근 권한을 부여했는지 확인하는 예이다.

router.beforeResolve(async to => {
  if (to.meta.requiresCamera) {
    try {
      await askForCameraPermission()
    } catch (error) {
      if (error instanceof NotAllowedError) {
        // ... 오류를 처리한 다음 탐색을 취소합니다.
        return false
      } else {
        // 예기치 않은 오류, 탐색을 취소하고 오류를 전역 핸들러에 전달
        throw error
      }
    }
  }
})

유저가 페이지에 접근할 수 없는 경우, router.beforeResolve는 이것을 처리하기 위해 데이터를 가져오거나 다른 작업을 실행하기에 이상적인 곳이다.

전역: afterEach 훅

전역으로 탐색 후 훅을 등록할 수도 있지만, 가드와 달리 next함수를 전달받지 않으며, 탐색에 영향을 줄 수 없다.

router.afterEach((to, from) => {
  sendToAnalytics(to.fullPath)
})

애널리틱스, 페이지 <title> 변경, 페이지 정보를 알리는 접근성 기능 및 기타 여러 작업에 유용하다.

또한 탐색 실패를 세번째 인자로 전달한다.

router.afterEach((to, from, failure) => {
  if (!failure) sendToAnalytics(to.fullPath)
})

참고: 가이드 - 탐색 실패open in new window