Spring 부트

23-02-23) 스프링 부트 12강 - 자바측 API 통신(카카오 로그인)

JadeStone 2023. 2. 24. 01:51

카카오 로그인

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의 문자열을 자바의 객체형식으로 바꿔주는 라이브러리.

* 카카오 서버에서 요청에대한 응답으로 넘어온 제이슨 형식의 문자열을 자바의 객체형식으로 바꿔주기 위해서

 모듈을 받았음.