[OAuth 2.0] 카카오 로그인 Api 호출하기

이주훈·2023년 8월 13일
0

이번 포스팅에서는 앞서 공부했던 OAuth 2.0 기반의 카카오 로그인 Api를 직접 호출해볼것이다.

1. Spring boot 프로젝트 추가

  1. Api 호출을 Spring boot를 사용하여 호출할 것이다. 따라서 Spring 프로젝트를 생성한다.

    gradle을 사용할 예정이며 java, jdk17 버전을 사용할 예정이다. 사용할 라이브러리들을 추가하여 프로젝트를 생성한다.

2. 카카오 developers 추가

  1. 카카오 developer 사이트에 접속한다 링크
  2. 상단 메뉴 바에 있는 내 애플리케이션을 클릭한다.
  3. 아래의 +버튼을 클릭한다.
  4. 앱 이름과 사업자명을 입력한 후 저장을 클릭한다.
  5. 생성한 애플리케이션을 클릭 후 플랫폼 설정하기를 클릭한다.
  6. 웹 플랫폼 등록을 클릭한다.
  7. 서버 url을 입력 후 저장 버튼을 누른다.
    (필자는 로컬에서 서버를 가동할 예정이므로 로컬 주소를 입력했다.)
  8. 주소가 잘 들어갔는지 확인 후 하단에 등록하러 가기 링크를 클릭한다.
  9. 활성화 상태를 ON으로 설정한 후 Rediret URI 등록 버튼을 클릭한다.
  10. redirect uri 주소를 입력한다. (백앤드 개발자와 상의 후 주소를 입력한다.)
  11. url을 호출할 때 가져오고 싶은 정보에 대한 항목을 활성화한다. (활성화하지 않으면 정보를 가져올 수 없다.)
  12. 앱 키를 복사한 후 프로젝트에서 활용한다(노출 안 되게 조심해야한다.)

3. 프로젝트 설정

  1. 다운로드 받은 스프링 프로젝트 압축을 해제하고 오픈한다.(처음 오픈할 시 라이브러리를 다운로드 하는 시간이 소요된다.)
  2. application.yml 파일에 프로젝트 구성을 설정한다.(properties도 사용 가능하다.)
    필자는 mysql과 jpa를 사용할 것이기 때문에 두 개를 설정하였다.
    또한 api key를 노출이 안 되게 하기 위해 application-API-KEY.properties 파일에 따로 보관하였다. 이를 Spring 인식하기 위해 설정을 해주었다.
spring:
  profiles:
    include: API-KEY
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: 아이디
    password: 비밀번호
    url: jdbc:mysql://localhost:3306/oAuth?serverTimezone=UTC&characterEncoding=UTF-8

  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        format_sql: true

logging:
  level:
    org.hibernate.sql: debug
  1. 프로젝트를 위와 같이 설정한 후 Controller 패키지를 생성 후 kakaoLoginController를 생성했다.
    어노테이션을 추가한 후 카카오 api 키를 KAKAO_CLIENT_KEY 필드에 저장한 후 Model에 담아서 home.html에 뿌려주었다.
package com.example.loginApiStudy.Controller;

import com.example.loginApiStudy.Service.KakaoService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequiredArgsConstructor
public class kakaoLoginController {
    private final KakaoService kakaoService;

    @Value("${kakao-client-id}")
    private String KAKAO_CLIENT_KEY;

    @GetMapping("/")
    public String home(Model model) {
        model.addAttribute("apiKey", KAKAO_CLIENT_KEY);
        return "home";
    }
}
  1. model에 저장된 api-key를 사용해 링크를 눌렀을 때 카카오 인증 서버로 파라미터를 넣어 전송한다.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a th:href="@{https://kauth.kakao.com/oauth/authorize(response_type='code',client_id=${apiKey}
,redirect_uri='http://localhost:8080/login/kakao')}">
    Kakao 로그인
</a>
</body>
</html>
  1. Service 패키지를 생성 후 KakaoService 클래스를 작성한다.
    getKakaoAccessToken 메서드와 receiveKakaoUser 메서드를 작성한다.
    getKakaoAccessToken 메서드는 유저의 resource에 접근할 수 있는 토큰을 발급받는 메서드이다.
    receiveKakaoUser 메서드는 유저의 resource를 가져오는 메서드이다.
package com.example.loginApiStudy.Service;

import com.example.loginApiStudy.KakaoDTO;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

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.net.http.HttpHeaders;
import java.util.List;

@Service
@RequiredArgsConstructor
public class KakaoService {
    String access_Token="";
    String refresh_Token ="";
    String authorizeURL = "https://kauth.kakao.com/oauth/token";
    String resourceURL = "https://kapi.kakao.com/v2/user/me";

    @Value("${kakao-client-id}")
    private String KAKAO_CLIENT_KEY;
    public String getKakaoAccessToken(String code) throws Exception {
        if (code == null) throw new Exception("Faild get code");

        try {
            URL url = new URL(authorizeURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            // POST 요청 설정
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);

            // URL 파라미터 값 설정
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
            StringBuilder sb = new StringBuilder();
            sb.append("grant_type=authorization_code");
            sb.append("&client_id=" + KAKAO_CLIENT_KEY); // TODO REST_API_KEY 입력
            sb.append("&redirect_uri=http://localhost:8080/login/kakao"); // TODO 인가코드 받은 redirect_uri 입력
            sb.append("&code=" + code);
            bw.write(sb.toString());
            bw.flush();

            // 요청 결과 코드 확인
            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);

            // JSON 파싱을 통해 Access Token 및 Refresh Token 추출
            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();

            return access_Token;
        } catch (Exception e) {
            throw new Exception("API call failed");
        }
    }


    public void receiveKakaoUser(String token) throws Exception {
        if(token == null) throw new Exception("failed get Id_Token");
        try {
            URL url = new URL(resourceURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setRequestProperty("Authorization", "Bearer " + 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;
            }
            String[] str = result.split(",");
            for (String i: str) {
                System.out.println(i);
            }
            System.out.println("response body : " + result);

            //Gson 라이브러리로 JSON파싱
            JsonParser parser = new JsonParser();
            JsonElement element = parser.parse(result);

            Long id = element.getAsJsonObject().get("id").getAsLong();
            boolean hasEmail = element.getAsJsonObject().get("kakao_account").getAsJsonObject().get("has_email").getAsBoolean();
            String email = "";
            if (hasEmail) {
                email = element.getAsJsonObject().get("kakao_account").getAsJsonObject().get("email").getAsString();
            }
            String nickname = element.getAsJsonObject().get("properties").getAsJsonObject().get("nickname").getAsString();
            System.out.println("id : " + id);
            System.out.println("email : " + email);
            System.out.println("nickname : " + nickname);

            br.close();

        } catch (Exception e) {
            throw new Exception("API call failed");
        }

    }
}
  1. 설정한 redirect url에 형식에 맞춰 컨트롤러에 메서드를 추가한다.
    코드를 잘 전달 받았는지 확인 후 의존성 주입을 통해 kakaoService에 있는 getKakaoAccessToken 메서드를 호출한다.
    getKakaoAccessToken을 통해 전달받은 인증 토큰을 receiveKakaoUser 메서드에 넘겨준 후 유저의 정보를 가져온다.
    @GetMapping("/login/kakao")
    public void kakaoCallBack(@RequestParam String code) throws Exception {
        System.out.println(code);
        String kakaoAccessToken = kakaoService.getKakaoAccessToken(code);
        kakaoService.receiveKakaoUser(kakaoAccessToken);
    }

4. 결과

호출이 잘 된 것을 확인할 수 있었다.


5. 소감

api 응답과 호출 과정을 공부하고 직접 호출을 해보면서 http의 헤더 부분을 공부할 수 있었다. 우리가 쓰던 html은 body 부분일 뿐이었고, header는 지금까지 알고 있던 것보다 더 다양한 역할을 하고 있던 것을 확인할 수 있었다. 코드를 짜는 것도 당연히 중요하지만 시스템 아키택처와 CS를 열심히 공부해야겠다고 생각했다. 다음 포스팅에서는 호출한 정보를 데이터베이스에 저장시킬 것이다.

profile
코딩 잘하고 싶다..

0개의 댓글