o2o-castad-backend/docs/user/kakao.md

341 lines
11 KiB
Markdown

# 카카오 소셜 로그인 구현 가이드
## 목차
1. [개요](#1-개요)
2. [인증 흐름](#2-인증-흐름)
3. [User 모델 구조](#3-user-모델-구조)
4. [API 엔드포인트](#4-api-엔드포인트)
5. [환경 설정](#5-환경-설정)
6. [에러 처리](#6-에러-처리)
---
## 1. 개요
CastAD는 카카오 소셜 로그인만 지원하며, 인증 후 자체 JWT 토큰을 발급합니다.
### 인증 방식
| 항목 | 설명 |
|------|------|
| 소셜 로그인 | 카카오 OAuth 2.0 |
| 자체 인증 | JWT (Access Token + Refresh Token) |
| 카카오 토큰 저장 | X (1회 검증 후 폐기) |
---
## 2. 인증 흐름
### 2.1 전체 흐름도
```
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Client │ │ Backend │ │ Kakao │ │ DB │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
│ │ │ │
│ 1. 로그인 요청 │ │ │
│──────────────>│ │ │
│ │ │ │
│ 2. 카카오 로그인 URL 반환 │ │
│<──────────────│ │ │
│ │ │ │
│ 3. 카카오 로그인 페이지로 이동 │ │
│──────────────────────────────>│ │
│ │ │ │
│ 4. 사용자 인증 후 code 반환 │ │
│<──────────────────────────────│ │
│ │ │ │
│ 5. code 전달 │ │ │
│──────────────>│ │ │
│ │ │ │
│ │ 6. code로 토큰 요청 │
│ │──────────────>│ │
│ │ │ │
│ │ 7. access_token 반환 │
│ │<──────────────│ │
│ │ │ │
│ │ 8. 사용자 정보 조회 │
│ │──────────────>│ │
│ │ │ │
│ │ 9. 사용자 정보 반환 │
│ │<──────────────│ │
│ │ │ │
│ │ 10. kakao_id로 회원 조회 │
│ │──────────────────────────────>│
│ │ │ │
│ │ 11. 회원 정보 반환 (없으면 생성) │
│ │<──────────────────────────────│
│ │ │ │
│ 12. 자체 JWT 발급 및 반환 │ │
│<──────────────│ │ │
│ │ │ │
```
### 2.2 단계별 설명
| 단계 | 설명 | 관련 API |
|------|------|----------|
| 1-2 | 클라이언트가 로그인 요청, 백엔드가 카카오 인증 URL 생성 | `GET /user/auth/kakao/login` |
| 3-4 | 사용자가 카카오에서 로그인, 인가 코드(code) 발급 | 카카오 OAuth |
| 5-9 | 백엔드가 code로 카카오 토큰/사용자정보 획득 | 카카오 API |
| 10-11 | DB에서 회원 조회, 없으면 신규 가입 | 내부 처리 |
| 12 | 자체 JWT 토큰 발급 후 클라이언트에 반환 | `POST /user/auth/kakao/callback` |
---
## 3. User 모델 구조
### 3.1 테이블 스키마
```
┌─────────────────────────────────────────────────────────────┐
│ user │
├─────────────────────┬───────────────┬───────────────────────┤
│ Column │ Type │ Description │
├─────────────────────┼───────────────┼───────────────────────┤
│ id │ BIGINT (PK) │ 고유 식별자 (자동증가) │
│ kakao_id │ BIGINT (UQ) │ 카카오 회원번호 │
│ email │ VARCHAR(255) │ 이메일 (선택) │
│ nickname │ VARCHAR(100) │ 닉네임 (선택) │
│ profile_image_url │ VARCHAR(2048) │ 프로필 이미지 URL │
│ thumbnail_image_url │ VARCHAR(2048) │ 썸네일 이미지 URL │
│ is_active │ BOOLEAN │ 계정 활성화 상태 │
│ is_admin │ BOOLEAN │ 관리자 권한 │
│ last_login_at │ DATETIME │ 마지막 로그인 일시 │
│ created_at │ DATETIME │ 생성 일시 │
│ updated_at │ DATETIME │ 수정 일시 │
└─────────────────────┴───────────────┴───────────────────────┘
```
### 3.2 카카오 API 응답 매핑
```json
// 카카오 API 응답 예시
{
"id": 1234567890,
"kakao_account": {
"email": "user@kakao.com",
"profile": {
"nickname": "홍길동",
"profile_image_url": "https://k.kakaocdn.net/.../profile.jpg",
"thumbnail_image_url": "https://k.kakaocdn.net/.../thumb.jpg"
}
}
}
```
| User 필드 | 카카오 응답 경로 |
|-----------|-----------------|
| `kakao_id` | `id` |
| `email` | `kakao_account.email` |
| `nickname` | `kakao_account.profile.nickname` |
| `profile_image_url` | `kakao_account.profile.profile_image_url` |
| `thumbnail_image_url` | `kakao_account.profile.thumbnail_image_url` |
---
## 4. API 엔드포인트
### 4.1 카카오 로그인 URL 요청
```
GET /user/auth/kakao/login
```
**Response:**
```json
{
"auth_url": "https://kauth.kakao.com/oauth/authorize?client_id=...&redirect_uri=...&response_type=code"
}
```
### 4.2 카카오 콜백 (로그인/가입 처리)
```
POST /user/auth/kakao/callback
```
**Request:**
```json
{
"code": "인가코드"
}
```
**Response (성공):**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"user": {
"id": 1,
"nickname": "홍길동",
"email": "user@kakao.com",
"profile_image_url": "https://...",
"is_new_user": false
}
}
```
### 4.3 토큰 갱신
```
POST /user/auth/refresh
```
**Request:**
```json
{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}
```
**Response:**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}
```
### 4.4 로그아웃
```
POST /user/auth/logout
Authorization: Bearer {access_token}
```
### 4.5 모든 기기에서 로그아웃
```
POST /user/auth/logout/all
Authorization: Bearer {access_token}
```
### 4.6 내 정보 조회
```
GET /user/auth/me
Authorization: Bearer {access_token}
```
**Response:**
```json
{
"id": 1,
"kakao_id": 1234567890,
"nickname": "홍길동",
"email": "user@kakao.com",
"profile_image_url": "https://...",
"is_admin": false,
"created_at": "2026-01-14T16:00:00"
}
```
---
## 5. 환경 설정
### 5.1 카카오 개발자 설정
1. [카카오 개발자 콘솔](https://developers.kakao.com) 접속
2. 애플리케이션 생성
3. 플랫폼 > Web 사이트 도메인 등록
4. 카카오 로그인 > Redirect URI 등록
5. 동의항목 > 필요한 정보 설정
### 5.2 .env 설정
```env
# 카카오 OAuth
KAKAO_CLIENT_ID=your_rest_api_key
KAKAO_CLIENT_SECRET=your_client_secret # 선택
KAKAO_REDIRECT_URI=https://your-domain.com/api/v1/auth/kakao/callback
# JWT
JWT_SECRET=your-super-secret-key-min-32-characters
JWT_ALGORITHM=HS256
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60
JWT_REFRESH_TOKEN_EXPIRE_DAYS=7
```
### 5.3 config.py 설정
```python
class KakaoSettings(BaseSettings):
KAKAO_CLIENT_ID: str = Field(...)
KAKAO_CLIENT_SECRET: str = Field(default="")
KAKAO_REDIRECT_URI: str = Field(...)
model_config = _base_config
class JWTSettings(BaseSettings):
JWT_SECRET: str = Field(...)
JWT_ALGORITHM: str = Field(default="HS256")
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = Field(default=60)
JWT_REFRESH_TOKEN_EXPIRE_DAYS: int = Field(default=7)
model_config = _base_config
```
---
## 6. 에러 처리
### 6.1 에러 코드 정의
| HTTP Status | Error Code | 설명 |
|-------------|------------|------|
| 400 | `INVALID_CODE` | 유효하지 않은 인가 코드 |
| 400 | `KAKAO_AUTH_FAILED` | 카카오 인증 실패 |
| 401 | `TOKEN_EXPIRED` | 토큰 만료 |
| 401 | `INVALID_TOKEN` | 유효하지 않은 토큰 |
| 401 | `TOKEN_REVOKED` | 취소된 토큰 |
| 403 | `USER_INACTIVE` | 비활성화된 계정 |
| 403 | `ADMIN_REQUIRED` | 관리자 권한 필요 |
| 404 | `USER_NOT_FOUND` | 사용자 없음 |
| 500 | `KAKAO_API_ERROR` | 카카오 API 오류 |
### 6.2 에러 응답 형식
```json
{
"detail": {
"code": "TOKEN_EXPIRED",
"message": "토큰이 만료되었습니다. 다시 로그인해주세요."
}
}
```
---
## 부록: 파일 구조
```
app/user/
├── __init__.py
├── models.py # User, RefreshToken 모델
├── exceptions.py # 사용자 정의 예외
├── schemas/
│ ├── __init__.py
│ └── user_schema.py # Pydantic 스키마
├── services/
│ ├── __init__.py
│ ├── auth.py # 인증 서비스
│ ├── jwt.py # JWT 유틸리티
│ └── kakao.py # 카카오 OAuth 클라이언트
├── dependencies/
│ ├── __init__.py
│ └── auth.py # 인증 의존성 (get_current_user 등)
└── api/
└── routers/
└── v1/
├── __init__.py
└── auth.py # 인증 API 라우터
```