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 typing import Optional
from fastapi import APIRouter, Depends, Header, Request, status from fastapi import APIRouter, Depends, Header, Request, status
@ -12,6 +13,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
from config import prj_settings from config import prj_settings
from app.database.session import get_session from app.database.session import get_session
logger = logging.getLogger(__name__)
from app.user.dependencies import get_current_user from app.user.dependencies import get_current_user
from app.user.models import User from app.user.models import User
from app.user.schemas.user_schema import ( from app.user.schemas.user_schema import (
@ -40,7 +43,9 @@ async def kakao_login() -> KakaoLoginResponse:
프론트엔드에서 URL로 사용자를 리다이렉트하면 프론트엔드에서 URL로 사용자를 리다이렉트하면
카카오 로그인 페이지가 표시됩니다. 카카오 로그인 페이지가 표시됩니다.
""" """
logger.info("[ROUTER] 카카오 로그인 URL 요청")
auth_url = kakao_client.get_authorization_url() auth_url = kakao_client.get_authorization_url()
logger.debug(f"[ROUTER] 카카오 인증 URL 생성 완료 - auth_url: {auth_url}")
return KakaoLoginResponse(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 추출
ip_address = request.client.host if request.client else None ip_address = request.client.host if request.client else None
@ -71,6 +78,8 @@ async def kakao_callback(
if forwarded_for: if forwarded_for:
ip_address = forwarded_for.split(",")[0].strip() ip_address = forwarded_for.split(",")[0].strip()
logger.debug(f"[ROUTER] 클라이언트 정보 - ip: {ip_address}, user_agent: {user_agent}")
result = await auth_service.kakao_login( result = await auth_service.kakao_login(
code=code, code=code,
session=session, session=session,
@ -84,6 +93,7 @@ async def kakao_callback(
f"?access_token={result.access_token}" f"?access_token={result.access_token}"
f"&refresh_token={result.refresh_token}" f"&refresh_token={result.refresh_token}"
) )
logger.info(f"[ROUTER] 카카오 콜백 완료, 프론트엔드로 리다이렉트 - redirect_url: {redirect_url[:50]}...")
return RedirectResponse(url=redirect_url, status_code=302) 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 추출
ip_address = request.client.host if request.client else None ip_address = request.client.host if request.client else None
@ -127,13 +139,18 @@ async def kakao_verify(
if forwarded_for: if forwarded_for:
ip_address = forwarded_for.split(",")[0].strip() 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, code=body.code,
session=session, session=session,
user_agent=user_agent, user_agent=user_agent,
ip_address=ip_address, 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( @router.post(
"/refresh", "/refresh",

View File

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

View File

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