# Route Protection Architecture - 최종 구조

## 개요

**2단계 보호 시스템:**
1. **Middleware (서버)**: 모든 페이지 요청 시 인증 확인
2. **Layout Hook (클라이언트)**: 보호된 페이지의 브라우저 캐시 방지

---

## 폴더 구조

```
src/app/[locale]/
├── (auth)/                    # 게스트 전용 페이지
│   └── login/
│       └── page.tsx          # 로그인 페이지 (컴포넌트 재사용)
│
├── (protected)/               # ✅ 보호된 페이지 그룹
│   ├── layout.tsx            # 🔒 useAuthGuard() 여기서만!
│   └── dashboard/
│       └── page.tsx          # useAuthGuard() 불필요
│
├── login/                     # 직접 접근용 로그인 페이지
│   └── page.tsx
│
├── signup/                    # 직접 접근용 회원가입 페이지
│   └── page.tsx
│
├── page.tsx                   # 홈페이지 (공개)
└── layout.tsx                 # 루트 레이아웃
```

**Route Group 설명:**
- `(auth)`: 괄호로 감싸져 있어 URL에 포함되지 않음
  - `/login` → `src/app/[locale]/login/page.tsx`
  - `/(auth)/login` → 동일한 `/login` URL
- `(protected)`: Layout 기반 보호 그룹
  - `/dashboard` → `src/app/[locale]/(protected)/dashboard/page.tsx`
  - Layout의 `useAuthGuard()`가 자동 적용

---

## 보호 레이어 상세

### Layer 1: Middleware (서버 사이드)

**파일:** `src/middleware.ts`

**역할:**
- 모든 HTTP 요청 차단 (페이지, API, 리소스)
- HttpOnly 쿠키 검증
- 인증 실패 시 `/login` 리다이렉트

**적용 범위:**
- URL 직접 입력
- 링크 클릭
- 새로고침 (F5)
- 프로그래매틱 네비게이션

**코드:**
```typescript
// src/middleware.ts
function checkAuthentication(request: NextRequest) {
  const tokenCookie = request.cookies.get('user_token');
  if (tokenCookie?.value) {
    return { isAuthenticated: true, authMode: 'bearer' };
  }
  return { isAuthenticated: false, authMode: null };
}

// 보호된 경로 체크
if (!isAuthenticated && !isPublicRoute && !isGuestOnlyRoute) {
  return NextResponse.redirect(new URL('/login', request.url));
}
```

---

### Layer 2: Protected Layout (클라이언트 사이드)

**파일:** `src/app/[locale]/(protected)/layout.tsx`

**역할:**
- 페이지 마운트 시 인증 재확인
- 브라우저 BFCache (뒤로가기 캐시) 감지 및 새로고침
- 다른 탭에서 로그아웃 시 동기화

**적용 범위:**
- `(protected)` 폴더 하위 모든 페이지
- 브라우저 뒤로가기
- 페이지 캐시 복원

**코드:**
```typescript
// src/app/[locale]/(protected)/layout.tsx
"use client";

import { useAuthGuard } from '@/hooks/useAuthGuard';

export default function ProtectedLayout({ children }) {
  useAuthGuard(); // 모든 하위 페이지에 자동 적용
  return <>{children}</>;
}
```

---

## 시나리오별 동작

### ✅ 시나리오 1: URL 직접 입력 (비로그인)

```
http://localhost:3000/dashboard 입력
    ↓
🛡️ Middleware 실행
    → 쿠키 없음
    → /login 리다이렉트
    ↓
로그인 페이지 표시
(Layout Hook은 실행되지 않음)
```

**결과:** Middleware만으로 차단 완료 ✅

---

### ✅ 시나리오 2: 정상 로그인 후 접근

```
로그인 성공 → /dashboard 이동
    ↓
🛡️ Middleware 실행
    → 쿠키 있음
    → 통과
    ↓
(protected)/layout.tsx 마운트
    → useAuthGuard() 실행
    → /api/auth/check 호출
    → 인증 성공
    ↓
dashboard/page.tsx 렌더링
```

**결과:** 이중 검증 통과 ✅

---

### ✅ 시나리오 3: 로그아웃 후 뒤로가기 (핵심!)

```
/dashboard 접속 (로그인 상태)
    ↓
Logout 버튼 클릭
    → /api/auth/logout 호출
    → HttpOnly 쿠키 삭제
    → /login 이동
    ↓
브라우저 뒤로가기 버튼 클릭
    ↓
⚠️ 브라우저 캐시에서 /dashboard 복원
    → 서버 요청 없음
    → Middleware 실행 안됨 ❌
    ↓
🛡️ (protected)/layout.tsx 복원
    → useAuthGuard() 실행
    → pageshow 이벤트 감지
    → event.persisted === true (캐시됨)
    → window.location.reload() 실행
    ↓
새로고침 → 서버 요청 발생
    ↓
🛡️ Middleware 실행
    → 쿠키 없음
    → /login 리다이렉트
    ↓
로그인 페이지 표시
```

**결과:** Layout Hook이 캐시 우회 → Middleware 재실행 ✅

---

### ✅ 시나리오 4: 다른 탭에서 로그아웃

```
탭 A: /dashboard 접속 (로그인 상태)
탭 B: 로그아웃
    ↓
탭 A: 페이지 새로고침 또는 네비게이션
    ↓
🛡️ Middleware 실행
    → 쿠키 없음 (탭 B에서 삭제됨)
    → /login 리다이렉트
```

**결과:** 쿠키 공유로 즉시 차단 ✅

---

## 새 페이지 추가 방법

### 보호된 페이지 추가

**단계:**
1. `(protected)` 폴더 안에 페이지 생성
2. **끝!** (자동으로 보호됨)

**예시:**
```bash
# Profile 페이지 생성
mkdir -p src/app/[locale]/(protected)/profile
```

```tsx
// src/app/[locale]/(protected)/profile/page.tsx
"use client";

export default function Profile() {
  // useAuthGuard() 불필요! Layout에서 자동 처리
  return <div>Profile Content</div>;
}
```

**URL:** `/profile` (Route Group 괄호는 URL에 포함 안됨)

---

### 공개 페이지 추가

**단계:**
1. `(protected)` 폴더 **밖**에 페이지 생성
2. `auth-config.ts`의 `publicRoutes`에 추가 (필요시)

**예시:**
```bash
# About 페이지 생성 (공개)
mkdir -p src/app/[locale]/about
```

```tsx
// src/app/[locale]/about/page.tsx
export default function About() {
  return <div>About Us (Public)</div>;
}
```

```typescript
// src/lib/api/auth/auth-config.ts
export const AUTH_CONFIG = {
  publicRoutes: [
    '/about',  // 추가
  ],
  // ...
};
```

---

## 구현 상세

### useAuthGuard Hook

**파일:** `src/hooks/useAuthGuard.ts`

```typescript
export function useAuthGuard() {
  const router = useRouter();

  useEffect(() => {
    // 1. 페이지 로드 시 인증 확인
    const checkAuth = async () => {
      const response = await fetch('/api/auth/check');
      if (!response.ok) {
        router.replace('/login');
      }
    };

    checkAuth();

    // 2. 브라우저 캐시 감지 및 새로고침
    const handlePageShow = (event: PageTransitionEvent) => {
      if (event.persisted) {
        console.log('🔄 캐시된 페이지 감지: 새로고침');
        window.location.reload();
      }
    };

    window.addEventListener('pageshow', handlePageShow);

    return () => {
      window.removeEventListener('pageshow', handlePageShow);
    };
  }, [router]);
}
```

**핵심 로직:**
1. `checkAuth()`: `/api/auth/check` 호출로 실시간 인증 확인
2. `pageshow` 이벤트: `event.persisted`로 캐시 감지
3. `window.location.reload()`: 강제 새로고침으로 Middleware 재실행

---

### Auth Check API

**파일:** `src/app/api/auth/check/route.ts`

```typescript
export async function GET(request: NextRequest) {
  const token = request.cookies.get('user_token')?.value;

  if (!token) {
    return NextResponse.json(
      { error: 'Not authenticated', authenticated: false },
      { status: 401 }
    );
  }

  return NextResponse.json(
    { authenticated: true },
    { status: 200 }
  );
}
```

**역할:**
- HttpOnly 쿠키 읽기
- 인증 상태 반환 (200 or 401)

---

## 보안 장점

### ✅ 이전 (각 페이지에 Hook)
```
각 페이지마다 useAuthGuard() 수동 추가
→ 누락 위험 ⚠️
→ 보일러플레이트 코드 증가
```

### ✅ 현재 (Layout 기반)
```
(protected)/layout.tsx에서 한 번만
→ 새 페이지 자동 보호
→ 누락 불가능
→ 코드 중복 제거
```

---

## 설정 파일

### auth-config.ts

**파일:** `src/lib/api/auth/auth-config.ts`

```typescript
export const AUTH_CONFIG = {
  // 🔓 공개 라우트 (인증 불필요)
  publicRoutes: [],

  // 🔒 보호된 라우트 (참고용, 실제로는 기본 정책으로 보호)
  protectedRoutes: [
    '/dashboard',
    '/profile',
    '/settings',
    '/admin',
    // ... 모든 보호된 경로
  ],

  // 👤 게스트 전용 라우트 (로그인 후 접근 불가)
  guestOnlyRoutes: [
    '/login',
    '/signup',
    '/forgot-password',
  ],

  // 리다이렉트 설정
  redirects: {
    afterLogin: '/dashboard',
    afterLogout: '/login',
    unauthorized: '/login',
  },
};
```

---

## 테스트 체크리스트

### 필수 테스트

- [ ] **URL 직접 입력 (비로그인)**
  - `/dashboard` 입력 → `/login` 리다이렉트

- [ ] **로그인 후 접근**
  - 로그인 → `/dashboard` 정상 표시

- [ ] **로그아웃 후 뒤로가기**
  - 로그아웃 → 뒤로가기 → 캐시 감지 → 새로고침 → `/login` 리다이렉트

- [ ] **다른 탭에서 로그아웃**
  - 탭 A: `/dashboard` 유지
  - 탭 B: 로그아웃
  - 탭 A: 새로고침 → `/login` 리다이렉트

- [ ] **새 보호된 페이지 추가**
  - `(protected)/profile` 생성 → 자동 보호 확인

---

## 트러블슈팅

### 문제: 로그아웃 후 뒤로가기 시 페이지 보임

**원인:** Layout이 Client Component가 아님

**해결:**
```tsx
// (protected)/layout.tsx 파일 상단에 추가
"use client";
```

---

### 문제: 404 에러 (페이지를 찾을 수 없음)

**원인:** 폴더 이름 오타 또는 Route Group 괄호 누락

**확인:**
```bash
# 올바른 경로
src/app/[locale]/(protected)/dashboard/page.tsx

# 잘못된 경로
src/app/[locale]/protected/dashboard/page.tsx  # 괄호 없음
```

---

### 문제: 무한 리다이렉트

**원인:** `/login` 페이지에도 보호 적용됨

**확인:**
- `/login`이 `(protected)` 폴더 **밖**에 있는지 확인
- `guestOnlyRoutes`에 `/login` 포함 확인

---

## 성능 고려사항

### API 호출 최소화
- `useAuthGuard`는 페이지 마운트 시 **1회만** 호출
- 브라우저 캐시 복원 시에만 추가 호출 (새로고침)

### 사용자 경험
- 인증 확인은 비동기로 처리 (UI 블로킹 없음)
- `router.replace()` 사용으로 뒤로가기 히스토리 오염 방지

---

## 향후 페이지 추가 계획

### 즉시 적용 가능 (보호됨)
`(protected)` 폴더에 추가하면 자동 보호:

```
(protected)/
├── profile/          # 사용자 프로필
├── settings/         # 설정
├── admin/            # 관리자
│   ├── users/
│   ├── tenants/
│   └── reports/
├── inventory/        # 재고 관리
├── finance/          # 재무
├── hr/               # 인사
└── crm/              # CRM
```

---

## 요약

### ✅ 최종 아키텍처

```
보호 정책:
1. Middleware (서버): 모든 요청 차단
2. Layout (클라이언트): 캐시 우회 및 실시간 동기화

폴더 구조:
- (protected)/layout.tsx: 한 곳에서만 관리
- (protected)/**/page.tsx: 자동으로 보호됨

장점:
✅ 코드 중복 제거
✅ 누락 불가능
✅ 브라우저 캐시 문제 해결
✅ 확장성 (새 페이지 자동 보호)
✅ 유지보수성 향상
```

---

## 참고 문서

- **HttpOnly Cookie 구현**: `claudedocs/httponly-cookie-implementation.md`
- **Auth Guard 사용법**: `claudedocs/auth-guard-usage.md`
- **Middleware 설정**: `src/middleware.ts`
- **Auth 설정**: `src/lib/api/auth/auth-config.ts`