# Middleware 인증 문제 해결 보고서 ## 📅 작성일: 2025-11-07 ## 🔍 문제 증상 로그인하지 않은 상태에서 `/dashboard`에 접근 시, 인증 체크가 작동하지 않고 대시보드에 바로 접근되는 문제가 발생했습니다. ### 증상 상세 - ✅ 로그인/로그아웃 기능 정상 작동 - ✅ 쿠키(`user_token`) 저장/삭제 정상 - ❌ Middleware에서 보호된 라우트 접근 차단 실패 - ❌ Middleware console.log가 터미널에 전혀 출력되지 않음 --- ## 🐛 발견된 문제들 ### 1. Next.js 15 + next-intl 호환성 문제 **위치**: `next.config.ts` **원인**: - Next.js 15에서 next-intl v4를 사용할 때 `turbopack` 설정이 필수 - 이 설정이 없으면 middleware가 제대로 컴파일되지 않음 **해결**: ```typescript // next.config.ts const nextConfig: NextConfig = { turbopack: {}, // ✅ 추가 }; ``` --- ### 2. 복잡한 Matcher 정규식 **위치**: `src/middleware.ts` - `config.matcher` **원인**: - 너무 복잡한 regex 패턴으로 라우트 매칭 실패 - 중복된 matcher 패턴 (정규식 + 명시적 경로) **기존 코드**: ```typescript matcher: [ '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico)$).*)', '/dashboard/:path*', '/login', '/register', ] ``` **해결**: ```typescript matcher: [ '/((?!api|_next/static|_next/image|favicon.ico|.*\\..*|robots\\.txt).*)', ] ``` --- ### 3. isPublicRoute 함수 로직 버그 ⭐ (핵심 문제) **위치**: `src/middleware.ts` - `isPublicRoute()` 함수 **원인**: ```typescript // 문제 코드 function isPublicRoute(pathname: string): boolean { return AUTH_CONFIG.publicRoutes.some(route => pathname === route || pathname.startsWith(route) ); } ``` **버그 시나리오**: 1. `AUTH_CONFIG.publicRoutes`에 `'/'` 포함 2. `/dashboard`.startsWith('/') → `true` 반환 3. 모든 경로가 public route로 잘못 판단됨 4. 인증 체크가 스킵되어 보호된 라우트 접근 가능 **해결**: ```typescript function isPublicRoute(pathname: string): boolean { return AUTH_CONFIG.publicRoutes.some(route => { // '/' 는 정확히 일치해야만 public if (route === '/') { return pathname === '/'; } // 다른 라우트는 시작 일치 허용 return pathname === route || pathname.startsWith(route + '/'); }); } ``` **수정 후 동작**: - `/` → public ✅ - `/dashboard` → protected ✅ - `/about` → public ✅ - `/about/team` → public ✅ --- ## ✅ 해결 결과 ### 적용된 수정 사항 1. ✅ `next.config.ts`에 `turbopack: {}` 추가 2. ✅ Middleware matcher 단순화 3. ✅ `isPublicRoute()` 함수 로직 수정 4. ✅ 디버깅 로그 제거 (클린 코드) ### 검증 결과 ```bash # 로그아웃 상태에서 /dashboard 접근 시: [Auth Required] Redirecting to /login from /dashboard → 자동으로 /login 페이지로 리다이렉트 ✅ # 로그인 상태에서 /dashboard 접근 시: [Authenticated] Mode: bearer, Path: /dashboard → 정상 접근 ✅ ``` --- ## 📝 교훈 ### 1. Middleware 디버깅 - **브라우저 콘솔이 아닌 서버 터미널**에서 로그 확인 - `console.log`는 서버 사이드에서 실행되므로 터미널 출력 ### 2. 문자열 매칭 주의 - `startsWith('/')` 같은 패턴은 모든 경로와 매칭됨 - Root path(`/`)는 항상 정확한 일치(`===`) 사용 ### 3. Next.js 버전별 설정 - Next.js 15 + next-intl 사용 시 `turbopack` 설정 필수 - 공식 문서 및 마이그레이션 가이드 확인 필요 --- ## 🔗 관련 파일 ### 수정된 파일 - `next.config.ts` - turbopack 설정 추가 - `src/middleware.ts` - isPublicRoute 로직 수정, matcher 단순화 ### 관련 설정 파일 - `src/lib/api/auth/auth-config.ts` - 라우트 설정 - `src/lib/api/auth/sanctum-client.ts` - 인증 로직 - `src/lib/api/auth/token-storage.ts` - 토큰 관리 --- ## 🎯 현재 인증 플로우 ### 로그인 1. 사용자가 `/login`에서 인증 정보 입력 2. PHP API(`/api/v1/login`)로 요청 (API Key 포함) 3. Bearer Token 발급 (`user_token`) 4. localStorage 저장 + Cookie 동기화 5. `/dashboard`로 리다이렉트 ### 보호된 라우트 접근 1. Middleware에서 요청 가로채기 2. Cookie에서 `user_token` 확인 3. 토큰 있음 → 통과 4. 토큰 없음 → `/login`으로 리다이렉트 ### 로그아웃 1. PHP API(`/api/v1/logout`) 호출 2. localStorage 및 Cookie 정리 3. `/login`으로 리다이렉트 --- ## 📚 참고 자료 - Next.js 15 Middleware 공식 문서 - next-intl v4 마이그레이션 가이드 - `claudedocs/research_nextjs15_middleware_authentication_2025-11-07.md`