SPACE RUMI

Hi, I am rumi. Let's Splattack!

[STUDY] 스터디/NEXT.js Docs

[Routing] Next 공식문서 한글번역 / Routing - Middleware / 라우팅 미들웨어

백루미 2023. 5. 5. 14:41
반응형

Next.js beta문서가 공식문서랑 병합 되었다.

 

미들웨어

미들웨어를 사용하면 요청이 완료되기 전에 코드를 실행할 수 있습니다. 그런 다음 들어오는 요청에 따라 request 또는 response header를 재작성, 리디렉션, 수정하거나 직접 응답하여 response를 수정할 수 있습니다.

알아두면 좋은 사항

  • 미들웨어는 캐시된 콘텐츠보다 먼저 실행되므로 정적 파일과 페이지를 개인화할 수 있습니다. 미들웨어의 일반적인 예로는 인증, A/B 테스트, 로컬라이즈된 페이지, 봇 보호 등이 있습니다.
  • 미들웨어는 app 라우터 또는 page 라우터보다 먼저 실행되므로 미들웨어 동작은 동일합니다.
  • 12.2 이전 버전의 미들웨어를 사용 중인 경우 업그레이드 가이드를 참조하세요.

 

미들웨어 사용하기

미들웨어 사용을 시작하려면 아래 단계를 따르세요:

Next.js 업데이트
최신 버전의 Next.js를 설치합니다:

npm install next@latest


미들웨어 파일 생성
프로젝트의 루트 디렉터리(app 또는 page 디렉터리와 같은 수준)에 middleware.ts(또는 .js) 파일을 만듭니다.

참고src 디렉터리를 사용하는 경우 미들웨어 파일을 그 안에 넣으세요.

미들웨어 함수 내보내기
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));
}
 
// 자세한 내용은 아래의 '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/, 페이지 등)
  • next.config.js의 afterFiles(rewrites)
  • 동적 경로(/blog/[slug])
  • next.config.js의 fallback(rewrites)


미들웨어가 실행될 경로를 정의하는 방법에는 두 가지가 있습니다:

  1. 사용자 정의 matcher 구성
  2. 조건문

 

Matcher

matcher를 사용하면 특정 경로에서 실행할 미들웨어를 필터링할 수 있습니다.

export const config = {
  matcher: '/about/:path*',
};

배열 구문을 사용하여 단일 경로 또는 여러 경로를 일치시킬 수 있습니다:

export const config = {
  matcher: ['/about/:path*', '/dashboard/:path*'],
};

matcher 구성은 전체 정규식을 허용하므로 네거티브 룩헤드(ex: X(?!Y) X 다음에 Y 가 오지 않는 경우) 또는 문자 일치와 같은 매칭이 지원됩니다. 특정 경로를 제외한 모든 경로를 일치시키는 네거티브 룩헤드의 예는 여기에서 확인할 수 있습니다:

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).*)',
  ],
};

참고: matcher 값은 빌드 시 정적으로 분석할 수 있도록 상수여야 합니다. 변수와 같은 동적 값은 무시됩니다.

구성된  matcher:

  • 반드시 /로 시작해야 합니다.
  • 명명된 매개변수를 포함할 수 있습니다: /about/:path는 /about/a 및 /about/b와 일치하지만 /about/a/c와는 일치하지 않습니다.
  • 명명된 매개변수에 수정자를 포함할 수 있습니다(:로 시작):
    /about/:path* matches /about/a/b/c . 왜냐면 * 은 0 또는 이상 이고, ? 는 0 또는 1 그리고 + 는 1 또는 이상입니다. (뭔소리야)
  • 괄호로 묶인 정규식을 사용할 수 있습니다: /about/(.*)/about/:path*와 동일합니다.
  • 경로 정규식 문서에서 자세한 내용을 읽어보세요.

참고: 이전 버전과의 호환성을 위해 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

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

  • 들어오는 요청을 다른 URL로 redirect합니다.
  • 지정된 URL을 표시하여 응답을 rewrite합니다.
  • API 경로, getServerSideProps, rewrite 대상에 대한 요청 헤더 설정
  • 응답 쿠키 설정
  • 응답 헤더 설정

 

미들웨어에서 응답을 생성하려면 다음을 수행할 수 있습니다:

  1. 응답을 생성하는 경로(page 또는 엣지 API 경로)로 rewrite합니다.
  2. NextResponse를 직접 반환합니다. 응답 생성하기를 참조하세요.

 

쿠키 사용

쿠키는 일반 헤더입니다. 요청 시에는 쿠키 헤더에 저장됩니다. 응답 시에는 Set-Cookie 헤더에 저장됩니다. Next.js는 NextRequest 및 NextResponse의 쿠키 확장을 통해 이러한 쿠키에 액세스하고 조작할 수 있는 편리한 방법을 제공합니다.

  1. 들어오는 요청의 경우 쿠키에는 다음과 같은 메서드가 제공됩니다: getgetAll, setdelete 쿠키. has를 사용하여 쿠키의 존재 여부를 확인하거나 clear를 사용하여 모든 쿠키를 제거할 수 있습니다.
  2. 발신 응답의 경우 쿠키에는 getgetAllsetdelete 메서드가 있습니다.
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')?.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
 
  // 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: '/test',
  });
  cookie = response.cookies.get('vercel');
  console.log(cookie); // => { name: 'vercel', value: 'fast', Path: '/test' }
  // The outgoing response will have a `Set-Cookie:vercel=fast;path=/test` header.
 
  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) {
  // 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 또는 NextResponse 인스턴스를 반환하여 직접 응답할 수 있습니다. (이 기능은 Next.js v13.1.0부터 사용 가능).

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.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
  // 플래그가 없으면 /hello 로 정규화됩니다.
}

 

반응형