[Next.js] Next.js 14에서 private route처리하기 (middleware.ts)
리액트에서 private route를 처리할 때 HOC를 사용해오곤 했는데 next에서는 다르게 처리할 것 같아 찾아보니 middleware로 제어하는 방식을 알게되었다. 공식문서의 middleware파트에서도 middleware의 다양한 활용 중 대표적인 사례로 페이지 요청 전 인증 확인 작업을 통한 페이지 접근 제한 방식을 소개하고 있다.
Authentication and Authorization: Ensure user identity and check session cookies
before granting access to specific pages or API routes.
Middleware
넥스트 미들웨어를 사용하면 요청이 완료되기 전에 코드를 실행할 수 있다. 그런 다음 들어오는 요청에 따라 요청 또는 응답 헤더를 다시 작성, 리다이렉션, 수정하거나 직접 응답하여 응답을 수정할 수 있다. 따라서 페이지 이동 전에 로그인한 사용자인지 확인 후 정해진 페이지로 리다이렉션 시킬 수 있는 것이다..!
middleware파일은 middleware.ts(or middleware.js)라는 이름으로 src폴더 아래, app폴더와 같은 계층에 위치해야한다.
첫 번째 코드
우리 프로젝트는 랜딩, 로그인, 회원가입을 제외한 모든 페이지가 로그인이 필요했기 때문에, private path를 다 적기보다는 public path를 적고 !private path로 처리를 하고 싶었다. 그래서 아래처럼 작성을 했는데 리다이렉트 후 페이지 CSS가 깨져보이는 에러가 발생했다.
import { getToken } from '@/app/utils/cookie/getToken'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(req: NextRequest) {
const token = getToken()
const publicPaths = ['/', '/login', '/sign-up']
const isPublicPath = publicPaths.includes(req.nextUrl.pathname)
if (isPublicPath && token) {
return NextResponse.redirect(new URL('/main', req.url))
}
if (!isPublicPath && !token) {
return NextResponse.redirect(new URL('/login', req.url))
}
return NextResponse.next()
}
토근도 정상적으로 오고, isPublicPath일때 문제가 안생기는 걸 보고 !isPublicPath에서 뭔가 정적데이터가 잘못처리되고 있다고 생각은했는데, 한참을 원인을 못찾다가 콘솔로 현재 요청을 다 찍어보고 원인을 알았다.
// 회원이 public path에 접속하면 -> main으로 정상 리다이렉트
if (isPublicPath && token) {
return NextResponse.redirect(new URL('/main', req.url))
}
// 비회원이 private path에 접속하면 -> /login으로 리다이렉트 되지만 css깨지는 오류발생
if (!isPublicPath && !token) {
return NextResponse.redirect(new URL('/login', req.url))
}
로그인 페이지에서 console.log(!isPublicPath, req.nextUrl.pathname) 로 찍었을 때 내가 기대한 결과는 false '/login' 이었는데, 그 이외에도 다양한 정적 요소에 대해서도 req가 들어오고 있었고, 당연히 publicPaths에 포함되어있지않았던 다양한 정적 주소들이 true로 찍히고 있었다. 즉, 정적요소들이 privatePath로 인식되면서 제대로 로드되지 못하고 있던 것이다.
문제의 원인을 파악 후 privatePaths를 모두 지정해 작성했을 때 문제는 해결되었지만 역시 페이지가 너무 많아 비효율적이란 생각이 들었다.
두 번째 코드
isFile 변수를 사용하여 요청된 경로가 정적 파일(CSS, JS, 이미지 등)을 가리키는지 확인 후, 파일이 아닌 경로에 대해서만 경로 리다이렉트 처리를 했다. 이렇게 하니 CSS가 깨지지 않고 정상 작동했고, private route도 정상적으로 작동하였다!
import { getToken } from '@/app/utils/cookie/getToken'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(req: NextRequest) {
const token = getToken()
const publicPaths = ['/', '/login', '/sign-up']
const isPublicPath = publicPaths.includes(req.nextUrl.pathname)
const isFile = req.nextUrl.pathname.match(/\.(.*)$/)
if (isFile) {
return NextResponse.next()
}
if (isPublicPath && token) {
return NextResponse.redirect(new URL('/main', req.url))
}
if (!isPublicPath && !token) {
return NextResponse.redirect(new URL('/login', req.url))
}
return NextResponse.next()
}
Rreferences
https://nextjs.org/docs/app/building-your-application/routing/middleware