본문 바로가기
공부/Spring

[Spring] Spring Security 없이 카카오 로그인 구현하기

by KimSuyong 2024. 7. 28.

사이드 프로젝트에서 제가 로그인 기능을 개발하지 않아서인지 사이드 프로젝트의 인증과 인가 부분을 이해하기가 어려웠습니다.
Oauth를 이용한 최근 어플리케이션들에서는 필수로 알아야 하는 지식이므로 꼭 이해하고 넘어가고 싶었습니다.
(사실 옛날에 시도했다가 동작은 되지만 이해는 하나도 못 하는 수준으로 개발하였기에 제대로 이해하고 싶은 마음도 있습니다. 😁)

✅ 노랑색 마킹한 부분을 잘 확인해 주세요. ✅

1. Kakao Developers 설정하기

1-1. 내 애플리케이션 등록

  • Kakao Developers에 접속하여 내 애플리케이션 추가하기 버튼을 눌러서 애플리케이션을 등록합니다.

1-2. 카카오 로그인 활성화하기

  • 1-1에서 등록한 내 애플리케이션을 클릭하여 설정 화면으로 이동합니다.

  • 사이드 메뉴에서 카카오 로그인을 클릭하고 카카오 로그인을 활성화해줍니다.

1-3. Redirect URI 등록하기

  • Redirect URI 등록 버튼을 클릭하여 Redirect URI를 등록해 줍니다. 저는 따로 배포하지 않고 진행하였기에 http://localhost:8080/oauth/kakao를 Redirect URI로 등록하였습니다.

1-4. 동의항목 설정하기

  • 동의항목에서 닉네임을 필수동의로 변경해 줍니다. (여기서 필수동의가 카카오의 Resource서버에서 제공해주는 유저 정보가 됩니다.)
  • 원래 카카오 계정도 필수 동의를 받아야 하는데, Biz앱 전환을 하지 않으면 카카오에서 이메일을 제공해 주지 않습니다.

2. 인가 코드 받기

위 이미지는 카카오 로그인 과정을 나타내고 있는 시퀀스 다이어그램입니다. 지금부터 하는 게 Step 1에 해당하는 부분입니다.

2-1. GET https://kauth.kakao.com/oauth/authorize 요청 보내기

https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}

시퀀스 다이어그램상 가장 처음 해야 할 것은 GET https://kauth.kakao.com/oauth/authorize을 처리하는 것입니다. 카카오 로그인시 일반적으로 GET 요청은 흔히 보이는 카카오 로그인 버튼을 클릭했을 때 가장 처음 동작합니다.

즉 사용자가 버튼을 클릭하는 순간 일어나는 일은

  1. 인가 코드를 받는다.
  2. 받은 인가 코드로 Access Token을 받는다.
  3. Access Token으로 유저 정보를 받는다.

위 3개의 과정으로 진행되고 있는 것입니다.

다시 본론으로 돌아와서 저는 카카오 로그인 버튼이 따로 없기 때문에카카오 버튼만 있는 화면을 하나 만들어 주겠습니다.

<!doctype html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>카카오 로그인</title>
</head>
<body>
<a href="/login/kakao">
    <img src="/img/kakao_login.png" alt="카카오 버튼" style="width: 150px; height: auto;">
</a>
</body>
</html>

버튼에 걸려있는 uri가 /login/kakao 이므로 이것을 처리해주는 컨트롤러를 하나 만들어줍니다.

@RequestMapping("/login")
@RequiredArgsConstructor
@Controller
public class LoginController {

    @GetMapping("/{provider}")
    public String login(@PathVariable String provider) {
        String url = "https://kauth.kakao.com/oauth/authorize?" +
                "client_id=" + "{REST API KEY}" +
                "&redirect_uri=" + "http://localhost:8080/oauth/" + provider +
                "&response_type=code";

        return "redirect:" + url;
    }
}

위 Controller 코드에서 보면 만들어주고 있는 url이 카카오에서 제공해주는 예제와 똑같은 형식임을 알 수 있습니다. 이렇게 하면 인가 코드를 받을 수 있습니다.

인가 코드는 정상적으로 수행되었을 경우 위와 같은 형태로 응답됩니다.

3. 토큰 받고, 유저 정보 받기

위에서 인가 코드를 받는 과정을 진행했는데 인가 코드를 잘 받은 것을 어떻게 알 수 있을까요? 시퀀스 다이어그램을 보면 Redirect URI로 인가 코드를 전달한다고 되어 있습니다. 여기서 말하는 Redirect Uri1-3. Redirect URI 등록하기에서 설정했던 그 Redirect Uri입니다.

저는 Redirect Uri를 localhost:8080/oauth/kakao로 설정하였기 때문에 이 uri로 Redirect 되었을 때의 경우를 처리해 주어야 합니다. 그리고 요청 url에는 위에서 받은 인가 코드가 같이 있을 겁니다.

@Slf4j
@RequestMapping("/oauth")
@RequiredArgsConstructor
@RestController
public class OauthController {

    @GetMapping("/{provider}")
    public JsonNode login(@RequestParam String code, @PathVariable String provider) {
        log.info("provier: {}, code: {}", provider, code);

        // 1. 헤더 생성
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.add("Accept", "application/json");

        // 2. 바디 생성
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", "authorization_code"); // grant_type은 고정 값
        params.add("client_id", "{REST API KEY}"); // rest api key
        params.add("redirect_uri", "http://localhost:8080/oauth/kakao"); // redirect_uri
        params.add("client_secret", "{SECRET_KEY}");
        params.add("code", code);

        // 3. 헤더랑 바디 합치기
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);

        // 4. HTTP 요청
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<JsonNode> response = restTemplate.exchange(
                "https://kauth.kakao.com/oauth/token",
                HttpMethod.POST,
                request,
                JsonNode.class
        );

        // 5. token 발급이 잘 되었나 확인
        log.info("response: {}", response);

        // 6. Resource 서버에 유저 정보를 요청
        HttpHeaders requestResourceHeader = new HttpHeaders();
        requestResourceHeader.set("Authorization", "Bearer " + response.getBody().get("access_token").asText());
        requestResourceHeader.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<Object> requestEntity = new HttpEntity<>(requestResourceHeader);

        JsonNode body = restTemplate.exchange(
                "https://kapi.kakao.com/v2/user/me",
                HttpMethod.POST,
                requestEntity,
                JsonNode.class
        ).getBody();

        return body;
    }
}

위 코드에서는 1부터 5까지의 과정이 토큰을 발급받는 과정입니다. 그리고 6번은 유저 정보를 요청하는 과정입니다. 다음으로 해야 할 일은 유저 정보를 받은 이후에 유저 정보를 데이터베이스에 저장하는 일입니다.