Middleware
Middleware란?
- 요청이 서버로 들어오거나 응답이 클라이언트로 나가기 전에 실행되는 코드
- 요청을 가로채고, 이를 변형하거나 로깅, 인증, 권한 검사 등 다양한 작업을 수행이 가능
Middleware 사용 예제
- 인증 권한 관리
- 사용자가 특정 페이지에 접근하기 전에 인증 및 권한을 확인하는데 주로 사용
- 로깅 및 분석
- 모든 요청에 대한 로깅을 수행하여 트래픽 분석 및 모니터링
- URL Redirect 및 Rewrite
- 특정 조건에 따라 요청된 URL을 다른 URL로 Redirect하거나 내부적으로 다른 경로로 Rewrite처리
- 보안 강화
- 보안 헤더를 추가하거나, 특정 요청을 차단하여 보안을 강화
Middleware 예시코드
import { NextResponse } from "next/server";
export function middleware(request) {
console.log("Middleware Test!");
const { pathname } = request.nextUrl;
if (pathname.startsWith("/new")) {
console.log("Rewriting to /new-path");
const newUrl = new URL("/new-path", request.url);
return NextResponse.rewrite(newUrl);
}
if (pathname.includes("/old-page")) {
console.log("Redirecting to root");
const newUrl = new URL("/", request.url);
return NextResponse.redirect(newUrl);
}
return NextResponse.next();
}
export const config = {
matcher: "/:path*",
};
export default function OldPage() {
return <h1>Old page 입니다.</h1>;
}
export default function NewPathPage() {
return <h1>New path page입니다.</h1>;
}
src/
├── app
│ ├── layout.js (RootLayout)
│ ├── page.js (Home 페이지)
│ ├── old
│ │ └── page.js (Old 페이지)
│ └── new-path
│ └── page.js (NewPath 페이지)
└── middleware.js (Middleware 설정)
위의 코드는 middleware를 이용하여 원치않는 페이지에 접근 시, 라우트를 제어하는 코드
우선 middleware를 사용하기 위해서는 Root경로에 middleware.js
를 생성해야하며, 내부 함수 선언식 또한 middleware
로 작성.
함수 선언식 middleware는 parameter값을 받는데, 이 때 parameter는 NextRequest객체
이다.
- NextRequest 객체속성
nextUrl
:NextURL
객체를 반환하며, URL의 정보를 포함cookies
: 쿠키에 접근할 수 있는 객체headers
: 요청 헤더에 접근할 수 있는 객체method
: 요청 메소드(GET
,POST
등)을 나타내는 문자열ip
: 요청을 보낸 클라이언트의 IP 주소
middleware의 parameter를 통해 현재 접근하고 있는 라우트에 대한 정보를 얻을 수 있으며, 이를 통해 라우트를 제어 가능하다.
앞서 middleware의 라우트 제어하기 전, 어느 경로에서부터 처리해야되는지에 대한 코드가 필요하다.
middleware 설정을 인식하기 위해서는 생성해야할 변수명이 config
로 생성해야된다.
export const config = {
matcher: "/:path*",
};
config변수를 생성 후, config내에 여러 속성들을 정의하여 사용이 가능
- config 속성
matcher
: 미들웨어가 실행될 경로를 정의regions
: 특정 지역에서만 middleware가 실행되도록 제한runtime
: 특정 런타임 환경에서만 middleware가 실행되도록 설정
matcher
속성은 일반적으로 자주 사용되며, 나머지 regions
, runtime
은 요구 사항이 있을 경우에만 사용
예시 코드와 같은 경우, 간단한 라우팅에 대한 제어만 하기 위해 matcher만 사용
config의 matcher속성에 문자열로 middleware를 동작시킬 경로를 작성하면 된다.
matcher속성에 대해 경 로를 작성할 경우, 여러 가지 방법이 있다.
- matcher속성
단일 경로 매칭
: 해당 경로에서만 middleware가 실행- ex)
matcher: "/about"
- ex)
와일드 카드 매칭
: 해당 경로와 그 하위 경로에 대해 middleware가 실행- ex)
matcher: "/about/:path*"
- ex)
다중 경로 매칭
: 배열을 이용하여 여러 경로에 대해 middleware를 실행- ex)
matcher: ["/about", "/old/:path*"]
- ex)
정규 표현식 매칭
: 정규표현식을 이용하여 표현식에 맞는 경우 middleware를 실행- ex)
matcher: ["/(old|new-path)", "/(about|profile)/:path*"]
- ex)
위와 같이 middleware를 사용하기 전, config에 대한 설정을 해야된다.
middleware함수 내부 코드 분석
const { pathname } = request.nextUrl;
if (pathname.startsWith("/new")) {
console.log("Rewriting to /new-path");
const newUrl = new URL("/new-path", request.url);
return NextResponse.rewrite(newUrl);
}
접근하려고 하는 라우트의 경로가 문자열/new
로 시작할 경우, 해당 조건이 발생을 한다.
URL객체를 통해 첫 번째 Argument에 상대 경로 혹은 절대 경로를 작성
두 번째 Argument는 base URL을 작성하여 현재 경로의 값을 가지고 있는 pathname
값을 URL기준을 잡는다.
새로 지정한 경로를 newUrl
값을 rewrite한다.
즉, 내가 이동하려고 하는 라우트의 경로 이름 중 new
가 들어갈 경우, /new-path
페이지에 대한 리소스를 제공한다.
위의 이미지와 같이 /new
경로에 진입하였을 때, 해당 경로 URL은 /new
에 있지만
렌더링된 페이지는 new-path
의 page.js가 렌더링된 것을 확인할 수 있다.
const { pathname } = request.nextUrl;
if (pathname.includes("/old-page")) {
console.log("Redirecting to root");
const newUrl = new URL("/old", request.url);
return NextResponse.redirect(newUrl);
}
rewrite에 대한 코드 로직과 동일하지만 반환값이 redirect이다.
/old-page
에 대한 경로를 설정하지 않아, 해당 경로에 접근을 할 경우, /old
페이지가 렌더링이 되도록 구현하였다.
위의 이미지는 /old-page
를 입력 후, 페이지가 렌더링된 화면이다.
즉, redirect는 해당 경로에 접근을 할 경우, 기존 접근하려고 했던 URL에 대신 설정한 URL 및 페이지를 렌더링한다.
Rewrite와 Redirect
- Redirect:
- 클라이언트가 새로운 URL로 이동하며, 브라우저 주소창의 URL이 변경
- 주로 사용자를 다른 페이지로 안내할 때 사용
- Rewrite:
- 서버 측에서 요청된 URL을 다른 URL로 내부적으로 처리하며, 브라우저 주소창의 URL은 변경되지 않음.
- 주로 서버에서 다른 리소스를 제공할 때 사용