23-02-23) 스프링 부트 12강 - 자바측 API 통신(카카오 로그인)
카카오 로그인
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info
* 모든 언어에서 API 통신 언제든지 가능.
사용하는 언어만 다를뿐.
* 위 흐름을 알아야 프로그램 코드를 짤 수 있음.
<사용자 정보 받기>
# login.html (로그인 페이지)
* 로그인 페이지에서 로그인 버튼을 누르면 카카오 로그인 api 를 요청함.
( 보내야할 데이터를 get방식 중 쿼리스트링 방식으로 붙여가지고 요청을 보냄)
* uri=http://127.0.0.1:8484/user/kakao 이와같이 요청에 대한 응답을 우리서버(컨트롤러)에 /user/kakao 요청으로 가도록 해주었다
<script>
$(".kakaoBtn").click(function(){
location.href="https://kauth.kakao.com/oauth/authorize?client_id=359e54e709c52575968102e8872281f1&redirect_uri=http://127.0.0.1:8484/user/kakao&response_type=code"
})
</script>
# 컨트롤러 (사용자의 정보를 받는 과정)
1. 로그인 페이지에서 카카오 서버로 인가코드를 요청했던것이
응답을 받아서 인가코드와 함께 /kakao 경로로 넘어왔다.
2. 인가코드를 실어서 카카오서버로 토큰을 요청.
응답으로 토큰이 실려서 다시 돌아옴.
3. 토큰을 실어서 카카오 서버로 유저 정보를 요청해서
응답으로 넘어 온 유저정보를 map 에 담아서 받았다.
//카카오 redirect_uri
@GetMapping("/kakao")
public String kakao(@RequestParam("code") String code) {
System.out.println("인가코드:" + code);
String token = kakao.getAccessToken(code);
System.out.println("어세스토큰:" + token);
Map<String, Object> map = kakao.getUserInfo(token);
System.out.println("사용자데이터:" + map.toString());
//우리데이터베이스에서 조회해서 로그인처리...~??
return "redirect:/main";
}
# KakaoAPI.java (카카오 서버로 요청보내는 기능 구현)
* KakaoAPI 클래스를 빈으로 생성하도록 함
- @Component("kakao") -> Component 어노테이션을 선언하고 이름을 kakao 로 지정.
* 토큰발급기능 (getAccessToken메서드)
- 요청을 보낼 url과 응답을 받을 url을 선언
String requestURL = "https://kauth.kakao.com/oauth/token";
String redirect_url = "http://127.0.0.1:8484/user/kakao";
- post의 폼 데이터 형식 ( 키=값&키=값...) 으로 보낼 데이터를 만들어서 변수에 담음
//post의 폼데이터 형식 키=값&키=값....
String data = "grant_type=authorization_code"
+ "&client_id=359e54e709c52575968102e8872281f1"
+ "&redirect_uri=" + redirect_url
+ "&code=" + code;
* API 통신 부분( 요청을 보내고 받음)
요청부분
- URL 객체를 생성해서 매개변수에 요청을 보낼 주소를 담아줌.
- url 객체의 openConnection() 메서드를 이용해서 실질적으로 데이터를 주고받는 통신을 해줄 URLConnection 객체를 반 환 받는다.
(URLConnection은 웹을 통해 데이터를 주고 받는데 사용 됨)
반환받은 URLConnenction은 HttpURLConnenction의 인스턴스가 될 수 있기 때문에
리턴된 URLConnection을 HttpURLConnction으로 캐스팅해서 conn 이라는 변수에 담아줌. -> (이해가 안감 ★)
즉, conn은 URLConnection(데이터를 주고 받아주는 객체) 라고 보면됨.
HttpURLconnection 관련 내용 참고 블로그
https://velog.io/@ieed0205/HttpURLConnection
- 보내는 데이터 처리
//데이터전송을 위한 클래스
//OutputStream out = conn.getOutputStream();
//OutputStreamWriter osw = new OutputStreamWriter(out);
//BufferedWriter br = new BufferedWriter(osw);
BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(conn.getOutputStream()));
bw.write(data); //데이터를 쓰고
bw.flush(); //데이터를 밀어줌
응답받음
- 응답 결과를 conn 객체에서 받음
- 요청이 성공했는지 여부(응답의 상태 - status) 를 확인해서 성공인 200인 경우 넘어온 데이터에 대한 처리를 함.
- BufferedReader객체의 readLine() 메서드는 받은 문자열 데이터를 한줄씩 읽어서 반환해줌.
//응답결과를 conn객체에서 받음
System.out.println("요청결과:" + conn.getResponseCode());
if(conn.getResponseCode() == 200) { //요청성공
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String result = "";
String str = null;
while((str = br.readLine())!=null) { //한줄씩읽음 - 읽을값이 없다면 null
result += str;
}
System.out.println(result);
이 때 넘어온 데이터 형식은 제이슨의 문자열 형식.
result 출력내용
{"access_token":"zkTk1QoREQIWXl7wV1Cr-rek0gq46T2ixr4NDoIfCisNHgAAAYZ-vBvZ",
"token_type":"bearer",
"refresh_token":"XWVKBL2sUe7COaU3HQq_0Hz7hT8u1NX42YBZSlwECisNHgAAAYZ-vBvY",
"expires_in":21599,"scope":"account_email profile_image profile_nickname",
"refresh_token_expires_in":5183999}
이 문자열을 손수 다 분해해서 자바의 객체 형태로 변환해주어야만 사용할 수 있다.
직접 작업하는데에는 번거로우니 이 작업을 자동으로 해주는 GSON 모듈(라이브러리)을 사용했음. (글 하단부분참고)
json오브젝트에서 토큰에 대한 정보를 get해서 String타입의 access_token 변수에 담아서 반환해줌.
//제이슨 데이터 분해
JsonParser json = new JsonParser(); //com.google.~~~
JsonElement element = json.parse(result); //json데이터 전달
JsonObject obj = element.getAsJsonObject(); //json오브젝트 형변환
access_token = obj.get("access_token").getAsString();
refresh_token = obj.get("refresh_token").getAsString();
}
} catch (Exception e) {
e.printStackTrace();
}
return access_token;
}
토큰 발급기능(getAccessToken 메서드 끝)
★ 토큰 발급기능 코드 한번에 확인
//토큰발급기능
public String getAccessToken(String code) {
String requestURL = "https://kauth.kakao.com/oauth/token";
String redirect_url = "http://127.0.0.1:8484/user/kakao";
String access_token = "";
String refresh_token = "";
//post의 폼데이터 형식 키=값&키=값....
String data = "grant_type=authorization_code"
+ "&client_id=359e54e709c52575968102e8872281f1"
+ "&redirect_uri=" + redirect_url
+ "&code=" + code;
try {
URL url = new URL(requestURL);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST"); //post형식
conn.setDoOutput(true); //카카오서버로 부터 데이터 응답을 허용
//데이터전송을 위한 클래스
//OutputStream out = conn.getOutputStream();
//OutputStreamWriter osw = new OutputStreamWriter(out);
//BufferedWriter br = new BufferedWriter(osw);
BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(conn.getOutputStream()));
bw.write(data); //데이터를 쓰고
bw.flush(); //데이터를 밀어줌
//응답결과를 conn객체에서 받음
System.out.println("요청결과:" + conn.getResponseCode());
if(conn.getResponseCode() == 200) { //요청성공
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String result = "";
String str = null;
while((str = br.readLine())!=null) { //한줄씩읽음 - 읽을값이 없다면 null
result += str;
}
System.out.println(result);
//제이슨 데이터 분해
JsonParser json = new JsonParser(); //com.google.~~~
JsonElement element = json.parse(result); //json데이터 전달
JsonObject obj = element.getAsJsonObject(); //json오브젝트 형변환
access_token = obj.get("access_token").getAsString();
refresh_token = obj.get("refresh_token").getAsString();
}
} catch (Exception e) {
e.printStackTrace();
}
return access_token;
}
* 토큰 기반으로 유저정보 얻기
- 전반적으로 토큰을 가져오는 때의 로직과 비슷하나,
카카오에서 유저정보를 얻기위해서는 요청하는 url 도 다르고
토큰 값을 어떻게 담아서 보내주는지 유의해서 보자.
- 요청시 보내는 데이터가 없으므로 BufferedWriiter 객체는 필요가 없었음.
위 그림에서 유저정보가 어떤 모습으로 넘어왔는지 우리가 구하고자 했던 데이터인 nickname 값과 email 값을
어떻게 GSON 으로 축출해서 가져오는지 유의해서 보자.
//토큰기반으로 유저정보 얻기
public Map<String, Object> getUserInfo(String access_token){
Map<String, Object> map = new HashMap<>();
String requestURL = "https://kapi.kakao.com/v2/user/me";
try {
URL url = new URL(requestURL);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST"); //post형식
conn.setDoOutput(true); //카카오서버로 부터 데이터 응답을 허용
//헤더에 토큰정보를 추가
conn.setRequestProperty("Authorization", "Bearer " + access_token);
System.out.println("토큰요청결과:" + conn.getResponseCode() );
if(conn.getResponseCode() == 200 ) { //사용자 데이터 요청 성공
BufferedReader br = new BufferedReader( new InputStreamReader( conn.getInputStream()) );
String result = "";
String str = null;
while( (str = br.readLine() ) != null ) { //한줄씩읽음 - 읽을값이 없다면 null
result += str;
}
System.out.println(result);
JsonParser json = new JsonParser();
JsonElement element = json.parse(result);
//json에서 key를 꺼내고, 다시 key를 꺼낸다.
JsonObject properties = element.getAsJsonObject().get("properties").getAsJsonObject();
JsonObject kakao_account = element.getAsJsonObject().get("kakao_account").getAsJsonObject();
String nickname = properties.getAsJsonObject().get("nickname").getAsString();
String email = kakao_account.getAsJsonObject().get("email").getAsString();
//맵에 저장
map.put("nickname", nickname);
map.put("email", email);
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
# JSON의 문자열을 자바의 객체형식으로 바꿔주는 라이브러리.
* 카카오 서버에서 요청에대한 응답으로 넘어온 제이슨 형식의 문자열을 자바의 객체형식으로 바꿔주기 위해서
모듈을 받았음.