Compare commits

..

5 Commits

Author SHA1 Message Date
jaehwang bcd2c0a96f Merge branch 'main' into scraper-poc 2026-01-21 04:59:16 +00:00
Dohyun Lim 36de908431 add kakao_verify endpoint for kakao login at frontend side 2026-01-21 08:53:16 +09:00
Dohyun Lim b4e5d04dbb update tags_metadata at main.py 2026-01-20 18:32:08 +09:00
Dohyun Lim b6e50be9ca update router tags 2026-01-20 17:41:21 +09:00
Dohyun Lim 47aca58b02 add redirect_url at kakao_login 2026-01-20 17:02:26 +09:00
6 changed files with 63 additions and 11 deletions

View File

@ -102,7 +102,7 @@ def _extract_region_from_address(road_address: str | None) -> str:
"model": ErrorResponse,
},
},
tags=["crawling"],
tags=["Crawling"],
)
async def crawling(request_body: CrawlingRequest):
"""네이버 지도 장소 크롤링"""
@ -379,7 +379,7 @@ print(response.json())
200: {"description": "이미지 업로드 성공"},
400: {"description": "이미지가 제공되지 않음", "model": ErrorResponse},
},
tags=["Image"],
tags=["Image-Server"],
)
async def upload_images(
images_json: Optional[str] = Form(
@ -597,7 +597,7 @@ curl -X POST "http://localhost:8000/image/upload/blob" \\
200: {"description": "이미지 업로드 성공"},
400: {"description": "이미지가 제공되지 않음", "model": ErrorResponse},
},
tags=["image"],
tags=["Image-Blob"],
)
async def upload_images_blob(
images_json: Optional[str] = Form(

View File

@ -15,6 +15,7 @@ from app.user.dependencies import get_current_user
from app.user.models import User
from app.user.schemas.user_schema import (
AccessTokenResponse,
KakaoCodeRequest,
KakaoLoginResponse,
LoginResponse,
RefreshTokenRequest,
@ -78,6 +79,54 @@ async def kakao_callback(
)
@router.post(
"/kakao/verify",
response_model=LoginResponse,
summary="카카오 인가 코드 검증 및 토큰 발급",
description="""
프론트엔드에서 카카오 로그인 받은 인가 코드를 검증하고 JWT 토큰을 발급합니다.
## 사용 시나리오
1. 프론트엔드가 카카오 로그인 완료 인가 코드(code) 받음
2. 프론트엔드가 엔드포인트에 code를 POST로 전달
3. 서버가 카카오 서버에 code 검증 사용자 정보 조회
4. JWT 토큰 발급 사용자 정보 반환
## 응답
- 신규 사용자인 경우 `user.is_new_user` `true` 반환됩니다.
- `redirect_url` 로그인 이동할 프론트엔드 URL입니다.
""",
)
async def kakao_verify(
request: Request,
body: KakaoCodeRequest,
session: AsyncSession = Depends(get_session),
user_agent: Optional[str] = Header(None, alias="User-Agent"),
) -> LoginResponse:
"""
카카오 인가 코드 검증 토큰 발급
프론트엔드가 카카오 콜백에서 받은 인가 코드를 전달하면
카카오 서버에서 검증 JWT 토큰을 발급합니다.
신규 사용자인 경우 자동으로 회원가입이 처리됩니다.
"""
# 클라이언트 IP 추출
ip_address = request.client.host if request.client else None
# X-Forwarded-For 헤더 확인 (프록시/로드밸런서 뒤에 있는 경우)
forwarded_for = request.headers.get("X-Forwarded-For")
if forwarded_for:
ip_address = forwarded_for.split(",")[0].strip()
return await auth_service.kakao_login(
code=body.code,
session=session,
user_agent=user_agent,
ip_address=ip_address,
)
@router.post(
"/refresh",
response_model=AccessTokenResponse,

View File

@ -1,6 +1,6 @@
from app.user.schemas.user_schema import (
AccessTokenResponse,
KakaoCallbackRequest,
KakaoCodeRequest,
KakaoLoginResponse,
KakaoTokenResponse,
KakaoUserInfo,
@ -13,7 +13,7 @@ from app.user.schemas.user_schema import (
__all__ = [
"AccessTokenResponse",
"KakaoCallbackRequest",
"KakaoCodeRequest",
"KakaoLoginResponse",
"KakaoTokenResponse",
"KakaoUserInfo",

View File

@ -27,8 +27,8 @@ class KakaoLoginResponse(BaseModel):
}
class KakaoCallbackRequest(BaseModel):
"""카카오 콜백 요청 (인가 코드)"""
class KakaoCodeRequest(BaseModel):
"""카카오 인가 코드 검증 요청 (프론트엔드에서 전달)"""
code: str = Field(..., min_length=1, description="카카오 인가 코드")
@ -163,6 +163,7 @@ class LoginResponse(BaseModel):
token_type: str = Field(default="Bearer", description="토큰 타입")
expires_in: int = Field(..., description="액세스 토큰 만료 시간 (초)")
user: UserBriefResponse = Field(..., description="사용자 정보")
redirect_url: str = Field(..., description="로그인 후 리다이렉트할 프론트엔드 URL")
model_config = {
"json_schema_extra": {
@ -177,7 +178,8 @@ class LoginResponse(BaseModel):
"email": "user@kakao.com",
"profile_image_url": "https://k.kakaocdn.net/dn/.../profile.jpg",
"is_new_user": False
}
},
"redirect_url": "http://localhost:3000"
}
}
}

View File

@ -111,6 +111,7 @@ class AuthService:
profile_image_url=user.profile_image_url,
is_new_user=is_new_user,
),
redirect_url="http://localhost:3000",
)
async def refresh_tokens(

View File

@ -43,12 +43,12 @@ tags_metadata = [
"description": "홈 화면 및 프로젝트 관리 API",
},
{
"name": "crawling",
"name": "Crawling",
"description": "네이버 지도 크롤링 API - 장소 정보 및 이미지 수집",
},
{
"name": "image",
"description": "이미지 업로드 API - 로컬 서버 또는 Azure Blob Storage",
"name": "Image-Blob",
"description": "이미지 업로드 API - Azure Blob Storage",
},
{
"name": "Lyric",