310 lines
6.9 KiB
Markdown
310 lines
6.9 KiB
Markdown
|
|
# 인증 시스템 구현 가이드
|
|||
|
|
|
|||
|
|
## 📋 개요
|
|||
|
|
|
|||
|
|
Laravel PHP 백엔드와 Next.js 15 프론트엔드 간의 3가지 인증 방식을 지원하는 통합 인증 시스템
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔐 지원 인증 방식
|
|||
|
|
|
|||
|
|
### 1️⃣ Sanctum Session (웹 사용자)
|
|||
|
|
- **대상**: 웹 브라우저 사용자
|
|||
|
|
- **방식**: HTTP-only 쿠키 기반 세션
|
|||
|
|
- **보안**: XSS 방어 + CSRF 토큰
|
|||
|
|
- **Stateful**: Yes
|
|||
|
|
|
|||
|
|
### 2️⃣ Bearer Token (모바일/SPA)
|
|||
|
|
- **대상**: 모바일 앱, 외부 SPA
|
|||
|
|
- **방식**: Authorization: Bearer {token}
|
|||
|
|
- **보안**: 토큰 만료 시간 관리
|
|||
|
|
- **Stateful**: No
|
|||
|
|
|
|||
|
|
### 3️⃣ API Key (시스템 간 통신)
|
|||
|
|
- **대상**: 서버 간 통신, 백그라운드 작업
|
|||
|
|
- **방식**: X-API-KEY: {key}
|
|||
|
|
- **보안**: 서버 사이드 전용 (환경 변수)
|
|||
|
|
- **Stateful**: No
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📁 파일 구조
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
src/
|
|||
|
|
├─ lib/api/
|
|||
|
|
│ ├─ client.ts # 통합 HTTP Client (3가지 인증 방식)
|
|||
|
|
│ │
|
|||
|
|
│ └─ auth/
|
|||
|
|
│ ├─ types.ts # 인증 타입 정의
|
|||
|
|
│ ├─ auth-config.ts # 인증 설정 (라우트, URL)
|
|||
|
|
│ │
|
|||
|
|
│ ├─ sanctum-client.ts # Sanctum 전용 클라이언트
|
|||
|
|
│ ├─ bearer-client.ts # Bearer 토큰 클라이언트
|
|||
|
|
│ ├─ api-key-client.ts # API Key 클라이언트
|
|||
|
|
│ │
|
|||
|
|
│ ├─ token-storage.ts # Bearer 토큰 저장 관리
|
|||
|
|
│ ├─ api-key-validator.ts # API Key 검증 유틸
|
|||
|
|
│ └─ server-auth.ts # 서버 컴포넌트 인증 유틸
|
|||
|
|
│
|
|||
|
|
├─ contexts/
|
|||
|
|
│ └─ AuthContext.tsx # 클라이언트 인증 상태 관리
|
|||
|
|
│
|
|||
|
|
├─ middleware.ts # 통합 미들웨어 (Bot + Auth + i18n)
|
|||
|
|
│
|
|||
|
|
└─ app/[locale]/
|
|||
|
|
├─ (auth)/
|
|||
|
|
│ └─ login/page.tsx # 로그인 페이지
|
|||
|
|
│
|
|||
|
|
└─ (protected)/
|
|||
|
|
└─ dashboard/page.tsx # 보호된 페이지
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔧 환경 변수 설정
|
|||
|
|
|
|||
|
|
### .env.local (실제 키 값)
|
|||
|
|
```env
|
|||
|
|
# API Configuration
|
|||
|
|
NEXT_PUBLIC_API_URL=https://api.5130.co.kr
|
|||
|
|
NEXT_PUBLIC_FRONTEND_URL=http://localhost:3000
|
|||
|
|
|
|||
|
|
# Authentication Mode
|
|||
|
|
NEXT_PUBLIC_AUTH_MODE=sanctum
|
|||
|
|
|
|||
|
|
# API Key (서버 사이드 전용 - 절대 공개 금지!)
|
|||
|
|
API_KEY=42Jfwc6EaRQ04GNRmLR5kzJp5UudSOzGGqjmdk1a
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### .env.example (템플릿)
|
|||
|
|
```env
|
|||
|
|
NEXT_PUBLIC_API_URL=https://api.5130.co.kr
|
|||
|
|
NEXT_PUBLIC_FRONTEND_URL=http://localhost:3000
|
|||
|
|
NEXT_PUBLIC_AUTH_MODE=sanctum
|
|||
|
|
API_KEY=your-secret-api-key-here
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 구현 단계
|
|||
|
|
|
|||
|
|
### Phase 1: 핵심 인프라 (필수)
|
|||
|
|
1. `lib/api/auth/types.ts` - 타입 정의
|
|||
|
|
2. `lib/api/auth/auth-config.ts` - 인증 설정
|
|||
|
|
3. `lib/api/client.ts` - 통합 HTTP 클라이언트
|
|||
|
|
4. `lib/api/auth/sanctum-client.ts` - Sanctum 클라이언트
|
|||
|
|
|
|||
|
|
### Phase 2: Middleware 통합
|
|||
|
|
1. `middleware.ts` 확장 - 인증 체크 로직 추가
|
|||
|
|
2. 라우트 보호 구현 (protected/guest-only)
|
|||
|
|
|
|||
|
|
### Phase 3: 로그인 페이지
|
|||
|
|
1. `app/[locale]/(auth)/login/page.tsx`
|
|||
|
|
2. 기존 validation schema 활용
|
|||
|
|
|
|||
|
|
### Phase 4: 보호된 페이지
|
|||
|
|
1. `app/[locale]/(protected)/dashboard/page.tsx`
|
|||
|
|
2. Server Component로 구현
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔒 보안 고려사항
|
|||
|
|
|
|||
|
|
### 환경 변수 보안
|
|||
|
|
```yaml
|
|||
|
|
✅ NEXT_PUBLIC_*: 브라우저 노출 가능
|
|||
|
|
❌ API_KEY: 절대 NEXT_PUBLIC_ 붙이지 말 것!
|
|||
|
|
✅ .env.local은 .gitignore에 포함됨
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 인증 방식별 보안
|
|||
|
|
```yaml
|
|||
|
|
Sanctum:
|
|||
|
|
✅ HTTP-only 쿠키 (XSS 방어)
|
|||
|
|
✅ CSRF 토큰 자동 처리
|
|||
|
|
✅ Same-Site: Lax
|
|||
|
|
|
|||
|
|
Bearer Token:
|
|||
|
|
⚠️ localStorage 사용 (XSS 취약)
|
|||
|
|
✅ 토큰 만료 시간 체크
|
|||
|
|
✅ Refresh token 권장
|
|||
|
|
|
|||
|
|
API Key:
|
|||
|
|
⚠️ 서버 사이드 전용
|
|||
|
|
✅ 환경 변수 관리
|
|||
|
|
✅ 주기적 갱신 대비
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 Middleware 인증 플로우
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Request
|
|||
|
|
↓
|
|||
|
|
1. Bot Detection (기존)
|
|||
|
|
├─ Bot → 403 Forbidden
|
|||
|
|
└─ Human → Continue
|
|||
|
|
↓
|
|||
|
|
2. Static Files Check
|
|||
|
|
├─ Static → Skip Auth
|
|||
|
|
└─ Dynamic → Continue
|
|||
|
|
↓
|
|||
|
|
3. Public Routes Check
|
|||
|
|
├─ Public → Skip Auth
|
|||
|
|
└─ Protected → Continue
|
|||
|
|
↓
|
|||
|
|
4. Authentication Check
|
|||
|
|
├─ Sanctum Session Cookie
|
|||
|
|
├─ Bearer Token (Authorization header)
|
|||
|
|
└─ API Key (X-API-KEY header)
|
|||
|
|
↓
|
|||
|
|
5. Protected Routes Guard
|
|||
|
|
├─ Authenticated → Allow
|
|||
|
|
└─ Not Authenticated → Redirect /login
|
|||
|
|
↓
|
|||
|
|
6. Guest Only Routes
|
|||
|
|
├─ Authenticated → Redirect /dashboard
|
|||
|
|
└─ Not Authenticated → Allow
|
|||
|
|
↓
|
|||
|
|
7. i18n Routing
|
|||
|
|
↓
|
|||
|
|
Response
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚀 API 엔드포인트
|
|||
|
|
|
|||
|
|
### 로그인
|
|||
|
|
```
|
|||
|
|
POST /api/v1/login
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
Request:
|
|||
|
|
{
|
|||
|
|
"user_id": "hamss",
|
|||
|
|
"user_pwd": "StrongPass!1234"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Response (성공):
|
|||
|
|
{
|
|||
|
|
"user": {
|
|||
|
|
"id": 1,
|
|||
|
|
"name": "홍길동",
|
|||
|
|
"email": "hamss@example.com"
|
|||
|
|
},
|
|||
|
|
"message": "로그인 성공"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Cookie: laravel_session=xxx; HttpOnly; SameSite=Lax
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 로그아웃
|
|||
|
|
```
|
|||
|
|
POST /api/v1/logout
|
|||
|
|
|
|||
|
|
Response:
|
|||
|
|
{
|
|||
|
|
"message": "로그아웃 성공"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 현재 사용자 정보
|
|||
|
|
```
|
|||
|
|
GET /api/user
|
|||
|
|
Cookie: laravel_session=xxx
|
|||
|
|
|
|||
|
|
Response:
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"name": "홍길동",
|
|||
|
|
"email": "hamss@example.com"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📝 사용 예시
|
|||
|
|
|
|||
|
|
### 1. Sanctum 로그인 (웹 사용자)
|
|||
|
|
```typescript
|
|||
|
|
import { sanctumClient } from '@/lib/api/auth/sanctum-client';
|
|||
|
|
|
|||
|
|
const user = await sanctumClient.login({
|
|||
|
|
user_id: 'hamss',
|
|||
|
|
user_pwd: 'StrongPass!1234'
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. API Key 요청 (서버 사이드)
|
|||
|
|
```typescript
|
|||
|
|
import { createApiKeyClient } from '@/lib/api/auth/api-key-client';
|
|||
|
|
|
|||
|
|
const client = createApiKeyClient();
|
|||
|
|
const data = await client.fetchData('/api/external-data');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. Bearer Token 로그인 (모바일)
|
|||
|
|
```typescript
|
|||
|
|
import { bearerClient } from '@/lib/api/auth/bearer-client';
|
|||
|
|
|
|||
|
|
const user = await bearerClient.login({
|
|||
|
|
email: 'user@example.com',
|
|||
|
|
password: 'password'
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ⚠️ 주의사항
|
|||
|
|
|
|||
|
|
### API Key 갱신
|
|||
|
|
- PHP 팀에서 주기적으로 새 키 발급
|
|||
|
|
- `.env.local`의 `API_KEY` 값만 변경
|
|||
|
|
- 코드 수정 불필요, 서버 재시작만 필요
|
|||
|
|
|
|||
|
|
### Git 보안
|
|||
|
|
- `.env.local`은 절대 커밋 금지
|
|||
|
|
- `.env.example`만 템플릿으로 커밋
|
|||
|
|
- `.gitignore`에 `.env.local` 포함 확인
|
|||
|
|
|
|||
|
|
### 개발 환경
|
|||
|
|
- 개발 서버 시작 시 API Key 자동 검증
|
|||
|
|
- 콘솔에 검증 상태 출력
|
|||
|
|
- 에러 발생 시 명확한 가이드 제공
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 트러블슈팅
|
|||
|
|
|
|||
|
|
### API Key 에러
|
|||
|
|
```
|
|||
|
|
❌ API_KEY is not configured!
|
|||
|
|
📝 Please check:
|
|||
|
|
1. .env.local file exists
|
|||
|
|
2. API_KEY is set correctly
|
|||
|
|
3. Restart development server (npm run dev)
|
|||
|
|
|
|||
|
|
💡 Contact backend team if you need a new API key.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### CORS 에러
|
|||
|
|
- Laravel `config/cors.php` 확인
|
|||
|
|
- `supports_credentials: true` 설정
|
|||
|
|
- `allowed_origins`에 Next.js URL 포함
|
|||
|
|
|
|||
|
|
### 세션 쿠키 안받아짐
|
|||
|
|
- Laravel `SANCTUM_STATEFUL_DOMAINS` 확인
|
|||
|
|
- `localhost:3000` 포함 확인
|
|||
|
|
- `SESSION_DOMAIN` 설정 확인
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📚 참고 문서
|
|||
|
|
|
|||
|
|
- [Laravel Sanctum 공식 문서](https://laravel.com/docs/sanctum)
|
|||
|
|
- [Next.js Middleware 문서](https://nextjs.org/docs/app/building-your-application/routing/middleware)
|
|||
|
|
- [claudedocs/authentication-design.md](./authentication-design.md)
|
|||
|
|
- [claudedocs/api-requirements.md](./api-requirements.md)
|