Spring으로 카카오 로그인 구현하기.
우선 프로젝트 셋팅 정보를 알려드리겠습니다. 스프링부트와 thymeleaf 그리고 응답값을 JSON 형식으로 받기 때문에 parsing을 위해 json-simple 라이브러리를 빌드했습니다.
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'
진행할 전체적인 순서는 아래와 같습니다.
우선 카카오 devloper에 접속후 로그인을 합니다. https://developers.kakao.com/
로그인을 하고 나면 오른쪽 상단에 "내 애플리케이션" 항목이 보입니다. 이를 클릭하여 줍니다.
카카오 로그인에 사용할 테스트 애플리케이션을 만들어 주기 위해 애플리케이션 추가하기 버튼을 눌러준후 정보를 입력합니다.(이미 애플리케이션이 존재할 시 생략 가능)
이제 생성한 애플리케이션을 누르면 API 호출에 필요한 Key를 사용할 수 있습니다.
이글은 REST API 기준이기 때문에 "REST API키"를 사용하도록 하겠습니다.
이제는 사이트 도메인을 등록하고 인가코드를 받을 Redirect URI 등록을 해야합니다. 플랫폼 버튼을 눌러줍니다. 그러면 아래와 같은 화면이 나옵니다.
위 화면에서 "Web" 항목에 사이트 도메인을 등록해줍니다. 그리고 Redirect URI를 등록하기 위해 "등록하러 가기" 버튼을 눌러줍니다.
그러면 위와 같은 화면이 보이며 "Redirect URI" 항목의 수정 버튼을 눌러 URI를 등록해줍니다.
위 과정을 모두 마치면 API 사용에 필요한 모든 준비가 완료 되었습니다.
위 화면을 보면 카카오 로그인은 API 키를 이용해 사용자 유효성을 검증하고 그리고 나서 인가 코드를 Redirect URI에 등록된 곳으로 반환받는걸 알 수 있습니다. 이 인가 코드를 이용해 토큰을 발급 받을 수 있으며 토큰을 발급 받고 나면 사용자 정보등을 조회 할 수 있습니다.
API에 대한 정확한 상세 설명은 공식 문서를 참고바랍니다.
https://developers.kakao.com/docs/latest/ko/kakaologin/common
이미지 저장 경로
resources/static/images/이미지파일
로그인 페이지
templates/kakaoCI/login.html
<html xmlns:th="http://www.thymeleaf.org">
<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=2aad40910868e3c5fa9594f8de34a07b&redirect_uri=http://localhost:8080/member/kakao&response_type=code">
<img th:src="@{/images/kakao_login_medium_narrow.png}" style="height:60px"/>
</a>
</body>
</html>
응답 정보 출력 페이지
templates/index.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<li th:text="|인가코드 = ${code}|">code</li>
<li th:text="|유효토큰 = ${access_token}|">code</li>
<li th:text="|사용자정보 = ${userInfo}|">code</li>
<li th:text="|동의정보 = ${agreementInfo}|">code</li>
<a href="https://kauth.kakao.com/oauth/logout?client_id=2aad40910868e3c5fa9594f8de34a07b&logout_redirect_uri=http://localhost:8080/member/do">로그아웃</a>
</body>
</html>
Service code
@Service
public class KaKaoService {
public String getToken(String code) throws IOException {
// 인가코드로 토큰받기
String host = "https://kauth.kakao.com/oauth/token";
URL url = new URL(host);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
String token = "";
try {
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true); // 데이터 기록 알려주기
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream()));
StringBuilder sb = new StringBuilder();
sb.append("grant_type=authorization_code");
sb.append("&client_id=2aad40910868e3c5fa9594f8de34a07b");
sb.append("&redirect_uri=http://localhost:8080/member/kakao");
sb.append("&code=" + code);
bw.write(sb.toString());
bw.flush();
int responseCode = urlConnection.getResponseCode();
System.out.println("responseCode = " + responseCode);
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String line = "";
String result = "";
while ((line = br.readLine()) != null) {
result += line;
}
System.out.println("result = " + result);
// json parsing
JSONParser parser = new JSONParser();
JSONObject elem = (JSONObject) parser.parse(result);
String access_token = elem.get("access_token").toString();
String refresh_token = elem.get("refresh_token").toString();
System.out.println("refresh_token = " + refresh_token);
System.out.println("access_token = " + access_token);
token = access_token;
br.close();
bw.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
return token;
}
public Map<String, Object> getUserInfo(String access_token) throws IOException {
String host = "https://kapi.kakao.com/v2/user/me";
Map<String, Object> result = new HashMap<>();
try {
URL url = new URL(host);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestProperty("Authorization", "Bearer " + access_token);
urlConnection.setRequestMethod("GET");
int responseCode = urlConnection.getResponseCode();
System.out.println("responseCode = " + responseCode);
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String line = "";
String res = "";
while((line=br.readLine())!=null)
{
res+=line;
}
System.out.println("res = " + res);
JSONParser parser = new JSONParser();
JSONObject obj = (JSONObject) parser.parse(res);
JSONObject kakao_account = (JSONObject) obj.get("kakao_account");
JSONObject properties = (JSONObject) obj.get("properties");
String id = obj.get("id").toString();
String nickname = properties.get("nickname").toString();
String age_range = kakao_account.get("age_range").toString();
result.put("id", id);
result.put("nickname", nickname);
result.put("age_range", age_range);
br.close();
} catch (IOException | ParseException e) {
e.printStackTrace();
}
return result;
}
public String getAgreementInfo(String access_token)
{
String result = "";
String host = "https://kapi.kakao.com/v2/user/scopes";
try{
URL url = new URL(host);
HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setRequestProperty("Authorization", "Bearer "+access_token);
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String line = "";
while((line=br.readLine())!=null)
{
result+=line;
}
int responseCode = urlConnection.getResponseCode();
System.out.println("responseCode = " + responseCode);
// result is json format
br.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
Controller code
@Controller
@RequestMapping("/member")
public class KaKaoController {
@Autowired
KaKaoService ks;
@GetMapping("/do")
public String loginPage()
{
return "kakaoCI/login";
}
@GetMapping("/kakao")
public String getCI(@RequestParam String code, Model model) throws IOException {
System.out.println("code = " + code);
String access_token = ks.getToken(code);
Map<String, Object> userInfo = ks.getUserInfo(access_token);
model.addAttribute("code", code);
model.addAttribute("access_token", access_token);
model.addAttribute("userInfo", userInfo);
//ci는 비즈니스 전환후 검수신청 -> 허락받아야 수집 가능
return "index";
}
}