글 작성자의 부족한 실력으로 외부 API를 연동해본 경험을 적습니다.
부족한 부분이 많을 수 있으니 양해 부탁드립니다. 코드리뷰는 항상 열려있습니다.
RestTemplate은 스프링 프레임워크에서 제공하는 HTTP 클라이언트 라이브러리이며, RESTful 웹 서비스를 호출하고 그 결과를 받아오는 기능을 제공한다.
로그인 또는 회원가입시 본인 서비스에서 사용하거나, 필요한 정보를 선택한다.
kakao:
apiKey: #카카오 디벨로퍼에서 발급받은 REST_API_KEY 기입
클래스에 변수로 설정하지 않고 스프링 부트의 Value 어노테이션을 이용하여 보안 유지
@Configuration
public class Config {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
RestTemplate에 대한 의존성 관리를 스프링 컨테이너가 관리하도록 등록한다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/openapi")
public class OpenApiController {
private final OpenApiService openApiService;
@GetMapping("/kakao")
public String kakao(){
return "";
}
}
아직 리턴값과 결과값을 알 수없기에 간단하게 작성
@Service
@RequiredArgsConstructor
public class OpenApiService {
private final RestTemplate restTemplate;
@Value("${kakao.apiKey}")
private String kakaoKey;
public void getToken(){
}
}
Authentication(인증) :
ID와 비밀번호로 사용자 신원을 확인
각 서비스에 사용자가 카카오계정으로 로그인할 수 있는 기능 지원
서비스에서 각 사용자를 식별할 수 있는 고유한 회원번호 제공
Authorization(인가) :
사용자 개인정보와 같은 자원(Resource)에 대한 접근 권한 획득
사용자 동의를 바탕으로 사용자 정보나 기능에 대한 접근 권한을 토큰 형태로 서비스에 부여
본인의 인가 코드를 사용하여 카카오 서버에 인가를 요청하고, 인가가 완료되면 카카오 측에서 Redirect를 사용하여 사용자에게 카카오 로그인 화면을 연결시켜 줍니다.
연결된 로그인 화면에서 사용자가 카카오 로그인을 성공하면 카카오 서버에서 토큰을 발급하고, 토큰내에 존재하는 값을 가지고 로그인 또는 회원가입로직을 구성할 수 있습니다.
프론트에서 버튼, 이미지을 클릭할 경우 카카오 서버로 부터 Code를 얻기 위한 URL에 연결되게끔한다.
https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code HTTP/1.1
카카오로 부터 발급받은 API키와 서비스의 URL을 입력해주고 카카오 서버로 부터 코드를 요청한다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/openapi")
public class OpenApiController {
private final OpenApiService openApiService;
@GetMapping("/kakao")
public String kakao(@RequestParam("code") String code){
openApiService.getToken(code);
return "";
}
}
사용자가 로그인을 성공적으로 한 경우 카카오 서버에서 Redirect 주소로
response에 code를 넘겨준다.
Controller는 해당 code를 사용하기 위해 파라미터로 받고 로직을 처리하기 위한 Service로 넘겨준다.
@ToString
@Getter
@Setter
public class LoginResponseDto {
private String token_type;
private String access_token;
private String id_token;
private String refresh_token;
private Integer expires_in;
private Integer refresh_token_expires_in;
private String scope;
}
KAKAO 서버측에서 로그인 성공 및 인증이 완료되면 응답값과 토큰을 받기 위한 DTO
@Service
@RequiredArgsConstructor
public class OpenApiService {
private final RestTemplate restTemplate;
@Value("${kakao.apiKey}")
private String kakaoKey;
public void getToken(String code){
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
MultiValueMap<String,String> body = new LinkedMultiValueMap<>();
body.add("grant_type","authorization_code");
body.add("client_id", kakaoKey);
body.add("redirect_uri","http://localhost:8080/openapi/kakao");
body.add("code",code);
LoginResponseDto loginResponseDto = restTemplate.postForObject(
"https://kauth.kakao.com/oauth/token",
new HttpEntity<>(body,headers),
LoginResponseDto.class);
System.out.println(loginResponseDto);
}
}
토큰을 받기 위한 기본정보 세팅
RestTemplate를 이용하여 외부 API와 통신하고 정보를 주고 받는다.
토큰 발급 후 토큰을 이용하여 여러가지 응답을 다시 요청할 수 있다.
API 연동을 하면서 알게된 사실
로그인 서버 측에서 Content-type이 JSON인 경우 JSONObject 클래스를 사용하여 body에 담아 요청하고,
application/x-www-form일 경우 body에 파라미터 형식으로 넘긴다.(LinkedMultiValueMap 사용)
LoginResponseDto
(
token_type=bearer,
access_token=AAAAAA,
id_token=null,
refresh_token=BBBBBB,
expires_in=21599,
refresh_token_expires_in=5183999,
scope=account_email profile_nickname
)
@Service
@RequiredArgsConstructor
public class KakaoLogin extends SocialLoginServiceTemplate{
private final RestTemplate restTemplate;
@Value("${kakao.apiKey}")
private String kakaoKey;
public void getToken(String code){
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
MultiValueMap<String,String> body = new LinkedMultiValueMap<>();
body.add("grant_type","authorization_code");
body.add("client_id", kakaoKey);
body.add("redirect_uri","http://localhost:8080/openapi/kakao");
body.add("code", code);
LoginResponseDto loginResponseDto = restTemplate.postForObject(
"https://kauth.kakao.com/oauth/token",
new HttpEntity<>(body,headers),
LoginResponseDto.class);
getUserInfo(loginResponseDto.getAccess_token());
}
@Override
protected void getUserInfo(String token) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
headers.add("Authorization", "Bearer "+token);
ResponseEntity<String> response =
restTemplate.exchange("https://kapi.kakao.com/v2/user/me",
HttpMethod.GET,
new HttpEntity<>(null, headers),
String.class);
String body = response.getBody();
System.out.println(body);
}
}
토큰을 이용한 사용자의 정보를 가져오기 위해서 Header에 Content-type 과 토큰 정보만 추가하고 body에 추가할 데이터는 없다.
{
"id": 11111111,
"connected_at": "2023-04-27T06:49:20Z",
"properties": {
"nickname": "NURI"
},
"kakao_account": {
"profile_nickname_needs_agreement": false,
"profile": {
"nickname": "NURI"
},
"has_email": true,
"email_needs_agreement": false,
"is_email_valid": true,
"is_email_verified": true,
"email": "1111@naver.com"
}
}