2022. 10. 26. 00:53ㆍ3층 1구역 - 개발의 장/ajax통신
1. 서론
잡기술의 끝판왕에 대해 알아보자.
요즘 '인스타그램' 이라던가, '카카오' 같이 소셜 로그인이 되는 앱들이 많은데
이번엔 그 소셜 로그인 중 카카오 로그인을 해보고자 한다.
2.본론
카카오 로그인 서비스는 위와 같은 순서로 이루어진다고 한다.
연동 개발로는
REST API
자바스크립트
안드로이드
IOS
등등 여러가지가 존재하며 필자가 사용해볼 것은 REST API이다.
먼저 첫 단계는 인가 코드를 받는 것이다.
인가코드를 받기 전에 사전으로 작업해 줄 것이 있는데...
플랫폼 등록, 카카오 로그인 활성화, Redirect URI등록, 동의 항목은 필수로 설정을 해주어야 한다.
2-1. 플랫폼 등록
디벨롭퍼스에 진입해서 로그인을 하게 되면 간단하게 회원가입을 할 수도 있고 안 할 수도 있는데 진행하고 로그인을 하게 되면 상단 우측쯤에 있는 내 어플리케이션을 클릭하도록 하자.
처음 접속한 여러분들은 아마 비어있을 것이다. 애플리케이션 추가하기를 클릭하도록 하자.
그럼 이와 같은 창이 나올텐데 어차피 실습용이기 때문에 마음가는대로 원하는대로 짓도록 하자.
그 후 저장을 누르자.
다 만들었다면 위와 같이 앱이 하나 추가됐을 것이다. 클릭해보자.
그러면 다음과 같은 화면으로 넘어올텐데 이제 플랫폼을 설정해주어야 한다.
'플랫폼'을 클릭하도록 하자.
그러면 위와 같은 페이지로 넘어온다. 아직 초기라면 web도 플랫폼 등록 이라고 파란 버튼 하나가 있을 것이다.
클릭하면 현재 필자는 수정으로 나오지만 사이트 도메인 등록 창이 위와 같이 비스무리하게 나올 것이다.
현재 도메인 입력하고 저장을 클릭하자.
축하한다!! 인가 코드를 받는 첫단계를 클리어했다!
2-2. 카카오 로그인 활성화 및 Redirect URI
이제 2단계에 들어가자!!!
플랫폼 설정까지 했다면
위와 같이 나올텐데
Redirect URI를 등록해야 합니다. 옆에 '등록하러 가기' 클릭
초기에는 활성화 설정의 상태가 OFF로 되어있을텐데 ON으로 전환해주고, Redirect URI도 설정해주도록 하자.
축하한다!! 2번째 단계까지 무사히 클리어했다!
동의 화면 미리보기를 클릭하면 이런화면도 볼 수 있다~
2-3. 동의 항목
위 경로로 들어가 보면 동의항목이라고 있다. 더 늘리거나 닉네임만 받거나 할 수 있지만...
닉네임은 필수동의, 카카오계정은 선택 동의를 클릭해보자.
축하한다!! 3번째 단계까지 무사히 클리어했다!
이렇게 인가코드를 받기 위한 밑작업이 끝났다.
인가 코드는 GET방식이기 때문에 url을 get형식으로 주어야 한다.
GET /oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code HTTP/1.1
Host: kauth.kakao.com
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<c:if test="${not empty msg }" >
<script>alert("${msg}");</script>
</c:if>
<h3>로그인 페이지</h3><br>
<c:choose>
<c:when test="${empty sessionScope.id }">
<form action="login" method="post">
<input type="text" name="id" placeholder="아이디" /> <br>
<input type="password" name="pw" placeholder="비밀번호" /><br>
<input type="submit" value="로그인"/>
<input type="button" value="취소" onclick="location.href='index'" />
</form>
<!--
이미지 경로
https://developers.kakao.com/tool/demo/login/login?method=dynamic
문서 경로 : https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#request-code
인가 코드 요청 URI :
/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code
-->
<a href="https://kauth.kakao.com/oauth/authorize?
client_id=${REST_API_KEY}&
redirect_uri=${REDIRECT_URI}&
response_type=code">
<img src="//k.kakaocdn.net/14/dn/btroDszwNrM/I6efHub1SN5KCJqLm1Ovx1/o.jpg"
width="111" alt="카카오 로그인 버튼" />
</a>
</c:when>
<c:otherwise>
${sessionScope.name }님 환영합니다.<br><br>
<a href="index">인덱스 페이지로 이동</a>
</c:otherwise>
</c:choose>
</body>
</html>
필자는 위와 같이 구성했다. a태그에 밑작업했을 때 발급받았던 REST API 키와 Redirect uri를 입력해주면 된다.
a태그 그대로 사용해도 좋고 궁금하면 경로도 남겨뒀으니 들어가서 확인해보면 좋을 듯
로그인 페이지에 진입했을 때 이다.
이제 인가 코드를 받아보도록 하자!!
MemberController.java
// 카카오 로그인 시작====================================================================
@GetMapping("kakaoLogin")
public String kakaoLogin(String code) {
System.out.println("인가 코드 : " + code);
}
// 카카오 로그인 도착====================================================================
a태그에 url이 GET방식의 url이다 보니 GetMapping을 사용하여 코드를 작성해 주었다.
변수 같은 것은 맘대로 설정해도 되냐?
라고 물어보면
글쎄요?
가급적이면 디벨롭퍼스에서 정해준 변수로 가져가야 오류가 나도 금방 캐치하지 않을까?? 필자는 그렇게 생각한다.
다 이렇게 명시되어 있으니 가급적 이것을 따라가도록 하자...
쨋든 카카오 로그인 버튼을 눌러보자.
이상이 없다면 url을 확인했을 때 a태그에 설정한 값이 그대로 받아간 것을 볼 수 있고 아까 미리보기 화면이 나왔다!!
여기서 체크하고 '동의하고 계속하기' 클릭해보자.
println을 주었기 때문에 위와 같이 인가코드가 잘 받아와 진것을 확인할 수 있다.
인가코드가 받아와졌다면 이제 토큰을 받아와야 한다.
인가코드만 있다고 카카오에 로그인이 되는 것은 아니다.
요청에 필요한 파라미터는 grant_type, client_id, redirect_uri, code 총 4개이다.
그 전에....의존성 하나만 주입하도록 하자..
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
Gson인데 아마 String데이터를 Json데이터로 변환하거나 아니면 반대작업을 많이 해야할 것이다.
KakaoService.java(토큰값 얻어오기)
package com.care.db.basic.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 java.util.HashMap;
import org.springframework.stereotype.Service;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
@Service
public class KakaoService {
public String getAccessToken(String code) {
String accessToken = "";
String reqURL = "https://kauth.kakao.com/oauth/token";
try {
String sendMessage = "grant_type=authorization_code" +
"&client_id=6a41b90998ff83b72f116f724f43c819"
+ "&redirect_uri=http://localhost:8085/db/kakaoLogin" + "&code=" + code;
URL url = new URL(reqURL); // POST 요청에 필요로 요구하는 파라미터 스트림을 통해 전송
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //이유? web의 요청이기 때문(web이면 여기까지의 코드가 동일함.) 연결 맺는 작업(28,29줄)
conn.setRequestMethod("POST"); // POST 요청을 위해 기본값 false에서 setDoOutput을 true로 변경
conn.setDoOutput(true); // POST 메소드를 이용해서 데이터를 전달하기 위한 설정, true일 경우 운영체제한테 데이터가 있으니 buffer를 봐라.
//자바프로그램과 OS(운영체제) 사이에는 Buffer라는 사이 중재자가 있음.
// 기본 outputStream을 통해 문자열로 처리할 수 있는 OutPutStreamWriter 변환 후 처리속도를 빠르게 하기위한
// BufferedWriter로 변환해서 사용한다.
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
bw.write(sendMessage);
//초기화 걸었다.
bw.flush();
int responseCode = conn.getResponseCode(); // 결과 코드가 200이라면 성공
System.out.println("responseCode : " + responseCode);
// responseCode : 200
// 요청을 통해 얻은 JSON타입의 Response 메세지 읽어오기
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = "", result = "";
while ((line = br.readLine()) != null) {
result += line;
}
//45~49줄 운영체제가 buffer에 담아준 데이터를 java프로그램으로 가져오는 작업
System.out.println("response body : " + result);
// System.out.println("result.split : " + result.split(","));
// response body : {"access_token":"eX3lELzLATzjEGuB7gddy54SByzVmLKicabAjwSiCj11mwAAAYQN0rbU",
// "token_type":"bearer","refresh_token":"Iy-Je-L7-NS-dBzS12_legQk7vHZioS-m_khxZo_Cj11mwAAAYQN0rbS",
// "expires_in":21599,"scope":"account_email profile_nickname","refresh_token_expires_in":5183999}
// Gson 라이브러리에 포함된 클래스로 JSON파싱 객체 생성
JsonElement element = JsonParser.parseString(result);
//JsonElement으로는 점(.)을 찍어 값을 가져올 수 없으니....
//Object로 형변환을 시켜준다.
accessToken = element.getAsJsonObject().get("access_token").getAsString();
System.out.println("access_token : " + accessToken);
// access_token : eX3lELzLATzjEGuB7gddy54SByzVmLKicabAjwSiCj11mwAAAYQN0rbU
br.close();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
return accessToken;
}
}
글씨가 작아서 안 보일거 같긴한데....대충 이러한 흐름으로 진행된다.
이렇게 토큰값까지 얻어왔다.
다음은 로그인을 한 사람의 정보를 얻어와 보고자 한다.
필자는 아까 얻어온 토큰값을 이용할 것이기 때문에 여기서 필요한건 Authorization이 필요하네
형식은.....??
Authorization : Bearer ${ACCESS_TOKEN}이니까... HashMap을 활용해야 겠다.
@Service
public class KakaoService {
public HashMap<String, String> getUserInfo(String accessToken) {
HashMap<String, String> userInfo = new HashMap<String, String>();
String reqURL = "https://kapi.kakao.com/v2/user/me";
try {
URL url = new URL(reqURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
// 요청에 필요한 Header에 포함될 내용
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
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);
JsonElement element = JsonParser.parseString(result);
// JsonObject properties = element.getAsJsonObject().get("properties").getAsJsonObject();
// String nickname = properties.getAsJsonObject().get("nickname").getAsString();
JsonObject kakao_account = element.getAsJsonObject().get("kakao_account").getAsJsonObject();
JsonObject profile = kakao_account.getAsJsonObject().get("profile").getAsJsonObject();
String nickname = profile.get("nickname").getAsString();
String email = kakao_account.get("email").getAsString();
userInfo.put("nickname", nickname);
userInfo.put("email", email);
} catch (IOException e) {
e.printStackTrace();
}
return userInfo;
}
}
그래서 결과를 확인해보면?
잘 출력되는 것을 볼 수 있다.
KakaoService.java(최종)
package com.care.db.basic.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 java.util.HashMap;
import org.springframework.stereotype.Service;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
@Service
public class KakaoService {
public String getAccessToken(String code) {
String accessToken = "";
String reqURL = "https://kauth.kakao.com/oauth/token";
try {
String sendMessage = "grant_type=authorization_code" +
"&client_id=6a41b90998ff83b72f116f724f43c819"
+ "&redirect_uri=http://localhost:8085/db/kakaoLogin" + "&code=" + code;
URL url = new URL(reqURL); // POST 요청에 필요로 요구하는 파라미터 스트림을 통해 전송
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //이유? web의 요청이기 때문(web이면 여기까지의 코드가 동일함.) 연결 맺는 작업(28,29줄)
conn.setRequestMethod("POST"); // POST 요청을 위해 기본값 false에서 setDoOutput을 true로 변경
conn.setDoOutput(true); // POST 메소드를 이용해서 데이터를 전달하기 위한 설정, true일 경우 운영체제한테 데이터가 있으니 buffer를 봐라.
//자바프로그램과 OS(운영체제) 사이에는 Buffer라는 사이 중재자가 있음.
// 기본 outputStream을 통해 문자열로 처리할 수 있는 OutPutStreamWriter 변환 후 처리속도를 빠르게 하기위한
// BufferedWriter로 변환해서 사용한다.
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
bw.write(sendMessage);
//초기화 걸었다.
bw.flush();
int responseCode = conn.getResponseCode(); // 결과 코드가 200이라면 성공
System.out.println("responseCode : " + responseCode);
// responseCode : 200
// 요청을 통해 얻은 JSON타입의 Response 메세지 읽어오기
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = "", result = "";
while ((line = br.readLine()) != null) {
result += line;
}
//45~49줄 운영체제가 buffer에 담아준 데이터를 java프로그램으로 가져오는 작업
System.out.println("response body : " + result);
// System.out.println("result.split : " + result.split(","));
// response body : {"access_token":"eX3lELzLATzjEGuB7gddy54SByzVmLKicabAjwSiCj11mwAAAYQN0rbU",
// "token_type":"bearer","refresh_token":"Iy-Je-L7-NS-dBzS12_legQk7vHZioS-m_khxZo_Cj11mwAAAYQN0rbS",
// "expires_in":21599,"scope":"account_email profile_nickname","refresh_token_expires_in":5183999}
// Gson 라이브러리에 포함된 클래스로 JSON파싱 객체 생성
JsonElement element = JsonParser.parseString(result);
//JsonElement으로는 점(.)을 찍어 값을 가져올 수 없으니....
//Object로 형변환을 시켜준다.
accessToken = element.getAsJsonObject().get("access_token").getAsString();
System.out.println("access_token : " + accessToken);
// access_token : eX3lELzLATzjEGuB7gddy54SByzVmLKicabAjwSiCj11mwAAAYQN0rbU
br.close();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
return accessToken;
}
public HashMap<String, String> getUserInfo(String accessToken) {
HashMap<String, String> userInfo = new HashMap<String, String>();
String reqURL = "https://kapi.kakao.com/v2/user/me";
try {
URL url = new URL(reqURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
// 요청에 필요한 Header에 포함될 내용
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
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);
// response body : {"id":2498109672,"connected_at":"2022-10-25T06:27:04Z",
// "properties":{"nickname":"상이태상"},
// "kakao_account":{"profile_nickname_needs_agreement":false,"profile":{"nickname":"상이태상"},
// "has_email":true,"email_needs_agreement":false,"is_email_valid":true,"is_email_verified":true,"email":"내 이메일"}}
JsonElement element = JsonParser.parseString(result);
// JsonObject properties = element.getAsJsonObject().get("properties").getAsJsonObject();
// String nickname = properties.getAsJsonObject().get("nickname").getAsString();
JsonObject kakao_account = element.getAsJsonObject().get("kakao_account").getAsJsonObject();
JsonObject profile = kakao_account.getAsJsonObject().get("profile").getAsJsonObject();
String nickname = profile.get("nickname").getAsString();
String email = kakao_account.get("email").getAsString();
userInfo.put("nickname", nickname);
userInfo.put("email", email);
} catch (IOException e) {
e.printStackTrace();
}
return userInfo;
}
public void unlink(String accessToken) {
String reqURL = "https://kapi.kakao.com/v1/user/unlink";
URL url;
try {
url = new URL(reqURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
// 요청에 필요한 Header에 포함될 내용
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
int responseCode = conn.getResponseCode();
System.out.println("responseCode : " + responseCode);
} catch (Exception e) {
e.printStackTrace();
}
}
}
MemberController.java(수정)
// 카카오 로그인 시작====================================================================
@Autowired private KakaoService kakaoService;
@GetMapping("kakaoLogin")
public String kakaoLogin(String code) {
System.out.println("인가 코드 : " + code);
String accessToken = kakaoService.getAccessToken(code);
HashMap<String, String> userInfo = kakaoService.getUserInfo(accessToken);
System.out.println("이메일 : " + userInfo.get("email"));
System.out.println("이름 : " + userInfo.get("nickname"));
session.setAttribute("accessToken", accessToken);
if(userInfo.isEmpty())
return "login";
return "index";
}
// 카카오 로그인 도착====================================================================
// 로그아웃 시작(카카오 로그아웃 추가)========================================================================
@RequestMapping("logout")
public String logout(Model model) {
String accessToken = (String)session.getAttribute("accessToken");
session.invalidate();
model.addAttribute("msg", "로그 아웃");
if(accessToken != null)
kakaoService.unlink(accessToken);
return "logout";
}
// 로그아웃 끝(카카오 로그아웃 추가)==========================================================================
혹시 선택사항을 체크하지 않아서 체크하지 않은 값이 null은 나오는데 로그아웃도 안되고 답답한 상황일때
네이버 검색창에 카카오계정이라고 검색후 밑에 있는 '카카오' 클릭
상단에 계정 이용 클릭
외부 서비스 전체보기 클릭
그러면 딱봐도 내가 만든 서비스가 보인다.
연결끊기를 클릭 후 비밀번호 입력하면 연결이 끊어진당
그 다음 모두 동의하면 원하는 값을 얻을 수 있을 것이다.
모바일에서는??
더보기에서 톱니바퀴 클릭
카카오계정 클릭
연결된 서비스 관리 클릭
외부 서비스에 이질적인 녀석이 하나 있다.
이질적인 녀석 클릭
마찬가지로 연결 끊기를 터치하고 비밀번호 입력하면 된다.
이건 카카오로 '리세마라'(좋은 유닛이나 카드가 나올 때까지 계속 초회뽑기 및 게임리셋을 반복하는 행위)를 많이 해본 사람이라면 아는 거겠지만....혹시나 모르는 사람도 있을테니 끄적여 보았다.
3. 결론
service 관해서 사실 이게 무슨 코드이고 무슨 코드인지 잘 모른다.
이게 어떻게 동작하는건가? 는 완전히 포기해버렸다
그냥 아하 이런 흐름이구나 만 알아도 나중에 이 게시물을 볼 때 좀 더 수월하지 않을까 라는 생각이 든다.
더불어 프로젝트 만들 때도 모르고 코드 갖다쓰는게 아니라 대충 알고 있는 상태로 복습하는 차원으로 사용할 수 있으리라는 기대를 갖고 있다.
'3층 1구역 - 개발의 장 > ajax통신' 카테고리의 다른 글
ajax - 다음 주소 API(2022-10-26) (0) | 2022.10.26 |
---|---|
ajax - ajax를 이용해 아이디 중복체크 하는 예제 (2022-10-24) (0) | 2022.10.25 |
ajax - 예제 java 및 Mapper코드 (2022-10-24) (0) | 2022.10.24 |
ajax - Json을 이용한 select태그를 사용하여 일부검색 (2022-10-24) (0) | 2022.10.24 |
ajax - Json을 이용한 제목! 일부검색 (2022-10-24) (0) | 2022.10.24 |