본문 바로가기
OAuth/Kakao Login API

[Kakao Login API] 카카오 로그인 API 서비스 구현 (Spring Boot 환경에서 카카오 로그인 API RESTful방식으로 연동하기 -3장)

by 임채훈 2019. 3. 22.

2019/03/23 - [OAuth/Kakao Login API] - [Kakao Login API] 카카오 계정의 유저 정보 받아오기 및 마무리 (Spring Boot 환경에서 카카오 로그인 API RESTful방식으로 연동하기 -4장 마무리) (0)

2019/03/22 - [OAuth/Kakao Login API] - [Kakao Login API] 프로젝트 생성 및 기본 구성 셋팅 (Spring Boot 환경에서 카카오 로그인 API RESTful방식으로 연동하기 -2장)

2019/03/22 - [OAuth/Kakao Login API] - [Kakao Login API] 카카오 로그인 API 사용 준비하기 (Spring Boot 환경에서 카카오 로그인 API RESTful방식으로 연동하기 -1장)



관련글 - Google Login API Spring Boot 환경에서 구현하기

2020/10/18 - [OAuth/Google Login API] - [Google Login API] Google APIs 신규 프로젝트 생성 및 개발환경 구성 (Spring Boot 레퍼런스를 보면서 구현해보는 구글 소셜 로그인 REST API - 1)





이전장에 이어서 실제로 카카오 로그인 API를 이용할 수 있도록 서비스를 구현해보겠습니다.




1. 먼저 카카오 개발자센터에서 REST API 개발가이드를 읽어봐야 됩니다.


- 아래의 내용을 대충 요약하자면 로그인 버튼을 클릭하면 어떠한 경로로부터 인증 코드 발급받아야 된답니다.





2. 다음과 같은 URL에 필수 파라미터를 포함해서 요청을 하라는것 같습니다.


- 쉽게 말해서 https://kauth.kakao.com/oauth/authorize의 경로에 파라미터로 client_id, redirect_uri, response_type을 실어서 보내라는 말입니다.





3. 위의 URL을 통해 인증 코드를 요청하면 아래와 같이 우리가 개발자 센터에서 앱을 만들때 입력했던 Redirect_uri로 이동하면서 code라는 key값으로 인증코드를 파라미터로 넘겨줍니다.




4. 위의 내용을 참고해서 index.jsp파일 작성


- index.jsp파일에서 카카오 로그인 버튼을 클릭했을때 요구하는 경로에 요구하는 파라미터를 담은 링크로 이동하도록 a 태그의 href속성에 링크 작성


1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
    <c:if test="${userId eq null}">
        <a href="https://kauth.kakao.com/oauth/authorize
            ?client_id=b5f85af25d1bdf961d4f2016bafe3c6e
            &redirect_uri=http://localhost:8000/login
            &response_type=code">
            <img src="/img/kakao_account_login_btn_medium_wide_ov.png">
        </a>
    </c:if>
    <c:if test="${userId ne null}">
        <h1>로그인 성공입니다</h1>
    </c:if>
</body>
cs




5. 카카오 로그인 버튼을 클릭했을때의 결과창으로써 이메일 정보제공에 체크를하고 동의를 합니다.





6. 동의를 눌렀을때 다음과 같은 경로로 이동을 합니다.


- 카카오 개발자 센터의 내용대로 우리가 지정해준 Redirect 경로로 code를 실어서 이동하는것을 확인할 수 있습니다. 그럼 우리는 저 코드를 받아서 다음 절차를 밟으면 되는겁니다.





7. 설정해준 /login 요청을 받을 컨트롤러 메소드에서 API인증을 통해 얻어낸 code의 값 받아오기



- com.personal.kakaoLogin.controller.HomeController.java


1
2
3
4
5
@RequestMapping(value="/login")
public String login(@RequestParam("code"String code) {
    System.out.println("code : " + code);
    return "index";
}
cs



- 작성 후 카카오 로그인 버튼 클릭 후 콘솔창의 결과화면





8. 성공적으로 받아온 코드로 사용자 토큰(Access Token, Refresh Token) 받기


- Access Token : 사용자가 로그인 인증에 성공했을시 부여되는 수명이 하루가 되지않는 토큰입니다.

- Refresh Token : 사용자가 로그인 인증에 성공했을시 부여되는 수명이 보통 적게는 2주에서 길게는 한달정도 되는 토큰이고, Access Token의 수명이 만료되었을때 Access Token을 갱신하는 목적으로 사용되는 토큰이며, 그 특성상 일반적으로 서버측 DB에 보관해두거나 클라이언트 측 쿠키에 보관을 하는 토큰입니다.



- 이제 받아온 인증 코드를 통해 어떻게 Access Token 및 Refresh Token을 받아오는지 다시 개발가이드를 확인해보겠습니다.



1. 이전에 인증코드 받는 방법을 설명해둔것과 동일하게 설명이 되어있습니다.

2. https://kauth.kakao.com/oauth/token의 경로로 필수로 요구하는 파라미터를 포함해서 POST방식으로 요청을 하라는 설명입니다.

3. Request를 통한 Response Body는 JSON객체로 넘어온답니다.

4. 그럼 아래의 설명을 참고해 코드를 작성해보도록 하겠습니다.






9. JSON객체 파싱에 유용한 Gson 외부 라이브러리 Dependency 추가



- pom.xml


1
2
3
4
5
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
</dependency>
cs




10. 인증 코드로 Access_Token을 받아올 작업을 수행할 Service클래스 생성 및 getAccessToken메소드 작성


- 디렉토리 구조




- com.personal.kakaoLogin.service.KakaoAPI.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.personal.kakaoLogin.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 org.springframework.stereotype.Service;
 
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
 
@Service
public class KakaoAPI {
    
    public String getAccessToken (String authorize_code) {
        String access_Token = "";
        String refresh_Token = "";
        String reqURL = "https://kauth.kakao.com/oauth/token";
        
        try {
            URL url = new URL(reqURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            
            //    POST 요청을 위해 기본값이 false인 setDoOutput을 true로
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            
            //    POST 요청에 필요로 요구하는 파라미터 스트림을 통해 전송
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
            StringBuilder sb = new StringBuilder();
            sb.append("grant_type=authorization_code");
            sb.append("&client_id=b5f85af25d1bdf961d4f2016bafe3c6e");
            sb.append("&redirect_uri=http://localhost:8000/login");
            sb.append("&code=" + authorize_code);
            bw.write(sb.toString());
            bw.flush();
            
            //    결과 코드가 200이라면 성공
            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);
            
            //    Gson 라이브러리에 포함된 클래스로 JSON파싱 객체 생성
            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();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        
        return access_Token;
    }
}
 
cs




11. Controller 클래스에서 방금 전 작성한 KakaoAPI클래스의 getAccessToken메소드 호출


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.personal.kakaoLogin.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
 
import com.personal.kakaoLogin.service.KakaoAPI;
 
@Controller
public class HomeController {
 
    @Autowired
    private KakaoAPI kakao;
    
    @RequestMapping(value="/")
    public String index() {
        
        return "index";
    }
    
    @RequestMapping(value="/login")
    public String login(@RequestParam("code"String code) {
        String access_Token = kakao.getAccessToken(code);
        System.out.println("controller access_token : " + access_Token);
        
        return "index";
    }
}
cs




12. 카카오 로그인 요청 후 콘솔창의 결과





위와 같은 결과가 나온다면 클라이언트로부터 카카오 로그인 요청을 받아 카카오 API로부터 인증코드를 부여받아 그 인증코드를 이용해서 성공적으로 사용자 토큰 (Access Token, Refresh Token)을 얻어냈습니다.


다음장에서는 발급받은 Access Token을 이용해 우리가 직접 회원의 정보로 사용해야될 이메일, 닉네임 등과 같은 정보를 받아오는 기능을 구현해보도록 하겠습니다.



댓글17

  • 익명 2019.09.21 22:08

    비밀댓글입니다
    답글

  • pyc 2020.02.20 16:06

    responsecode 에서 400에러가 나면 어떻게 해결해야하나요??
    답글

  • 우영 2020.05.27 16:03

    저도 400에러 발생합니다..
    답글

  • Favicon of http://tvple.me/tv/학려화정 BlogIcon 학려화정 2020.06.02 14:24

    잘 배우고 갑니다
    답글

  • Favicon of http://local BlogIcon 개발자 2020.06.10 16:23

    저도 새로고침 시 responsecode 에서 400에러가납니다 해결방법 공유 부탁드려요 ㅜㅜ
    답글

  • 익명 2020.06.13 19:15

    비밀댓글입니다
    답글

  • dudwl7676@naver.com 2020.06.13 19:27

    responsecode에서 400에러가 납니다 ㅠㅠ 해결방법이 뭐가있을까요?
    답글

  • 지나가는사람 2020.07.09 13:58

    400에러나면 KakaoAPI 에서 redirect_uri 확인하세요 포트번호랑 주소랑... 저는 그것때문에 400떳었어요
    답글

  • Umsal 2020.09.09 18:34

    401 에러가 나는데 혹시 이유를 아시나요?
    답글

  • 익명 2020.09.14 17:11

    비밀댓글입니다
    답글

  • 이정환 2020.11.22 19:58

    안녕하세요. 동의를 누르면 index로 돌아가되 code를 가지고 가는데 저는 동의를 누르면 주소창에 코드는 갖고있는데 index창이 아닌
    // Whitelabel Error Page
    This application has no explicit mapping for /error, so you are seeing this as a fallback.

    Sun Nov 22 19:50:15 KST 2020
    There was an unexpected error (type=Not Found, status=404).
    No message available //
    이게 뜨네요 왜그런지 알수있을까요?ㅠㅠ
    답글

  • Favicon of https://jino-dev-diary.tistory.com BlogIcon 고소하게 2022.01.11 14:31 신고

    안녕하세요. 내용 잘 봤습니다.

    sb.append("grant_type=authorization_code");
    sb.append("&client_id=b5f85af25d1bdf961d4f2016bafe3c6e");
    sb.append("&redirect_uri=http://localhost:8000/login");
    sb.append("&code=" + authorize_code);

    이 부분을 저는
    HashMap에 담은 후에 Json으로 파싱해서 writer.write(json.toString())으로 요청을 보냈는데
    401 에러가 나오더라구요.

    이유를 도저히 모르겠어서 찾아보다가 저 방법대로 해보니까 잘 되네요
    감사합니다.
    답글

  • ㅇㅇ 2022.06.02 11:46

    401에러 아시는 분 있나요?
    답글