δ Spring 카카오 로그인 REST API

@雲原ジェリー·2022년 1월 1일
4

Spring Framework

목록 보기
4/6

스프링 프레임워크에서 카카오 로그인 REST API 사용하기!

스프링 프레임워크에서 카카오 로그인 REST API사용하는 방법!

  • 참고사항(필독!)
    • 프로젝트가 다 끝나고 기억나는대로 정리하는거라 import 내용이 틀릴 수 있음.
    • 카카오에서 갖고오는 정보는 사용자 이름(ex 카톡 이름), 사용자 이메일만 갖고오는 코드.
    • 카카오 개발자 센터에서 redirect_uri 등 기본적인것들은 먼저 설정하셔야 합니다.
      • REST API KEY를 사용함.
      • 플랫폼 Web & 사이트 도메인 설정을 해야함.
      • Redirect URI 설정을 해야함.
    • 설정 방법은 생략하겠습니다. 직접 찾아서 해보세요.
    • 아직 학생이고 배우는중이라 제가 올린 정보가 정확하지 않습니다! 참고용으로만 보세요.
    • DB에 저장하는 방법은 나중에 나중에 올리겠습니다.
    • Kakao Developers
    • 참고 사이트

로그인 API 사용 구조

  1. 사용자가 카카오 API를 호출함.

  2. 카카오 서버가 호출됨.

  3. 로그인 정보를 입력받음.

  4. 문제가 없을 시 카카오 서버는 redirect_uri로 인증 코드(code)를 넘겨줌.

  5. 사용자는 받은 인증 코드로 카카오 서버에 토큰(access_Token)을 요청함.

  6. 카카오 서버에서 토큰을 넘겨줌

  7. 받은 토큰을 카카오 서버에 보내 로그인한 사용자 정보를 요청함.

  8. 카카오 서버에서 토큰을 받고 그에 맞는 사용자 정보를 보냄

  9. 정보를 받아 처리

    여기서 redirect_uri이란 jsp주소가 아닌 컨트롤러 메서드의 value값입니다.
    카카오 및 네이버에서 넘겨주는 사용자 정보는 JSON 타입입니다.
    JSON안에 담겨있는 정보의 타입은 String 타입으로 담겨있습니다.
    ※위 내용은 제가 공부하며 정리한 것으로 실제 내용과 다를 수 있습니다!


1. login.jsp에 카카오 로그인 만들기

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Insert title here</title>
</head>
<body>
	<!-- 카카오 로그인 -->
	<a class="p-2" href="https://kauth.kakao.com/oauth/authorize?client_id=REST_API키&redirect_uri=REDIRECT_URL&response_type=code">
	<!-- REST_API키 및 REDIRECT_URi는 본인걸로 수정하세요 -->
	<!-- 저는 redirect_uri을 http://localhost:8080/member/kakaoLogin로 했습니다. -->
	<!-- 본인걸로 수정 시 띄어쓰기 절대 하지 마세요. 오류납니다. -->
	
		<img src="/resources/icon/kakao_login_large_narrow.png" style="height:60px">
      		<!-- 이미지는 카카오 개발자센터에서 제공하는 login 이미지를 사용했습니다. -->

	</a>
</body>
</html>

2. Controller에 code를 받을 메서드 만들기

package com.spring.project.controller;

import java.io.IOException;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping(value="/member/*")
public class MemberController {

	@RequestMapping(value="/kakaoLogin", method=RequestMethod.GET)
	public String kakaoLogin(@RequestParam(value = "code", required = false) String code) throws Exception {
		System.out.println("#########" + code);
		return "member/testPage";
		/*
		 * 리턴값의 testPage는 아무 페이지로 대체해도 괜찮습니다.
		 * 없는 페이지를 넣어도 무방합니다.
		 * 404가 떠도 제일 중요한건 #########인증코드 가 잘 출력이 되는지가 중요하므로 너무 신경 안쓰셔도 됩니다.
		 */
    	}

}

여기까지 만들고 로그인을 했을 때 카카오 로그인 창이 뜨며 로그인시 아래처럼 코드가 넘어옴.

// 결과 예시, 아래와 똑같은 값이 나오는 것이 아닌 아래처와 같은 형식으로 나온다는 것임.
#########GJ0ouweigBr9fDyCfu-SsfZVjY69LOz0SDS2xJat1bMecBZeBAthQGQD-gSoac-2GqYyHgorDKcAAAFzarIl2g

3. code를 보내 access_Token 얻기

  1. Controller에 내용 추가 (서비스 호출 및 토큰값 출력)
package com.spring.project.controller;

import java.io.IOException;

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

import com.spring.project.service.MemberService;

@Controller
@RequestMapping(value="/member/*")
public class MemberController {

	@Autowried
	private MemberService ms;

	@RequestMapping(value="/kakaoLogin", method=RequestMethod.GET)
	public String kakaoLogin(@RequestParam(value = "code", required = false) String code) throws Exception {
		System.out.println("#########" + code);
        
		// 위에서 만든 코드 아래에 코드 추가
		String access_Token = ms.getAccessToken(code);
		System.out.println("###access_Token#### : " + access_Token);
        
		return "member/testPage";
    	}

}

  1. Service에 access_Token을 요청하는 메서드 생성
package com.spring.project.service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;

import org.springframework.stereotype.Service;

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

@Service
public class MemberService {

	public String getAccessToken (String authorize_code) {
		String access_Token = "";
		String refresh_Token = "";
		String reqURL = "https://kauth.kakao.com/oauth/token";

		try {
			URL url = new URL(reqURL);
            
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			// POST 요청을 위해 기본값이 false인 setDoOutput을 true로
            
			conn.setRequestMethod("POST");
			conn.setDoOutput(true);
			// POST 요청에 필요로 요구하는 파라미터 스트림을 통해 전송
            
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
			StringBuilder sb = new StringBuilder();
			sb.append("grant_type=authorization_code");
            
			sb.append("&client_id=REST_API키"); //본인이 발급받은 key
			sb.append("&redirect_uri=REDIRECT_URI"); // 본인이 설정한 주소
            
			sb.append("&code=" + authorize_code);
			bw.write(sb.toString());
			bw.flush();
            
			// 결과 코드가 200이라면 성공
			int responseCode = conn.getResponseCode();
			System.out.println("responseCode : " + responseCode);
            
			// 요청을 통해 얻은 JSON타입의 Response 메세지 읽어오기
			BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
			String line = "";
			String result = "";
            
			while ((line = br.readLine()) != null) {
				result += line;
			}
			System.out.println("response body : " + result);
            
			// Gson 라이브러리에 포함된 클래스로 JSON파싱 객체 생성
			JsonParser parser = new JsonParser();
			JsonElement element = parser.parse(result);
            
			access_Token = element.getAsJsonObject().get("access_token").getAsString();
			refresh_Token = element.getAsJsonObject().get("refresh_token").getAsString();
            
			System.out.println("access_token : " + access_Token);
			System.out.println("refresh_token : " + refresh_Token);
            
			br.close();
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return access_Token;
	}
}

통신 결과가 200이여야 성공인겁니다. 200이 아니면 통신 오류입니다.

// 결과 예시, 아래와 똑같은 값이 나오는 것이 아닌 아래와 같은 형식으로 나온다는 것임.
// responseCode가 200이 아닐 시 통신에 실패했다는 것으로 다시 확인해보세요.
responseCode : 200
response body : {"access_token":"LilJmueUBPlH6pIuAnEra6igTiRllke-n6RnFQo9dZwAAAFzatW0ew","token_type":"bearer","refresh_token":"zHKhaFpAmDXwNJmSLa-wdwvx4g69Rt6CrP_qgQo9dZwAAAFzatW0eg","expires_in":21599,"scope":"account_email profile","refresh_token_expires_in":5183999}
access_token : LilJmueUBPlH6pIuAnEra6igTiRllke-n6RnFQo9dZwAAAFzatW0ew
refresh_token : zHKhaFpAmDXwNJmSLa-wdwvx4g69Rt6CrP_qgQo9dZwAAAFzatW0eg
###access_Token#### : LilJmueUBPlH6pIuAnEra6igTiRllke-n6RnFQo9dZwAAAFzatW0ew

4. access_Token을 보내 사용자 정보 얻기

  1. Controller에 내용 추가 (서비스 호출 및 사용자 정보 출력)
package com.spring.project.controller;

import java.io.IOException;
import java.util.HashMap;

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

import com.spring.project.service.MemberService;

@Controller
@RequestMapping(value="/member/*")
public class MemberController {

	@Autowried
	private MemberService ms;

	@RequestMapping(value="/kakaoLogin", method=RequestMethod.GET)
	public String kakaoLogin(@RequestParam(value = "code", required = false) String code) throws Exception {
		System.out.println("#########" + code);
		String access_Token = ms.getAccessToken(code);
        
		// 위에서 만든 코드 아래에 코드 추가
		HashMap<String, Object> userInfo = ms.getUserInfo(access_Token);
		System.out.println("###access_Token#### : " + access_Token);
		System.out.println("###nickname#### : " + userInfo.get("nickname"));
		System.out.println("###email#### : " + userInfo.get("email"));
        
		return "member/testPage";
    	}

}

  1. 서비스에 사용자 정보를 요청하는 내용 생성
  • 위에 만든 서비스 메서드 아래에 새로운 메서드를 만드는 겁니다!
public HashMap<String, Object> getUserInfo(String access_Token) {

	// 요청하는 클라이언트마다 가진 정보가 다를 수 있기에 HashMap타입으로 선언
	HashMap<String, Object> userInfo = new HashMap<String, Object>();
	String reqURL = "https://kapi.kakao.com/v2/user/me";
	try {
		URL url = new URL(reqURL);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("GET");

		// 요청에 필요한 Header에 포함될 내용
		conn.setRequestProperty("Authorization", "Bearer " + access_Token);

		int responseCode = conn.getResponseCode();
		System.out.println("responseCode : " + responseCode);

		BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

		String line = "";
		String result = "";

		while ((line = br.readLine()) != null) {
			result += line;
		}
		System.out.println("response body : " + result);

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

		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();

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

	} catch (IOException e) {
		e.printStackTrace();
	}
	return userInfo;
}

위 코드까지 성공적으로 마쳤으면 로그인 시 정보를 얻어올 수 있을겁니다.
DB에 저장하는 코드는 차후에 기회가 되면 다루겠습니다.

// 결과 예시, 아래와 똑같은 값이 나오는 것이 아닌 아래와 같은 형식으로 나온다는 것임.
// responseCode가 200이 아닐 시 통신에 실패했다는 것으로 다시 확인해보세요.
// 이름과 이메일은 개인정보 보호를 위해 임의로 바꿨습니다.
// 실제로는 로그인한 사람의 이름과 이메일이 출력됩니다.
responseCode : 200
response body : {"access_token":"LilJmueUBPlH6pIuAnEra6igTiRllke-n6RnFQo9dZwAAAFzatW0ew","token_type":"bearer","refresh_token":"zHKhaFpAmDXwNJmSLa-wdwvx4g69Rt6CrP_qgQo9dZwAAAFzatW0eg","expires_in":21599,"scope":"account_email profile","refresh_token_expires_in":5183999}
access_token : LilJmueUBPlH6pIuAnEra6igTiRllke-n6RnFQo9dZwAAAFzatW0ew
refresh_token : zHKhaFpAmDXwNJmSLa-wdwvx4g69Rt6CrP_qgQo9dZwAAAFzatW0eg
###access_Token#### : LilJmueUBPlH6pIuAnEra6igTiRllke-n6RnFQo9dZwAAAFzatW0ew
###nickname#### : 하늘
###email#### : sky@gmail.com

마치며

정말 어 . 렵 . 습 . 니 . 다. 그러니 좌절하지마시고,
제가 종합한 내용으로 모두 성공하시면 좋겠습니다.


전체 코드

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Insert title here</title>
</head>
<body>
	<a class="p-2" href="https://kauth.kakao.com/oauth/authorize?client_id=REST_API키&redirect_uri=REDIRECT_URI&response_type=code">
	<!-- REST_API키 및 REDIRECT_URI는 본인걸로 수정하세요 -->
	<!-- 본인걸로 수정 시 띄어쓰기 절대 하지 마세요. 오류납니다. -->
		<img src="/resources/icon/kakao_login_large_narrow.png" style="height:60px">
	</a>			
</body>
</html>

Controller (MemberController)

package com.spring.project.controller;

import java.io.IOException;
import java.util.HashMap;

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

import com.spring.project.service.MemberService;

@Controller
@RequestMapping(value="/member/*")
public class MemberController {

	@Autowried
	private MemberService ms;

	@RequestMapping(value="/kakaoLogin", method=RequestMethod.GET)
	public String kakaoLogin(@RequestParam(value = "code", required = false) String code) throws Exception {
		System.out.println("#########" + code);
		String access_Token = ms.getAccessToken(code);
		HashMap<String, Object> userInfo = ms.getUserInfo(access_Token);
		System.out.println("###access_Token#### : " + access_Token);
		System.out.println("###nickname#### : " + userInfo.get("nickname"));
		System.out.println("###email#### : " + userInfo.get("email"));
		return "member/testPage";
    	}

}

Service (MemberService)

package com.spring.project.service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;

import org.springframework.stereotype.Service;

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

@Service
public class MemberService {

	public String getAccessToken (String authorize_code) {
		String access_Token = "";
		String refresh_Token = "";
		String reqURL = "https://kauth.kakao.com/oauth/token";
		try {
			URL url = new URL(reqURL);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("POST");
			conn.setDoOutput(true);
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
			StringBuilder sb = new StringBuilder();
			sb.append("grant_type=authorization_code");
			sb.append("&client_id=REST_API키"); //본인이 발급받은 key
			sb.append("&redirect_uri=REDIRECT_URI"); // 본인이 설정한 주소
			sb.append("&code=" + authorize_code);
			bw.write(sb.toString());
			bw.flush();
			int responseCode = conn.getResponseCode();
			System.out.println("responseCode : " + responseCode);
			BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
			String line = "";
			String result = "";
			while ((line = br.readLine()) != null) {
				result += line;
			}
			System.out.println("response body : " + result);
			JsonParser parser = new JsonParser();
			JsonElement element = parser.parse(result);
			access_Token = element.getAsJsonObject().get("access_token").getAsString();
			refresh_Token = element.getAsJsonObject().get("refresh_token").getAsString();
			System.out.println("access_token : " + access_Token);
			System.out.println("refresh_token : " + refresh_Token);
			br.close();
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return access_Token;
	}
    
	public HashMap<String, Object> getUserInfo(String access_Token) {
		HashMap<String, Object> userInfo = new HashMap<String, Object>();
		String reqURL = "https://kapi.kakao.com/v2/user/me";
		try {
			URL url = new URL(reqURL);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("GET");
			conn.setRequestProperty("Authorization", "Bearer " + access_Token);
			int responseCode = conn.getResponseCode();
			System.out.println("responseCode : " + responseCode);
			BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
			String line = "";
			String result = "";
			while ((line = br.readLine()) != null) {
				result += line;
			}
			System.out.println("response body : " + result);
			JsonParser parser = new JsonParser();
			JsonElement element = parser.parse(result);
			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();
			userInfo.put("nickname", nickname);
			userInfo.put("email", email);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return userInfo;
	}
    
}

2개의 댓글

comment-user-thumbnail
2023년 7월 26일

사용자 정보까지는 불러와지는데 왜 닉네임이 깨져보일까요 ㅜㅜ

답글 달기
comment-user-thumbnail
2023년 10월 6일

감사합니다! 도움이 많이 되었습니다.

답글 달기