📌 AppRouter, v.15.1.6
https://nextjs.org/docs/app/building-your-application/routing/middleware
미들웨어는 요청이 완료되기 전에 코드를 실행시킬 수 있습니다. 그리고 요청에 대해 rewrite, redirect, 요청/응답 헤더 수정 등으로 응답을 수정하거나 직접 응답할 수 있습니다.
미들웨어는 캐시된 콘텐츠와 경로가 매칭되기 전에 실행됩니다. 자세한 내용은 Matching Paths에서 확인할 수 있습니다.
애플리케이션에 미들웨어를 통합하면 성능, 보안, 사용자 경험이 크게 향상될 수 있습니다. 미들웨어가 특히 효과적인 시나리오는 다음과 같습니다.
반대로 미들웨어가 최적의 접근 방식이 아닌 것을 인지하는 것도 중요합니다. 주의해야 할 시나리오는 다음과 같습니다.
프로젝트의 루트 디렉터리에 middleware.ts(또는 .js) 파일을 생성해 미들웨어를 정의할 수 있습니다. 예를 들어, pages, app 과 같은 레벨에 있거나 src 디렉터리 안에 있을 수 있습니다.
참고로, 프로젝트 당 하나의 middleware.ts 파일만 지원되지만 미들웨어 로직을 모듈식으로 구성할 수 있습니다. 미들웨어 기능들을 .ts 또는 .js 파일로 나누고 middleware.ts 파일에서 이들을 가져와 사용할 수 있습니다. 이를 통해 경로별 미들웨어를 효율적으로 관리할 수 있으며 middleware.ts에서 중앙 집중적으로 통합하여 제어할 수 있습니다. 단일 미들웨어 파일을 사용하도록 함으로써 구성이 단순해지고 잠재적인 충돌을 방지하며 성능을 최적화할 수 있습니다.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// 내부에서 await이 사용되는 경우 async 사용이 가능합니다.
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
export const config = {
matcher: '/about/:path*',
}
미들웨어는 프로젝트의 모든 경로에서 호출됩니다. 이를 감안하면, matcher를 사용해 타겟을 정확히 지정하거나 특정 경로를 제외시키는 것이 매우 중요합니다. 실행 순서는 다음과 같습니다.
next.config.js > headersnext.config.js > redirectsnext.config.js > rewrites > beforeFiles next.config.js > rewrites > afterFiles next.config.js > rewrites > fallback 미들웨어가 실행되는 경로를 지정하는 방법은 두 가지가 있습니다.
matcher 는 미들웨어가 특정 경로에서 실행되도록 필터링할 수 있습니다.
export const config = {
matcher: '/about/:path*'
}
단일 경로를 지정하거나 배열으로 여러 개의 경로를 지정할 수 있습니다.
export const config = {
matcher:['/about/:path*', '/dashboard/:path*']
}
matcher 는 정규식을 허용하므로 부정 전방 탐색이나 문자 매칭을 사용할 수 있습니다. 특정 경로를 제외한 모든 경로를 매칭하는 부정 전방 탐색 예제는 다음과 같습니다.
export const config = {
matcher: [
/*
* 다음으로 시작하는 요청 경로를 제외한 모든 경로와 매칭됨.
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico, sitemap.xml, robots.txt (metadata files)
*/
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
}
missing 또는 has 배열을 사용하거나 둘을 조합하여 특정 요청에 대해 미들웨어를 우회할 수도 있습니다.
export const config = {
matcher: [
/*
* 다음으로 시작하는 요청 경로를 제외한 모든 경로와 매칭됨.
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico, sitemap.xml, robots.txt (metadata files)
*/
{
source:
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
{
source:
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
has: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
{
source:
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
has: [{ type: 'header', key: 'x-present' }],
missing: [{ type: 'header', key: 'x-missing', value: 'prefetch' }],
},
],
}
❗
matcher값은 빌드 시 정적 분석이 가능하도록 상수여야 합니다. 변수와 같은 동적인 값은 무시됩니다.
matcher 구성
/ 로 시작해야 합니다./about/:path 는 /about/a 또는 /about/b 와 매칭되지만 /about/a/b 와는 매칭되지 않습니다.: 로 시작하는)를 사용할 수 있습니다./about/:path* 는 /about/a/b/c 와 매칭됩니다.
| * | 0회 이상 반복 |
|---|---|
| ? | 0회 또는 1회 반복 |
| + | 1회 이상 반복 |
/about/(.*) 는 /about/:path* 와 같습니다.❗ 이전 버전과의 호환성을 위해 Next.js는
/public을/public/index로 간주합니다.
따라서 matcher가/public/:path일 경우/public도 매칭됩니다.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}