Next.js의 Middleware는 요청이 완료되기 전에 실행되는 실행되는 서버 측 코드로 요청(Request)과 응답(Response)을 수정하거나 특정 로직을 적용할 수 있는 기능을 제공한다. 이는 클라이언트와 서버간의 데이터 흐름을 제어하거나 요청을 리다이렉트, 리라이트, 응답 제한 등을 적용하는데 유용하다.
Middlware란?
Middleware는 HTTP요청에 대한 중간 역할을 하며 Next.js애플리케이션에서 URL을 기반으로 로직을 실행할 수 있다. 요청을 변경하거나 응답을 조작하며 Next.js의 라우팅 및 SSR과 결합되어 강력한 제어 기능을 제공한다.
Middleware의 주요역할
- 리다이렉션: 특정 경로로 사용자를 리다이렉트한다.
- Rewriting: URL을 변환하여 다른 경로로 요청을 전달한다.
- 응답수정 : 헤더를 추가하거나 응답 내용을 직접 변경한다.
- 인증 및 권한 : 사용자가 인증된 상태인지 확인한다.
- 언어감지 : 요청의 헤더 정보를 기반으로 다국어 페이지를 제공한다.
Middleware의 동작원리
1. 클라이언트의 요청이 서버로 전달되면 Middleware가 요청을 가로챈다.
2. Middleware는 요청에 대해 조건문을 사용하여 특정 동작을 수행할지 결정한다.
3. 요청이 수정되거나 응답이 생성된 후, Next.js의 나머지 서버 로직이 실행된다.
Middleware함수의 구조
Middleware는 기본적으로 next/server에서 제공되는 NextResponse객체와 함께 사용된다.
import { NextResponse } from "next/server";
export function middleware(request) {
// 로직 처리
if (request.nextUrl.pathname === "/protected") {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next(); // 요청을 계속 진행
}
요청 흐름
1. 클라이언트 -> Middleware(요청 처리)
2. Middleware -> 필요한 경우 수정된 요청 전달
3. Next.js서버 로직 -> 클라이언트 응답
Middleware에서 사용할 수 있는 주요 객체
request 객체
- 클라이언트의 요청 정보를 포함
- request.nextUrl: URL 정보를 가져오기 위한 유틸리티
- request.headers: 요청 헤더에 접근 가능
- request.cookies: 쿠키 정보 접근 가능
NextResponse 객체
- 응답을 생성하거나 수정할 때 사용
- 주요 메서드
- NextResponse.next(): 요청을 계속 진행
- NextResponse.redirect(url): 특정 URL로 리다이렉트
- NextResponse.rewrite(url): 요청을 다른 경로로 재작성
- NextResponse.json(data): JSON 형식의 응답 반환
Middleware의 기본 설정
Middleware는 프로젝트의 middleware.js 또는 middleware.ts 파일에 정의됩니다. 이 파일은 Next.js의 루트 디렉토리 또는 특정 경로에 위치해야 합니다.
// 프로젝트 루트의 middleware.js
import { NextResponse } from "next/server";
export function middleware(request) {
// 예제: 특정 페이지로 리다이렉트
if (request.nextUrl.pathname.startsWith("/admin")) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
// 특정 경로에만 적용
export const config = {
matcher: "/admin/:path*", // '/admin'으로 시작하는 모든 경로에 적용
};
Middleware의 고급 기능
Matcher
config.matcher를 사용하여 Middleware가 적용될 경로를 제어합니다.
export const config = {
matcher: [
"/about/:path*", // '/about' 경로와 하위 경로에 적용
"/dashboard/:path*", // '/dashboard' 경로와 하위 경로에 적용
],
};
Rewriting
요청 URL을 변환하여 다른 경로로 라우팅합니다.
import { NextResponse } from "next/server";
export function middleware(request) {
const url = request.nextUrl.clone();
if (url.pathname === "/old-route") {
url.pathname = "/new-route"; // URL을 '/new-route'로 변경
return NextResponse.rewrite(url);
}
return NextResponse.next();
}
쿠키와 헤더 관리
Middleware를 사용하여 쿠키를 읽고 설정하거나, 응답 헤더를 수정할 수 있습니다.
import { NextResponse } from "next/server";
export function middleware(request) {
const response = NextResponse.next();
// 쿠키 추가
response.cookies.set("token", "12345", { httpOnly: true });
// 헤더 추가
response.headers.set("x-custom-header", "Hello World");
return response;
}
사용 사례
인증이 필요한 경로 보호
로그인되지 않은 사용자를 /login으로 리다이렉트.
import { NextResponse } from "next/server";
export function middleware(request) {
const isLoggedIn = request.cookies.get("isLoggedIn");
if (!isLoggedIn && request.nextUrl.pathname.startsWith("/protected")) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
다국어 지원
사용자의 Accept-Language 헤더를 기반으로 언어를 감지하고 리다이렉트.
import { NextResponse } from "next/server";
export function middleware(request) {
const locale = request.headers.get("accept-language")?.split(",")[0] || "en";
request.nextUrl.locale = locale;
return NextResponse.next();
}
API 요청 로깅
API 요청을 모니터링하거나 로그를 기록.
export function middleware(request) {
console.log(`API 요청 경로: ${request.nextUrl.pathname}`);
return NextResponse.next();
}
제약 사항
- Middleware는 Edge Runtime에서 실행되며, Node.js API를 사용할 수 없습니다. (예: fs 모듈)
- 응답은 항상 스트리밍 방식으로 처리됩니다.
- 빌드 시 middleware.js는 자동으로 Edge Function으로 배포됩니다.