미들웨어를 사용하면 요청이 완료되기 전에 코드를 실행할 수 있습니다. 그런 다음 들어오는 요청에 따라 요청 또는 응답 헤더를 재작성, 리디렉션, 수정하거나 직접 응답하며 응답을 수정할 수 있습니다.
미들웨어는 캐시된 콘텐츠와 경로가 일치하기 전에 실행됩니다. 자세한 내용은 경로 매칭을 참조하세요.
프로젝트의 루트에서 middleware.js 파일을 사용하여 미들웨어를 정의합니다. 예를 들어 page 또는 app과 같은 수준 또는 해당되는 경우 src안에 정의합니다.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
// See "Matching Paths" below to learn more
export const config = {
matcher: '/about/:path*',
}
프로젝트의 모든 경로에 대해 미들웨어가 호출됩니다. 실행 순서는 다음과 같습니다.
next.confing.js의 headersnext.confing.js의 redirectsnext.confing.js의 beforeFilesnext.confing.js의 afterFilesnext.confing.js의 fallback미들웨어가 실행될 경로를 정의하는 방법에는 두 가지가 있습니다.
matcher를 사용하면 특정 경로에서 실행되도록 미들웨어를 필터링할 수 있습니다.
export const config = {
matcher: '/about/:path*',
}
배열 구문을 사용해 단일 경로, 또는 여러 경로를 일치시킬 수 있습니다.
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
matcher 설정은 전체 정규식을 허용하므로 네거티브 록헤드 또는 문자 일치와 같은 매칭이 지원됩니다. 특정 경로를 제외한 모든 경로를 일치시키는 네거티트 록헤드의 예는 아래에서 확인할 수 있습니다.
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}
matcher 값은 빌드 시 정적으로 분석할 수 있도록 상수여야 합니다. 변수와 같은 동적 값은 무시됩니다.
반드시 /로 시작해야 합니다.
명명된 매개변수를 포함할 수 있습니다. : /about/:path는 about/a 및 about/b 일치하지만 about/a/c와 일치하지 않습니다.
명명된 매개변수에 수정자를 포함할 수 있습니다. : about/:path*는 /about/a/b/c와 일치합니다. 이는 *는 0과 그 이상, ?는 0과 1 중 하나, +은 1과 그 이상을 의미하기 때문입니다.
괄호로 묶인 정규식을 사용할 수 있습니다. : /about/(.*)는 /about/:path*와 같은 의미입니다.
더 자세한 내용은 path-to-regexp을 참조하세요.
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))
}
}
NextResponse API를 사용하면 다음을 수행할 수 있습니다.
rewirte합니다.getServerSideProps 및 rewrite 대상에 대한 설정미들웨어에서 응답을 생성하려면 다음과 같이 할 수 있습니다.
쿠키는 일반 헤더입니다. 요청 시에는 쿠키 헤더에 저장됩니다. 응답에서는 Set-Cookie 헤더에 저장됩니다. Next.js는 NextRequest의 쿠키 확장을 통해 이러한 쿠키에 엑세스하고 조작할 수 있는 편리한 방법을 제공합니다.
들어오는 요청의 경우 쿠키에는 다음과 같은 메서드가 제공됩니다. : get, getAll, set, delete, has를 사용하여 쿠키의 존재 여부를 확인하거나 clear를 사용해 모든 쿠키를 제거할 수도 있습니다.
발신 응답의 경우 쿠키에는 get, getAll, set, delete 메서드가 있습니다.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Assume a "Cookie:nextjs=fast" header to be present on the incoming request
// Getting cookies from the request using the `RequestCookies` API
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// Setting cookies on the response using the `ResponseCookies` API
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// The outgoing response will have a `Set-Cookie:vercel=fast;path=/test` header.
return response
}
NextResponse API를 사용하여 요청 및 응답 헤더를 설정할 수 있습니다.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Clone the request headers and set a new header `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// You can also set request headers in NextResponse.rewrite
const response = NextResponse.next({
request: {
// New request headers
headers: requestHeaders,
},
})
// Set a new response header `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
미들웨어에서 Response 또는 NextResponse 인스턴스를 반환하여 직접 응답할 수 있습니다.
import { NextRequest, NextResponse } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Limit the middleware to paths starting with `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request: NextRequest) {
// Call our authentication function to check the request
if (!isAuthenticated(request)) {
// Respond with JSON indicating an error message
return new NextResponse(
JSON.stringify({ success: false, message: 'authentication failed' }),
{ status: 401, headers: { 'content-type': 'application/json' } }
)
}
}
Next.js v13.1에서는 고급 사용 사례를 처리하기 위해 미들웨어에 대한 두 가지 추가 플러그인 skipMiddlewareUrlNormalize, skipTrailingSlashRedirect가 도입되었습니다.
skipTrailingSlashRedirect를 사용하면 후행 슬래시를 추가하거나 제거하기 위해 Next.js 기본 리디렉션을 비활성화하여 미들웨어 내부에서 사용자 지정 처리를 할 수 있으므로 일부 경로에는 후행 슬래시를 유지하지만 다른 경로에는 유지하지 않아 증분 마이그레이션을 쉽게 할 수 있습니다.
module.exports = {
skipTrailingSlashRedirect: true,
}
const legacyPrefixes = ['/docs', '/blog']
export default async function middleware(req) {
const { pathname } = req.nextUrl
if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
return NextResponse.next()
}
// apply trailing slash handling
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
req.nextUrl.pathname += '/'
return NextResponse.redirect(req.nextUrl)
}
}
skipMiddlewareUrlNormalize을 사용하면 Next.js가 수행하는 URL 정규화를 비활성화하여 직접 방문과 클라이어트 전환을 동일하게 처리할 수 있습니다. 원본 URL을 사용하여 완전한 제어가 필요한 고급 사용 사례가 있는데, 이 기능을 사용하면 잠금이 해제됩니다.
module.exports = {
skipMiddlewareUrlNormalize: true,
}
export default async function middleware(req) {
const { pathname } = req.nextUrl
// GET /_next/data/build-id/hello.json
console.log(pathname)
// with the flag this now /_next/data/build-id/hello.json
// without the flag this would be normalized to /hello
}