middleware

Nuxt는 특정 라우트로 이동하기 전에 코드를 실행할 수 있는 미들웨어를 제공합니다.

Nuxt는 애플리케이션 전반에서 사용할 수 있는 라우트 미들웨어 프레임워크를 제공하며, 특정 라우트로 이동하기 전에 실행하고 싶은 코드를 추출하는 데 이상적입니다.

라우트 미들웨어에는 세 가지 종류가 있습니다:

  1. 익명(또는 인라인) 라우트 미들웨어는 페이지 내에서 직접 정의됩니다.
  2. middleware/에 위치한 이름 있는 라우트 미들웨어는 페이지에서 사용될 때 비동기 임포트로 자동 로드됩니다.
  3. middleware/.global 접미사가 붙은 전역 라우트 미들웨어는 모든 라우트 변경 시 실행됩니다.

첫 번째와 두 번째 종류의 라우트 미들웨어는 definePageMeta에서 정의할 수 있습니다.

미들웨어의 이름은 케밥 케이스로 정규화됩니다: myMiddlewaremy-middleware가 됩니다.
라우트 미들웨어는 Nuxt 앱의 Vue 부분에서 실행됩니다. 이름이 비슷하지만, 서버 미들웨어와는 완전히 다르며, 서버 미들웨어는 앱의 Nitro 서버 부분에서 실행됩니다.

사용법

라우트 미들웨어는 현재 라우트와 다음 라우트를 인자로 받는 내비게이션 가드입니다.

middleware/my-middleware.ts
export default defineNuxtRouteMiddleware((to, from) => {
  if (to.params.id === '1') {
    return abortNavigation()
  }
  // 실제 앱에서는 모든 라우트를 `/`로 리다이렉트하지 않을 것입니다
  // 하지만 리다이렉트 전에 반드시 `to.path`를 확인해야 하며
  // 그렇지 않으면 무한 리다이렉트 루프에 빠질 수 있습니다
  if (to.path !== '/') {
    return navigateTo('/')
  }
})

Nuxt는 미들웨어에서 직접 반환할 수 있는 두 가지 전역 헬퍼를 제공합니다.

  1. navigateTo - 지정한 라우트로 리다이렉트합니다
  2. abortNavigation - 내비게이션을 중단하며, 선택적으로 에러 메시지를 전달할 수 있습니다.

navigation guards와는 달리, vue-router의 세 번째 인자인 next()는 전달되지 않으며, 리다이렉트 또는 라우트 취소는 미들웨어에서 값을 반환함으로써 처리됩니다.

가능한 반환 값은 다음과 같습니다:

  • 아무것도 반환하지 않음(단순히 return 또는 반환문 없음) - 내비게이션을 차단하지 않으며, 다음 미들웨어 함수(있다면)로 이동하거나 라우트 내비게이션을 완료합니다
  • return navigateTo('/') - 지정한 경로로 리다이렉트하며, 서버 측에서 리다이렉트가 발생하면 리다이렉트 코드를 302 Found로 설정합니다
  • return navigateTo('/', { redirectCode: 301 }) - 지정한 경로로 리다이렉트하며, 서버 측에서 리다이렉트가 발생하면 리다이렉트 코드를 301 Moved Permanently로 설정합니다
  • return abortNavigation() - 현재 내비게이션을 중단합니다
  • return abortNavigation(error) - 현재 내비게이션을 에러와 함께 거부합니다
Read more in Docs > API > Utils > Navigate To.
Read more in Docs > API > Utils > Abort Navigation.
리다이렉트나 내비게이션 중단을 수행할 때는 위의 헬퍼 함수를 사용하는 것을 권장합니다. vue-router 문서에 설명된 다른 반환 값도 동작할 수 있지만, 향후 호환성 문제가 발생할 수 있습니다.

미들웨어 실행 순서

미들웨어는 다음 순서로 실행됩니다:

  1. 전역 미들웨어
  2. 페이지에서 정의된 미들웨어 순서(배열 문법으로 여러 개 선언된 경우)

예를 들어, 다음과 같은 미들웨어와 컴포넌트가 있다고 가정해봅시다:

middleware/ directory
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
pages/profile.vue
<script setup lang="ts">
definePageMeta({
  middleware: [
    function (to, from) {
      // 커스텀 인라인 미들웨어
    },
    'auth',
  ],
});
</script>

미들웨어는 다음과 같은 순서로 실행됩니다:

  1. analytics.global.ts
  2. setup.global.ts
  3. 커스텀 인라인 미들웨어
  4. auth.ts

전역 미들웨어 순서 지정

기본적으로 전역 미들웨어는 파일 이름을 기준으로 알파벳 순서대로 실행됩니다.

하지만 특정 순서로 실행하고 싶을 때가 있을 수 있습니다. 예를 들어, 위의 예시에서 setup.global.tsanalytics.global.ts보다 먼저 실행되어야 할 수 있습니다. 이 경우, 전역 미들웨어 파일 이름 앞에 '알파벳' 번호를 붙이는 것을 권장합니다.

Directory structure
-| middleware/
---| 01.setup.global.ts
---| 02.analytics.global.ts
---| auth.ts
'알파벳' 번호 매기기가 익숙하지 않다면, 파일 이름은 숫자가 아닌 문자열로 정렬된다는 점을 기억하세요. 예를 들어, 10.new.global.ts2.new.global.ts보다 먼저 올 수 있습니다. 그래서 예시에서는 한 자리 숫자 앞에 0을 붙였습니다.

미들웨어가 실행되는 시점

사이트가 서버 렌더링되거나 정적으로 생성된 경우, 초기 페이지의 미들웨어는 페이지가 렌더링될 때와 클라이언트에서 다시 한 번 실행됩니다. 미들웨어가 브라우저 환경을 필요로 할 때(예: 정적 사이트, 응답을 적극적으로 캐시, local storage에서 값을 읽고 싶을 때) 이 동작이 필요할 수 있습니다.

하지만 이 동작을 피하고 싶다면 다음과 같이 할 수 있습니다:

middleware/example.ts
export default defineNuxtRouteMiddleware(to => {
  // 서버에서 미들웨어 건너뛰기
  if (import.meta.server) return
  // 클라이언트에서 미들웨어 완전히 건너뛰기
  if (import.meta.client) return
  // 또는 클라이언트의 초기 로드에서만 미들웨어 건너뛰기
  const nuxtApp = useNuxtApp()
  if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
})

이것은 서버에서 미들웨어에서 에러를 throw하여 에러 페이지가 렌더링되는 경우에도 마찬가지입니다. 미들웨어는 브라우저에서 다시 실행됩니다.

에러 페이지 렌더링은 완전히 별도의 페이지 로드이므로, 등록된 모든 미들웨어가 다시 실행됩니다. 미들웨어에서 에러가 처리 중인지 확인하려면 useError를 사용할 수 있습니다.

미들웨어 동적 추가

addRouteMiddleware() 헬퍼 함수를 사용하여 플러그인 등에서 전역 또는 이름 있는 라우트 미들웨어를 수동으로 추가할 수 있습니다.

export default defineNuxtPlugin(() => {
  addRouteMiddleware('global-test', () => {
    console.log('이 전역 미들웨어는 플러그인에서 추가되었으며, 모든 라우트 변경 시 실행됩니다')
  }, { global: true })

  addRouteMiddleware('named-test', () => {
    console.log('이 이름 있는 미들웨어는 플러그인에서 추가되었으며, 동일한 이름의 기존 미들웨어를 덮어씁니다')
  })
})

예시

Directory Structure
-| middleware/
---| auth.ts

페이지 파일에서 이 라우트 미들웨어를 참조할 수 있습니다:

<script setup lang="ts">
definePageMeta({
  middleware: ["auth"]
  // 또는 middleware: 'auth'
})
</script>

이제 해당 페이지로 이동이 완료되기 전에 auth 라우트 미들웨어가 실행됩니다.

Read and edit a live example in Docs > Examples > Routing > Middleware.

빌드 타임에 미들웨어 설정하기

각 페이지에서 definePageMeta를 사용하는 대신, pages:extend 훅 내에서 이름 있는 라우트 미들웨어를 추가할 수 있습니다.

nuxt.config.ts
import type { NuxtPage } from 'nuxt/schema'

export default defineNuxtConfig({
  hooks: {
    'pages:extend' (pages) {
      function setMiddleware (pages: NuxtPage[]) {
        for (const page of pages) {
          if (/* 어떤 조건 */ true) {
            page.meta ||= {}
            // 이 코드는 페이지 내의 `definePageMeta`에서 설정한 미들웨어를 덮어쓴다는 점에 유의하세요
            page.meta.middleware = ['named']
          }
          if (page.children) {
            setMiddleware(page.children)
          }
        }
      }
      setMiddleware(pages)
    }
  }
})