package com.takensoft.common.oauth.vo; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.util.Map; /** * @author takensoft * @since 2025.05.22 * @modification * since | author | description * 2025.05.22 | takensoft | 최초 등록 * * OAuth2 사용자 정보 통합 VO */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class OAuth2UserInfoVO { private Map attributes; // OAuth2 응답 전체 데이터 private String provider; // 제공업체 (kakao, naver, google 등) private String id; // 사용자 ID private String name; // 사용자 이름 private String email; // 이메일 private String imageUrl; // 프로필 이미지 URL // 제공업체별 데이터를 통일된 형태로 변환하는 생성자 public OAuth2UserInfoVO(String provider, Map attributes) { this.provider = provider; this.attributes = attributes; switch (provider.toLowerCase()) { case "kakao": setKakaoUserInfo(attributes); break; case "naver": setNaverUserInfo(attributes); break; case "google": setGoogleUserInfo(attributes); break; } } private void setKakaoUserInfo(Map attributes) { try { Map kakaoAccount = (Map) attributes.get("kakao_account"); if (kakaoAccount == null) { throw new IllegalArgumentException("kakao_account 정보가 없습니다."); } Map profile = (Map) kakaoAccount.get("profile"); if (profile == null) { throw new IllegalArgumentException("profile 정보가 없습니다."); } this.id = String.valueOf(attributes.get("id")); this.name = (String) profile.get("nickname"); this.email = (String) kakaoAccount.get("email"); this.imageUrl = (String) profile.get("profile_image_url"); // 필수 정보 검증 validateRequiredFields("kakao"); } catch (Exception e) { throw new RuntimeException("카카오 로그인 정보 처리 실패", e); } } private void setNaverUserInfo(Map attributes) { try { Map response = (Map) attributes.get("response"); if (response == null) { throw new IllegalArgumentException("response 정보가 없습니다."); } this.id = (String) response.get("id"); this.name = (String) response.get("name"); this.email = (String) response.get("email"); this.imageUrl = (String) response.get("profile_image"); // 필수 정보 검증 validateRequiredFields("naver"); } catch (Exception e) { throw new RuntimeException("네이버 로그인 정보 처리 실패", e); } } private void setGoogleUserInfo(Map attributes) { try { // 구글은 직접 attributes에서 정보 추출 this.id = (String) attributes.get("sub"); this.name = (String) attributes.get("name"); this.email = (String) attributes.get("email"); this.imageUrl = (String) attributes.get("picture"); // 구글 특정 필드 검증 및 대안 처리 if (this.id == null) { this.id = (String) attributes.get("id"); // 대안 필드 } if (this.name == null) { // given_name과 family_name 조합 String givenName = (String) attributes.get("given_name"); String familyName = (String) attributes.get("family_name"); if (givenName != null || familyName != null) { this.name = (givenName != null ? givenName : "") + (familyName != null ? " " + familyName : "").trim(); } } // 이메일 검증 상태 확인 Boolean emailVerified = (Boolean) attributes.get("email_verified"); // 필수 정보 검증 validateRequiredFields("google"); } catch (Exception e) { throw new RuntimeException("구글 로그인 정보 처리 실패", e); } } /** * 필수 필드 검증 */ private void validateRequiredFields(String provider) { if (this.id == null || this.id.trim().isEmpty()) { throw new IllegalArgumentException(provider + " 사용자 ID가 없습니다."); } if (this.email == null || this.email.trim().isEmpty()) { throw new IllegalArgumentException(provider + " 이메일 정보가 없습니다."); } if (this.name == null || this.name.trim().isEmpty()) { this.name = this.email.split("@")[0]; } } /** * 파싱 실패 시 기본값 설정 */ private void setDefaultValues() { if (this.id == null) this.id = "unknown_" + System.currentTimeMillis(); if (this.name == null) this.name = "Unknown User"; if (this.email == null) this.email = this.id + "@unknown.com"; if (this.imageUrl == null) this.imageUrl = ""; } }