
이전 포스팅에서 App Router 환경에서 SSR 시 쿠키 전달 문제를 SSR 측 리디렉션 처리로 해결하고, 클라이언트가 토큰 재발급을 수행하도록 위임하는 전략을 다뤘습니다.
이번 글에서는 Next.js Middleware를 활용하여 SSR 전 JWT Cookie를 관리하는 방법에 대해 살펴보겠습니다.
웹 어플리케이션에서 미들웨어는 클라이언트의 요청(Request)과 서버의 응답(Response) 사이에서 요청을 가공하거나, 검사하거나, 추가 처리를 해주는 중간 역할의 함수 또는 구성 요소입니다.
Next.js의 Middleware는 라우팅이 일어나기 전에 실행되는 코드로, 요청(Request)을 조작하거나 조건에 따라 리다이렉트, 응답 처리 등을 미리 수행할 수 있는 기능입니다.
즉, 사용자가 어떤 페이지에 접근하기 전에, 요청을 가로채어 필요한 작업(예: 인증 체크, URL 리다이렉트, 응답 헤더 추가 등)을 할 수 있습니다.
Next.js의 Middleware를 활용하여 인증이 필요한 페이지 SSR 실행 전 WT 토큰 검사 및 재발급 처리를 미리 수행할 수 있습니다.
Nextjs의 Middleware 파일은 최상단 root 폴더 하나만 생성할 수 있습니다. Next.js가 인식 가능하도록 파일이름은 반드시 middleware로 설정해야합니다.
my-nextjs-app/
├── app/
├── pages/
├── public/
├── styles/
├── middleware.ts ✅ 꼭 여기에 위치해야 함
├── next.config.js
└── ...
export function middleware() {
//...
}
config.matcher는 middleware에서 middleware가 실행될 페이지를 설정하는 역할을 합니다.
middleware는 config.matcher를 설정하지 않으면 기본적으로 모든 경로에 적용됩니다.
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { isValidToken } from "./src/lib/api/token";
export async function middleware(req: NextRequest) {
// 응답 생성
const response = NextResponse.next();
// request 쿠키에서 accessToken 값 가져오기
const accessToken = req.cookies.get("accessToken")?.value || "";
// request 쿠키에서 refreshToken 값 가져오기
const refreshToken = req.cookies.get("refreshToken")?.value || "";
// accessToken 유효성 검사
const tokenVaildation = await isValidToken(
accessToken,
process.env.NEXT_SECRET_ACCESS_TOKEN_KEY as string
);
// accessToken이 만료된 경우
if (
!tokenVaildation?.isValid &&
tokenVaildation?.message.includes("claim timestamp check failed")
) {
try {
// 모든 쿠키 가져오기
const cookieStore = cookies();
const cookie = (await cookieStore);
.getAll()
.map(({ name, value }) => `${name}=${value}`)
.join("; ");
// 토큰 재발급 요청
const refreshTokenRes = await fetch(
`${req.nextUrl.origin}/api/auth/refresh-token`,
{
method: "POST",
headers: {
Cookie: cookie, // 쿠키 넣기
}
credentials: "include",
}
);
const data = await refreshTokenRes.json();
// response headers 쿠키 설정
response.cookies.set("accessToken", data.accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "strict",
path: "/",
});
// 에러 발생시 throw
if (!refreshTokenRes.ok) {
throw refreshTokenRes;
}
} catch (error) {
if (error instanceof Response) {
// refreshToken 만료 시 (로그인 만료 처리)
if (error.status === 401) {
// headers 쿠키 삭제
const response = NextResponse.redirect(new URL("/", req.url));
response.cookies.delete("accessToken");
response.cookies.delete("refreshToken");
return response;
} else {
throw error;
}
}
}
return response;
}
}
// middleware를 실행할 페이지 설정
export const config = {
matcher: ["/profile/:path*"],
};
Next.js Middleware는 SSR이 실행되기 전에 먼저 실행되어, 쿠키 검사 및 토큰 재설정 등 인증 관련 작업을 미리 수행할 수 있습니다.
이를 통해 클라이언트에 노출되지 않고, 보다 안전하게 SSR 환경에서 인증을 제어할 수 있습니다. 토큰이 만료되었을 경우, 미들웨어가 자동으로 재발급 처리 또는 리다이렉트 처리를 수행하여 사용자 경험을 보호합니다.