add loggers for kakao login

insta
Dohyun Lim 2026-01-21 17:24:07 +09:00
parent 219d8798ad
commit 7038faaf74
3 changed files with 62 additions and 3 deletions

View File

@ -4,6 +4,7 @@
카카오 로그인, 토큰 갱신, 로그아웃, 정보 조회 엔드포인트를 제공합니다.
"""
import logging
from typing import Optional
from fastapi import APIRouter, Depends, Header, Request, status
@ -12,6 +13,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
from config import prj_settings
from app.database.session import get_session
logger = logging.getLogger(__name__)
from app.user.dependencies import get_current_user
from app.user.models import User
from app.user.schemas.user_schema import (
@ -40,7 +43,9 @@ async def kakao_login() -> KakaoLoginResponse:
프론트엔드에서 URL로 사용자를 리다이렉트하면
카카오 로그인 페이지가 표시됩니다.
"""
logger.info("[ROUTER] 카카오 로그인 URL 요청")
auth_url = kakao_client.get_authorization_url()
logger.debug(f"[ROUTER] 카카오 인증 URL 생성 완료 - auth_url: {auth_url}")
return KakaoLoginResponse(auth_url=auth_url)
@ -63,6 +68,8 @@ async def kakao_callback(
신규 사용자인 경우 자동으로 회원가입이 처리됩니다.
"""
logger.info(f"[ROUTER] 카카오 콜백 수신 - code: {code[:20]}...")
# 클라이언트 IP 추출
ip_address = request.client.host if request.client else None
@ -71,6 +78,8 @@ async def kakao_callback(
if forwarded_for:
ip_address = forwarded_for.split(",")[0].strip()
logger.debug(f"[ROUTER] 클라이언트 정보 - ip: {ip_address}, user_agent: {user_agent}")
result = await auth_service.kakao_login(
code=code,
session=session,
@ -84,6 +93,7 @@ async def kakao_callback(
f"?access_token={result.access_token}"
f"&refresh_token={result.refresh_token}"
)
logger.info(f"[ROUTER] 카카오 콜백 완료, 프론트엔드로 리다이렉트 - redirect_url: {redirect_url[:50]}...")
return RedirectResponse(url=redirect_url, status_code=302)
@ -119,6 +129,8 @@ async def kakao_verify(
신규 사용자인 경우 자동으로 회원가입이 처리됩니다.
"""
logger.info(f"[ROUTER] 카카오 인가 코드 검증 요청 - code: {body.code[:20]}...")
# 클라이언트 IP 추출
ip_address = request.client.host if request.client else None
@ -127,13 +139,18 @@ async def kakao_verify(
if forwarded_for:
ip_address = forwarded_for.split(",")[0].strip()
return await auth_service.kakao_login(
logger.debug(f"[ROUTER] 클라이언트 정보 - ip: {ip_address}, user_agent: {user_agent}")
result = await auth_service.kakao_login(
code=body.code,
session=session,
user_agent=user_agent,
ip_address=ip_address,
)
logger.info(f"[ROUTER] 카카오 인가 코드 검증 완료 - user_id: {result.user.id}, is_new_user: {result.user.is_new_user}")
return result
@router.post(
"/refresh",

View File

@ -71,24 +71,36 @@ class AuthService:
Returns:
LoginResponse: 토큰 사용자 정보
"""
logger.info(f"[AUTH] 카카오 로그인 시작 - code: {code[:20]}..., ip: {ip_address}")
# 1. 카카오 토큰 획득
logger.info("[AUTH] 1단계: 카카오 토큰 획득 시작")
kakao_token = await kakao_client.get_access_token(code)
logger.debug(f"[AUTH] 카카오 토큰 획득 완료 - token_type: {kakao_token.token_type}")
# 2. 카카오 사용자 정보 조회
logger.info("[AUTH] 2단계: 카카오 사용자 정보 조회 시작")
kakao_user_info = await kakao_client.get_user_info(kakao_token.access_token)
logger.debug(f"[AUTH] 카카오 사용자 정보 조회 완료 - kakao_id: {kakao_user_info.id}")
# 3. 사용자 조회 또는 생성
logger.info("[AUTH] 3단계: 사용자 조회/생성 시작")
user, is_new_user = await self._get_or_create_user(kakao_user_info, session)
logger.info(f"[AUTH] 사용자 처리 완료 - user_id: {user.id}, is_new_user: {is_new_user}")
# 4. 비활성화 계정 체크
if not user.is_active:
logger.error(f"[AUTH] 비활성화 계정 접근 시도 - user_id: {user.id}")
raise UserInactiveError()
# 5. JWT 토큰 생성
logger.info("[AUTH] 5단계: JWT 토큰 생성 시작")
access_token = create_access_token(user.id)
refresh_token = create_refresh_token(user.id)
logger.debug(f"[AUTH] JWT 토큰 생성 완료 - user_id: {user.id}")
# 6. 리프레시 토큰 DB 저장
logger.info("[AUTH] 6단계: 리프레시 토큰 저장 시작")
await self._save_refresh_token(
user_id=user.id,
token=refresh_token,
@ -96,11 +108,16 @@ class AuthService:
user_agent=user_agent,
ip_address=ip_address,
)
logger.debug(f"[AUTH] 리프레시 토큰 저장 완료 - user_id: {user.id}")
# 7. 마지막 로그인 시간 업데이트
user.last_login_at = datetime.now(timezone.utc)
await session.commit()
redirect_url = f"https://{prj_settings.PROJECT_DOMAIN}"
logger.info(f"[AUTH] 카카오 로그인 완료 - user_id: {user.id}, redirect_url: {redirect_url}")
logger.debug(f"[AUTH] 응답 토큰 정보 - access_token: {access_token[:30]}..., refresh_token: {refresh_token[:30]}...")
return LoginResponse(
access_token=access_token,
refresh_token=refresh_token,
@ -113,7 +130,7 @@ class AuthService:
profile_image_url=user.profile_image_url,
is_new_user=is_new_user,
),
redirect_url=f"{prj_settings.PROJECT_DOMAIN}",
redirect_url=redirect_url,
)
async def refresh_tokens(

View File

@ -4,10 +4,14 @@
카카오 로그인 인증 흐름을 처리하는 클라이언트입니다.
"""
import logging
import aiohttp
from config import kakao_settings
logger = logging.getLogger(__name__)
from app.user.exceptions import KakaoAPIError, KakaoAuthFailedError
from app.user.schemas.user_schema import KakaoTokenResponse, KakaoUserInfo
@ -39,12 +43,15 @@ class KakaoOAuthClient:
Returns:
카카오 OAuth 인증 페이지 URL
"""
return (
auth_url = (
f"{self.AUTH_URL}"
f"?client_id={self.client_id}"
f"&redirect_uri={self.redirect_uri}"
f"&response_type=code"
)
logger.info(f"[KAKAO] 인증 URL 생성 - redirect_uri: {self.redirect_uri}")
logger.debug(f"[KAKAO] 인증 URL 상세 - auth_url: {auth_url}")
return auth_url
async def get_access_token(self, code: str) -> KakaoTokenResponse:
"""
@ -60,6 +67,7 @@ class KakaoOAuthClient:
KakaoAuthFailedError: 토큰 발급 실패
KakaoAPIError: API 호출 오류
"""
logger.info(f"[KAKAO] 액세스 토큰 요청 시작 - code: {code[:20]}...")
try:
async with aiohttp.ClientSession() as session:
data = {
@ -72,20 +80,27 @@ class KakaoOAuthClient:
if self.client_secret:
data["client_secret"] = self.client_secret
logger.debug(f"[KAKAO] 토큰 요청 데이터 - redirect_uri: {self.redirect_uri}, client_id: {self.client_id[:10]}...")
async with session.post(self.TOKEN_URL, data=data) as response:
result = await response.json()
logger.debug(f"[KAKAO] 토큰 응답 상태 - status: {response.status}")
if "error" in result:
error_desc = result.get(
"error_description", result.get("error", "알 수 없는 오류")
)
logger.error(f"[KAKAO] 토큰 발급 실패 - error: {result.get('error')}, description: {error_desc}")
raise KakaoAuthFailedError(f"카카오 토큰 발급 실패: {error_desc}")
logger.info("[KAKAO] 액세스 토큰 발급 성공")
logger.debug(f"[KAKAO] 토큰 정보 - token_type: {result.get('token_type')}, expires_in: {result.get('expires_in')}")
return KakaoTokenResponse(**result)
except KakaoAuthFailedError:
raise
except Exception as e:
logger.error(f"[KAKAO] API 호출 오류 - error: {str(e)}")
raise KakaoAPIError(f"카카오 API 호출 중 오류 발생: {str(e)}")
async def get_user_info(self, access_token: str) -> KakaoUserInfo:
@ -102,21 +117,31 @@ class KakaoOAuthClient:
KakaoAuthFailedError: 사용자 정보 조회 실패
KakaoAPIError: API 호출 오류
"""
logger.info("[KAKAO] 사용자 정보 조회 시작")
try:
async with aiohttp.ClientSession() as session:
headers = {"Authorization": f"Bearer {access_token}"}
async with session.get(self.USER_INFO_URL, headers=headers) as response:
result = await response.json()
logger.debug(f"[KAKAO] 사용자 정보 응답 상태 - status: {response.status}")
if "id" not in result:
logger.error(f"[KAKAO] 사용자 정보 조회 실패 - response: {result}")
raise KakaoAuthFailedError("카카오 사용자 정보를 가져올 수 없습니다.")
kakao_id = result.get("id")
kakao_account = result.get("kakao_account", {})
profile = kakao_account.get("profile", {})
logger.info(f"[KAKAO] 사용자 정보 조회 성공 - kakao_id: {kakao_id}")
logger.debug(f"[KAKAO] 사용자 상세 정보 - nickname: {profile.get('nickname')}, email: {kakao_account.get('email')}")
return KakaoUserInfo(**result)
except KakaoAuthFailedError:
raise
except Exception as e:
logger.error(f"[KAKAO] API 호출 오류 - error: {str(e)}")
raise KakaoAPIError(f"카카오 API 호출 중 오류 발생: {str(e)}")