middleware.ts
(또는 .js
) 파일을 생성하여 사용한다.pages
나 app
과 같은 레벨에 두거나 필요한 경우 src
안에 생성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))
}
// 미들웨어를 호출할 라우트 - 아래 Matching Paths 참조
export const config = {
matcher: '/about/:path*',
}
headers
from next.config.js
redirects
from next.config.js
rewrites
, redirects
, etc.)beforeFiles
(rewrites
) from next.config.js
public/
, _next/static/
, pages/
, app/
, etc.)afterFiles
(rewrites
) from next.config.js
/blog/[slug]
)fallback
(rewrites
) from next.config.js
미들웨어가 실행될 경로를 정의하는 방법은 두 가지가 있다.
export const config = {
matcher: '/about/:path*',
}
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
export const config = {
matcher: [
/*
* 다음과 같이 시작하는 경로를 제외한 모든 요청 경로와 일치:
* - api (API 라우트)
* - _next/static (정적 파일)
* - _next/image (이미지 최적화 파일)
* - favicon.ico (파비콘 파일)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}
참고 : Matcher 값은 반드시 정적으로 분석될 수 있어야 하므로 상수이어야 한다. 변수와 같은 동적 값은 무시된다.
Matcher 작성 방법
/
로 시작해야 한다./about/:path
는 /about/a
와 /about/b
와 일치하지만 /about/a/c
와는 일치하지 않는다.:
)으로 시작한다./about/:path*
는 *
이 0개 이상이기 때문에 /about/a/b/c
와 일치한다.?
는 0개 또는 1개, +
는 1개 이상을 의미한다./about/(.*)
는 /about/:path
*와 동일하다.침고 : 이전 버전과의 호환성을 위해 Next.js는 항상 /public
을 /public/index
로 간주한다.
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))
}
}
NextRequest
와 NextResponse
의 cookies 확장을 통해 쿠키에 간편하게 접근하고 조작할 수 있는 편리한 방법을 제공한다.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) {
// 들어오는 요청에 "Cookie:nextjs=fast" 헤더가 있다고 가정
// `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
// `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: '/' }
// 나가는 응답에는 `Set-Cookie:vercel=fast;path=/test` 헤더가 있을 것
return response
}
431 Request Header Fields Too Large
오류가 발생할 수 있으므로 주의.import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// 요청 헤더를 복제하고 새 헤더 `x-hello-from-middleware1` 설정
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// NextResponse.rewrite에서도 요청 헤더를 설정할 수 있음
const response = NextResponse.next({
request: {
// 새 요청 헤더
headers: requestHeaders,
},
})
// 새 응답 헤더 `x-hello-from-middleware2` 설정
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// `/api/`로 시작하는 경로에 대한 미들웨어 제한
export const config = {
matcher: '/api/:function*',
}
export function middleware(request: NextRequest) {
// 요청을 확인하기 위해 인증 함수 호출
if (!isAuthenticated(request)) {
// 오류 메시지를 나타내는 JSON으로 응답
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}
next.config.js
module.exports = {
skipTrailingSlashRedirect: true,
}
middleware.js
const legacyPrefixes = ['/docs', '/blog']
export default async function middleware(req) {
const { pathname } = req.nextUrl
if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
return NextResponse.next()
}
// 후행 슬래시 처리 적용
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
req.nextUrl.pathname += '/'
return NextResponse.redirect(req.nextUrl)
}
}
next.config.js
module.exports = {
skipMiddlewareUrlNormalize: true,
}
middleware.js
export default async function middleware(req) {
const { pathname } = req.nextUrl
// GET /_next/data/build-id/hello.json
console.log(pathname)
// skipMiddlewareUrlNormalize가 true일때 /_next/data/build-id/hello.json
// skipMiddlewareUrlNormalize가 false일때 /hello
}
middleware.ts
파일을 생성한다.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) {
const accessToken = request.cookies.get('accessToken')?.value;
const { pathname } = request.nextUrl;
// 로그인 페이지 접근 시, 이미 로그인 되어 있다면 메인 페이지로 리다이렉트
if (pathname === '/login') {
if (accessToken) {
return NextResponse.redirect(new URL('/', request.url));
}
// 로그인 페이지는 보호되지 않은 경로이므로, accessToken이 없어도 접근 가능
return NextResponse.next();
}
if (!accessToken) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
// See "Matching Paths" below to learn more
export const config = {
matcher: [
'/login',
'/',
// 인증이 필요한 경로 전체 작성
],
};
References