""" 카카오 OAuth API 클라이언트 카카오 로그인 인증 흐름을 처리하는 클라이언트입니다. """ import aiohttp from config import kakao_settings from app.user.exceptions import KakaoAPIError, KakaoAuthFailedError from app.user.schemas.user_schema import KakaoTokenResponse, KakaoUserInfo class KakaoOAuthClient: """ 카카오 OAuth API 클라이언트 카카오 로그인 인증 흐름: 1. get_authorization_url()로 카카오 로그인 페이지 URL 획득 2. 사용자가 카카오에서 로그인 후 인가 코드(code) 발급 3. get_access_token()으로 인가 코드를 액세스 토큰으로 교환 4. get_user_info()로 사용자 정보 조회 """ AUTH_URL = "https://kauth.kakao.com/oauth/authorize" TOKEN_URL = "https://kauth.kakao.com/oauth/token" USER_INFO_URL = "https://kapi.kakao.com/v2/user/me" def __init__(self) -> None: self.client_id = kakao_settings.KAKAO_CLIENT_ID self.client_secret = kakao_settings.KAKAO_CLIENT_SECRET self.redirect_uri = kakao_settings.KAKAO_REDIRECT_URI def get_authorization_url(self) -> str: """ 카카오 로그인 페이지 URL 반환 Returns: 카카오 OAuth 인증 페이지 URL """ return ( f"{self.AUTH_URL}" f"?client_id={self.client_id}" f"&redirect_uri={self.redirect_uri}" f"&response_type=code" ) async def get_access_token(self, code: str) -> KakaoTokenResponse: """ 인가 코드로 액세스 토큰 획득 Args: code: 카카오 로그인 후 발급받은 인가 코드 Returns: KakaoTokenResponse: 카카오 토큰 정보 Raises: KakaoAuthFailedError: 토큰 발급 실패 시 KakaoAPIError: API 호출 오류 시 """ try: async with aiohttp.ClientSession() as session: data = { "grant_type": "authorization_code", "client_id": self.client_id, "redirect_uri": self.redirect_uri, "code": code, } if self.client_secret: data["client_secret"] = self.client_secret async with session.post(self.TOKEN_URL, data=data) as response: result = await response.json() if "error" in result: error_desc = result.get( "error_description", result.get("error", "알 수 없는 오류") ) raise KakaoAuthFailedError(f"카카오 토큰 발급 실패: {error_desc}") return KakaoTokenResponse(**result) except KakaoAuthFailedError: raise except Exception as e: raise KakaoAPIError(f"카카오 API 호출 중 오류 발생: {str(e)}") async def get_user_info(self, access_token: str) -> KakaoUserInfo: """ 액세스 토큰으로 사용자 정보 조회 Args: access_token: 카카오 액세스 토큰 Returns: KakaoUserInfo: 카카오 사용자 정보 Raises: KakaoAuthFailedError: 사용자 정보 조회 실패 시 KakaoAPIError: API 호출 오류 시 """ 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() if "id" not in result: raise KakaoAuthFailedError("카카오 사용자 정보를 가져올 수 없습니다.") return KakaoUserInfo(**result) except KakaoAuthFailedError: raise except Exception as e: raise KakaoAPIError(f"카카오 API 호출 중 오류 발생: {str(e)}") kakao_client = KakaoOAuthClient()