[Next 13] Routing - 미들웨어

Jeongho·2023년 9월 4일
0
  • 미들웨어를 사용하면 요청이 완료되기 전에 코드를 실행할 수 있습니다. 그런 다음 들어오는 요청에 따라 요청 또는 응답 헤더를 다시 작성, 리디렉션, 수정하거나 직접 응답하여 응답을 수정할 수 있습니다
  • 미들웨어는 캐시된 콘텐츠와 경로가 일치하기 전에 실행됩니다. 자세한 내용은 일치하는 경로를 참조하세요.

Convention

  • 미들웨어를 정의하려면 프로젝트 루트에 있는 middleware.ts(또는 .js) 파일을 사용하세요.
  • 예를 들어 페이지나 앱과 동일한 수준이거나 해당하는 경우 src 내부입니다.

Example

// 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) {
  return NextResponse.redirect(new URL('/home', request.url))
}
 
// See "Matching Paths" below to learn more
export const config = {
  matcher: '/about/:path*',
}

Matching Paths (일치하는 경로)

  • 프로젝트의 모든 경로에 대해 미들웨어가 호출됩니다. 실행 순서는 다음과 같습니다.
    1. next.config.js 에서 headers
    2. next.config.js 에서 redirects
    3. 미들웨어 (rewrites, redirects 등)
    4. next.config.js에서 beforeFiles(rewrites)
    5. 파일 시스템 라우터 (public/ , /_next/static/, pages/ ,app/, etc)
    6. next.config.js에서 afterFiles(rewrites)
    7. 동적 경로 (/blog/[slug])
    8. next.config.js에서 fallback (rewrites)
  • 미들웨어가 실행될 경로를 정의하는 방법에는 두 가지가 있습니다.

Matcher

  • matcher를 사용하면 미들웨어를 필터링하여 특정 경로에서 실행되도록 할 수 있습니다.
// middleware.js
export const config = {
  matcher: '/about/:path*',
}
  • 배열 구문을 사용하여 단일 경로 또는 다중 경로를 일치시킬 수 있습니다.
// middleware.js
export const config = {
  matcher: ['/about/:path*', '/dashboard/:path*'],
}
  • 일치자 구성은 전체 정규식을 허용하므로 부정 예측 또는 문자 일치와 같은 일치가 지원됩니다.
  • 특정 경로를 제외한 모든 경로와 일치하는 부정 예측의 예는 여기에서 볼 수 있습니다.
// middleware.js
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).*)',
  ],
}

알아두면 좋은 점

  • 빌드 시 정적으로 분석할 수 있도록 일치자 값은 상수여야 합니다. 변수와 같은 동적 값은 무시됩니다.

구성된 Matchers

  • /로 시작해야합니다.
  • 명명된 매개변수를 포함할 수 있습니다. /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로 간주합니다. 따라서 /public/:path의 일치자가 일치합니다.

Conditional Statements (조건문)

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

NextResponse

  • NextResponse API를 사용하여 다음을 수행할 수 있습니다.
    • 들어오는 요청을 다른 URL로 리디렉션
    • 주어진 URL을 렌더링하여 응답을 다시 작성합니다.
    • API 경로, getServerSideProps에 대한 요청 헤더 설정 및 대상 재작성
    • response 쿠키 설정
    • response 헤더 설정
  • 미들웨어에서 응답을 생성하려면 다음을 수행할 수 있습니다.
    • response를 생성하는 경로(페이지 또는 Route Handler)에 다시 작성
    • NextResponse를 직접 반환합니다. 응답 생성을 참조하세요.

Cookies 사용

  • 쿠키는 일반 헤더입니다. 요청 시 쿠키 헤더에 저장됩니다. 응답에서는 Set-Cookie 헤더에 있습니다.
  • Next.js는 NextRequest 및 NextResponse의 쿠키 확장을 통해 이러한 쿠키에 액세스하고 조작하는 편리한 방법을 제공합니다.
    1. 들어오는 요청의 경우 쿠키에는 쿠키 가져오기, getAll, 설정 및 삭제 메서드가 함께 제공됩니다. hash를 사용하여 쿠키의 존재를 확인하거나 Clear를 사용하여 모든 쿠키를 제거할 수 있습니다.
    2. 나가는 응답의 경우 쿠키에는 get, getAll, set 및 delete 메서드가 있습니다.
// middleware.ts
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')
  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
 
  // 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: '/',
  })
  cookie = response.cookies.get('vercel')
  console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
  // The outgoing response will have a `Set-Cookie:vercel=fast;path=/test` header.
 
  return response
}

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) {
  // 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 요청 헤더 필드가 너무 큼 오류가 발생할 수 있으므로 큰 헤더를 설정하지 마십시오.

Response 생성

  • Response 또는 NextResponse 인스턴스를 반환하여 미들웨어에서 직접 응답할 수 있습니다. (Next.js v13.1.0부터 사용 가능)
// middleware.ts
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' } }
    )
  }
}

고급 미들웨어 플래그

  • Next.js v13.1에서는 고급 사용 사례를 처리하기 위해 미들웨어용으로 두 개의 추가 플래그인 SkipMiddlewareUrlNormalize 및 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()
  }
 
  // 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.json
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)
  // with the flag this now /_next/data/build-id/hello.json
  // without the flag this would be normalized to /hello
}

Reference

profile
주도적으로 문제를 정의하고 코드를 통해 해결합니다.

0개의 댓글