쌓고 쌓다

카카오 로그인 API 및 쿠키 사용하기 본문

프로그래밍/spring

카카오 로그인 API 및 쿠키 사용하기

승민아 2023. 9. 3. 21:01

 

애플리케이션 추가와 REST API 키를 얻는 방법은 정말 간단하기에 생략한다.

아래의 공식 문서를 따라 애플리케이션을 추가하자.

https://developers.kakao.com/docs/latest/ko/getting-started/app

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

사용자가 모든 필수 동의 항목에 동의하고 [동의하고 계속하기] 버튼을 누른 경우

=>redirect_uri로 인가 코드를 담은 쿼리 스트링 전달

 

사용자가 동의 화면에서 [취소] 버튼을 눌러 로그인을 취소한 경우

=>redirect_uri로 에러 정보를 담은 쿼리 스트링 전달

 

 

 

사용자 인증, 카카오 API 호출을 위해 엑세스 토큰이 필요하다.

엑세스 토큰을 발급 받기 위해서는 인가 코드를 통해서 요청해야한다.

 

즉. 인가 코드 -> 엑세스 토큰 -> API 호출 가능 순서이다.

 

 

이제부터 설명할 REST API 로그인은 아래의 문서를 따른다.

https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

 

1.  인가 코드 받기

https://kauth.kakao.com/oauth/authorize GET  요청을 보내면 된다.

요청 쿼리 파라미터로 client_id, redirect_uri, response_type를 보낸다.

이름 타입 설명 필수
client_id String 앱 REST API 키 O
redirect_uri String 인가 코드를 전달받을
서비스 서버의 URI
O
response_type String code로 고정 O

필수가 아닌 파라미터는 설명하지 않겠다.

 

예시. REST API 키가 abc123이라고 가정.

https://kauth.kakao.com/oauth/authorize
?client_id=abc123
&redirect_uri=http://localhost:8080/kakao/test
&response_type=code

 

이 요청에 대한 응답은

redirect_uri에 GET 요청으로 들어온다.

응답의 파라미터로 code가 넘어오며

이 code가 엑세스 토큰을 받기 위한 code가 된다.

 

@GetMapping("kakao/test")
public String kakaoOauth(@RequestParam(name="code", required = false) String ingaCode) {

    Map<String, String> token = kaKaoToken.getToken(ingaCode);
    ...
}

redirect uri인 "/kakao/test"에 GET 매핑을 해놨다.

이 메서드로 code가 넘어오며

이제 이 code로 토큰을 발급 받자.

2. 토큰 받기

https://kauth.kakao.com/oauth/token로 POST  요청을 보내 토큰을 받을 수 있다.

 

요청에 필요한 헤더와 body는 다음과 같다.

 

토큰 POST 요청 보내기

@Component
public class KaKaoToken {

    private final String tokenUri = "https://kauth.kakao.com/oauth/token";
    private final String grantType = "authorization_code";
    private final String clientId = "abc123";
    private final String redirectUri = "http://localhost:8080/kakao/test";

    public Map getToken(String ingaCode) {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("grant_type", grantType);
        map.add("client_id", clientId);
        map.add("redirect_uri", redirectUri);
        map.add("code", ingaCode);

        HttpEntity<Map> request = new HttpEntity<>(map, httpHeaders);
        ResponseEntity<Map> stringResponseEntity = restTemplate.postForEntity(tokenUri, request, Map.class);
        return stringResponseEntity.getBody();
    }

}

 

스프링에서 외부 API를 호출하는 방법은 여러가지가 있으나 RestTemplate를 사용해봤다.

호출 방법 종류들 참고 : https://jie0025.tistory.com/531

 

HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

헤더 설정을 하는 과정이다.

요구하는 헤더의 Content-type이 application/x-www-form-urlencoded이였다.

 

MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("grant_type", grantType);
map.add("client_id", clientId);
map.add("redirect_uri", redirectUri);
map.add("code", ingaCode);

바디에 넣을 값들이다. HashMap을 사용하면 "content type : x-www-form-urlencoded"로 변환할 HttpMessageConverter가 없어 에러가 나므로 MultiValudeMap을 사용했다.

 

HttpEntity<MultiValueMap> request = new HttpEntity<>(map, httpHeaders);

요청에 사용할 Entity를 만드는 과정이다.

HttpEntity 클래스는 HTTP 요청 또는 응답에 Header와 Body를 포함하는 클래스이다.

Body에 만들어 놓은 map, Header에 만들어 놓은 httpHeaders를 넣어준다.

 

restTemplate.postForEntity(tokenUri, request, Map.class);

uri로 POST 요청을 보내는 메서드이며 응답 타입을 Map.class로 받았다.

 

3. 토큰으로 API 호출해보기

https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

@Component
public class KaKaoUser {
    private final String uri = "https://kapi.kakao.com/v2/user/me";

    public Member getUser(String access_token) {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Authorization", "Bearer " + access_token);
        httpHeaders.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        String str = "property_keys=[\"kakao_account.email\", \"kakao_account.profile\"]";
        HttpEntity<String> request = new HttpEntity<>(str, httpHeaders);
        ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(uri, request, String.class);

        //kakao_account.profile.nickname, kakao_account.email
        JSONObject jsonObject = new JSONObject(stringResponseEntity.getBody());
        JSONObject kakao_account = jsonObject.getJSONObject("kakao_account");
        JSONObject profile = kakao_account.getJSONObject("profile");
        String nickName = profile.getString("nickname");
        String email = kakao_account.getString("email");

        Member member = new Member();
        member.setId(1L);
        member.setNickname(nickName);
        member.setEmail(email);
        return member;
    }
}

이제 앞서 설명한 과정과 동일하다.

엑세스 토큰을 가지고 요청에 맞춰 요청을 보내고 응답으로 데이터를 받아오면 된다.

 

응답으로 JSON을 받는데 여기서 String.class 타입으로 받았으므로

JSON 형태의 String을 다루는 방법이 필요하다. 앞전에 작성한 포스팅을 참조하자.

https://non-stop.tistory.com/581

 

쿠키 등록하기

@Controller
public class TestController {

    private Map<Long, Member> repository = new HashMap<>();

    @GetMapping("kakao/test")
    public String kakaoOauth(@RequestParam(name="code", required = false) String ingaCode, HttpServletResponse response) {
    	...
        Member member = kaKaoUser.getUser(token.get("access_token")); // id=1L, nickname, email 처리

        //신규라고만 가정함.
        repository.put(member.getId(), member);
        Cookie idCookie = new Cookie("memberId", String.valueOf(member.getId()));
        idCookie.setPath("/");
        response.addCookie(idCookie);
        return "redirect:/";
    }

    @GetMapping("/")
    public String home(@CookieValue(name="memberId", required = false) Long memberId, Model model) {
        System.out.println("memberId = " + memberId);
        if(memberId==null)
            return "home";
        else {
            Member loginMember = repository.get(memberId);
            if(loginMember==null)
                return "home";
            else {
                model.addAttribute("member", loginMember);
                return "loginHome";
            }
        }
    }
    
}

 

쿠키 등록은 아래와 같이 생성한다.

Cookie idCookie = new Cookie("memberId", String.valueOf(member.getId()));
idCookie.setPath("/");
response.addCookie(idCookie);

쿠키를 memberId=1 형태로 등록했다.

이제 쿠키로 유저 정보를 찾아 이용하면 된다.

 

쿠키 읽기

    @GetMapping("/")
    public String home(@CookieValue(name="memberId", required = false) Long memberId) {
        if(memberId==null)
            return "home";
        else {
            Member loginMember = repository.get(memberId);
            if(loginMember==null)
                return "home";
            else {
                model.addAttribute("member", loginMember);
                return "loginHome";
            }
        }
    }

@CookieValue를 통해서 쿠키를 읽는다.

로그인하지 않은 사람도 GET "/" 요청을 보낼 수 있으므로 require=false로 하자.

쿠키를 통해 memberId가 존재한다면 Id로 정보를 찾아서 사용하자.

memberId가 존재해도 서비스에 없을 수 있으니 loginMember==null 인지 체크를 해주자.

 

쿠키 삭제(로그아웃)

    @PostMapping("/logout")
    public String logout(HttpServletResponse response) {
        Cookie cookie = new Cookie("memberId", null);
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        return "redirect:/";
    }

쿠키의 MagAge를 0으로 설정하여 로그아웃을 처리할 수 있다.

 

<form action="/logout" method="post">
    <button type="submit">로그아웃</button>
</form>

로그아웃의 버튼은 form에 버튼을 만들어 POST 요청을 보내게 했다.

 

구현 결과

로그인 과정

 

로그인 상태시 GET "/" 응답
로그아웃시 GET "/" 응답

쿠키는 보안 이슈가 있으므로 세션을 다음에 사용해보겠다.

Comments