NextJS: Middleware

hwisaac·2023년 3월 13일
5

Next.js

목록 보기
27/29

미들웨어를 사용하면 요청이 완료되기 전에 코드를 실행할 수 있으며, 들어오는 요청을 기반으로 응답을 수정하여 다시 작성, 리디렉션, 요청 또는 응답 헤더 수정 또는 직접 응답하는 것이 가능합니다.

미들웨어는 캐시된 콘텐츠보다 먼저 실행되므로 정적 파일 및 페이지를 개인화할 수 있습니다. 미들웨어의 일반적인 예는 인증, A/B 테스트, 로컬화된 페이지, 봇 보호 등이 있습니다. 로컬화된 페이지의 경우 i18n 라우팅으로 시작하여 더 고급 사용 사례에 미들웨어를 구현할 수 있습니다.

참고: 12.2 이전에 미들웨어를 사용하고 있었다면 업그레이드 가이드를 참조하세요.

미들웨어 사용 방법

미들웨어를 사용하려면 다음 단계를 따르세요:

  1. Next.js의 최신 버전을 설치하세요:
npm install next@latest
  1. 페이지와 동일한 수준(루트 또는 src 디렉터리에)에서 middleware.ts(또는 .js) 파일을 만듭니다.
  2. middleware.ts 파일에서 미들웨어 함수를 내보냅니다:
// 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('/about-2', request.url))
}

// 자세한 내용은 아래의 "경로 일치"를 참조하세요.
export const config = {
  matcher: '/about/:path*',
}

경로 일치

미들웨어는 프로젝트의 모든 라우트에서 호출됩니다. 다음은 실행 순서입니다.

  1. next.config.js에서 headers
  2. next.config.js에서 redirects
  3. 미들웨어(rewrite, 리디렉션 등)
  4. next.config.js에서 beforeFiles(rewrite)
  5. 파일 시스템 라우트(public/, _next/static/, Pages 등)
  6. next.config.js에서 afterFiles(rewrite)
  7. 동적 라우트(/blog/[slug])
  8. next.config.js에서 fallback(rewrite)

미들웨어가 실행되는 경로를 정의하는 방법은 두 가지가 있습니다.

  • 사용자 정의 matcher config
  • 조건문

Matcher

Matcher를 사용하면 특정 경로에서 Middleware가 실행되도록 필터링할 수 있습니다.

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 값은 빌드 시간에 정적으로 분석할 수 있도록 상수여야 합니다. 변수와 같은 동적 값은 무시됩니다.

구성된 matcher:

  1. 반드시 /로 시작해야 합니다.
  2. 이름 있는 매개변수를 포함할 수 있습니다: /about/:path/about/a/about/b와 일치하지만 /about/a/c와는 일치하지 않습니다.
  3. 이름 있는 매개변수에 수정자를 사용할 수 있습니다(콜론으로 시작합니다): /about/:path**0개 이상이므로 /about/a/b/c와 일치합니다. ?0개 또는 1개를 의미하며 +1개 이상을 의미합니다.
  4. 괄호로 묶인 정규식을 사용할 수 있습니다: /about/(.)/about/:path와 동일합니다.
    path-to-regexp 문서에서 자세한 내용을 확인하세요.

참고: 하위 호환성을 위해 Next.js는 항상 /public/public/index로 간주합니다. 따라서 /public/:path와 같은 matcher는 일치합니다.

조건문

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

NextResponse API

NextResponse API를 사용하면 다음을 수행할 수 있습니다:

  • 들어오는 요청을 다른 URL로 리디렉션합니다.
  • 주어진 URL을 표시하여 응답을 다시 작성합니다.
  • API 라우트, getServerSideProps 및 리다이렉트 대상을 위해 요청 헤더를 설정합니다.
  • 응답 쿠키를 설정합니다.
  • 응답 헤더를 설정합니다.

Middleware에서 응답을 생성하려면 다음을 수행할 수 있습니다:

  1. 응답을 생성하는 라우트(Page 또는 Edge API Route)로 다시 작성합니다.
  2. NextResponse를 직접 반환합니다. 응답 생성 참조

쿠키 사용하기

쿠키는 일반적인 헤더입니다. 요청에서는 쿠키가 Cookie 헤더에 저장됩니다. 응답에서는 Set-Cookie 헤더에 저장됩니다. Next.jsNextRequestNextResponse의 쿠키 확장을 통해 이러한 쿠키에 액세스하고 조작하는 편리한 방법을 제공합니다.

  1. 들어오는 요청에 대해 cookies는 다음과 같은 메서드를 가지고 있습니다: get, getAll, set, delete cookies. has를 사용하여 쿠키의 존재 여부를 확인하거나 clear를 사용하여 모든 쿠키를 제거할 수 있습니다.
  2. 나가는 응답에 대해 cookies는 다음과 같은 메서드를 가지고 있습니다: 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')?.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

  // 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' }
  // 나가는 응답은 `Set-Cookie:vercel=fast;path=/test` 헤더를 가질 것입니다.

  return response
}

헤더 설정하기

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: {
      // 새로운 요청 헤더
      headers: requestHeaders,
    },
  })

  // 새로운 응답 헤더 'x-hello-from-middleware2'를 설정합니다.
  response.headers.set('x-hello-from-middleware2', 'hello')
  return response
}

참고: 백엔드 웹 서버 구성에 따라 431 Request Header Fields Too Large 오류가 발생할 수 있으므로 큰 헤더 설정은 피하십시오.

응답 생성하기

Middleware에서 NextResponse를 반환하여 직접 응답할 수 있습니다. (Middleware에서 응답하는 것은 Next.js v13.1.0 이후 사용 가능합니다).

활성화된 경우 Response 또는 NextResponse API를 사용하여 미들웨어에서 응답을 제공할 수 있습니다:

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

고급 미들웨어 플래그

Next.js의 버전 13.1에서는 미들웨어에 대한 두 가지 추가 플래그인 skipMiddlewareUrlNormalizeskipTrailingSlashRedirect가 소개되었습니다. 이 플래그는 고급 사용 사례를 처리할 수 있도록 도와줍니다.

skipTrailingSlashRedirect는 URL의 마지막 슬래시 추가 또는 제거에 대한 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.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)
  // with the flag this now /_next/data/build-id/hello.json
  // without the flag this would be normalized to /hello
}

두 플래그는 일부 경우에는 매우 유용하지만, 고급 사용 사례이므로 필요에 따라 사용하도록 권장합니다.

0개의 댓글