middleware.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import { type NextRequest, NextResponse } from 'next/server';
  2. export default function middleware(req: NextRequest)
  3. {
  4. // 인증 페이지는 로그인 상태에서도 접근 가능
  5. const skipRoutes = ['/approval', '/verify-email'];
  6. if (skipRoutes.some((path) => req.nextUrl.pathname.startsWith(path))) {
  7. return NextResponse.next();
  8. }
  9. const { pathname } = req.nextUrl;
  10. const accessToken = req.cookies.get('accessToken')?.value;
  11. const refreshToken = req.cookies.get('refreshToken')?.value;
  12. const publicRoutes = ['/', '/login', '/register', '/forgot-password', '/reset-password', '/welcome'];
  13. if (publicRoutes.includes(pathname)) {
  14. if (accessToken && publicRoutes.filter(r => r !== '/').includes(pathname)) {
  15. return NextResponse.redirect(new URL('/', req.url)); // 로그인 상태에서 접근 불가
  16. }
  17. return NextResponse.next(); // 로그인 상태가 아니면 접근 가능
  18. }
  19. // 로그인이 필요한 경로
  20. const protectedRoutes = [
  21. '/profile', // 내 정보
  22. '/change-email', // 이메일 변경
  23. '/change-name',// 별명 변경
  24. '/change-password', // 비밀번호 변경
  25. '/change-approve', // 알림 설정
  26. '/change-photo', // 사진 변경
  27. '/change-summary', // 한마디 변경
  28. '/change-intro', // 자기소개 변경
  29. '/certificate', // 본인 인증
  30. '/my-post', // 작성 게시글
  31. '/my-comment', // 작성 댓글
  32. '/exp-log', // 경험치 내역
  33. '/login-log', // 로그인 기록
  34. '/withdraw' // 회원탈퇴
  35. ];
  36. const isProtected = protectedRoutes.some(route => {
  37. // 단순히 prefix 매칭; 동적 세그먼트는 :path*로 처리
  38. if (route.endsWith('/:path*')) {
  39. return pathname.startsWith(route.replace('/:path*', ''));
  40. }
  41. return pathname === route;
  42. });
  43. if (!isProtected) {
  44. return NextResponse.next(); // 404
  45. }
  46. // 보호된 경로에 토큰이 없으면 로그인으로
  47. if (!accessToken || !refreshToken) {
  48. return NextResponse.redirect(new URL('/login', req.url));
  49. }
  50. return NextResponse.next();
  51. }
  52. export const config =
  53. {
  54. // 보호 대상 경로만 지정
  55. matcher: [
  56. '/((?!_next|api|static|public|resources|editor|favicon.ico).*)',
  57. '/',
  58. '/login',
  59. '/register',
  60. '/forgot-password',
  61. '/reset-password',
  62. '/welcome',
  63. '/profile/:path*',
  64. '/change-email',
  65. '/change-name',
  66. '/change-password',
  67. '/certificate',
  68. '/my-post',
  69. '/my-comment',
  70. '/exp-log',
  71. '/login-log',
  72. '/withdraw',
  73. '/board/:path*',
  74. '/post/:path*',
  75. '/comment/:path*',
  76. '/support/:path*'
  77. ]
  78. }