44import com .swyp .picke .domain .oauth .dto .google .GoogleTokenResponse ;
55import com .swyp .picke .domain .oauth .dto .google .GoogleUserResponse ;
66import lombok .RequiredArgsConstructor ;
7+ import lombok .extern .slf4j .Slf4j ; // 1. 로그 추가
78import org .springframework .beans .factory .annotation .Value ;
89import org .springframework .http .HttpHeaders ;
10+ import org .springframework .http .HttpStatusCode ;
911import org .springframework .http .MediaType ;
1012import org .springframework .stereotype .Component ;
11- import org .springframework .util .LinkedMultiValueMap ;
12- import org .springframework .util .MultiValueMap ;
13+ import org .springframework .web .reactive .function .BodyInserters ; // 2. 추가
1314import org .springframework .web .reactive .function .client .WebClient ;
15+ import reactor .core .publisher .Mono ;
1416
17+ import java .net .URLDecoder ;
18+ import java .nio .charset .StandardCharsets ;
19+
20+ @ Slf4j
1521@ Component
1622@ RequiredArgsConstructor
1723public class GoogleOAuthClient {
@@ -24,23 +30,33 @@ public class GoogleOAuthClient {
2430
2531 // 인가 코드 → 구글 access_token
2632 public String getAccessToken (String code , String redirectUri ) {
27- MultiValueMap <String , String > body = new LinkedMultiValueMap <>();
28- body .add ("grant_type" , "authorization_code" );
29- body .add ("client_id" , clientId );
30- body .add ("client_secret" , clientSecret );
31- body .add ("redirect_uri" , redirectUri );
32- body .add ("code" , code );
33+ // 3. 인코딩된 코드가 들어올 경우를 대비해 디코딩 처리
34+ String decodedCode = URLDecoder .decode (code , StandardCharsets .UTF_8 );
35+
36+ log .info ("[Google Login] 요청 시작 - redirectUri: {}, code: {}" , redirectUri , decodedCode );
3337
3438 GoogleTokenResponse response = WebClient .create ()
3539 .post ()
3640 .uri ("https://oauth2.googleapis.com/token" )
3741 .contentType (MediaType .APPLICATION_FORM_URLENCODED )
38- .bodyValue (body )
42+ // 4. BodyInserters를 사용하여 데이터 전송 (가장 안전한 방식)
43+ .body (BodyInserters .fromFormData ("grant_type" , "authorization_code" )
44+ .with ("client_id" , clientId )
45+ .with ("client_secret" , clientSecret )
46+ .with ("redirect_uri" , redirectUri )
47+ .with ("code" , decodedCode ))
3948 .retrieve ()
49+ // 5. 400 Bad Request 발생 시 구글이 보내는 진짜 이유를 로그로 확인
50+ .onStatus (HttpStatusCode ::isError , clientResponse ->
51+ clientResponse .bodyToMono (String .class ).flatMap (errorBody -> {
52+ log .error ("[Google Auth Error] 상세 내용: {}" , errorBody );
53+ return Mono .error (new RuntimeException ("구글 토큰 발급 실패" ));
54+ })
55+ )
4056 .bodyToMono (GoogleTokenResponse .class )
4157 .block ();
4258
43- return response .getAccessToken ();
59+ return response != null ? response .getAccessToken () : null ;
4460 }
4561
4662 // 구글 access_token → 사용자 정보
@@ -55,4 +71,4 @@ public OAuthUserInfo getUserInfo(String accessToken) {
5571
5672 return new OAuthUserInfo ("GOOGLE" , response .getId (), response .getEmail ());
5773 }
58- }
74+ }
0 commit comments