17주차 - Spring (24) 카카오 로그인을 구현해보자 - 2

2022. 10. 26. 22:25Spring framwork

이번의 목표

01. 카카오 인가 코드를 받아보자.

02. 토큰을 받아보자.

03. 사용자 정보를 받아서 세션에 저장하자


지난 시간에는 카카오 로그인을 하기 위한 사전 준비작업을 마쳤습니다.

이번에는 실제 카카오 로그인 코드를 작성해보겠습니다.

 

01. 카카오 인가 코드를 받아보자

'문서'->'카카오 로그인' -> 'REST API' 에서 카카오 로그인의 인가 코드를 받는 방법에 대한 설명이 있습니다.

위의 내용을 보면 인가 코드를 받기 위해서는 기본 정보에 적혀있는 코드로 연결되어야합니다.

이때 코드에 필요한 값으로 REST_API_KEY, REDIRECT_URI가 필요합니다.

이 두 코드는 이전 시간에 체크를 해두었으므로 이를 사용합니다.

 

실제 스프링에서 코드 사용 예시는 아래와 같습니다.

	<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>

이때 저 <img> 의 src는 카카오 디벨로퍼에 있는 미리보기를 그대로 사용했습니다.

'도구'->'JS SDK데모'->'카카오 로그인' -> '로그인'에 접속하면 찾아볼 수 있습니다.

카카오 디벨로퍼에서 로그인 아이콘 예시 코드 경로

 

카카오에서 지원하는 표준 디자인에서 간단하게 변경이 가능합니다.

변경된 아이콘을 사용하고자 한다면 '도구'->'리소스 다운로드'->'카카오 로그인' 에서 사용이 가능합니다.

실행 화면은 아래와 같습니다.

 

이렇게 하여 인가 코드를 받는것까지가 완료 되었습니다.

 

02.토큰을 받아보자.

카카오에서 토큰을 받기위해 연결할 url은 아래와 같습니다.

이때 전달해야할 파라미터는 아래와 같습니다.

위의 목록에서 client_secret을 제외한 나머지는 Required로 필수항목임을 알 수 있습니다.

response에서 받을 수 있는 값은 위의 목록과 같습니다.

token_type, access_token, expires_in, refresh_token은 필수 항목이고 id_token은 제품 설정에서 OpenID Connect를 설정함으로써 얻을 수 있습니다.

 

저는 카카오에서 토큰을 받는 기능만을 담당할 kakaoService 라는 클래스를 하나 만들어 사용했습니다.

코드는 아래와 같습니다.

// 매개변수가 인가 코드
public String getAccessToken(String code) {
    String accessToken = "";

    // 요청할 URL의 주소
    String reqURL = "https://kauth.kakao.com/oauth/token";
    try {
        String sendMessage = "grant_type=authorization_code" + 
                "&client_id=${REST_API_KEY}"
            + "&redirect_uri=${REDIRECT_URI}" + "&code=" + code;
        // client_id, redirect_uri가 틀리는 경우가 비일비재하니 조심하자.

        //URL 객체의 생성자에는 url주소가 필요하다.지금은 요청하고자 하는 url이 위의 reqURL이다.
        URL url = new URL(reqURL); // POST 요청에 필요로 요구하는 파라미터 스트림을 통해 전송

        // openConnection()을 사용하여 url주소로 연결 요청
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        // POST로 보낼것인지 GET으로 보낼것인지
        conn.setRequestMethod("POST"); // POST 요청을 위해 기본값 false에서 setDoOutput을 true로 변경

        // output할 데이터가 있는가? 있다면 true, 없다면 false. 하지만 디폴트가 false이므로 없다면 작성하지 않아도 무방.
        conn.setDoOutput(true);// POST 메소드를 이용해서 데이터를 전달하기 위한 설정

        // 기본 outputStream을 통해 문자열로 처리할 수 있는 OutPutStreamWriter 변환 후 처리속도를 빠르게 하기위한
        // BufferedWriter로 변환해서 사용한다.
        // url -> HttpURLConnection -> outputStreamWriter -> BufferedWriter 로 연결 
        // 지금까지의 과정은 결국 url의 링크로 데이터를 전송하기 위함이다.
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
        // BufferedWriter의 매개변수는 Writer인데, Writer는 추상 클래스이기 때문에 자식 클래스가 이를 받아 사용한다.
        // ~~Writer 객체들은 Writer의 자식 클래스임을 유추할 수 있다.

        bw.write(sendMessage); // buffer에 sendMesagge의 전달
        bw.flush(); // buffer의 초기화.

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

        String line ="", result ="";
        while((line=br.readLine())!=null) { // BufferdReader의 readLine() 메서드는 더이상 읽을 것이 없을때 null을 반환
            result += line;
        }

        System.out.println("result : " + result);

        // 이렇게 받아온 값을 확인해보면 access_token 키의 값으로 데이터가 저장 되어있다. 이것이 필요하기 때문에 이를 저장한다.
        JsonElement element =  JsonParser.parseString(result);
        accessToken = element.getAsJsonObject().get("access_token").getAsString();

        // Buffer와 연결되는 두 객체를 close한다.
        br.close();
        bw.close();


    } catch (Exception e) {
        e.printStackTrace();
    }

    return accessToken;
}

 

이렇게 return값으로 반환된 accessToken을 컨트롤러에서 세션에 저장하는 것으로 카카오로 로그인을 했음을 확인할 수 있었습니다.

 

03. 사용자 정보를 받아서 세션에 저장하자

이번에는 카카오 사용자 정보를 가져와서 이를 세션에 저장하는 코드를 작성해보겠습니다.

사용자 정보를 가져오기 위해서 연결될 url은 아래와 같습니다.

여기서는 ACCESS_TOKEN을 사용하는 방법과 APP_ADMIN_KEY를 사용하는 방법이 있습니다.

위에서 저는 ACCESS_TOKEN을 저장했기 때문에 이 방법을 사용하겠습니다.

 

++여기서 첫번째 줄과 Host가 뜻하는 의미는

Host에 GET 또는 POST 방식으로 /v2/user/me (첫번째줄) 로 연결하라는 의미입니다.

ex) https://kapi.kakao.com/v2/user/me

토큰을 받을 때에도 host 주소에 첫번째 줄의 주소로 연결 했습니다.

 

 

엑세스토큰을 사용하여 연결할때에는 request의 header에 Authorization의 정보가 입력되어야 하며 이때 필요한 것이 ACCESS_TOKEN입니다.

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);

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

        String line ="", result = "";
        while((line = br.readLine()) != null) {
            result += line;
        }

        JsonElement element =  JsonParser.parseString(result);
        String id = element.getAsJsonObject().get("id").getAsString();

        JsonObject kakao_account = element.getAsJsonObject().get("kakao_account").getAsJsonObject();
        JsonObject profile = kakao_account.get("profile").getAsJsonObject();
        String name = profile.get("nickname").getAsString();
        String email = kakao_account.get("email").getAsString();
        String image = profile.get("profile_image_url").getAsString();

        userInfo.put("name", name);
        userInfo.put("email", email);

        br.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return userInfo;
}

 

위에서 BufferReader를 통해 받아온 정보를 result에 받고 result의 값을 출력해보면 데이터가 키:값의 형태를 가지고 있는것을 확인할 수 있습니다.

그렇기 때문에 JsonObject를 사용하여 이를 받아 HashMap에 담고 반환했습니다.

이 데이터를 바탕으로 컨트롤러에서는 세션에 저장하는 것으로 마무리가 되었습니다.