[TIL] Next.js 13 Middleware(redirect, rewrite, protecting routes)

최소희·2024년 3월 17일
0

프론트엔드 학습

목록 보기
9/13

Middleware

Middleware는 Next.js 애플리케이션에서 요청과 응답의 흐름을 가로채고(intercept) 수정하고 제어하는 도구이다.

서버 사이드로 렌더링된 웹사이트 또는 완전한 웹 애플리케이션을 개발할 때, Middleware는 프로젝트 내 데이터 흐름을 크게 향상시킬 수 있다.

1. Middleware 이해

Middleware란?

웹사이트 또는 애플리케이션을 개발할 때, Middleware는 요청-응답 주기의 다양한 측면을 가로채고 조작하는 유용한 기능이다.

이를 통해 들어오는 요청을 조작하거나 나가는 응답을 조작하여, 애플리케이션의 필요에 맞게 조정할 수 있다.

use case 1: Routing

Routing control은 애플리케이션에서 복잡한 라우팅 시나리오를 관리할 때 특히 유용하다.

요청을 다른 URL로 리디렉션하거나 URL 패턴을 강제하는 기능을 통해, 웹 애플리케이션의 네비게이션 구조를 더욱 효율적으로 관리할 수 있게 된다.

이를 통해 사용자 요청에 따라 적절한 리소스로 안내하거나, 서비스의 구조를 개편하는 과정에서 기존 URL을 새로운 주소로 매끄럽게 연결하는 등의 작업을 수행할 수 있습니다

// Next.js

// 구 url에서 신 url로 리다이렉트
export function redirectMiddleware(request: NextRequest) {
  return NextResponse.redirect('/new-url');
}
// Javascript

function redirectMiddleware(request) {
    // 신규 url로 리다이렉트
  window.location.href = '/new-url';
  return {
    status: 204, // No content
    body: '',
  };
}
// Next.js - 지역화 예시

export const middleware = (request: NextRequest) => {
	const pathname = request.nextUrl.pathname;
	const countryName = request.headers.get('country');
	const locale = getLocale(request);

	if (countryName === 'KR')
		return NextResponse.redirect(
			new URL(`original url`),
		);
  1. 버전 관리: 애플리케이션의 새로운 버전을 출시할 때, 구 버전과 신 버전 간의 원활한 전환을 위해 사용될 수 있다.

  2. 지역화: 사용자의 지역에 맞춰 콘텐츠를 제공하기 위해, 지역별로 다른 URL로 요청을 리디렉션할 때 사용된다.

  3. 유지보수: 특정 페이지나 리소스가 유지보수 중일 때, 사용자를 대체 페이지로 안내하기 위해 사용된다.

  4. A/B 테스팅: 사용자의 일부에게는 A 버전을, 다른 사용자에게는 B 버전을 보여주기 위해 사용될 수 있다.

미들웨어는 들어온 request와 나가는 reponse에 대한 쿠키를 관리할 수 있다.

유저 세션 쿠키를 세팅하거나, 인증을 위한 쿠키를 읽거나, 유저가 로그아웃 할 때 쿠키를 삭제하는 등의 작업에 필수적이다.

// Next.js 

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

export function cookieManagementMiddleware(request: NextRequest) {
  const response = NextResponse.next();
  response.cookies.set('myCookie', '123');
  return response;
}

Next.js의 서버 측에서 미들웨어를 정의하고, NextResponse 객체의 cookies.set 메서드를 사용하여 쿠키를 설정한다.

// Javascript

function cookieManagementMiddleware(request) {
  const response = {
    status: 200,
    cookies: {
      myCookie: '123',
    },
  };
  return response;
}

use case 3: Authentication

미들웨어는 특정 라우트나 리소스에 대한 접근을 하기 전, 해당 유저가 인증된 유저인지 체크할 수 있다.

// Next.js

export function authenticationMiddleware(request: NextRequest) {
  if (!request.headers.cookie?.includes('authenticated=true')) {
    // 로그인으로 리다이렉트
    return NextResponse.redirect('/login');
  }
  return NextResponse.next();
}

유저가 protected route에 접근하려고 할 때, 미들웨어 함수가 쿠키를 통해 인증을 체크함으로써 유저를 식별한다. 유저가 인증되지 않았다면, 리다이렉트된다.

// Javascript

function authenticationMiddleware(request) {
  if (!(request.headers.cookie && request.headers.cookie.includes('authenticated=true'))) {
    // 로그인으로 리다이렉트
    window.location.href = '/login';
    return {
      status: 204, // No content
      body: '',
    };
  }
  // 다음 미들웨어 혹은 라우트 핸들러를 진행시킨다.
  return {
    status: 200,
  };
}

2. Next.js에서의 Middleware

Middleware 파일

Next.js에서 Middleware는 일반적으로 프로젝트 루트에 위치한 middleware.ts(또는 .js) 파일에 정의된다.

// middleware.ts

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

export function myMiddleware(request: NextRequest) {
  // midleware login
  return NextResponse.next(); // 다음 미들웨어 혹은 라우트 핸들러 실행

NextResponse API

Next.js의 NextResponse는 Middleware에서 응답을 다루는 데 사용되는 API이다.

리다이렉션, 응답 재작성, 헤더 설정, 쿠키 설정 등을 위한 메서드를 제공한다.

  • Redirect
    • 요청을 받아, 사용자를 다른 url로 보낸다. 사용자의 브라우저 주소창에 보이는 url이 변경된다.
    • 아래 예시 코드처럼, 요청을 redirect 하기 전, request.nextUrl 프로퍼티를 사용하여 현재 url에 쿼리 매개변수를 설정한 후 리다이렉트 시킬 수 있다.
      import { NextResponse } from 'next/server'
       
       // 현재 요청한 URL 호스트로 login이라는 새로운 URL 인스턴스 생성
      const loginUrl = new URL('/login', request.url)
      // /login URL에 ?from=/incoming-url 을 더함
      loginUrl.searchParams.set('from', request.nextUrl.pathname)
      // 새로운 loginURL로 리다이렉트
      return NextResponse.redirect(loginUrl)
  • Rewrite
    • url 경로를 내부적으로 다른 경로로 매핑한다. 사용자는 이 과정을 인식하지 못하며, 브라우저 주소창에는 원래의 url이 그대로 표시된디.
    • use case: 사용자에게 보이는 url을 유지하면서 내부적으로 다른 페이지나 API로 요청을 처리하고 싶을 때, url은 유지하되 내용만 변경하여 제공하고자 할 때 유용하다.
    • /about 페이지에 접근하는 사용자의 권한 또는 선호도에 따라 다른 정보를 보여주고 싶을 때, 내부적으로 다양한 경로로 rewrite하여 사용자별 맞춤화된 경로(/proxy)로 처리한다.
      import { NextResponse } from 'next/server'
       
      // Incoming request: /about, browser shows /about
      // Rewritten request: /proxy, browser shows /about
      return NextResponse.rewrite(new URL('/proxy', request.url))

3. Next.js 13.4에서 미들웨어를 사용하여 라우트 보호하기(protecting routes)

현재 라우트의 필요한 상태와 사용자 상태를 비교하여 일치하지 않는 경우, redirect 를 사용하여 사용자를 다른 페이지로 리다이렉트한다.

// middleware.js

import { NextResponse } from 'next/server';

// 사용자의 권한을 확인하는 함수
function getUserRole(token) {

  if (token === 'superadmin') {
    return 'superadmin';
  } else if (token === 'admin') {
    return 'admin';
  } else if (token === 'manager') {
    return 'manager';
  } else {
    return 'user';
  }
}

// 페이지에 필요한 권한을 반환하는 함수
function getRequiredRole(pathname) {

  if (pathname === '/superadmin-page') {
    return 'superadmin';
  } else if (pathname === '/admin-page') {
    return 'admin';
  } else if (pathname === '/manager-page') {
    return 'manager';
  } else {
    return 'user';
  }
}

// 미들웨어 함수
export default function middleware(req) {
  // 사용자의 토큰을 가져옴
  const token = req.cookies.token;
  // 사용자의 권한을 가져옴
  const userRole = getUserRole(token);
  // 현재 요청된 페이지의 필요한 권한을 가져옴
  const requiredRole = getRequiredRole(req.nextUrl.pathname);

  // 사용자의 권한이 필요한 권한보다 낮을 경우
  if (userRole !== requiredRole && userRole !== 'superadmin') {
    // 권한이 없는 페이지로 리디렉션
    return NextResponse.redirect('/unauthorized');
  }
  
  // 사용자의 권한이 필요한 권한과 일치하거나 슈퍼 관리자일 경우 다음 미들웨어나 라우트 핸들러로 이동
  return NextResponse.next();
}

참고 자료
Middleware in Next.js: A Comprehensive Guide
NextReponse API

profile
프론트엔드 개발자 👩🏻‍💻

0개의 댓글