import { type NextRequest, NextResponse } from 'next/server'; function isTokenValid(token: string): boolean { try { const payload = JSON.parse(atob(token.split('.')[1])); return payload.exp * 1000 > Date.now(); } catch { return false; } } 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 isLoggedIn = !!accessToken && isTokenValid(accessToken); const publicRoutes = ['/', '/login', '/register', '/forgot-password', '/reset-password', '/welcome']; if (publicRoutes.includes(pathname) || pathname.startsWith('/login/google/')) { if (isLoggedIn && publicRoutes.filter(r => r !== '/').includes(pathname)) { return NextResponse.redirect(new URL('/', req.url)); // 로그인 상태에서 접근 불가 } return NextResponse.next(); } // 로그인이 필요한 경로 (startsWith로 하위 경로까지 포함) const protectedRoutes = [ '/profile', // 내 정보 '/change-email', // 이메일 변경 '/change-name',// 별명 변경 '/change-password', // 비밀번호 변경 '/change-approve', // 알림 설정 '/change-thumb', // 사진 변경 '/change-summary', // 한마디 변경 '/change-intro', // 자기소개 변경 '/certificate', // 본인 인증 '/my-post', // 작성 게시글 '/my-comment', // 작성 댓글 '/exp-log', // 경험치 내역 '/login-log', // 로그인 기록 '/withdraw', // 회원탈퇴 '/valid-email', // 이메일 변경 인증 '/studio' // 크리에이터 스튜디오 ]; const isProtected = protectedRoutes.some(route => pathname === route || pathname.startsWith(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).*)' ] }