본문 바로가기
nextjs

nextjs middleware.js | ts

by spare8433 2024. 11. 26.

설명

middleware.tsNext.js에서 애플리케이션의 특정 경로 요청에 대해 공통적으로 실행할 작업middleware 를 정의하여 요청(Request)이 처리되기 전에 실행됩니다.

 

직접적으로 response 및 request / response header 헤더를 수정하거나 rewriting, redirecting 등을 처리 할 수 있습니다.

 

프로젝트 루트 즉 쉽게 pagesapp 과 같은 레벨에서 작성해서 사용합니다.




주요 예시

 

1. 인증 및 권한 부여 (Authentication and Authorization)

특정 페이지나 API 경로에 대한 액세스 권한을 부여하기 전에 사용자 신원을 확인하거나 세션 쿠키를 확인 할 수 있습니다.



2. 서버 측 redirects (Server-Side Redirects)

특정 조건(예: 로케일, 사용자 역할)에 따라 서버 수준에서 사용자를 redirect 할 수 있습니다.



ex) 사용자 인증 및 redirects

import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

export function middleware(req: NextRequest) {
  const token = req.cookies.get("authToken"); // JWT 쿠키 확인

  if (!token) {
    return NextResponse.redirect(new URL("/auth/login", req.url));
  }

  return NextResponse.next(); // 인증 통과
}



3. 경로 재작성 (Path Rewriting)

요청 경로를 동적으로 변경하여 다른 페이지나 API 로 연결할 수 있습니다. A/B 테스트, 기능 롤아웃, 혹은 레거시 URL을 처리할 때 사용할 수 있습니다



ex) A/B 테스트

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const url = request.nextUrl.clone();

  // 랜덤하게 A/B 테스트 분류
  const isGroupA = Math.random() < 0.5;

  // A 그룹은 /new-design으로, B 그룹은 /old-design으로 경로 재작성
  url.pathname = isGroupA ? '/new-design' : '/old-design';

  return NextResponse.rewrite(url);
}



ex) 레거시 경로 처리

export function middleware(request: NextRequest) {
  const url = request.nextUrl.clone();

  if (url.pathname.startsWith('/blog')) {
    url.pathname = url.pathname.replace('/blog', '/posts'); // 새로운 경로로 변경
    return NextResponse.rewrite(url);
  }

  return NextResponse.next(); // 통과
}



※ 용어 정리

  • A/B 테스트: 두 가지 콘텐츠를 비교하여 방문자/뷰어가 더 높은 관심을 보이는 버전을 확인하는 테스트분할 테스트 또는 버킷 테스트라고도 합니다
  • 기능 롤아웃: 개발자가 사용자 기반에 새로운 기능을 점진적으로 출시하는 기능 관리 전략



4. 봇 감지 (Bot Detection)

악성 봇 트래픽을 차단하여 서버 리소스를 보호하고, 적법한 사용자를 위한 성능을 유지합니다.



ex) 봇 감지 및 차단

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const userAgent = request.headers.get('user-agent') || '';

  // 특정 User-Agent를 가진 요청 차단
  const isBot = /bot|crawl|spider|scraper|curl/i.test(userAgent);
  if (isBot) {
    return NextResponse.json(
      { message: 'Access denied: Bots are not allowed.' },
      { status: 403 }
    );
  }

  return NextResponse.next();
}



5. 로깅 및 분석 (Logging and Analytics)

페이지나 API가 요청을 처리하기 전에 로그를 기록하거나 분석 데이터를 수집할 수 있습니다.




주의 사항

 

1. 많은 계산 작업

당연하게도 미들웨어는 가볍고 빠르게 반응해야 하며 무거운 계산 작업이나 장기 실행 프로세스는 페이지 로드가 지연될 수 있습니다.



2. 복잡한 data fetch 및 데이터베이스 작업

middleware 는 직접적인 data fetch 나 database 작업을 위해 설계되지 않았습니다. 대신 이 작업은 Route Handlers or server-side 에서 수행하는 것이 적절합니다.



3. 광범위한 session 관리

middleware 는기본적인 세션 작업을 관리할 수 있지만, 광범위한 세션 관리는 전담 인증 서비스나 경로 핸들러 내에서 관리하는 것이 적절합니다




경로 지정 (Matching)

특정 경로를 정확하게 타겟팅하거나 제외하여 사용 가능합니다.

export const config = {
  matcher: [
    '/about/:path*',
    '/dashboard/:path*',
    '/((?!api|_next/static|_next/image|favicon.ico).*)' // 정규식
  ],
}



조건부 경로

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // '/about' 경로에 대한 처리
  if (request.nextUrl.pathname.startsWith('/about')) {
    return NextResponse.rewrite(new URL('/about-2', request.url))
  }

  // '/dashboard' 경로에 대한 처리
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.rewrite(new URL('/dashboard/user', request.url))
  }
}




NextResponse

NextResponse 를 사용해 여러 기능을 사용할 수 있습니다.

 

  • redirect : 요청을 다른 URL로 리다이렉트합니다.
  • rewrite: 요청 경로를 다른 경로로 변경합니다.
  • next: 요청을 미들웨어가 처리하지 않고 다음 단계(페이지, API, 다른 미들웨어)로 넘깁니다.
  • json: JSON 형식으로 응답을 반환




cookie 관련

cookie 는 request 에는 Cookie 헤더에 저장되며, response 에는 Set-Cookie 헤더에 저장됩니다.

Next.js는 NextRequestNextResponse의 쿠키 확장을 통해 이러한 쿠키에 액세스하고 조작하는 편리한 방법을 제공합니다.




Request , Response header 세팅

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {

  // 요청 헤더를 복제하고 새로운 헤더 설정 
  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.headers.set('x-hello-from-middleware2', 'hello')
  return response
}




참고

공식문서: https://nextjs.org/docs/14/app/building-your-application/routing/middleware