import { type NextRequest, NextResponse } from 'next/server'; export default function middleware(req: NextRequest) { // 인증 페이지는 로그인 상태에서도 접근 가능 const skipRoutes = ['/approval', '/verify-email']; if (skipRoutes.some((path) => req.nextUrl.pathname.startsWith(path))) { return NextResponse.next(); } const { pathname } = req.nextUrl; const accessToken = req.cookies.get('accessToken')?.value; const refreshToken = req.cookies.get('refreshToken')?.value; const publicRoutes = ['/', '/login', '/register', '/forgot-password', '/reset-password', '/welcome']; if (publicRoutes.includes(pathname)) { if (accessToken && publicRoutes.filter(r => r !== '/').includes(pathname)) { return NextResponse.redirect(new URL('/', req.url)); // 로그인 상태에서 접근 불가 } return NextResponse.next(); // 로그인 상태가 아니면 접근 가능 } // 로그인이 필요한 경로 const protectedRoutes = [ '/profile', // 내 정보 '/change-email', // 이메일 변경 '/change-name',// 별명 변경 '/change-password', // 비밀번호 변경 '/change-approve', // 알림 설정 '/change-photo', // 사진 변경 '/change-summary', // 한마디 변경 '/change-intro', // 자기소개 변경 '/certificate', // 본인 인증 '/my-post', // 작성 게시글 '/my-comment', // 작성 댓글 '/exp-log', // 경험치 내역 '/login-log', // 로그인 기록 '/withdraw' // 회원탈퇴 ]; const isProtected = protectedRoutes.some(route => { // 단순히 prefix 매칭; 동적 세그먼트는 :path*로 처리 if (route.endsWith('/:path*')) { return pathname.startsWith(route.replace('/:path*', '')); } return pathname === route; }); if (!isProtected) { return NextResponse.next(); // 404 } // 보호된 경로에 토큰이 없으면 로그인으로 if (!accessToken || !refreshToken) { return NextResponse.redirect(new URL('/login', req.url)); } return NextResponse.next(); } export const config = { // 보호 대상 경로만 지정 matcher: [ '/((?!_next|api|static|public|resources|editor|favicon.ico).*)', '/', '/login', '/register', '/forgot-password', '/reset-password', '/welcome', '/profile/:path*', '/change-email', '/change-name', '/change-password', '/certificate', '/my-post', '/my-comment', '/exp-log', '/login-log', '/withdraw', '/board/:path*', '/post/:path*', '/comment/:path*', '/support/:path*' ] }