Nuxt는 애플리케이션 전반에서 사용할 수 있는 라우트 미들웨어 프레임워크를 제공하며, 특정 라우트로 이동하기 전에 실행하고 싶은 코드를 추출하기에 이상적입니다.
라우트 미들웨어에는 세 가지 종류가 있습니다:
app/middleware/ 에 위치하며, 페이지에서 사용될 때 비동기 import를 통해 자동으로 로드됩니다.app/middleware/ 에 .global 접미사를 붙여 두며, 모든 라우트 변경 시 실행됩니다.앞의 두 종류의 라우트 미들웨어는 definePageMeta 안에서 정의할 수 있습니다.
myMiddleware 는 my-middleware 가 됩니다.라우트 미들웨어는 내비게이션 가드로, 현재 라우트와 다음 라우트를 인자로 받습니다.
export default defineNuxtRouteMiddleware((to, from) => {
if (to.params.id === '1') {
return abortNavigation()
}
// 실제 앱에서는 아마 모든 라우트를 `/` 로 리다이렉트하지 않을 것입니다.
// 하지만 리다이렉트 전에 `to.path` 를 확인하는 것이 중요합니다.
// 그렇지 않으면 무한 리다이렉트 루프에 빠질 수 있습니다.
if (to.path !== '/') {
return navigateTo('/')
}
})
Nuxt는 미들웨어에서 직접 반환할 수 있는 두 개의 전역 헬퍼를 제공합니다.
navigateTo - 주어진 라우트로 리다이렉트합니다.abortNavigation - 내비게이션을 중단하며, 선택적으로 에러 메시지를 전달할 수 있습니다.vue-router 의 navigation guards 와는 달리, 세 번째 인자인 next() 는 전달되지 않으며, 리다이렉트나 라우트 취소는 미들웨어에서 값을 반환하는 방식으로 처리됩니다.
가능한 반환 값은 다음과 같습니다:
return 하거나 아예 반환하지 않음) - 내비게이션을 막지 않으며, 다음 미들웨어 함수(있다면)로 넘어가거나 라우트 내비게이션을 완료합니다.return navigateTo('/') - 주어진 경로로 리다이렉트하며, 서버 측에서 리다이렉트가 발생하면 리다이렉트 코드를 302 Found 로 설정합니다.return navigateTo('/', { redirectCode: 301 }) - 주어진 경로로 리다이렉트하며, 서버 측에서 리다이렉트가 발생하면 리다이렉트 코드를 301 Moved Permanently 로 설정합니다.return abortNavigation() - 현재 내비게이션을 중단합니다.return abortNavigation(error) - 현재 내비게이션을 에러와 함께 거부합니다.미들웨어는 다음 순서로 실행됩니다:
예를 들어, 다음과 같은 미들웨어와 컴포넌트가 있다고 가정해 봅시다:
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
<script setup lang="ts">
definePageMeta({
middleware: [
function (to, from) {
// 커스텀 인라인 미들웨어
},
'auth',
],
})
</script>
미들웨어는 다음 순서로 실행될 것으로 예상할 수 있습니다:
analytics.global.tssetup.global.tsauth.ts기본적으로 전역 미들웨어는 파일 이름을 기준으로 알파벳 순서대로 실행됩니다.
하지만 특정 순서를 정의하고 싶을 때가 있을 수 있습니다. 예를 들어, 앞의 시나리오에서 setup.global.ts 가 analytics.global.ts 보다 먼저 실행되어야 할 수 있습니다. 이 경우, 전역 미들웨어에 '알파벳' 번호 접두사를 붙이는 것을 권장합니다.
-| middleware/
---| 01.setup.global.ts
---| 02.analytics.global.ts
---| auth.ts
10.new.global.ts 는 2.new.global.ts 보다 앞에 옵니다. 이 때문에 예제에서는 한 자리 숫자 앞에 0 을 붙였습니다.사이트가 서버 렌더링되거나 생성된 경우, 초기 페이지에 대한 미들웨어는 페이지가 렌더링될 때 한 번, 그리고 클라이언트에서 다시 한 번 실행됩니다. 이는 미들웨어가 브라우저 환경을 필요로 할 때(예: 사이트가 정적으로 생성되었거나, 응답을 적극적으로 캐시하거나, 로컬 스토리지에서 값을 읽고자 할 때) 필요할 수 있습니다.
하지만 이 동작을 피하고 싶다면 다음과 같이 할 수 있습니다:
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
}
})
이는 서버에서 미들웨어 안에서 에러를 던져 에러 페이지가 렌더링되는 경우에도 마찬가지입니다. 미들웨어는 브라우저에서 다시 실행됩니다.
useError 를 사용해 에러가 처리 중인지 확인할 수 있습니다.미들웨어에서 다음과 이전 라우트에 접근할 때는 항상 to 와 from 파라미터를 사용하세요. 이 컨텍스트에서는 useRoute() 컴포저블 사용을 완전히 피해야 합니다.
미들웨어에는 "현재 라우트"라는 개념이 없습니다. 미들웨어는 내비게이션을 중단하거나 다른 라우트로 리다이렉트할 수 있기 때문입니다. 이 컨텍스트에서 useRoute() 컴포저블은 항상 부정확한 값을 반환하게 됩니다.
useRoute() 를 사용하는 컴포저블을 호출할 수 있으며, 이 경우 미들웨어에서 직접 호출하지 않았더라도 이 경고가 발생할 수 있습니다.
이는 위에서 설명한 것과 동일한 문제를 야기하므로, 미들웨어에서 사용될 때는 라우트를 인자로 받도록 함수 구조를 잡는 것이 좋습니다.export default defineNuxtRouteMiddleware((to) => {
// 미들웨어에서 `useRoute()` 호출을 피하기 위해 라우트를 함수에 전달
doSomethingWithRoute(to)
// ❌ 이는 경고를 출력하며 권장되지 않습니다.
callsRouteInternally()
})
// 미들웨어에서 올바르게 사용할 수 있도록 라우트를 인자로 받습니다.
export function doSomethingWithRoute (route = useRoute()) {
// ...
}
// ❌ 이 함수는 미들웨어에서 사용하기에 적합하지 않습니다.
export function callsRouteInternally () {
const route = useRoute()
// ...
}
플러그인 내부 등에서 addRouteMiddleware() 헬퍼 함수를 사용해 전역 또는 명명된 라우트 미들웨어를 수동으로 추가할 수 있습니다.
export default defineNuxtPlugin(() => {
addRouteMiddleware('global-test', () => {
console.log('이 전역 미들웨어는 플러그인에서 추가되었으며 모든 라우트 변경 시 실행됩니다.')
}, { global: true })
addRouteMiddleware('named-test', () => {
console.log('이 명명된 미들웨어는 플러그인에서 추가되었으며, 동일한 이름의 기존 미들웨어를 덮어씁니다.')
})
})
-| middleware/
---| auth.ts
페이지 파일에서 이 라우트 미들웨어를 참조할 수 있습니다:
<script setup lang="ts">
definePageMeta({
middleware: ['auth'],
// 또는 middleware: 'auth'
})
</script>
이제 해당 페이지로의 내비게이션이 완료되기 전에 auth 라우트 미들웨어가 실행됩니다.
각 페이지에서 definePageMeta 를 사용하는 대신, pages:extend 훅 안에서 명명된 라우트 미들웨어를 추가할 수 있습니다.
import type { NuxtPage } from 'nuxt/schema'
export default defineNuxtConfig({
hooks: {
'pages:extend' (pages) {
function setMiddleware (pages: NuxtPage[]) {
for (const page of pages) {
if (/* some condition */ Math.random() > 0.5) {
page.meta ||= {}
// 이는 페이지 안의 `definePageMeta` 에서 설정된 어떤 미들웨어도 덮어쓴다는 점에 유의하세요.
page.meta.middleware = ['named']
}
if (page.children) {
setMiddleware(page.children)
}
}
}
setMiddleware(pages)
},
},
})