LUMI_dev
카카오 로그인 (OAuth) #1. 카카오 사용자 정보 가져오기 본문
카카오 로그인 (OAuth) #1. 카카오 사용자 정보 가져오기
luminous_dev 2025. 2. 7. 12:53
카카오계정
accounts.kakao.com
http://localhost:8080/api/user/kakao/callback
만약 카카오계정(이메일) 상태가 권한 없음으로 뜨면,
개인정보 보호를 위해 비즈니스 인증한 서비스에만 동의항목 제공되도록 변경되었으므로
비즈앱 전환 후, 비즈니스인증 받으시고 개인정보 동의 설정 권한 요청이 필요하다는 것
비즈니스 > 개인 개발자 비즈 앱 전환
그럼 이제 사용 가능
닉네임
이메일
login.html
<button id="login-kakao-btn" onclick="location.href='https://kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri=http://localhost:8080/api/user/kakao/callback&response_type=code'">
카카오로 로그인하기
</button>
해당 코드의 { REST_API_KEY }에 우리의 REST_API_KEY를 넣어주기
REST_API_KEY 찾는 방법
카카오 서버에서 보내주는 인가 코드를 처리하는 컨트롤러가 필요
애플리케이션 등록할 때 redirect url 설정해줌 → 이유) 카카오서버에서 redirect url로 인가코드를 보내줘서
인가코드를 받는 컨트롤러를 만들어줘야 함
UserController
private final KakaoService kakaoService;
.
.
.
//카카오에서 보내는 인가 코드는 쿼리 스트링 방식으로 넘어오고, @RequestParam으로 받을 수 있음
//완료되면 이 jwt를 생성해서 쿠키를 우리가 직접 만들어서 브라우저에게 시킬 것임
//이전에 회원 기능 구현할 때는 header에 jwt 넣었음
//우리 client 구조 상 카카오 login 했을 때는 우리가 직접 쿠키를 생성해서 jwt에 넣은 다음에 브라우저에 자동으로 set될 수 있도록 구현해보기
@GetMapping("/user/kakao/callback")
public String kakaoLogin(@RequestParam String code, HttpServletResponse response){
}
UserService
package com.sparta.myselectshop.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.myselectshop.dto.KakaoUserInfoDto;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
@Slf4j(topic = "KAKAO Login")
@Service
@RequiredArgsConstructor
public class KakaoService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final RestTemplate restTemplate; //빨간 줄 : RestTemplate을 그냥 Builder로 만들 수 있다
//빨간줄 뜨는 이유 : restTemplate이 bean이 아닌 Rest Template Builder을 통해서 생성할 수 있도록 유도
//Bean을 수동으로 등록해서 관리하는 방법
private final JwtUtil jwtUtil;
public String kakaoLogin(String code) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
String accessToken = getToken(code);
// 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
return null;
}
}
bean을 수동으로 등록해줘야함 > RestTemplateConfig 추가
//주의 : 버전 3.4.0 이상에서 지원 중단되며 제거될 예정인 메서드 있음
package com.sparta.myselectshop.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
// RestTemplate 으로 외부 API 호출 시 일정 시간이 지나도 응답이 없을 때
// 무한 대기 상태 방지를 위해 강제 종료 설정
.setConnectTimeout(Duration.ofSeconds(5)) // 5초
.setReadTimeout(Duration.ofSeconds(5)) // 5초
.build();
}
}
다시 KakaoService
아래 액세스 토큰 요청 코드 하단에 넣어주기
private String getToken(String code) throws JsonProcessingException {
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com")
.path("/oauth/token")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "본인의 REST API키");
body.add("redirect_uri", "http://localhost:8080/api/user/kakao/callback");
body.add("code", code);
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(body);
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);
// HTTP 응답 (JSON) -> 액세스 토큰 파싱
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
return jsonNode.get("access_token").asText();
}
지금 카카오 사용자 정보 가져오기 중 인증 코드로 토큰 요청 수행 중
KakaoService
package com.sparta.myselectshop.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.myselectshop.dto.KakaoUserInfoDto;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
@Slf4j(topic = "KAKAO Login")
@Service
@RequiredArgsConstructor
public class KakaoService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final RestTemplate restTemplate; //빨간 줄 : RestTemplate을 그냥 Builder로 만들 수 있다
//빨간줄 뜨는 이유 : restTemplate이 bean이 아닌 Rest Template Builder을 통해서 생성할 수 있도록 유도
//Bean을 수동으로 등록해서 관리하는 방법
private final JwtUtil jwtUtil;
public String kakaoLogin(String code) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
String accessToken = getToken(code);
// 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
return null;
}
//인증 코드로 토큰 요청
private String getToken(String code) throws JsonProcessingException {
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com")
.path("/oauth/token")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "본인의 REST API키");
body.add("redirect_uri", "http://localhost:8080/api/user/kakao/callback");
body.add("code", code);
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(body);
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);
// HTTP 응답 (JSON) -> 액세스 토큰 파싱
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
return jsonNode.get("access_token").asText();
}
}
이중 본인의 REST API 키에 다시 RESTAPI 넣어주기
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "본인의 REST API키"); //여기에 RESTAPI 코드 다시 넣어주기
body.add("redirect_uri", "http://localhost:8080/api/user/kakao/callback");
body.add("code", code);
사용자 정보 요청 KakaoService 하단에 추가
private KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kapi.kakao.com")
.path("/v2/user/me")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(new LinkedMultiValueMap<>());
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
Long id = jsonNode.get("id").asLong();
String nickname = jsonNode.get("properties")
.get("nickname").asText();
String email = jsonNode.get("kakao_account")
.get("email").asText();
log.info("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
return new KakaoUserInfoDto(id, nickname, email);
}
전체 KakaoService코드
package com.sparta.myselectshop.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.myselectshop.dto.KakaoUserInfoDto;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
@Slf4j(topic = "KAKAO Login")
@Service
@RequiredArgsConstructor
public class KakaoService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final RestTemplate restTemplate; //빨간 줄 : RestTemplate을 그냥 Builder로 만들 수 있다
//빨간줄 뜨는 이유 : restTemplate이 bean이 아닌 Rest Template Builder을 통해서 생성할 수 있도록 유도
//Bean을 수동으로 등록해서 관리하는 방법
private final JwtUtil jwtUtil;
public String kakaoLogin(String code) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
String accessToken = getToken(code);
// 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
return null;
}
//kakaoLogin에서 받은 인증 코드로 토큰 요청
private String getToken(String code) throws JsonProcessingException {
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com") //여긴 정해진 주소를 넣기
.path("/oauth/token")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
//kakao Developers에서 요청한대로 http 형태를 만들어서 Rest template으로 API를 요청함
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "13f0c1acd35319458b5893b3dbba382d"); //여기에 RESTAPI 코드 다시 넣어주기
body.add("redirect_uri", "http://localhost:8080/api/user/kakao/callback");
body.add("code", code); //받은 인가코드 넣기
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri) //body가 있으니까 POST
.headers(headers)
.body(body);
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange( //http 요청이 카카오 서버 쪽으로 감
requestEntity,
String.class
); //ResponseEntity<String>에서 반환되는 String이 토큰의 형태로 되어 있음 > 그 토큰을 한번 파싱할 것임 (.readTree(response.getBody()))
// HTTP 응답 (JSON) -> 액세스 토큰 파싱 = 우리가 딱 원하는 액세스 토큰만 딱 뽑아옴
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
return jsonNode.get("access_token").asText();
//뽑으면 .asText()니까 String값 반환
//맨 위의 String accessToken = getToken(code);의 String accessToken
}
//사용자 정보 요청 (getKakaoUserInfo 메서드)
private KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kapi.kakao.com")
.path("/v2/user/me")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri) //우리가 url 만든 거 보내주기
.headers(headers)
.body(new LinkedMultiValueMap<>()); //body는 따로 보내줄 필요가 없어서 new~
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class //String 타입으로 받아옴
);
//Long 타입으로 해당하는 id 받아오는 방법
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
//해댱하는 id값들을 받아올 수 있음
Long id = jsonNode.get("id").asLong();
//properties 중에서 nickname 값을 가져옴
String nickname = jsonNode.get("properties")
.get("nickname").asText();
//properties 중에서 email 값을 가져옴
String email = jsonNode.get("kakao_account")
.get("email").asText();
log.info("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
//KakaoUserInfoDto에 값을 넣어줌
return new KakaoUserInfoDto(id, nickname, email);
}
}
kakaoService 중
//Long 타입으로 해당하는 id 받아오는 방법
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
//해댱하는 id값들을 받아올 수 있음
Long id = jsonNode.get("id").asLong();
//properties 중에서 nickname 값을 가져옴
String nickname = jsonNode.get("properties")
.get("nickname").asText();
//properties 중에서 email 값을 가져옴
String email = jsonNode.get("kakao_account")
.get("email").asText();
카카오 사용자 정보 JSON 의 예
{
"id": 1632335751,
"properties": {
"nickname": "르탄이",
"profile_image": "http://k.kakaocdn.net/...jpg",
"thumbnail_image": "http://k.kakaocdn.net/...jpg"
},
"kakao_account": {
"profile_needs_agreement": false,
"profile": {
"nickname": "르탄이",
"thumbnail_image_url": "http://k.kakaocdn.net/...jpg",
"profile_image_url": "http://k.kakaocdn.net/...jpg"
},
"has_email": true,
"email_needs_agreement": false,
"is_email_valid": true,
"is_email_verified": true,
"email": "letan@sparta.com"
}
}
원하는 정보가 르탄이 (value)라고 하면 properties > nickname > 으로 들어가서 얻어와야 함
id는 바로 빠져나와있어서 그냥 가져옴
email은 kakao_account 한번 들어가고 > email
인가 코드를 받아오기 위한 컨트롤러
UserController
package com.sparta.myselectshop.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.sparta.myselectshop.dto.SignupRequestDto;
import com.sparta.myselectshop.dto.UserInfoDto;
import com.sparta.myselectshop.entity.UserRoleEnum;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.security.UserDetailsImpl;
import com.sparta.myselectshop.service.FolderService;
import com.sparta.myselectshop.service.KakaoService;
import com.sparta.myselectshop.service.UserService;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@Controller
@RequiredArgsConstructor
@RequestMapping("/api")
public class UserController {
private final UserService userService;
private final FolderService folderService;
private final KakaoService kakaoService;
@GetMapping("/user/login-page")
public String loginPage() {
return "login";
}
@GetMapping("/user/signup")
public String signupPage() {
return "signup";
}
@PostMapping("/user/signup")
public String signup(@Valid SignupRequestDto requestDto, BindingResult bindingResult) {
// Validation 예외처리
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
if (fieldErrors.size() > 0) {
for (FieldError fieldError : bindingResult.getFieldErrors()) {
log.error(fieldError.getField() + " 필드 : " + fieldError.getDefaultMessage());
}
return "redirect:/api/user/signup";
}
userService.signup(requestDto);
return "redirect:/api/user/login-page";
}
// 회원 관련 정보 받기
@GetMapping("/user-info")
@ResponseBody
public UserInfoDto getUserInfo(@AuthenticationPrincipal UserDetailsImpl userDetails) {
String username = userDetails.getUser().getUsername();
UserRoleEnum role = userDetails.getUser().getRole();
boolean isAdmin = (role == UserRoleEnum.ADMIN);
return new UserInfoDto(username, isAdmin);
}
@GetMapping("/user-folder")
public String getUserInfo(Model model, @AuthenticationPrincipal UserDetailsImpl userDetails) {
model.addAttribute("folders", folderService.getFolders(userDetails.getUser()));
return "index :: #fragment";
}
//카카오에서 보내는 인가 코드는 쿼리 스트링 방식으로 넘어오고, @RequestParam으로 받을 수 있음
//완료되면 이 jwt를 생성해서 쿠키를 우리가 직접 만들어서 브라우저에게 시킬 것임
//이전에 회원 기능 구현할 때는 header에 jwt 넣었음
//우리 client 구조 상 카카오 login 했을 때는 우리가 직접 쿠키를 생성해서 jwt에 넣은 다음에 브라우저에 자동으로 set될 수 있도록 구현해보기
@GetMapping("/user/kakao/callback")
public String kakaoLogin(@RequestParam String code, HttpServletResponse response) throws JsonProcessingException {
//kakaoService 단의 kakoLogin 메소드
String token = kakaoService.kakaoLogin(code); //String token = jwt token
//빨간 줄 : 예외를 시그니처에 추가 누르기
//kakao에서 받아온 token을 쿠키에 넣어주고 reponse 객체 넣어주는 작업
Cookie cookie = new Cookie(JwtUtil.AUTHORIZATION_HEADER, token);
cookie.setPath("/");
response.addCookie(cookie); //브라우저의 jwt 값이 set 될 것임
return "redirect:/";
}
}
KakaoService
1. 인가코드로 액세스 토큰 요청하는 getToken 메소드
2. getToken으로 전달 받아온 액세스 토큰을 다시 한번 카카오 서버에 요청을 해서 그 사용자의 정보를 받아온 다음에
그 사용자 정보에 우리가 원하는 필요한 데이터만 뽑아옴 (id,nickname, email)
package com.sparta.myselectshop.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.myselectshop.dto.KakaoUserInfoDto;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
@Slf4j(topic = "KAKAO Login")
@Service
@RequiredArgsConstructor
public class KakaoService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final RestTemplate restTemplate; //빨간 줄 : RestTemplate을 그냥 Builder로 만들 수 있다
//빨간줄 뜨는 이유 : restTemplate이 bean이 아닌 Rest Template Builder을 통해서 생성할 수 있도록 유도
//Bean을 수동으로 등록해서 관리하는 방법
private final JwtUtil jwtUtil;
public String kakaoLogin(String code) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
String accessToken = getToken(code);
// 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
return null;
}
//kakaoLogin에서 받은 인증 코드로 토큰 요청
private String getToken(String code) throws JsonProcessingException {
log.info("인가코드 : "+code);
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com") //여긴 정해진 주소를 넣기
.path("/oauth/token")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
//kakao Developers에서 요청한대로 http 형태를 만들어서 Rest template으로 API를 요청함
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "13f0c1acd35319458b5893b3dbba382d"); //여기에 RESTAPI 코드 다시 넣어주기
body.add("redirect_uri", "http://localhost:8080/api/user/kakao/callback");
body.add("code", code); //받은 인가코드 넣기
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri) //body가 있으니까 POST
.headers(headers)
.body(body);
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange( //http 요청이 카카오 서버 쪽으로 감
requestEntity,
String.class
); //ResponseEntity<String>에서 반환되는 String이 토큰의 형태로 되어 있음 > 그 토큰을 한번 파싱할 것임 (.readTree(response.getBody()))
// HTTP 응답 (JSON) -> 액세스 토큰 파싱 = 우리가 딱 원하는 액세스 토큰만 딱 뽑아옴
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
return jsonNode.get("access_token").asText();
//뽑으면 .asText()니까 String값 반환
//맨 위의 String accessToken = getToken(code);의 String accessToken
}
//사용자 정보 요청 (getKakaoUserInfo 메서드)
private KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
log.info("accessToken : "+accessToken);
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kapi.kakao.com")
.path("/v2/user/me")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri) //우리가 url 만든 거 보내주기
.headers(headers)
.body(new LinkedMultiValueMap<>()); //body는 따로 보내줄 필요가 없어서 new~
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class //String 타입으로 받아옴
);
//Long 타입으로 해당하는 id 받아오는 방법
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
//해댱하는 id값들을 받아올 수 있음
Long id = jsonNode.get("id").asLong();
//properties 중에서 nickname 값을 가져옴
String nickname = jsonNode.get("properties")
.get("nickname").asText();
//properties 중에서 email 값을 가져옴
String email = jsonNode.get("kakao_account")
.get("email").asText();
log.info("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
//KakaoUserInfoDto에 값을 넣어줌
return new KakaoUserInfoDto(id, nickname, email);
}
}