Spring으로 카카오 로그인 구현하기

정명진·2022년 4월 6일
0

Spring으로 카카오 로그인 REST API 방식으로 구현하기

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'

진행할 전체적인 순서는 아래와 같습니다.

  1. 카카오 developer에서 API Key 발급받기.
  2. 플랫폼 설정(사이트 도메인, Redirect URI 등록)
  3. 구현하기

1. 카카오 developer에서 API Key 발급받기.

우선 카카오 devloper에 접속후 로그인을 합니다. https://developers.kakao.com/
로그인을 하고 나면 오른쪽 상단에 "내 애플리케이션" 항목이 보입니다. 이를 클릭하여 줍니다.
카카오 로그인에 사용할 테스트 애플리케이션을 만들어 주기 위해 애플리케이션 추가하기 버튼을 눌러준후 정보를 입력합니다.(이미 애플리케이션이 존재할 시 생략 가능)

이제 생성한 애플리케이션을 누르면 API 호출에 필요한 Key를 사용할 수 있습니다.

이글은 REST API 기준이기 때문에 "REST API키"를 사용하도록 하겠습니다.

2. 플랫폼 설정(사이트 도메인, Redirect URI 등록)

이제는 사이트 도메인을 등록하고 인가코드를 받을 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";
    }

}

결과

profile
개발자로 입사했지만 정체성을 잃어가는중... 다시 준비 시작이다..

0개의 댓글