[토이프로젝트]당근마켓 구현하기(6) - Kakao API 적용하기

gamja·2022년 11월 16일
0
post-thumbnail

저번 글에서는 Kakao API 사용을 위한 기본 설정을 해보았다. 이번 글에서는 마지막 단계인 구현하기를 포스팅 할 것이다.

로그인

kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code
위의 주소에 접속한다. { }는 빼고 입력하기
그럼 아래처럼 code를 redirect URI로 전달해준다.

만약 사용자가 동의하지 않은 동의 항목에 대한 추가 동의를 요청하고 싶다면 아래와 같이 파라미터를 추가해줘야 한다.
인가 코드 받기 요청 시 scope 파라미터로 추가 동의받을 항목의 ID 목록을 지정해야 한다. 응답으로 받은 인가 코드로 토큰 받기를 요청해 카카오 로그인을 완료한 뒤, 이후 새로 발급받은 토큰을 사용한다. 아래는 scope에 이메일, 성별의 추가 동의를 요청하는 예제이다. 참고 하기!
https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=account_email,gender

  • redirect_uri : 지난 글에서 살펴본 카카오 Redirect_url
  • REST_API_KEY : 지난 글에서 살펴본 카카오 REST_API 키

login.html

<!-- 일반 회원가입 -->
<a class="w-100 btn btn-lg btn-outline-black mt-5" type="button" th:href="@{/account/signup}">회원가입</a>

<!-- 카카오 로그인 -->
<a class="p-2" href="https://kauth.kakao.com/oauth/authorize?client_id=3b9ab37490257b10f89f741a7912ceb7&redirect_uri=http://localhost:8080/oauth/kakao&response_type=code&scope=account_email, profile_nickname">
    <img class="rounded mx-auto d-block" th:src="@{https://developers.kakao.com/tool/resource/static/img/button/login/full/ko/kakao_login_medium_narrow.png}" alt="" style="height:60px"/>
</a>

나는 email이랑 nickname도 함께 받기 위해서 파라미터에 scope=account_email, profile_nickname 을 추가해줬다.

Controller


@Controller
@RequestMapping(value = "/oauth")
public class KakaoLoginController {
    /**
     * kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code
     * 주소로 이동하면 파라미터에 code가 담겨 oauth/kakao 페이지로 이동이 된다.
     *
     * 카카오 callback
     * [GET] /oauth/kakao/callback
     */
    @Autowired
    private KakaoService kakaoService;

    @GetMapping("/kakao")
    public String getCI(@RequestParam String code, Model model, HttpSession session) throws IOException {
        // 토큰 얻기
        String access_token = kakaoService.getToken(code);

		// 토큰으로 로그인 정보 가져오기
        Map<String, Object> userInfo = kakaoService.getUserInfo(access_token);

        session.setAttribute("access_token", access_token); // access_token을 이용해서 언제든 사용자 데이터를 가져올 수 있다.
        session.setAttribute("userInfo", userInfo);

        //ci는 비즈니스 전환후 검수신청 -> 허락받아야 수집 가능
        return "/home";
    }

인가코드로 토큰 받기 - KakaoService

kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code
위에 코드를 만들고 post로 전달하는 과정이다.

구현 과정
1. connection 생성
2. POST로 보낼 Body 작성
3. 받아온 결과 JSON 파싱 (Gson)

package daangnmarket.daangntoyproject.user.service;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

@Service
public class KakaoService {
    public String getToken(String code) throws IOException {
        // 인가코드로 토큰받기
        /*
        kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code
        위에 코드를 만들고 post로 전달하는 과정이다.
            구현 과정
        1. connection 생성
        2. POST로 보낼 Body 작성
        3. 받아온 결과 JSON 파싱 (Gson)
         */

        // 1. connection 생성
        String host = "https://kauth.kakao.com/oauth/token";
        URL url = new URL(host);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        String token = "";
        try {
            // POST 요청을 위해 기본값이 false인 setDoOutput을 true로 해준다.
            urlConnection.setRequestMethod("POST");
            urlConnection.setDoOutput(true);        // 데이터 기록 알려주기

            // 2. post로 보낼 바디 작성(POST 요청에 필요로 요구하는 파라미터 스트림을 통해 전송)
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream()));
            StringBuilder sb = new StringBuilder();
            sb.append("grant_type=authorization_code");
            sb.append("&client_id=3b9ab37490257b10f89f741a7912ceb7");       // TODO REST_API_KEY 입력
            sb.append("&redirect_uri=http://localhost:8080/oauth/kakao");   // TODO 인가코드 받은 redirect_uri 입력
            sb.append("&code=" + code);

            bw.write(sb.toString());
            bw.flush();

            // 결과 code가 200이면 성공
            int responseCode = urlConnection.getResponseCode();
            System.out.println("responseCode = " + responseCode);

            // 3. 받아온 결과 JSON으로 파싱
            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 라이브러리에 포함된 클래스로 json parsing 객체 생성
            JSONObject elem = (JSONObject) new JSONParser().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;
    }

사용자 정보 가져오기 - KakaoService

   // 사용자 정보 가져오기
   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<>();

        // Access_token을 이용하여 사용자 정보 조회
        try {
            URL url = new URL(host);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

            urlConnection.setRequestProperty("Authorization", "Bearer " + access_token);
            urlConnection.setRequestMethod("GET");

            // 받아온 결과가 200이면 성공
            int responseCode = urlConnection.getResponseCode();
            System.out.println("responseCode = " + responseCode);


            //요청을 통해 얻은 JSON타입의 Response 메세지 읽어오기
            BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String line = "";
            String res = "";

            while((line=br.readLine())!=null) {
                res+=line;
            }

            System.out.println("res = " + res);

            // JSON 라이브러리로 JSON 파싱
            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 email = kakao_account.get("email").toString();
            String nickname = properties.get("nickname").toString();


            result.put("id", id);
            result.put("email", email);
            result.put("nickname", nickname);

            br.close();


        } catch (IOException | ParseException e) {
            e.printStackTrace();
        }

        return result;
    }

길어보이지만 service 부분 차근차근 읽어보면 이해할 수 있다.


로그아웃

Logout Redirect URI 설정

common.html

<a type="button" class="btn btn-outline-white me-2" th:if="${not #strings.isEmpty(session.access_token)}" th:href="@{/logout}">Logout</a>    
  • th:if="${not #srtings.isEmpty()}" : 만약 해당 데이터가 있으면 true를 반환한다.
  • th:if="${not #srtings.isEmpty()}" : 만약 해당 데이터가 없으면 true를 반환한다.

HomeController

  @RequestMapping(value = "/logout")
    public String logout(HttpSession session){
        System.out.println("logout 시작");
        kakaoService.kakaoLogout((String)session.getAttribute("access_Token"));
        session.removeAttribute("access_token");
        session.removeAttribute("userInfo");
        System.out.println("session 삭제 완료");
        return "/home";
    }

저장해두었던 session 기록을 삭제해준다. 반드시 access_token 뿐만 아니라 모든 데이터를 session에서 삭제해줘야 한다.

KakaoService

 public void kakaoLogout(String access_Token) {
        String reqURL = "https://kakao.api.com/user/logout";
        try {
            URL url = new URL(reqURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Authorization", "Bearer " + access_Token);

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

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

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

            while ((line = br.readLine()) != null) {
                result += line;
            }
            System.out.println("Logout 결과 : " + result);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

참고 블로그
https://suyeoniii.tistory.com/79
https://velog.io/@dktlsk6/Spring%EC%9C%BC%EB%A1%9C-%EC%B9%B4%EC%B9%B4%EC%98%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

profile
눈도 1mm씩 쌓인다.

0개의 댓글