미들웨어
- Published on
- 요청이 완료되기 전에 코드를 실행하는 미들웨어의 사용법을 학습하세요.
Table of Contents
미들웨어는 요청이 완료되기 전에 코드를 실행할 수 있게 해줍니다. 들어오는 요청을 기반으로 응답을 리라이트, 리디렉션, 요청 또는 응답 헤더 수정, 또는 직접 응답하는 방식으로 수정할 수 있습니다.
미들웨어는 캐시된 컨텐츠와 라우트가 일치되기 전에 실행됩니다. 경로 매칭에서 자세한 내용을 확인하세요.
규칙
프로젝트의 루트에 middleware.ts
(또는 .js
) 파일을 사용하여 미들웨어를 정의하세요. 예를 들면, pages
또는 app
와 동일한 수준에서 또는 해당하는 경우 src
내부에 있습니다.
예제
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*',
}
JavaScript
import { NextResponse } from 'next/server'
// 이 함수는 내부에서 `await`를 사용하는 경우 `async`로 표시할 수 있습니다.
export function middleware(request) {
return NextResponse.redirect(new URL('/home', request.url))
}
// 아래 "Matching Paths"를 참조하여 자세히 알아보세요.
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
는 특정 경로에서 미들웨어를 실행하도록 필터링 할 수 있게 해줍니다.
export const config = {
matcher: '/about/:path*',
}
배열 문법을 사용하여 단일 경로 또는 여러 경로와 일치시킬 수 있습니다.
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
matcher
설정은 완전한 정규 표현식을 허용하므로 부정 전방 탐색이나 문자 매칭과 같은 매칭이 지원됩니다. 특정 경로를 제외한 모든 것과 일치하는 부정 전방 탐색의 예는 다음과 같습니다.
export const config = {
matcher: [
/*
* 다음으로 시작하는 경로를 제외한 모든 요청 경로와 일치:
* - api (API 라우트)
* - _next/static (정적 파일)
* - _next/image (이미지 최적화 파일)
* - favicon.ico (파비콘 파일)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}
matcher
값은 빌드 시간에 정적으로 분석될 수 있도록 상수여야 합니다. 변수와 같은 동적 값은 무시됩니다.
설정된 매처는 아래 규칙을 따라야 합니다.
/
로 시작해야 합니다.- 명명된 매개 변수를 포함할 수 있습니다.
/about/:path
는/about/a
와/about/b
와 일치하지만/about/a/c
와는 일치하지 않습니다. - 명명된 매개 변수 (':'로 시작하는)에 수정자를 가질 수 있습니다.
/about/:path*
는*
가 제로 또는 더 많은 때문에/about/a/b/c
와 일치합니다.?
는 제로 또는 하나 이고+
는 하나 또는 더 많은 입니다. - 괄호로 묶인 정규 표현식을 사용할 수 있습니다.
/about/(.*)
는/about/:path*
와 동일합니다.
path-to-regexp 문서에서 더 자세한 내용을 읽어보세요.
하위 호환성을 위해 Next.js는 항상
/public
을/public/index
로 간주합니다. 따라서/public/:path
매처는 일치하게 됩니다.
조건문
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))
}
}
JavaScript
import { NextResponse } from 'next/server'
export function middleware(request) {
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를 통해 다음 작업을 수행할 수 있습니다.
- 들어오는 요청을 다른 URL로
redirect
합니다. - 주어진 URL을 표시함으로써 응답을
rewrite
합니다. - API 라우트,
getServerSideProps
및rewrite
대상에 대한 요청 헤더 설정 - 응답 쿠키 설정
- 응답 헤더 설정
미들웨어에서 응답을 생성하려면 다음과 같은 방법이 있습니다.
- 응답을 생성하는 라우트(Page 또는 Route Handler)로
rewrite
합니다. NextResponse
를 직접 반환합니다. 응답 생성을 참조하세요.
미들웨어에서 응답을 생성하려면 다음과 같은 방법이 있습니다.
- 응답을 생성하는 라우트(Page 또는 Edge API Route)로
rewrite
합니다. NextResponse
를 직접 반환합니다. 응답 생성을 참조하세요.
쿠키 사용하기
쿠키는 일반적인 헤더입니다. Request
에서는 Cookie
헤더에 저장되며 Response
에서는 Set-Cookie
헤더에 있습니다. Next.js는 NextRequest
와 NextResponse
에 있는 cookies
확장을 통해 이러한 쿠키에 접근하고 조작하는 편리한 방법을 제공합니다.
- 들어오는 요청에 대해
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) {
// 들어오는 요청에 "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
}
JavaScript
import { NextResponse } from 'next/server'
export function middleware(request) {
// 들어오는 요청에 "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
}
헤더 설정하기
NextResponse
API를 사용하여 요청 및 응답 헤더를 설정할 수 있습니다 (요청 헤더 설정은 Next.js v13.0.0부터 사용 가능합니다).
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
}
JavaScript
import { NextResponse } from 'next/server'
export function middleware(request) {
// 요청 헤더를 복제하고 새 헤더 `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
}
큰 헤더를 설정하는 것은 백엔드 웹 서버 구성에 따라 431 Request Header Fields Too Large 오류를 발생시킬 수 있으므로 주의해야 합니다.
응답 생성하기
미들웨어에서 직접 Response
또는 NextResponse
인스턴스를 반환하여 응답할 수 있습니다. (Next.js v13.1.0부터 사용 가능합니다.)
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' }
}
)
}
}
JavaScript
import { NextResponse } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// 미들웨어를 `/api/`로 시작하는 경로로 제한합니다.
export const config = {
matcher: '/api/:function*',
}
export function middleware(request) {
// 요청을 확인하기 위해 인증 함수를 호출합니다.
if (!isAuthenticated(request)) {
// 오류 메시지를 나타내는 JSON으로 응답합니다.
return new NextResponse(
JSON.stringify(
{ success: false, message: 'authentication failed' }
),
{ status: 401, headers:
{ 'content-type': 'application/json' }
}
)
}
}
고급 미들웨어 플래그
Next.js의 v13.1
에서는 고급 사용 사례를 처리하기 위해 두 가지 추가 플래그인 skipMiddlewareUrlNormalize
및 skipTrailingSlashRedirect
가 도입되었습니다.
skipTrailingSlashRedirect
는 트레일링 슬래시(trailing slash)를 추가하거나 제거하기 위한 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()
}
// 트레일링 슬래시 처리 적용
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)
// 플래그가 있으면 /_next/data/build-id/hello.json으로 표시됩니다.
// 플래그 없이는 /hello로 정규화됩니다.
}