백앤드(스프링)

카카오 로그인 구현하기

유승혁 2022. 7. 31. 19:54

 요새 방학간에 어플 개발을 하니라고 블로그를 너무 소홀했다. 스프링 공부를 스스슥하고 어플에 적용하니라고 그랬는데 그래도 블로그에 정리 해야지^^. 어플 만들고 똑같은거 다시 구현할 때 그때 배웠던 부분을 다시 검색할 수는 없는 노릇이니.

 

1. 카카오 로그인

 카카오 로그인 방식은 유튜브나 블로그에 잘 정리되어 있고 정 안되면 카카오 문서를 보면 잘 나와 있으니 대충... 하핫. 내 구현 방식을 보여주는게 오히려 나아 보입니당.

 

2. 사전 지식

 OAuth 로그인들의 공통 사항들이다. 사실 우리가 직접 아이디, 비밀번호 테이블을 만들어서 할 수 있지만, 해결하기 어려운 보안 문제들도 많을 것이고 고객 입장에서의 신뢰성에도 어려움을 겪을만 하다. 유저 입장에서도 사이트마다 아이디 비번 만들기도 귀찮아서 계속 똑같은 거로 만들다 보면 다른 사이트에서 뚤려버리면 우리 사이트도 위험해 질 수 있다.

 그렇게 우리는 OAuth를 사용한다. 어떤 방식이냐하면, 유저가 우리 페이지에서 로그인 버튼을 누름과 동시에 우리가 카카오 개발자 페이지에 등록한 어플리케이션 키(api key)와 응답받을 주소를 HTTP에 담아 카카오 로그인 쪽으로 넘겨준다. 그러면!! 우리는 등록한 redirect url로 임의의 코드를 받는다. 우리는 이 코드를 받음으로써 아! 사용자가 카카오에 계정이 있구나를 알 수 있다.아무튼 이 코드를 써서 하나의 토큰을 받아올 수 있다.즉 랜덤한 값(12ejioqskdna.lm3p9rh와 같은 쏼라쏼라)을 받는다. 

 이 토큰은 우리가 카카오 디비에 있는 해당 클라이언트 정보를 조회할 수 있는 권한으로 작동된다.

 우리는 이 토큰과 함께 필요한 정보를 카카오 디비 서버에게 요청을 하면 우리가 원하는 값들을 쇽쇽 받을 수 있다. 물론 유저가 동의한 항목에 대해서만 제한이 된다.

 

3. 기본 세팅

 일단 kakoDeveloper 페이지에 들가서 문서 등록하고 뭐 리다이렉션 url 세팅 이런거는 다른 곳에 가서 알아봐. 쉬우니깐. 

 나도 그러한 방식으로 했고 기본 세팅에 필요한건 등록한 어플리 케이션의 api key 와 응답을 받을 url 이다. 다음과 같이 그러한 정보들을 따로 interface 코드로 만들었다.

public interface LoginArgs{
    String redirect_uri = "http://localhost:8080/login";
    String api_key = "비밀이지렁";
    String grant_type = "authorization_code";
    String get_token_uri = "https://kauth.kakao.com/oauth/token";
    String get_profile_uri = "https://kapi.kakao.com/v2/user/me";
    String kakao_auth = "https://kauth.kakao.com/oauth/authorize";
}

 위에 보이듯이 나는 /login에서 토큰이라던지 kakao id를 응답받을 예정이다.

 

4. 컨트롤러

@GetMapping("/login")
@ResponseBody
public String login(@RequestParam("code")String code) throws JsonProcessingException {
    Long kakaoKey = loginService.kakoLogin(code);

	return kakaoKey.toString();
}

눈에 들어오는 것은 code인데 유저가 카카오 페이지로 가서 로그인에 성공한 후에 저 코드가 등록한 uri의 ?code='~~"로 넘어오니 위와 같이 받아내면 된다.

 자, 그럼 loginService를 알아보아야만 하겠죠?!

 

5. 싸비스

 싸비스 코드가 다룰 게 많다. 하나하나 천천히 보자!

public Long kakoLogin(String code){
    ResponseEntity<String> response = getAccessToken(code);

    OAuthToken oAuthToken = JsonToObject(response);

    ResponseEntity<String> userInfo = getUserInfo(oAuthToken.getAccess_token());

    KakaoId kakaoId = JsonToKakao(userInfo);

    return kakaoId.getId();
}

흐름을 보자면, code값을 넘겨 받아 token을 받고 이 token으로 getUserInfo를 한다. 중간에 JsonToObject라던지, JsonToKakao는 말 그대로 Json 형식으로 넘어온 값을 매핑 되어주어 나중에 써먹게 해주는 DTO 비슷한 역할이라고 보면 된다. 보다시피 oAuthToken.getAces_token()을 이용한다. 추가적으로 KakaoId나 OAuthToken은 내가 직접 만든 객체 코드~~

 

private ResponseEntity<String> getAccessToken(String code){
    //Post방식으로 key=value 데이터를 요청(카카오 쪽으로)
    RestTemplate rt = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    //header
    headers.add("Content-type","application/x-www-form-urlencoded;charset=utf-8");
    //body
    MultiValueMap<String,String> params = new LinkedMultiValueMap<>();
    params.add("grant_type", LoginArgs.grant_type);
    params.add("client_id",LoginArgs.api_key);
    params.add("redirect_uri",LoginArgs.redirect_uri);
    params.add("code",code);

    //header body 연결
    HttpEntity<MultiValueMap<String,String>> kakaoTokenRequest =
            new HttpEntity<>(params,headers);

    //요청과 응답을 동시에
    ResponseEntity<String> response = rt.exchange(
            LoginArgs.get_token_uri,
            HttpMethod.POST,
            kakaoTokenRequest,
            String.class
    );

    return response;
}

 이제 본격적인데, RestTemplate은 요청하고 그대로 응답을 담아주는 메서드가 있는 굉장히 고마운 클래스이다. rt.exchange가 해당 역할을 한다. 이 메서드에 필요한 인자는 응답 받을 주소(위에서 기본 세팅한 값), 요청 방식(POST), HttpEntity (header와 body), 응답 Type이 된다.

 이렇게 토큰 값이 담겨져 있는 응답을 얻게 된다.

 위 코드는 해당 내용들이고 왜 저러한 값을 넣었냐면, 카카오 개발자 문서에서 요구한 사항들이다. 

 

private ResponseEntity<String> getUserInfo(String access_token){
    //Post방식으로 key=value 데이터를 요청(카카오 쪽으로)
    RestTemplate rt = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    //header
    headers.add("Authorization", "Bearer "+access_token);
    headers.add("Content-type","application/x-www-form-urlencoded;charset=utf-8");

    //header body 연결
    HttpEntity<MultiValueMap<String,String>> kakaoProfileRequest =
            new HttpEntity<>(headers);

    //요청과 응답을 동시에
    ResponseEntity<String> response = rt.exchange(
            LoginArgs.get_profile_uri,
            HttpMethod.POST,
            kakaoProfileRequest,
            String.class
    );

    return response;
}

 위 코드는 이제 얻어진 토큰 값으로 사용자 정보가 담긴 응답 메시지를 받았다.

 

 JsonTo~~ 와 같은 형변환 함수는 하나만 보여주겠다.

private KakaoId JsonToKakao(ResponseEntity<String> response){
    ObjectMapper mapper = new ObjectMapper();
    KakaoId kakaoId = null;
    try {
        kakaoId = mapper.readValue(response.getBody(), KakaoId.class);
    }catch(JsonMappingException e){
        e.printStackTrace();
    }catch(JsonProcessingException e){
        e.printStackTrace();
    }
    return kakaoId;
}

간단하기 때문이지.

 

그렇다. 끝!