미들웨어는 요청이 완료되기 전에 코드를 실행할 수 있게 해줍니다. 그런 다음 들어오는 요청에 따라 응답을 수정할 수 있습니다. 응답을 다시 작성하거나 리다이렉트하거나 요청 또는 응답 헤더를 수정하거나 직접 응답하는 등의 작업을 수행할 수 있습니다.
미들웨어는 캐시된 콘텐츠와 라우트가 일치하기 전에 실행됩니다. 자세한 내용은 '매칭 경로(Matching Paths)'를 참조하십시오.
Convention
프로젝트의 루트에 있는 'middleware.ts' (또는 .js) 파일을 사용하여 미들웨어를 정의하세요. 예를 들어, pages 디텍터리 또는 app디렉터리와 동일한 수준이거나 src 내부에 해당되는경우 위치시킬 수 있습니다.
Example
/* 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*',
}
Matching Paths
| 매칭 경로
미들웨어는 프로젝트의 모든 라우트에 대해 호출됩니다. 다음은 실행 순서입니다:
- headers from next.config.js
- redirects from next.config.js
- Middleware (rewrites, redirects, etc.)
- beforeFiles (rewrites) from next.config.js
- Filesystem routes (public/, _next/static/, pages/, app/, etc.)
- afterFiles (rewrites) from next.config.js
- Dynamic Routes (/blog/[slug])
- fallback (rewrites) from next.config.js
미들웨어가 실행될 경로를 정의하는 두 가지 방법이 있습니다.
1. Custom matcher config
matcher를 사용하면 특정 경로에서 실행되도록 미들웨어를 필터링할 수 있습니다.
/* middleware.ts */
export const config = {
matcher: '/about/:path*',
}
// 또는 배열 구문을 사용하여 단일 경로 또는 다중 경로를 일치시킬 수 있습니다.
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
매처 구성(config)은 완전한 정규식을 허용하기 때문에 부정적 전방탐색(negative lookahead)이나 문자 일치(character matching)와 같은 매칭이 가능합니다. 특정 경로를 제외한 모든 경로를 매칭하기 위한 부정적 전방탐색의 예는 다음과 같습니다:
/* middleware.ts */
export const config = {
matcher: [
/*
* 아래와 같이 시작하는 것들을 제외한 모두 경로를 매치합니다:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}
알아두면 좋은 정보: 매처 값은 빌드 시 정적으로 분석할 수 있도록 상수여야 합니다. 변수와 같은 동적 값은 무시됩니다.
매칭 구성을 위한 정규식 소개
- 경로는 반드시 슬래시 (/)로 시작해야 합니다.
- 이름이 지정된 매개변수를 포함할 수 있습니다. 예를 들어, /about/:path는 /about/a와 /about/b와 일치하지만 /about/a/c와는 일치하지 않습니다.
- 이름이 지정된 매개변수에는 수정자(modifier)를 사용할 수 있습니다. 수정자는 콜론(:)으로 시작합니다. 예를 들어, /about/:path*는 은 0개 이상을 나타내므로 /about/a/b/c와 일치합니다. 물음표(?)는 0개 또는 1개를 의미하며, 플러스(+)는 1개 이상을 의미합니다.
- 괄호로 둘러싼 정규식을 사용할 수 있습니다. 예를 들어, /about/(.*)은 /about/:path*와 동일한 매칭 규칙을 가지게 됩니다.
자세한 내용이 궁금하시다면 path-to-regexp 를 읽어주세요.
알아두면 좋은 점: 이전 버전과의 호환성을 위해 Next.js는 항상 /public을 /public/index로 간주합니다. 따라서 /public/:path의 매처가 일치합니다.
2. Conditional Statements
/* middleware.ts */
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
NextResponse API를 사용하면 다음과 같은 작업을 수행할 수 있습니다:
- 들어오는 요청을 다른 URL로 리디렉션할 수 있습니다.
- 지정된 URL을 표시하여 응답을 재작성할 수 있습니다.
- API 라우트, getServerSideProps 및 리다이렉션 대상을 위해 요청 헤더를 설정할 수 있습니다.
- 응답 쿠키를 설정할 수 있습니다.
- 응답 헤더를 설정할 수 있습니다.
미들웨어에서 응답을 생성하기 위해 다음과 같은 작업을 수행할 수 있습니다:
- 응답을 생성하는 라우트(페이지 또는 엣지 API 라우트)로 rewrite합니다.
- 직접 NextResponse를 반환합니다. '응답 생성하기(Producing a Response)'를 참조하세요.
Using Cookies
쿠키는 일반적인 헤더입니다. 요청에서는 쿠키가 Cookie 헤더에 저장되고, 응답에서는 Set-Cookie 헤더에 지정됩니다. Next.js는 NextRequest와 NextResponse의 cookies 확장을 통해 쿠키에 간편하게 접근하고 조작할 수 있는 편리한 방법을 제공합니다.
- 들어오는 요청(request)에 대해서는 다음과 같은 메서드를 통해 쿠키에 접근할 수 있습니다: get, getAll, set, delete. 특정 쿠키의 존재 여부를 확인하려면 'has'를 사용하거나 모든 쿠키를 삭제하려면 'clear'를 사용할 수 있습니다.
- 나가는 응답(response)에 대해서는 쿠키에 다음과 같은 메서드가 있습니다: get, getAll, set, delete.
/*middleware.ts*/
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
}
Setting Headers
NextResponse API를 사용하여 요청 및 응답 헤더를 설정할 수 있습니다 (요청 헤더 설정은 Next.js v13.0.0부터 사용 가능합니다.)
/*middleware.ts*/
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: {
// New request headers
headers: requestHeaders,
},
})
// 새 응답 헤더 `x-hello-from-middleware2` 설정
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
알아두면 좋은 정보: 백엔드 웹 서버 구성에 따라 431 Request Header Fields Too Large 오류가 발생할 수 있으므로 큰 헤더를 설정하지않는 것이 좋습니다.
Producing a Response
Response 또는 NextResponse 인스턴스를 반환하여 미들웨어에서 직접 응답할 수 있습니다. (Next.js v13.1.0부터 가능합니다.)
/*middleware.ts*/
import { NextRequest, NextResponse } 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 new NextResponse(
JSON.stringify({ success: false, message: 'authentication failed' }),
{ status: 401, headers: { 'content-type': 'application/json' } }
)
}
}
Advanced Middleware Flags
Next.js v13.1에서는 미들웨어에 대해 두 가지 추가적인 플래그인 도입되었습니다. 'skipMiddlewareUrlNormalize'과 'skipTrailingSlashRedirect'는 고급 사용 사례를 처리하기 위해 도입되었습니다.
'skipTrailingSlashRedirect'는 Next.js의 기본 리디렉션을 비활성화하여 미들웨어 내에서 추가 또는 제거할 때 후행 슬래시를 사용자 정의 처리할 수 있도록 합니다. 이를 통해 일부 경로에 대해 후행 슬래시를 유지하고 다른 경로에는 후행 슬래시를 사용하지 않을 수 있어 쉬운 점진적인 이전(migration)을 가능하게 합니다.
/*next.config.js*/
module.exports = {
skipTrailingSlashRedirect: true,
}
/*middleware.ts*/
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을 사용하여 완전한 제어가 필요한 일부 고급 사례에서 사용될 수 있습니다.
/*next.config.js*/
module.exports = {
skipMiddlewareUrlNormalize: true,
}
/*middleware.ts*/
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
}
느낀점
사용할 일은 거의 없겠지만 알아두면 언젠가 쓸일 있을거라 생각됩니다.
'기억보단 기록을 > Next JS (App Router)' 카테고리의 다른 글
NextJS 테스트 코드 작성하기 - NextJS 테스트를 위한 모듈 설치 및 설정 (0) | 2023.09.13 |
---|---|
NextJS 테스트 코드 작성하기 - 리액트 테스트에 대하여 (0) | 2023.09.13 |
[NextJS 13] Routing - Route Handlers & 활용방안 (0) | 2023.06.25 |
[NextJS 13] Routing - Intercepting Routes(라우트 가로채기) (0) | 2023.06.12 |
[NextJS 13] Routing - Parallel Routes (병렬 라우트) (0) | 2023.06.12 |