# 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`