# 07. 인증 흐름

> **대상**: 프론트엔드/백엔드 개발자
> **버전**: 1.0.0
> **최종 수정**: 2026-03-09

---

## 1. 인증 아키텍처 요약

```
┌─────────────────────────────────────────────────────┐
│ 브라우저                                              │
│                                                      │
│  [React 컴포넌트]                                     │
│       │ fetch('/api/proxy/...')                       │
│       │ (토큰 전송 안함 — 쿠키가 자동 포함)              │
│       ▼                                              │
│  [Next.js API Proxy]                                 │
│       │ 쿠키에서 access_token 읽기                     │
│       │ Authorization: Bearer {token} 헤더 추가        │
│       ▼                                              │
│  [authenticatedFetch 게이트웨이]                       │
│       │ 401 → refresh → retry (자동)                  │
│       ▼                                              │
│  [PHP Laravel Backend]                               │
│                                                      │
│  쿠키:                                                │
│  ├── access_token  (HttpOnly, 2시간)                  │
│  ├── refresh_token (HttpOnly, 7일)                    │
│  └── is_authenticated (non-HttpOnly, 상태 확인용)      │
└─────────────────────────────────────────────────────┘
```

---

## 2. 왜 HttpOnly 쿠키?

| 방식 | XSS 취약 | 토큰 갱신 | SAM 채택 |
|------|----------|----------|---------|
| localStorage | **취약** (JS 접근 가능) | 직접 구현 | ❌ |
| 일반 쿠키 | **취약** (JS 접근 가능) | 직접 구현 | ❌ |
| **HttpOnly 쿠키** | **안전** (JS 접근 불가) | 프록시에서 처리 | **✅** |

- JavaScript로 토큰을 읽을 수 없음 → XSS 공격에 안전
- 대신 서버(Next.js 프록시)에서만 읽기 가능 → 프록시 패턴 필수

---

## 3. 로그인 흐름

```
1. 사용자 → 로그인 폼 입력 (email, password)
2. 프론트 → POST /api/proxy/auth/login { email, password }
3. 프록시 → POST /api/v1/auth/login (Laravel)
4. Laravel → { access_token, refresh_token, expires_in }
5. 프록시 → Set-Cookie 3개 설정:
   - access_token=xxx;  HttpOnly; Max-Age=7200
   - refresh_token=xxx; HttpOnly; Max-Age=604800
   - is_authenticated=true; Max-Age=7200
6. 프론트 → 대시보드로 이동
```

---

## 4. API 호출 시 토큰 흐름

```
1. 컴포넌트 → Server Action 호출 (또는 fetch /api/proxy/...)
2. Server Action → serverFetch → authenticatedFetch
   - 쿠키에서 access_token 읽기
   - Authorization: Bearer {token} 헤더 추가
3. 백엔드 응답:
   - 200 OK → 정상 처리
   - 401 → 토큰 만료 (아래 갱신 흐름)
```

---

## 5. 토큰 갱신 (자동)

```
1. API 호출 → 401 Unauthorized 응답
2. authenticatedFetch 감지:
   a. refresh_token으로 POST /api/v1/auth/refresh
   b. 새 access_token 발급
   c. 새 토큰으로 원래 요청 재시도 (1회)
3. 결과:
   - 재시도 성공 → 정상 응답 + 새 쿠키 설정
   - 재시도 실패 → 쿠키 삭제 + 로그인 페이지 이동
```

**중요**: 동시에 여러 요청이 401을 받으면, refresh는 1번만 실행 (globalThis 캐싱으로 중복 방지)

---

## 6. 미들웨어 (middleware.ts)

요청 단계에서의 인증 검사 (토큰 갱신과는 별개):

```
요청 들어옴
  ↓
1. 내부 요청 필터링 (_next/*)
2. IE 브라우저 차단
3. 다국어 처리 (ko, en)
4. /dev/ 경로 프로덕션 차단
5. 봇 탐지 (40+ 패턴 차단)
6. API/정적 파일 통과
7. 인증 확인:
   - 비인증 사용자 → 로그인 페이지 리다이렉트
   - 인증된 사용자가 로그인 페이지 접근 → 대시보드 리다이렉트
8. 보안 헤더 설정 (X-Robots-Tag, CSP 등)
```

---

## 7. 프론트엔드 개발자 체크리스트

| 항목 | 설명 |
|------|------|
| 직접 토큰 관리 금지 | localStorage/sessionStorage에 토큰 저장 ❌ |
| fetch 직접 호출 금지 | Server Action 또는 `/api/proxy/` 경로만 사용 |
| 인증 상태 확인 | `is_authenticated` 쿠키 (non-HttpOnly) 또는 useAuthGuard() |
| 401 처리 | authenticatedFetch가 자동 처리 — 수동 처리 불필요 |

---

## 8. 백엔드 개발자 참고

| 항목 | 내용 |
|------|------|
| 인증 방식 | Bearer Token (Authorization 헤더) |
| 토큰 발급 | POST /api/v1/auth/login |
| 토큰 갱신 | POST /api/v1/auth/refresh |
| 401 응답 시 | 프론트가 자동 refresh → retry |
| API Key | `X-API-KEY` 헤더 (환경변수: `API_KEY`) |
