6. Middleware

 

프로젝트 루트 디렉터리에 middleware.ts (또는 .js) 파일을 사용하여 미들웨어를 정의할 수 있다. 예를 들어, pagesapp과 같은 수준에 두거나 적용 가능한 경우 src 내부에 위치시킬 수 있다.

 

Matching Paths

미들웨어는 프로젝트의 모든 라우트에 대해 호출된다.

실행 순서

  1. next.config.js에서 정의된 헤더
  2. next.config.js에서 정의된 redirects
  3. 미들웨어 (rewrites, redirects 등)
  4. next.config.js에서 정의된 beforeFiles (rewrites)
  5. 파일 시스템 routes (public/, _next/static/, pages/, app/ 등)
  6. next.config.js에서 정의된 afterFiles (리라이트)
  7. 동적 라우트 (/blog/[slug])
  8. next.config.js에서 정의된 fallback (리라이트)

Matcher

matcher는 특정 경로에서 미들웨어가 실행되도록 필터링하는 기능을 제공한다.

matcher 설정은 전체 정규식을 지원하여 negative lookaheads이나 character matching와 같은 매칭을 지원한다.

// 단일 경로
export const config = {
  matcher: '/about/:path*',
}

// 다중 경로
export const config = {
  matcher: ['/about/:path*', '/dashboard/:path*'],
}

// 특정 경로를 제외한 모든 경로를 매칭하기 위한 negative lookaheads
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).*)',
  ],
}

// 미들웨어를 통과할 필요가 없는 prefetches (from next/link)를 무시하려면 missing 배열을 사용할 수 있다.
export const config = {
  matcher: [
    {
      source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
      missing: [
        { type: 'header', key: 'next-router-prefetch' },
        { type: 'header', key: 'purpose', value: 'prefetch' },
      ],
    },
  ],
}

 

matchers 구성

  • 반드시 /로 시작해야 한다.
  • 명명된 매개변수를 포함할 수 있다: /about/:path는 /about/a 및 /about/b와 일치하지만 /about/a/c와 일치하지 않는다.
  • 명명된 매개변수에 수정자를 사용할 수 있다 (: 시작함): /about/:path*는 *가 0개 이상인 것으로 인해 /about/a/b/c와 일치합니다. ?는 0 또는 1, +는 1개 이상을 나타낸다.
  • 괄호 안에 정규식을 사용할 수 있다: /about/(.*)은 /about/: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))
  }
}

 

NextResponse

NextResponse API를 사용하면 다음과 같은 작업이 가능하다.

  • 수신된 요청을 다른 URL로 redirect
  • 지정된 URL을 표시하도록 응답을 rewrite
  • API Routes, getServerSideProps 및 rewrite 대상에 대한 요청 헤더 설정
  • 응답 쿠키 설정
  • 응답 헤더 설정

미들웨어에서 응답을 생성하려면 다음 중 하나를 수행할 수 있다.

  • 응답을 생성하는 페이지나 Route Handler로 rewrite
  • 직접 NextResponse를 반환.

 

쿠키 사용

Request에서는 Cookie 헤더에 저장되고, Response에서는 Set-Cookie 헤더에 저장됩니다. Next.js는 NextRequest와 NextResponse의 cookies 확장을 통해 이러한 쿠키에 접근하고 조작할 수 있는 편리한 방법을 제공한다.

  1. requests에 대해서는 다음과 같은 메서드를 사용할 수 있다: get, getAll, set, delete, has, clear를 사용하여 특정 쿠키의 존재 여부를 확인하거나 모든 쿠키를 제거할 수 있다.
  2. responses에 대해서는 다음과 같은 메서드를 사용할 수 있다: get, getAll, set, delete.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
  // 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: '/' }
  // response에는`Set-Cookie:vercel=fast;path=/` header가 있다.
 
  return response
}

 

Header 설정

NextResponse API를 사용하여 request 및 response header를 설정할 수 있다 (request header 설정은 Next.js v13.0.0부터 사용 가능)

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
  // request headers를 복제하고 새로운 headers '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,
    },
  })
 
  // 새로운 response header `x-hello-from-middleware2`를 설정한다.
  response.headers.set('x-hello-from-middleware2', 'hello')
  return response
}
 

Response 생성

 
미들웨어에서 직접 응답하려면 Response 또는 NextResponse 인스턴스를 반환할 수 있다. (Next.js v13.1.0부터 사용 가능)
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
 
// 미들웨어를 '/api/'로 시작하는 경로로 제한한다
export const config = {
  matcher: '/api/:function*',
}
 
export function middleware(request: NextRequest) {
  // request를 확인하기 위해 authentication 함수를 호출
  if (!isAuthenticated(request)) {
    // 오류 메세지를 나타내는 JSON으로 응답
    return Response.json(
      { success: false, message: 'authentication failed' },
      { status: 401 }
    )
  }
}
 

waitUntil과 NextFetchEvent

NextFetchEvent 객체는 기본 FetchEvent 객체를 확장하며 waitUntil() 메서드를 포함한다.

waitUntil() 메서드는 프로미스를 인자로 받아들이고, 해당 프로미스가 해결될 때까지 미들웨어의 수명을 확장한다. 이는 백그라운드에서 작업을 수행하는 데 유용합니다.

import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'
 
export function middleware(req: NextRequest, event: NextFetchEvent) {
  event.waitUntil(
    fetch('https://my-analytics-platform.com', {
      method: 'POST',
      body: JSON.stringify({ pathname: req.nextUrl.pathname }),
    })
  )
 
  return NextResponse.next()
}

 

Advanced Middleware Flags

Next.js v13.1에서는 두 가지 미들웨어에 대한 추가적인 플래그인 skipMiddlewareUrlNormalize 및 skipTrailingSlashRedirect가 소개되었다. 이 플래그들은 고급 사용 사례를 다루기 위한 것이다.

 

skipTrailingSlashRedirect

skipTrailingSlashRedirect 플래그는 Next.js 리다이렉션을 사용하여 슬래시를 추가하거나 제거하는 것을 비활성화합니다. 이를 통해 미들웨어 내에서 일부 경로에 대해 슬래시를 유지하고 다른 경로에 대해 슬래시를 유지하지 않도록 사용자 정의 처리할 수 있으며, 이는 점진적인 이전을 더 쉽게 만들 수 있다.

// 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)
  }
}
 

skipMiddlewareUrlNormalize

skipMiddlewareUrlNormalize는 Next.js에서 URL 정규화를 비활성화하는 기능으로, 직접 방문 및 클라이언트 전환을 동일하게 처리하도록 한다. 일부 고급 케이스에서 이 옵션을 사용하면 원래의 URL을 사용하여 완전한 제어를 제공할 수 있다.
// 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)
  // flag를 사용하여 /_next/data/build-id/hello.json
  // 사용하지 않을 시 /hello
}

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유