[React] 소셜 로그인 (2)
[인가 코드의 페이지 처리]
- 리액트 애블리케이션에서 Redirect Uri로 전달되는 경로의 페이지를 추가
import { useSearchParams } from "react-router-dom";
const KakaoRedirectPage = () => {
const [searchParams] = useSearchParams()
const authCode = searchParams.get("code")
return (
<div>
<div>Kakao Login Redirect</div>
<div>{authCode}</div>
</div>
)
}
export default KakaoRedirectPage
- redirect용 KakaoRedirectPage 추가
const KakaoRedirect = lazy(() => import("../pages/member/KakaoRedirectPage"))
{
path:"kakao",
element: <Suspense fallback={Loading}><KakaoRedirect/></Suspense>
}
- router에 kakao 경로 추가
- 인가 코드를 정상적으로 받아 오는 것을 확인 할 수 있음
[Access Token 호출]
- 위와 같이 Access Token 발급을 위한 값들을 확인할 수 있음(https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#refresh-token-response-body)
import axios from "axios"
const rest_api_key = `ffcbe30c51b11b3d8d2b9e1110c0ecfc`
const redirect_uri = `http://localhost:3000/member/kakao`
const auth_code_path = `https://kauth.kakao.com/oauth/authorize`
const access_token_url = `https://kauth.kakao.com/oauth/token`
export const getKakaoLoginLink = () => {
const kakaoURL = `${auth_code_path}?client_id=${rest_api_key}&redirect_uri=${redirect_uri}&response_type=code`;
return kakaoURL
}
export const getAccessToken = async (authCode) => {
const header = {
headers: {
"Content-Type": "application/x-www-from-urlencoded",
}
}
const params = {
grant_type: "authorization_code",
client_id: rest_api_key,
redirect_uri: redirect_uri,
code: authCode
}
const res = await axios.post(access_token_url, params, header)
const accessToken = res.data.access_token
return accessToken
}
- 필요 파라미터로 요청하여 Access Token을 응답 받도록 kakaoApi에 getAccessToken을 추가
- 에러 발생 새로고침이 아닌 새로 로그인해서 확인해야 함
- 새로고침의 경우 한번 사용한 authCode를 다시 사용하기 때문에 발생하는 오류
- 새로 로그인하니 정상적으로 동작 확인
[API 서버에서 Access Token 처리]
- Front-end에서 받은 Access Token을 API로 전달해서 사용자 정보를 처리하는 방식 개발
- https://kapi.kakao.com/v2/user/me 에서 사용자 정보를 가져옵니다.
[MemberService 개발]
- API 서버에서는 Access Token으로 기존의 회원정보를 이용하거나 새로운 회원으로 서비스 계층을 만들어서 추가
package org.zerock.mallapi.service;
import org.springframework.transaction.annotation.Transactional;
import org.zerock.mallapi.dto.MemberDTO;
@Transactional
public interface MemberService {
MemberDTO getKakaoMember(String accessToken);
}
private String getEmailFromKakaoAccessToken(String accessToken) {
String kakaoGetUserURL = "https://kapi.kakao.com/v2/user/me";
if(accessToken == null){
throw new RuntimeException("Access Token is null");
}
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-Type", "application/x-www-form-urlencoded");
HttpEntity<String> entity = new HttpEntity<>(headers);
UriComponents uriBuilder = UriComponentsBuilder.fromUriString(kakaoGetUserURL).build();
ResponseEntity<LinkedHashMap> response = restTemplate.exchange(uriBuilder.toString(), HttpMethod.GET, entity, LinkedHashMap.class);
log.info(response);
LinkedHashMap<String, LinkedHashMap> bodyMap = response.getBody();
log.info("-----------------------");
log.info(bodyMap);
LinkedHashMap<String, String> kakaoAccount = bodyMap.get("kakao_account");
log.info("kakaoAccount: " + kakaoAccount);
return kakaoAccount.get("email");
}
- MemberServiceImple에서 RestTemplate을 이용해 카카오 서비스를 호출합니다.
- Map 타입의 이메일을 추출하는 과정에서 fromHttpUrl 대신 Spring 6.2 이후에 권장되는 fromUriString를 사용합니다.
- 간혹 500 에러 발생하면서 email 값이 null로 나오는 경우 내 애플리케이션에서 비즈앱 전환 후 '카카오계정(이메일)'이 설정되어있는지 확인해주세요.
- 로그인하면 소셜회원으로 로그인 정보가 저장되는 것을 볼 수 있습니다.