Spring Boot

230223 Spring Boot API통신 -카카오톡 로그인 구현

주영재 2023. 2. 23. 21:58

자바측 API통신


API통신
JS를 학습할 때 비동기통신을 이용하여 카카오 or 구글 API를 사용했었다.
이는 자바에서도 가능. 모든 언어는 API통신을 위한 라이브러리들이 있다.

1. java.net패키지에 있는 URI클래스, HttpURLConnection클래스를 이용
2. 외부라이브러리 HttpClient를 이용


과정

 

1.HttpURLConnection 객체 생성
URL url = new URL(요청주소);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();



2.요청 메서드 설정
conn.setRequestMethod("POST");
conn.setDoOutput(true);



3.요청 헤더 설정(특정이름, 값)
conn.setRequestProperty("Authorization", "Bearer"+토큰값);



4.데이터보내기
BufferedWriter bw = new BufferedWriter(new OupStreamWriter(conn.getOutputStream));



String req="보낼데이터";
bw.write(req);
bw.flush();

데이터를 받게 되면 실제 데이터는 JSON이다.



5.응답받기
int result = conn.getResponseCode()



6.데이터 받기
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

String result="";
String str;
while(str=br.readLine()!=null){
  result+=str;
}



GSON은 이용한 JSON파싱하기
위의 result가 JSON 문자열 형식으로 나온다.

GSON은 구글에서 만든 JSON구문 분석을 위한 최고의 API. 이외에도 많은 parser가 있다.


build.gradle에 라이브러리 추가

depedencies{
	...
	implementation 'com.google.code.gson:gson:2.8.5'
}

카카오 api -카카오로그인

※시작하기 전에 kakao developers의 내 애플리케이션-플랫폼에 사이트 도메인과 Redirect URI를 등록한다.

 

카카오 로그인
카카오 rest server와 통신

카카오 로그인 요청
->인가 코드 받기 요청
->인증 및 동의 요청
->로그인 및 동의
->인가 코드 발급
->(앱에 등록된 Redirect URI) 인가 코드로 토큰 발급 요청
->토큰 발급(카카오 서버만 해석 가능)
->카카오 로그인 완료, 토큰 정보 조회 및 검증

토큰으로 사용자 정보 가져오기 요청
->요청 검증 및 처리
->제공받은 사용자 정보로 서비스 회원 여부 확인
->신규 사용자인 경우 회원 가입 처리로

서비스 세션 발급
->로그인 완료 처리


GET /oauth/authorize?client_id=${키}&redirect_uri=${리다이렉트주소}&response_type=code HTTP/1.1
Host: kauth.kakao.com


키와 경로를 입력할 때 ${}까지 전부 치운다.
경로는 kauth.kako.com~위의 주소(/oauth~~code) HTTP/1전까지

컨트롤러에서 kakao메서드의 return은 null.
리다이렉트 주소는
http://127.0.0.1:8484/user/kakao
로. 컨트롤러로 연결됨.

 

 

login.html에서

...

<button type="button" class="loginBtn kakaoBtn"></button>

...

<script src="/js/jquery-1.12.1.min.js"></script>
<script>
	$(".kakaoBtn").click(()=>{
		location.href="https://kauth.kakao.com/oauth/authorize?client_id=키&redirect_uri=http://127.0.0.1:8484/user/kakao&response_type=code";
	});

</script>

로 이벤트 부여.

 

 

 


GSON
파서객체생성->JSON엘리먼트->JSON오브젝트->문자열추출

JsonParser json = new JsonParser(); //파서객체생성
JsonElement element = json.parse(result); //JSON엘리먼트변경
JsonObject properties = element.getAsJsonObject().get("properties").getAsJsonObject(); 

//JSON오브젝트추출, properties추출, 오브젝트추출


여기까지가, 토큰을 받아온 것.


 

사용자 정보 가져오기

GET/POST /v2/user/me HTTP/1.1
Host: kapi.kakao.com
Authorization: Bearer ${ACCESS_TOKEN}/KakaoAK ${APP_ADMIN_KEY}
Content-type: application/x-www-form-urlencoded;charset=utf-8


Authorization을 실을 때, Bearer와 토큰 사이에 공백을 한칸 준다!
parameter인 secure_resource와 property_keys는 필수가 아님. 주고 싶으면 data에 &로 묶어서 준다.


토큰요청결과를 JSON 문자열형식으로.
결과모양은 동의항목내용에 따라 달라진다.


토큰발급


서비스도, 매퍼도, 컨트롤러도 무엇도 아닌 빈으로 생성하려면 @Component어노테이션 사용
서비스영역이랑 같음. 어노테이션만 다른 것.

 

POST /oauth/token HTTP/1.1
Host: kauth.kakao.com
Content-type: application/x-www-form-urlencoded;charset=utf-8

 

post방식, form데이터 타입.
데이터를 &형식으로 묶어서 보냄
키=값&키=값&...

js에서 fetch를 쓴 걸 java에선 url객체로.


여기는 암기보다 이해할 것.


컨트롤러와 @Component영역 KakaoAPI.java

더보기

KakaoAPI.java

package com.codehistory.myweb.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Component;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

@Component("kakao")
public class KakaoAPI {
	
	//토큰발급기능
	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=키"
					+"&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 ows=new OutputStreamWriter(out);
			//BufferedWriter br=new BufferedWriter(ows);
			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);
				JsonObject obj=element.getAsJsonObject(); //json오브젝트 형변환
				
				access_token=obj.get("access_token").getAsString();//json데이터를 문자혈로 형변환
				refresh_token=obj.get("refresh_token").getAsString();
			}
			
			
		}catch(Exception e) {
			e.printStackTrace();
		}
		
		
		
		return access_token;
	}
	
	//토큰기반으로 유저정보 얻기
	public Map<String, Object> getUserInfo(String access_token){
		
		//데이터 저장할 map
		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);
				
				JsonObject properties=element.getAsJsonObject().get("properties").getAsJsonObject();
				String nickname=properties.getAsJsonObject().get("nickname").getAsString();
				
				JsonObject kakao_account=element.getAsJsonObject().get("kakao_account").getAsJsonObject();
				String email=kakao_account.getAsJsonObject().get("email").getAsString();
				
				map.put("nickname", nickname);
				map.put("email",email);
			}
			
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
		return map;
	}
	
	
}

 

 

UserController.java

package com.codehistory.myweb.controller;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.codehistory.myweb.util.KakaoAPI;

@Controller
@RequestMapping("/user")
public class UserController {
	
	@Autowired
	private KakaoAPI kakao;

	@GetMapping("join")
	public String join() {
		return "user/join";
	}
	
	@GetMapping("login")
	public String login() {
		return "user/login";
	}
	
	@GetMapping("userDetail")
	public String userDetail() {
		return "user/userDetail";
	}
	
	//카카오 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에서

 

//토큰발급기능
	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=키"
					+"&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 ows=new OutputStreamWriter(out);
			//BufferedWriter br=new BufferedWriter(ows);
			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;
				}
				
				//제이슨 데이터 분해
				JsonParser json=new JsonParser(); //com.google.~~
				JsonElement element= json.parse(result);
				JsonObject obj=element.getAsJsonObject(); //json오브젝트 형변환
				
				access_token=obj.get("access_token").getAsString();//json데이터를 문자혈로 형변환
				refresh_token=obj.get("refresh_token").getAsString();
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
		return access_token;
	}

가 토큰발급기능이다.

 

post의 폼데이터 형식으로 키=값을 지정. 

requestURL은 카카오에서 지정, redirect_url은 직접 설정.

 

URL객체에 requestURL을 생성자에 매개변수로 준다.

url.openConnection()으로 HttpURLConnection을 받음.

 

conn.setRequestMethod()에 카카오가 지정한 대로 post형식을 넣는다.

conn.setDoOutPut()은 true로, 카카오서버로부터 데이터응답을 허용한다.

 

데이터전송을 위해

//데이터전송을 위한 클래스
//OutputStream out = conn.getOutputStream();
//OutputStreamWriter ows=new OutputStreamWriter(out);
//BufferedWriter br=new BufferedWriter(ows);
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));

을 생성. bw.write()에 카카오에서 지정한 방식대로 만든 data값을 넣는다.

bw.flush() 실행.

 

 

UserController.java에서

//카카오 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";
}

가 카카오가 redirect하는 uri다.

로그인을 했을 때 리다이렉트가 컨트롤러의 kakao메서드임. 로그인해서 받은 인가 코드를 컨트롤러에서 출력 가능.

그리고 그 코드를 다시 KakaoAPI.java의 getAccessToken()에 담아서 토큰을 반환받는다.

 

 

sysout code의 콘솔출력문

인가코드:카카오에서 보낸 인가코드.

 

 

 

 

응답결과는 똑같이 conn객체에서 받는다. 데이터도 보내고 결과도 받는다.

요청결과가 200(성공)이면 데이터를 받는다.

//응답결과를 conn객체에서 받음. 데이터를 날리고 결과도 받는다.
System.out.println("요청결과:"+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);

에서 데이터를 받아서 String result에 담는다.

 

콘솔출력문

{"access_token":"토큰-암호화된 랜덤 문자열"
,"token_type":"bearer"
,"refresh_token":"토큰-암호화된 랜덤 문자열. access_token과 다르다."
,"expires_in":21599,"scope":"account_email profile_image profile_nickname"
,"refresh_token_expires_in":5183999}

 

받은 result는 JSON이다.

파싱 필요.

//제이슨 데이터 분해
JsonParser json=new JsonParser(); //com.google.~~
JsonElement element= json.parse(result);
JsonObject obj=element.getAsJsonObject(); //json오브젝트 형변환

access_token=obj.get("access_token").getAsString();//json데이터를 문자혈로 형변환
refresh_token=obj.get("refresh_token").getAsString();

GSON을 통해 분해한다. 순서대로

-파서객체를 생성하고,

-JSON엘리먼트를 추출하고,

-JSON 오브젝트로 형변환

-원하는 내용을 get()을 통해 꺼내서 문자열로 형변환한다.

 

 

 

그리고 access_token을 return한다. 이는 컨트롤러에서 받는다.

String token=kakao.getAccessToken(code);
System.out.println("어세스토큰:"+token);

 

콘솔출력문

어세스토큰:토큰-암호화된 랜덤 문자열

사용자 정보 가져오기

KakaoAPI.java에서

//토큰기반으로 유저정보 얻기
	public Map<String, Object> getUserInfo(String access_token){
		
		//데이터 저장할 map
		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);
				
				JsonObject properties=element.getAsJsonObject().get("properties").getAsJsonObject();
				String nickname=properties.getAsJsonObject().get("nickname").getAsString();
				
				JsonObject kakao_account=element.getAsJsonObject().get("kakao_account").getAsJsonObject();
				String email=kakao_account.getAsJsonObject().get("email").getAsString();
				
				map.put("nickname", nickname);
				map.put("email",email);
			}
			
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
		return map;
	}

부분. 토큰발급과 유사하다. 

토큰을 받을 때와 똑같은 과정을 거친다. 단 BufferedWriter는 사용할 필요가 없다. 보낼 데이터가 없기 때문.

 

 

데이터를 저장할 map을 반환한다.

마찬가지로 requestURL은 카카오가 지정한 방식대로.

 

메서드가 생성될 때 access_token이 필요하다. 이는 컨트롤러에서 받은 token을 생성자에 넣어주면 된다.

컨트롤러에서

String token=kakao.getAccessToken(code);

Map<String, Object> map=kakao.getUserInfo(token);

로.

 

 

 

헤더에 토큰정보를 추가해야 한다.

//헤더에 토큰정보를 추가
conn.setRequestProperty("Authorization","Bearer "+access_token);

로. 이때 Bearer뒤에 공백을 한 칸 넣는다.

 

 

 

 

토큰요청결과도 마찬가지로 conn으로 받을 수 있다.

똑같이 conn.getResponseCode()를 사용하는데, requestURL이 다르기에 HttpURLConnection인 conn도 달라진다.

콘솔출력문

토큰요청결과:200

 

 

 

BufferedReader로 받은 데이터(JSON)를 result에 담아 출력하면

{"id":숫자형아이디,"connected_at":"2023-01-09T02:30:20Z"
,"properties":{"nickname":"이름","profile_image":"프로필이미지 href경로","thumbnail_image":"썸네일이미지 href경로"}
,"kakao_account":{"profile_nickname_needs_agreement":false,"profile_image_needs_agreement":false
				,"profile":
                			{"nickname":"이름","thumbnail_image_url":"썸네일이미지 href경로","profile_image_url":"프로필이미지 href경로",
    						"is_default_image":false}
                ,"has_email":true,"email_needs_agreement":false,"is_email_valid":true,"is_email_verified":true,"email":"이메일"}
 				}

가 나온다.

 

 

GSON을 이용해 파싱

JsonParser json=new JsonParser(); 
JsonElement element= json.parse(result);

JsonObject properties=element.getAsJsonObject().get("properties").getAsJsonObject();
String nickname=properties.getAsJsonObject().get("nickname").getAsString();

JsonObject kakao_account=element.getAsJsonObject().get("kakao_account").getAsJsonObject();
String email=kakao_account.getAsJsonObject().get("email").getAsString();

map.put("nickname", nickname);
map.put("email",email);

하고 전체 메서드의 return을 map으로.

 

 

 

 

 

하면 컨트롤러에서

Map<String, Object> map=kakao.getUserInfo(token);
System.out.println("사용자데이터:"+map.toString());

을 하면 

 

 

콘솔출력문

사용자데이터:{nickname=이름, email=이메일}

이 나온다. 

이 정보를 통해 db와 연결해서 로그인을 처리하거나 필요한 정보를 뽑아쓰거나 할 수 있다.