~☆~ 우하하!!~ 개발블로그

[SpringBoot][소셜로그인] kakao 소셜 로그인 기능 구현 본문

SpringBoot

[SpringBoot][소셜로그인] kakao 소셜 로그인 기능 구현

iwoohaha 2024. 11. 22. 14:53
반응형

https://iwoohaha.tistory.com/340 에서 kakao 개발자 센터에서 OAuth2 를 이용하기 위한 기본 설정을 마쳤다.

SpringBoot 에서 kakao 가 제공하는 OAuth2 기능을 사용하기 위한 첫번째 단계로 application.yml 에 설정값을 추가하는 방법은 다음과 같다.

spring:
  security:
    oauth2:
      client:
        registration:
          kakao:
            client-id: [내 애플리케이션 > 앱 설정 > 앱 키 에서 확인 가능한 REST API 키]
            client-secret: [내 애플리케이션 > 제품 설정 > 카카오 로그인 > 보안 에서 확인 가능한 Client Secret 코드]
            scope:
              - account_email
              - profile_nickname
            authorization-grant-type: authorization_code
            redirect-uri: http://localhost:8080/login/oauth2/code/kakao
            client-name: Kakao
            client-authentication-method: client_secret_post
        provider:
          kakao:
            authorization-uri: https://kauth.kakao.com/oauth/authorize
            token-uri: https://kauth.kakao.com/oauth/token
            user-info-uri: https://kapi.kakao.com/v2/user/me
            user-name-attribute: id

login.html 파일에 Kakao 로그인 기능을 하는 버튼 컴포넌트를 추가하자.

<style type="text/css">
...
    .css-kakao-button {
         -webkit-box-align: baseline;
         align-items: baseline;
         box-sizing: border-box;
         display: inline-flex;
         font-size: inherit;
         font-style: normal;
         font-family: inherit;
         max-width: 100%;
         position: relative;
         text-align: center;
         text-decoration: none;
         transition: background 0.1s ease-out, box-shadow 0.15s cubic-bezier(0.47, 0.03, 0.49, 1.38);
         white-space: nowrap;
         cursor: pointer;
         padding: 0px 10px;
         vertical-align: middle;
         width: 100%;
         -webkit-box-pack: center;
         justify-content: center;
         box-shadow: none;
         font-weight: bold;
         border: 1px solid rgb(193, 199, 208);
         border-radius: 3px;
         color: var(--ds-text, #42526E) !important;
         height: 40px !important;
         line-height: 40px !important;
         background: #fee500 !important;
    }
    .css-kakao-button:visited {
        background: #fee500 !important; /* 방문 후에도 동일한 배경색 유지 */
        color: #000000 !important; /* 텍스트 색상 */
    }

    .css-kakao-button:hover {
        background: #e4d000 !important; /* 호버 시 약간 어두운 색상 */
        text-decoration: inherit;
        transition-duration: 0s, 0.15s;
        color: #000000 !important;
    }

    .css-kakao-button:active {
        background: #cbbf00 !important; /* 클릭 시 더 어두운 색상 */
        transition-duration: 0s, 0s;
        color: #000000 !important;
    }

    .css-kakao-button:focus {
        outline: 2px solid #2684FF; /* 포커스 시 파란색 외곽선 */
        outline-offset: 2px;
    }

    .css-kakao-button:focus-visible {
        outline: 2px solid #2684FF; /* 접근성 고려한 포커스 표시 */
        outline-offset: 2px;
    }
    .css-kakao-button span {
         -webkit-box-pack: center;
         justify-content: center;
         display: flex !important;
    }
    .css-kakao-button span {
         -webkit-box-flex: unset;
         flex-grow: unset;
    }
    .css-kakao-button img {
         height: 24px;
         width: 24px;
         margin-right: 6px;
    }
    @media screen and (forced-colors: active), screen and (-ms-high-contrast: active){
        .css-1bthe7p:focus-visible {
             outline: solid 1px;
        }
        .css-kakao-button:focus-visible {
             outline: solid 1px;
        }
    }
    @media screen and (forced-colors: active), screen and (-ms-high-contrast: active){
        .css-1bthe7p:focus-visible {
             outline: solid 1px;
        }
        .css-kakao-button:focus-visible {
             outline: solid 1px;
        }
    }
    @media screen and (forced-colors: active), screen and (-ms-high-contrast: active){
        .css-1bthe7p:focus-visible {
             outline: solid 1px;
        }
        .css-kakao-button:focus-visible {
             outline: solid 1px;
        }
    }
</style>

...
                    <div class="css-1vymulm">
                        <a href="/oauth2/authorization/kakao">
                            <button id="kakao-auth-button" class="css-kakao-button" tabindex="0" type="button">
                                <span class="css-1ti50tg">
                                    <img src="/images/kakaologo.png" alt="Kakao Logo">
                                </span>
                                <span class="css-178ag6o">Kakao</span>
                            </button>
                        </a>
                    </div>
...

사용한 kakaologo.png 파일은 이거다.

일단 여기까지 수정한 상태에서 실행하면 다음과 같이 Kakao 버튼이 표시된다.

Kakao 버튼을 클릭하면 아래 주소로 Redirect 된다.

DEBUG 24-11-22 16:41:02[http-nio-8080-exec-8] [FilterChainProxy:223] - Securing GET /oauth2/authorization/kakao
DEBUG 24-11-22 16:41:02[http-nio-8080-exec-8] [DefaultRedirectStrategy:61] - Redirecting to https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=ae9c3facce8f3ae19fc3085a5ca408f5&scope=account_email%20profile_nickname&state=3VQlVELx1BxCwWtVPzRW_6g-xrtUBQ7mKswa2s_vXr4%3D&redirect_uri=http://localhost:8080/login/oauth2/code/kakao

화면에 표시되는 내용은 아래와 같다.

 

 

이중에서 어떤 계정을 선택하면 아래와 같이 잘못된 요청 에러가 발생한다.

"왜 에러가 발생하나요?" 를 열어보면

설정하지 않은 동의 항목이 표시된다.

여기에 표시되고 있는 항목들은 application.yml 에 기록한 항목들임을 알 수가 있다. 즉 요청한 항목과 카카오 개발자 센터에서 설정한 항목의 차이로 인해 잘못된 요청으로 처리되고 있다는 의미이다.

카카오 개발자 센터로 로그인해서 "내 애플리케이션 > 제품 설정 > 카카오 로그인 > 동의항목" 페이지로 이동해보자.

각각 사용안함, 권한 없음 이 표시되고 있는데, 이것을 변경해줄 필요가 있다.

상태가 "권한 없음"으로 표시되는 항목은 사업자 정보를 등록하고 비즈니스 인증을 완료하면 심사를 신청할 수 있다고 한다.

일단 심사가 불필요한 항목들에 대해서 요청 가능하도록 설정하는 방법을 알아보자.

상태가 "사용 안함" 으로 표시되고 있는 항목의 오른쪽 "설정" 버튼을 누르면 동의 항목 설정 화면이 나타난다.

동의 단계중 "필수 동의" 항목을 선택한 후 "저장" 버튼을 눌러보자.

이렇게 닉네임(profile_nickname) 과 프로필 사진(profile_image) 항목에 대해서 필수 동의 상태로 변경하였다.

카카오계정 이메일 정보는 권한 없음 상태여서 심사가 필요하므로 이번 로직 구현에서는 제외하기로 한다.

이제 application.yml 에 입력했던 정보를 아래와 같이 수정한다.

...
            scope:
              - profile_nickname
              - profile_image
...

프로그램을 실행시켜서 카카오계정을 선택하면 이번에는 아래와 같이 정상적으로 동의화면이 표시된다.

"동의하고 계속하기" 를 누르면 콘솔에 에러가 표시되고 화면에서는 다시 로그인 화면이 나타난다. 이것은 kakao 로그인시 전달받은 정보처리가 아직 구현되어 있지 않기 때문이다.

CustomOAuth2UserService 의 loadUser 함수에 BreakPoint 를 걸고 디버그모드로 실행해서 데이터 내용을 확인해보자.

이 데이터를 참고하여 provider 가 "kakao" 인 경우에 대한 처리를 아래와 같이 구현할 수 있다.

...
        OAuth2UserInfo oAuth2UserInfo = null;
        if (provider.equals("google")) {
            oAuth2UserInfo = new GoogleOAuth2UserInfo(oAuth2User.getAttributes());
        } else if (provider.equals("naver")) {
            oAuth2UserInfo = new NaverOAuth2UserInfo(oAuth2User.getAttribute("response"));
        } else if (provider.equals("kakao")) {
            oAuth2UserInfo = new KakaoOAuth2UserInfo(oAuth2User.getAttributes());
        }
...

KakaoOAuth2UserInfo 클래스는 아래와 같이 구현할 수 있겠다.

package com.woohahaapps.study.diary.domain;

import java.util.HashMap;
import java.util.Map;

public class KakaoOAuth2UserInfo implements OAuth2UserInfo{

    private final Map<String, Object> attributes;

    public KakaoOAuth2UserInfo(Map<String, Object> attributes) {
        this.attributes = attributes;
    }

    @Override
    public String getProvider() {
        return "kakao";
    }

    @Override
    public String getProviderId() {
        return String.valueOf(attributes.get("id"));
    }

    @Override
    public String getEmail() {
        return "";
    }

    @Override
    public String getName() {
        return (String) ((Map)((Map)attributes.get("kakao_account")).get("profile")).get("nickname");
    }

    @Override
    public String getProfileImageUrl() {
        return (String) ((Map)((Map)attributes.get("kakao_account")).get("profile")).get("profile_image_url");
    }

    @Override
    public Map<String, Object> convertToMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("provider", getProvider());
        map.put("providerid", getProviderId());
        map.put("email", getEmail());
        map.put("name", getName());
        map.put("profileimageurl", getProfileImageUrl());
        return map;
    }
}

이후의 프로세스는 이미 구현된 코드로 처리가 된다. 즉, 이메일 주소를 전달받지 못하였기 때문에 아래와 같이 이메일 주소를 입력받는 화면으로 연결되고,

이메일 계정을 등록하면 kakao 계정으로 회원가입 및 로그인 처리가 완료된다.

반응형