Middleware는 Next.js 애플리케이션에서 요청과 응답의 흐름을 가로채고(intercept) 수정하고 제어하는 도구이다.
서버 사이드로 렌더링된 웹사이트 또는 완전한 웹 애플리케이션을 개발할 때, Middleware는 프로젝트 내 데이터 흐름을 크게 향상시킬 수 있다.
웹사이트 또는 애플리케이션을 개발할 때, Middleware는 요청-응답 주기의 다양한 측면을 가로채고 조작하는 유용한 기능이다.
이를 통해 들어오는 요청을 조작하거나 나가는 응답을 조작하여, 애플리케이션의 필요에 맞게 조정할 수 있다.
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`),
);
버전 관리: 애플리케이션의 새로운 버전을 출시할 때, 구 버전과 신 버전 간의 원활한 전환을 위해 사용될 수 있다.
지역화: 사용자의 지역에 맞춰 콘텐츠를 제공하기 위해, 지역별로 다른 URL로 요청을 리디렉션할 때 사용된다.
유지보수: 특정 페이지나 리소스가 유지보수 중일 때, 사용자를 대체 페이지로 안내하기 위해 사용된다.
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;
}
미들웨어는 특정 라우트나 리소스에 대한 접근을 하기 전, 해당 유저가 인증된 유저인지 체크할 수 있다.
// 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,
};
}
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(); // 다음 미들웨어 혹은 라우트 핸들러 실행
Next.js의 NextResponse는 Middleware에서 응답을 다루는 데 사용되는 API이다.
리다이렉션, 응답 재작성, 헤더 설정, 쿠키 설정 등을 위한 메서드를 제공한다.
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)
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))
현재 라우트의 필요한 상태와 사용자 상태를 비교하여 일치하지 않는 경우, 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