Middleware
미들웨어를 사용하면 요청이 완료되기 전에 코드를 실행할 수 있다. 그런 다음 들어오는 요청에 따라 request
또는 response header
를 rewriting, redirecting , modifying하거나 직접 응답하여 response를 수정할 수 있다.
미들웨어 파일 생성
프로젝트의 루트에 있는 파일 middleware.ts
(또는 )을 사용하여 미들웨어를 정의한다. 예를 들어, page
와 app
과 같은 레벨에 파일을 생성한다. 만약 src
가 존재할 경우 src 파일 안에 생성한다.
Middleware 사용 예시
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.config.js
에서 headers
next.config.js
에서 redirects
rewrites
, redirects
등)next.config.js
에서 beforeFiles
(rewrites
)public/
, _next/static/
, pages/
, app/
등)next.config.js
에서 afterFiles
(rewrites
)/blog/[slug]
)next.config.js
에서 fallback
(rewrites
)미들웨어가 실행될 경로를 정의하는 방법은 두 가지가 있다.
matcher를 사용하면 미들웨어가 특정 경로에서 실행되도록 필터링
할 수 있다.
// middleware.js
export const config = {
matcher: '/about/:path*',
};
배열 구문
을 사용하여 단일 경로 또는 여러 경로를 일치
시킬 수 있다.
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
};
matcher 구성에서는 negative lookahead(?!)
나 문자 매칭과 같은 매칭이 지원되는 전체 정규식
을 사용할 수 있다. 특정 경로를 제외한 모든 경로를 매칭하는 negative lookahead
의 예는 다음과 같다.
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 값은 빌드 시 정적으로 분석할 수 있도록 상수
이어야 한다. 변수와 같은 동적 값은 무시
된다.
💡 negative lookahead ( ?! )
특정 조건을 만족하는 문자열을 찾되, 그중에서 제외
를 하고싶은 경우 사용한다.
Matcher 규칙
/
로 시작한다./about/:path
는 /about/a
와 /about/b
와 일치하지만 /about/a/c
와는 일치하지 않는다.modifiers(:로 시작하는)
를 사용할 수 있다. /about/:path*
는 *
가 0개 이상이기 때문에 /about/a/b/c
와 일치한다. ?
는 0개 또는 1개, +
는 1개 이상이다./about/(.*)
는 /about/:path
*와 동일하다./public
을 /public/index
로 간주한다. 따라서 /public/:path
와 같은 matcher가 일치한다.아래와 같이 조건문을 통해 미들웨어가 실행할 경로를 제어할 수 있다.
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
NextResponse API
를 사용하면 다음과 같은 작업을 수행할 수 있다.
redirect
response
을 rewrite
API Route
, getServerSideProps
및 rewrite
대상을 위해 요청 헤더를 설정한다.Middleware에서 응답을 생성
하려면 다음과 같은 방법을 사용할 수 있다.
응답을 생성하는 라우트
(Page 또는 Edge API Route)로 rewrite
쿠키 사용
쿠키는 일반적인 헤더이다. 요청에서는 쿠키 헤더에 저장되며, 응답에서는 Set-Cookie 헤더에 저장된다.
Next.js는 NextRequest
및 NextResponse
의 쿠키 확장
을 통해 이러한 쿠키에 쉽게 액세스하고 조작할 수 있는 편리한 방법을 제공한다.
들어오는 요청에 대해서는, cookies에 다음과 같은 메서드가 있다.
get
, getAll
, set
, delete
, has
메서드를 사용하여 쿠키의 존재 여부를 확인할 수 있으며, clear
메서드를 사용하여 모든 쿠키를 제거할 수 있다.
나가는 응답에 대해서는, cookies에 다음과 같은 메서드가 있다.
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')?.value;
console.log(cookie); // => 'fast'
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: '/test',
});
cookie = response.cookies.get('vercel');
console.log(cookie); // => { name: 'vercel', value: 'fast', Path: '/test' }
// The outgoing response will have a `Set-Cookie:vercel=fast;path=/test` header.
return response;
}
헤더 설정
NextResponse API
를 사용하여 요청 및 응답 헤더를 설정
할 수 있다.
요청 헤더 설정은 Next.js v13.0.0 이상에서 사용 가능하다.
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;
}
참고로 백엔드 웹 서버 구성에 따라 431 Request Header Fields Too Large
오류가 발생할 수 있으므로 큰 헤더를 설정하는 것은 권장되지 않는다.
응답 생성
Middleware
에서 Response
또는 NextResponse
인스턴스를 반환하여 직접 응답
을 보낼 수 있다.
이 기능은 Next.js v13.1.0 이상에서 사용 가능하다.
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' } },
);
}
}
Middleware 플래그
Next.js의 v13.1에서 Middleware에 대한 두 가지 추가 플래그인 skipMiddlewareUrlNormalize
및 skipTrailingSlashRedirect
가 도입되었다.
skipTrailingSlashRedirect
를 사용하면, Next.js의 기본 리디렉션을 비활성화
하여 끝에 슬래시를 추가하거나 제거하는 데 대한 사용자 정의 처리를 Middleware 내부에서 수행할 수 있다. 이를 통해 일부 경로에서는 끝에 슬래시를 유지하고 다른 경로에서는 제거하도록 설정하여 쉽게 점진적인 마이그레이션을 수행할 수 있다.
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에 대한 완전한 제어가 필요한 몇 가지 고급 경우가 있으며, skipMiddlewareUrlNormalize을 사용하면 이를 가능하게 한다.
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
}
Version 변경 내용
v13.1.0
: 고급 미들웨어 플래그 추가v13.0.0
: 미들웨어가 요청 헤더, 응답 헤더를 수정하고 응답을 보낼 수 있음v12.2.0
: 미들웨어 안정화v12.0.9
: Edge 런타임에서 절대 URL을 강제로 사용하도록 변경v12.0.0
: 미들웨어 (베타) 추가
[참고]
https://nextjs.org/docs/app/building-your-application/routing/middleware