o2o-castad-backend/app/user/schemas/user_schema.py

199 lines
7.0 KiB
Python

"""
User 모듈 Pydantic 스키마 정의
API 요청/응답 검증을 위한 스키마들입니다.
"""
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
# =============================================================================
# 카카오 OAuth 스키마
# =============================================================================
class KakaoLoginResponse(BaseModel):
"""카카오 로그인 URL 응답"""
auth_url: str = Field(..., description="카카오 인증 페이지 URL")
model_config = {
"json_schema_extra": {
"example": {
"auth_url": "https://kauth.kakao.com/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:8000/user/auth/kakao/callback&response_type=code"
}
}
}
class KakaoCodeRequest(BaseModel):
"""카카오 인가 코드 검증 요청 (프론트엔드에서 전달)"""
code: str = Field(..., min_length=1, description="카카오 인가 코드")
model_config = {
"json_schema_extra": {
"example": {
"code": "AUTHORIZATION_CODE_FROM_KAKAO"
}
}
}
# =============================================================================
# JWT 토큰 스키마
# =============================================================================
class TokenResponse(BaseModel):
"""토큰 발급 응답"""
access_token: str = Field(..., description="액세스 토큰")
refresh_token: str = Field(..., description="리프레시 토큰")
token_type: str = Field(default="Bearer", description="토큰 타입")
expires_in: int = Field(..., description="액세스 토큰 만료 시간 (초)")
model_config = {
"json_schema_extra": {
"example": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZXhwIjoxNzA1MzE1MjAwfQ.xxx",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3MDU4MzM2MDB9.yyy",
"token_type": "Bearer",
"expires_in": 3600
}
}
}
class RefreshTokenRequest(BaseModel):
"""토큰 갱신 요청"""
refresh_token: str = Field(..., min_length=1, description="리프레시 토큰")
model_config = {
"json_schema_extra": {
"example": {
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3MDU4MzM2MDB9.yyy"
}
}
}
# =============================================================================
# 사용자 정보 스키마
# =============================================================================
class UserResponse(BaseModel):
"""사용자 정보 응답"""
id: int = Field(..., description="사용자 ID")
kakao_id: int = Field(..., description="카카오 회원번호")
email: Optional[str] = Field(None, description="이메일")
nickname: Optional[str] = Field(None, description="닉네임")
profile_image_url: Optional[str] = Field(None, description="프로필 이미지 URL")
thumbnail_image_url: Optional[str] = Field(None, description="썸네일 이미지 URL")
is_active: bool = Field(..., description="계정 활성화 상태")
is_admin: bool = Field(..., description="관리자 여부")
last_login_at: Optional[datetime] = Field(None, description="마지막 로그인 일시")
created_at: datetime = Field(..., description="가입 일시")
model_config = {
"from_attributes": True,
"json_schema_extra": {
"example": {
"id": 1,
"kakao_id": 1234567890,
"email": "user@kakao.com",
"nickname": "홍길동",
"profile_image_url": "https://k.kakaocdn.net/dn/.../profile.jpg",
"thumbnail_image_url": "https://k.kakaocdn.net/dn/.../thumb.jpg",
"is_active": True,
"is_admin": False,
"last_login_at": "2026-01-15T10:30:00",
"created_at": "2026-01-01T09:00:00"
}
}
}
class UserBriefResponse(BaseModel):
"""사용자 간략 정보 (토큰 응답에 포함)"""
id: int = Field(..., description="사용자 ID")
nickname: Optional[str] = Field(None, description="닉네임")
email: Optional[str] = Field(None, description="이메일")
profile_image_url: Optional[str] = Field(None, description="프로필 이미지 URL")
is_new_user: bool = Field(..., description="신규 가입 여부")
model_config = {
"from_attributes": True,
"json_schema_extra": {
"example": {
"id": 1,
"nickname": "홍길동",
"email": "user@kakao.com",
"profile_image_url": "https://k.kakaocdn.net/dn/.../profile.jpg",
"is_new_user": False
}
}
}
class LoginResponse(BaseModel):
"""로그인 응답 (토큰 정보)"""
access_token: str = Field(..., description="액세스 토큰")
refresh_token: str = Field(..., description="리프레시 토큰")
token_type: str = Field(default="Bearer", description="토큰 타입")
expires_in: int = Field(..., description="액세스 토큰 만료 시간 (초)")
is_new_user: bool = Field(..., description="신규 가입 여부")
redirect_url: str = Field(..., description="로그인 후 리다이렉트할 프론트엔드 URL")
model_config = {
"json_schema_extra": {
"example": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZXhwIjoxNzA1MzE1MjAwfQ.xxx",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3MDU4MzM2MDB9.yyy",
"token_type": "Bearer",
"expires_in": 3600,
"is_new_user": False,
"redirect_url": "http://localhost:3000"
}
}
}
# =============================================================================
# 내부 사용 스키마 (카카오 API 응답 파싱)
# =============================================================================
class KakaoTokenResponse(BaseModel):
"""카카오 토큰 응답 (내부 사용)"""
access_token: str
token_type: str
refresh_token: Optional[str] = None
expires_in: int
scope: Optional[str] = None
refresh_token_expires_in: Optional[int] = None
class KakaoProfile(BaseModel):
"""카카오 프로필 정보 (내부 사용)"""
nickname: Optional[str] = None
profile_image_url: Optional[str] = None
thumbnail_image_url: Optional[str] = None
is_default_image: Optional[bool] = None
class KakaoAccount(BaseModel):
"""카카오 계정 정보 (내부 사용)"""
email: Optional[str] = None
profile: Optional[KakaoProfile] = None
class KakaoUserInfo(BaseModel):
"""카카오 사용자 정보 (내부 사용)"""
id: int
kakao_account: Optional[KakaoAccount] = None