𝝅번째 알파카의 개발 낙서장

screen

[OAuth2.0] ScribeJAVA로 OAuth2.0 인증서버 구축하기 - 6. KAKAO OAuth 서비스 신청 및 모듈 구현하기

posts

JAVA

시리즈 톺아보기

OAuth2.0 인증서버 구축기

OAuth2.0 인증서버 구축기
count

개요 🔗

세 번째 플랫폼으로, KAKAO에 OAuth 서비스를 신청하고 인증 모듈을 구현한다.

KAKAO OAuth 서비스 신청하기 🔗

API 정보를 얻기 위해 KAKAO OAuth 서비스를 신청하자.

1. Kakao Developers 접속하기 🔗

로그인 후 Kakao Developers에 접속하자.

상단 메뉴의 [내 애플리케이션]에서 애플리케이션 목록을 확인할 수 있다.

2. 애플리케이션 추가하기 🔗

image

OAuth 정보를 관리하게 될 애플리케이션을 생성하자. 이름은 아무렇게나 지어도 상관없다. 사업자 정보도 필수사항이므로, 사업자가 아니라면 무작위로 입력하거나 이름과 동일하게 입력하자. 로고는 추후 등록해도 무방하다.

3. 동의 화면 구성하기 🔗

image

OAuth API를 생성하기 위해선 먼저 동의 화면을 구성해야한다. 정보 제공 동의 그거 맞다.

좌측 사이드바의 [카카오 로그인 - 동의항목] 메뉴에서 구성 가능하다. 설정 가능한 상태는 아래와 같다.

  • 필수 동의 - 반드시 동의해야하는 항목. 해당 항목을 동의하지 않으면 로그인 불가능.
  • 선택 동의 - 사용자의 선택에 따라 동의하는 항목. 동의 여부가 로그인에 영향을 미치지 않음
  • 이용 중 동의 - 로그인 시에는 표출되지 않음. 추후 API를 통해 필요할 때 별도로 동의 요청
  • 사용 안함 - 사용하지 않음

사용자에게 어떤 항목도 동의를 강제하지 않는 네이버와 달리, 카카오는 필수 사항에 대해선 반드시 동의를 받아야만 로그인이 가능하도록 구성되어있다.

닉네임, 프로필사진, 이메일을 지정한다. 이메일의 경우 비즈니스 앱만 필수로 지정 가능하다.

비즈니스 앱?
KAKAO의 OAuth 애플리케이션 심사와 같은 개념이다. 단, 기본적으로 사업자 등록 번호 등을 요구하는데, 사업자 등록번호가 없는 개인의 경우 별도로 신청해야한다. 이는 차후 심사 단계에서 자세히 다룬다.

4. Redirect URI 등록 🔗

로그인 후 code를 전달할 리다이렉트 URI를 지정한다.

좌측 사이드바의 [카카오 로그인] 메뉴에서 구성 가능하다.

줄바꿈으로 구분하여 여러 URL을 등록할 수 있다.

5. 보안 활성화 🔗

좌측 사이드바의 [카카오 로그인 - 보안] 메뉴에서 Client Secret를 활성화한다.

이를 활성화해야 Secret키를 사용할 수 있다.

6. 카카오 로그인 활성화 🔗

좌측 사이드바의 [카카오 로그인] 메뉴에서 활성화로 체크하여 애플리케이션을 활성화할 수 있다.

7. API 키 확인 🔗

애플리케이션 메인인 [요약 정보]에서 바로 확인할 수 있다.

image

  • 네이티브 앱 키 - 모바일용
  • REST API 키 - HTTP 요청용
  • JavaScript 키 - SDK용
  • Admin 키 - 위 기능을 전부 통합한 관리자용

REST API 키를 사용하면 된다.

KAKAO 인증 모듈 구현하기 🔗

필요한 모든 준비가 갖춰졌으니, 카카오 인증 모듈을 구현해보자. 이전에 구현한 AuthModule을 상속받아 구현할 것이다.

JAVA

0public class KakaoAuthModule extends AuthModule
1{
2 // KAKAO 인증 모듈
3}

객체의 기본 형식은 위와 같다.

메서드 메서드 타입 내용 구현 필요 여부
getAuthorizationUrl 추상 인증 URL 반환 메서드 Y
getAccessToken 접근 토큰 반환 메서드 Y
getRefreshAccessToken 접근 토큰 갱신 및 반환 메서드
getUserInfo 사용자 정보 응답 반환 메서드
getRefreshTokenEndpoint 접근 토큰 재발급 요청 URL 반환 메서드
getApiKeyBean API 키 객체 반환 메서드
getUserInfoEndPoint 사용자 정보 요청 URL 반환 메서드
getUserInfoBean 추상 유저 정보 객체 반환 메서드 Y
deleteInfo 추상 연동 해제 결과 반환 메서드 Y
getUpdateAuthorizationUrl 추상 정보 제공 동의 갱신 URL 반환 메서드 Y
getAccessTokenEndpoint 추상 접근 토큰 요청 URL 반환 메서드 Y
getAuthorizationBaseUrl 추상 인증 API 요청 URL 반환 메서드 Y

카카오 모듈이 구현해야하는 대상은 위와 같다. 이전 플랫폼과 다르게 getAccessToken의 오버라이딩이 필요하다.

properties 파일 생성하기 🔗

WEB-INF 아래 kakao.properties 파일을 생성한다. 기 생성된 sample.properties를 복사해서 사용해도 된다.

PROPERTIES

0api=API_KEY
1secret=SECRET_KEY
2callback=CALLBACK_URL

기본적인 형식은 위와 같으며, 각 항목에 해당하는 값을 입력하면 된다.

인증 모듈 기본 메서드 및 변수 할당하기 🔗

인증 모듈이 정상적으로 동작하기 위해선 기본적으로 지정해줘야할 메서드와 변수들이 존재한다. API 정보 설정, 인스턴스 반환같은 것들이다.

JAVA

0private static final String MODULE_NAME = "kakao";
1
2private static final String API_KEY;
3private static final String SECRET_KEY;
4private static final String CALLBACK_URL;
5
6static
7{
8 ApiKeyBean apiKeyBean = getApiKeyBean(MODULE_NAME);
9
10 API_KEY = apiKeyBean.getApi();
11 SECRET_KEY = apiKeyBean.getSecret();
12 CALLBACK_URL = apiKeyBean.getCallback();
13}
14
15private static final ServiceBuilderOAuth20 SERVICE_BUILDER = new ServiceBuilder(API_KEY).apiSecret(SECRET_KEY).callback(CALLBACK_URL);
16
17private static final KakaoAuthModule INSTANCE = new KakaoAuthModule(SERVICE_BUILDER);
18
19private KakaoAuthModule(ServiceBuilderOAuth20 serviceBuilder)
20{
21 super(serviceBuilder);
22}
23
24public static KakaoAuthModule getInstance()
25{
26 return INSTANCE;
27}

Google은 반드시 scope를 특정해야한다. 사용하는 scope3-3. 범위 지정에서 선택한 그 범위를 집어넣으면 된다.

구분 형식 내용
MODULE_NAME String 모듈 이름
API_KEY String API키
SECRET_KEY String Secret키
CALLBACK_URL String 콜백 URL
SERVICE_BUILDER ServiceBuilderOAuth20 OAuth2.0 서비스 빌더
INSTANCE KakaoAuthModule 인스턴스

정의된 변수는 전부 static final로 선언되어 있어서, 인스턴스 생성 시 한 번만 선언되며 재할당이 불가능하도록 관리한다.

static{ } 구문을 통해 인스턴스 생성 시 API 정보를 할당하도록 구성했다.

API 할당 시 getApiKeyBean() 메서드를 통해 제공된 이름을 갖는 properties를 분석하여 ApiKeyBean 객체를 반환받아 사용한다.

API URL 할당하기 🔗

각 API 별 요청 URL을 반환하는 메서드를 구현하자.

JAVA

0@Override
1public String getAccessTokenEndpoint()
2{
3 return "https://kauth.kakao.com/oauth/token";
4}
5
6@Override
7protected String getAuthorizationBaseUrl()
8{
9 return "https://kauth.kakao.com/oauth/authorize";
10}
11
12@Override
13protected String getUserInfoEndPoint()
14{
15 return "https://kapi.kakao.com/v2/user/me";
16}
  • getAccessTokenEndpoint() - 토큰과 관련된 API는 해당 메소드가 반환하는 URL을 토대로 사용한다.
  • getAuthorizationBaseUrl() - 인증과 관련된 API는 해당 메소드가 반환하는 URL을 토대로 사용한다.
  • getUserInfoEndPoint() - 사용자 정보와 관련된 API는 해당 메소드가 반환하는 URL을 토대로 사용한다.

OAuth2.0 서비스를 수행함에 있어서 필요한 URL은 위와 같다. 이 중 getAccessTokenEndpoint()getAuthorizationBaseUrl()는 scribeJAVA 라이브러리의 객체인 DefaultApi20의 추상 메서드고 나머지 하나가 AuthModule의 추상 메서드다.

DefaultApi20는 사용자 계정 API에 관련된 메서드를 별도로 제공하지 않는다. 하지만 AuthModule에서 사용자 정보 확인 공통 메서드를 사용할 때 사용자 계정 API가 반드시 필요하므로 AuthModule의 추상 메서드로 관리한다.

인증 URL 반환 메서드 🔗

카카오 플랫폼 로그인 URL을 반환하는 기능을 구현한다.

우선 API를 살펴보자.


  • 요청

TXT

0GET https://kauth.kakao.com/oauth/authorize?response_type=code&client_id={:client_id}&redirect_uri={:redirect_uri}&state={:state}
parameter type data required description
{:response_type} path String Y 응답 타입. code로 고정
{:client_id} path String Y API키
{:redirect_uri} path String Y Callback URL
{:state} path String Y 고유 상태값

  • 응답

카카오 플랫폼 로그인 페이지


카카오 플랫폼 로그인 API는 위와 같다. 메서드가 요청의 URL을 반환하도록 설계하면 된다.

문자열 연산으로 URL을 직접 설계할 수도 있지만, service.getAuthorizationUrl() 메서드를 통해 URL을 간편하게 생성할 수 있다.


이미 AuthModule에 공통 메서드로 선언된 게 있으므로, 따로 구현하지 않아도 된다.

접근 토큰 반환 메서드 🔗

로그인 결과로 Code를 전달받으므로 Access Token으로 교환하는 기능을 구현한다.

카카오 API는 아래와 같다. scribeJAVA가 카카오에 대한 처리를 잘 못 하는건지, 동일한 인터페이스를 사용하면 파라미터를 제대로 입력하지 않아 오류가 뜬다. 때문에 어쩔 수 없이 직접 요청을 생성하여 사용해야한다.


  • 요청

TXT

0POST https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={:client_id}&client_secret={:client_secret}&redirect_uri={:redirect_uri}&code={:code}
parameter type data required description
{:grant_type} path String Y 인증 타입. authorization_code로 고정
{:client_id} path String Y API키
{:client_secret} path String Y Secret키
{:redirect_uri} path String Y Callback URL
{:code} path String Y 인가 코드

  • 응답

JSON

0{
1 "token_type": "bearer",
2 "access_token": "{ACCESS_TOKEN}",
3 "expires_in": 43199,
4 "refresh_token": "{REFRESH_TOKEN}",
5 "refresh_token_expires_in": 25184000,
6 "scope": "account_email profile"
7}
parameter data description
access_token String 인증 토큰
refresh_token String 리프레쉬 토큰
refresh_token_expires_in int 리프레쉬 토큰 만료일자 (초 단위)
token_type String 토큰 타입
expires_in int 만료일자 (초 단위)
scope String 접근 권한

AccessTokenRequestParams 객체로 요청을 생성하여 service.getAccessToken으로 응답을 받는다.

라이브러리의 처리 문제로 AuthModule의 공통 메서드가 아닌, 별도로 오버라이딩한 메서드를 사용해야한다.

접근 토큰 갱신 및 반환 메서드 🔗

Access Token은 만료시간이 한시간 정도로 매우 짧다. Access Token이 만료될 경우, 사용자에게 플랫폼 로그인을 통해 인증 정보를 다시 요구해야하지만 Refresh Token이 있다면 별도의 절차 없이 서비스 내부에서 Access Token을 재발급 받을 수 있다.

이 Refresh Token은 인증 권한은 없지만, Access Token을 재발급받는 권한을 가진다.

이를 구현한 카카오 API는 아래와 같다.


  • 요청

TXT

0POST https://kauth.kakao.com/oauth/token?grant_type=refresh_token&client_id={:client_id}&client_secret={:client_secret}&refresh_token=${:refresh_token}
parameter type data required description
{:grant_type} path String Y 인증 타입. refresh_token로 고정
{:client_id} path String Y API키
{:client_secret} path String Y Secret키
{:refresh_token} path String Y 리프레쉬 토큰

  • 응답

JSON

0{
1 "access_token": "{ACCESS_TOKEN}",
2 "token_type": "bearer",
3 "refresh_token": "{REFRESH_TOKEN}",
4 "refresh_token_expires_in": 25184000,
5 "expires_in": 43199,
6}
parameter data description
access_token String 인증 토큰
token_type String 토큰 타입
refresh_token String 리프레쉬 토큰
refresh_token_expires_in int 리프레쉬 토큰 만료일자 (초 단위)
expires_in int 만료일자 (초 단위)

AuthModule의 공통 메서드로 대체 가능하므로 별도로 구현하지 않는다.

사용자 정보 응답 반환 메서드 🔗

Access Token으로 유저 정보를 호출하는 기능을 구현한다. 발급받은 Access Token을 실제로 유의미하게 쓰는 부분이다.

카카오 API는 아래와 같다.


  • 요청

TXT

0GET/POST https://kapi.kakao.com/v2/user/me
1Authorization: Bearer {:access_token}
parameter type data required description
{:access_token} header String Y 접근 토큰

  • 응답

JSON

0{
1 "id":123456789,
2 "kakao_account": {
3 "profile_needs_agreement": false,
4 "profile": {
5 "nickname": "홍길동",
6 "thumbnail_image_url": "http://yyy.kakao.com/.../img_110x110.jpg",
7 "profile_image_url": "http://yyy.kakao.com/dn/.../img_640x640.jpg",
8 "is_default_image": false
9 },
10 "email_needs_agreement": false,
11 "is_email_valid": true,
12 "is_email_verified": true,
13 "email": "sample@sample.com",
14 "age_range_needs_agreement": false,
15 "age_range": "20~29",
16 "birthday_needs_agreement": false,
17 "birthday": "1130",
18 "gender_needs_agreement": false,
19 "gender": "female"
20 },
21 "properties": {
22 "nickname": "홍길동카톡",
23 "thumbnail_image": "http://xxx.kakao.co.kr/.../aaa.jpg",
24 "profile_image": "http://xxx.kakao.co.kr/.../bbb.jpg",
25 "custom_field1": "23",
26 "custom_field2": "여"
27 }
28}

응답 명세는 길이가 매우 방대한 관계로 생략한다. 카카오 개발자 docs를 참고하자.


id는 우리가 생각하는 xxx@google.com 형태의 아이디가 아니라 아이디별로 부여받는 고유 해쉬값이다.

AuthModule의 공통 메서드로 대체 가능하므로 별도로 구현하지 않는다.

유저 정보 객체 반환 메서드 🔗

카카오의 유저 정보 호출 API 응답 형식에 맞게끔 응답을 파싱하여 UserInfoBean로 반환하는 메서드를 구현한다.

이 프로젝트에선 이름, 이메일, 프로필사진 URL만을 사용하므로, 응답에서 해당 값을 빼내어 객체에 담는다.


  • 코드

JAVA

0@Override
1public UserInfoBean getUserInfoBean(String body) throws JsonProcessingException
2{
3 ObjectMapper mapper = new ObjectMapper();
4
5 JsonNode node = mapper.readTree(body);
6
7 String email = node.get("kakao_account").get("email") == null ? "미동의" : node.get("kakao_account").get("email").textValue();
8 String name = node.get("kakao_account").get("profile").get("nickname") == null ? "미동의" : node.get("kakao_account").get("profile").get("nickname").textValue();
9 String picture = node.get("kakao_account").get("profile").get("profile_image_url") == null ? "/oauth2/assets/images/logo.png" : node.get("kakao_account").get("profile").get("profile_image_url").textValue();
10
11 return new UserInfoBean(email, name, picture, MODULE_NAME);
12}

응답 형식에 맞추어 필요한 값을 추출한다. 만약, 사용자가 정보 제공에 동의하지 않았을 경우 대상 객체가 null을 반환한다. 데이터의 누락 시 오류를 방지하기 위해 데이터에 대한 null 처리를 반드시 해야한다.

연동 해제 결과 반환 메서드 🔗

카카오 아이디로 처음 로그인을 하면 정보 제공 동의를 수행하는데, 나중에 다시 로그인을 하면 이러한 동의 과정이 생략된다. 즉, 플랫폼에서 첫 로그인 시 정보 제공 동의를 받아 어딘가로부터 저장한다는 뜻이다. 만약 사용자가 서비스로부터 회원 탈퇴를 수행한다면 카카오와의 연동을 해제하여 정보를 완전히 삭제할 필요가 있다.

카카오 API는 아래와 같다.


  • 요청

TXT

0POST https://kapi.kakao.com/v1/user/unlink
1Authorization: Bearer {:access}
2Content-Type: application/x-www-form-urlencoded
parameter type data required description
{:access} header String Y 접근 토큰

  • 응답

JSON

0{
1 "id": 123456789
2}
parameter data description
id long 회원 번호

  • 코드

JAVA

0@Override
1public boolean deleteInfo(String access) throws IOException, ExecutionException, InterruptedException
2{
3 OAuthRequest oAuthRequest = new OAuthRequest(Verb.POST, "https://kapi.kakao.com/v1/user/unlink");
4 oAuthRequest.addHeader("Content-Type", "application/x-www-form-urlencoded");
5 oAuthRequest.addHeader("Authorization", Util.builder("Bearer ", access));
6
7 service.signRequest(access, oAuthRequest);
8
9 return service.execute(oAuthRequest).isSuccessful();
10}

구현은 간단하다. OAuthRequest 객체를 활용하면 요청을 쉽게 생성할 수 있다. 응답 자체는 중요하지 않다. response.isSuccessful() 메서드로 응답이 정상적인지 여부만 판단하여 boolean으로 반환한다.

정보 제공 동의 갱신 URL 반환 메서드 🔗

서비스 운영 중 추가적인 사용자 정보가 필요하거나 필요 없을 때, 사용자 정보 동의 갱신을 통해 동의 정보를 다시 지정할 수 있다.

카카오의 경우, 동의하지않은 선택 정보에 대해서만 동의가 가능하며, 이미 동의한 데이터는 별도의 API로 해제해야한다.


  • 요청

TXT

0GET https://kauth.kakao.com/oauth/authorize?response_type=code&client_id={:client_id}&redirect_uri={:redirect_uri}&state={:state}&scope={:scope}
parameter type data required description
{:response_type} path String Y 응답 타입. code로 고정
{:client_id} path String Y API키
{:redirect_uri} path String Y Callback URL
{:state} path String Y 고유 상태값
{:scope} path String Y 접근 권한

  • 코드

JAVA

0@Override
1public String getUpdateAuthorizationUrl(String state)
2{
3 HashMap<String, String> params = new HashMap<>();
4 params.put("state", state);
5 params.put("scope", "profile_nickname,profile_image,account_email");
6
7 return service.getAuthorizationUrl(params);
8}

null을 반환하여 동작을 수행하지 않도록 처리한다. 추후 프로세스에서 null값이 반환될 경우 별도의 처리를 따른다.

전체 코드 🔗

JAVA

0package oauth.account.module;
1
2import com.fasterxml.jackson.core.JsonProcessingException;
3import com.fasterxml.jackson.databind.JsonNode;
4import com.fasterxml.jackson.databind.ObjectMapper;
5import com.github.scribejava.core.builder.ServiceBuilder;
6import com.github.scribejava.core.builder.ServiceBuilderOAuth20;
7import com.github.scribejava.core.model.OAuth2AccessToken;
8import com.github.scribejava.core.model.OAuthRequest;
9import com.github.scribejava.core.model.Verb;
10import com.github.scribejava.core.oauth.AccessTokenRequestParams;
11import global.module.Util;
12import oauth.account.bean.ApiKeyBean;
13import oauth.account.bean.UserInfoBean;
14
15import java.io.IOException;
16import java.util.HashMap;
17import java.util.concurrent.ExecutionException;
18
19/**
20 * 카카오 인증 모듈 클래스
21 *
22 * @author RWB
23 * @since 2021.10.04 Mon 21:30:49
24 */
25public class KakaoAuthModule extends AuthModule
26{
27 private static final String MODULE_NAME = "kakao";
28
29 private static final String API_KEY;
30 private static final String SECRET_KEY;
31 private static final String CALLBACK_URL;
32
33 static
34 {
35 ApiKeyBean apiKeyBean = getApiKeyBean(MODULE_NAME);
36
37 API_KEY = apiKeyBean.getApi();
38 SECRET_KEY = apiKeyBean.getSecret();
39 CALLBACK_URL = apiKeyBean.getCallback();
40 }
41
42 private static final ServiceBuilderOAuth20 SERVICE_BUILDER = new ServiceBuilder(API_KEY).apiSecret(SECRET_KEY).callback(CALLBACK_URL);
43
44 private static final KakaoAuthModule INSTANCE = new KakaoAuthModule(SERVICE_BUILDER);
45
46 /**
47 * 생성자 메서드
48 *
49 * @param serviceBuilder: [ServiceBuilderOAuth20] API 서비스 빌더
50 */
51 private KakaoAuthModule(ServiceBuilderOAuth20 serviceBuilder)
52 {
53 super(serviceBuilder);
54 }
55
56 /**
57 * 인스턴스 반환 메서드
58 *
59 * @return [KakaoAuthModule] 인스턴스
60 */
61 public static KakaoAuthModule getInstance()
62 {
63 return INSTANCE;
64 }
65
66 /**
67 * 접근 토큰 반환 메서드
68 *
69 * @param code: [String] 인증 코드
70 *
71 * @return [OAuth2AccessToken] 접근 토큰
72 *
73 * @throws IOException 데이터 입출력 예외
74 */
75 @Override
76 public OAuth2AccessToken getAccessToken(String code) throws IOException, ExecutionException, InterruptedException
77 {
78 AccessTokenRequestParams params = new AccessTokenRequestParams(code);
79 params.addExtraParameter("client_id", API_KEY);
80 params.addExtraParameter("client_secret", SECRET_KEY);
81
82 return getAccessToken(params);
83 }
84
85 /**
86 * 유저 정보 객체 반환 메서드
87 *
88 * @param body: [String] OAuth 응답 내용
89 *
90 * @return [UserInfoBean] 유저 정보 객체
91 *
92 * @throws JsonProcessingException JSON 파싱 예외
93 */
94 @Override
95 public UserInfoBean getUserInfoBean(String body) throws JsonProcessingException
96 {
97 ObjectMapper mapper = new ObjectMapper();
98
99 JsonNode node = mapper.readTree(body);
100
101 String email = node.get("kakao_account").get("email") == null ? "미동의" : node.get("kakao_account").get("email").textValue();
102 String name = node.get("kakao_account").get("profile").get("nickname") == null ? "미동의" : node.get("kakao_account").get("profile").get("nickname").textValue();
103 String picture = node.get("kakao_account").get("profile").get("profile_image_url") == null ? "/oauth2/assets/images/logo.png" : node.get("kakao_account").get("profile").get("profile_image_url").textValue();
104
105 return new UserInfoBean(email, name, picture, MODULE_NAME);
106 }
107
108 /**
109 * 연동 해제 결과 반환 메서드
110 *
111 * @param access: [String] 접근 토큰
112 *
113 * @return [boolean] 연동 해제 결과
114 *
115 * @throws IOException 데이터 입출력 예외
116 * @throws ExecutionException 실행 예외
117 * @throws InterruptedException 인터럽트 예외
118 */
119 @Override
120 public boolean deleteInfo(String access) throws IOException, ExecutionException, InterruptedException
121 {
122 OAuthRequest oAuthRequest = new OAuthRequest(Verb.POST, "https://kapi.kakao.com/v1/user/unlink");
123 oAuthRequest.addHeader("Content-Type", "application/x-www-form-urlencoded");
124 oAuthRequest.addHeader("Authorization", Util.builder("Bearer ", access));
125
126 service.signRequest(access, oAuthRequest);
127
128 return service.execute(oAuthRequest).isSuccessful();
129 }
130
131 /**
132 * 정보 제공 동의 갱신 URL 반환 메서드
133 *
134 * @param state: [String] 고유 상태값
135 *
136 * @return [String] 정보 제공 동의 갱신 URL
137 */
138 @Override
139 public String getUpdateAuthorizationUrl(String state)
140 {
141 HashMap<String, String> params = new HashMap<>();
142 params.put("state", state);
143 params.put("scope", "profile_nickname,profile_image,account_email");
144
145 return service.getAuthorizationUrl(params);
146 }
147
148 /**
149 * 접근 토큰 요청 URL 반환 메서드
150 *
151 * @return [String] 접근 토큰 요청 URL
152 */
153 @Override
154 public String getAccessTokenEndpoint()
155 {
156 return "https://kauth.kakao.com/oauth/token";
157 }
158
159 /**
160 * 인증 API 요청 URL 반환 메서드
161 *
162 * @return [String] 인증 API 요청 URL
163 */
164 @Override
165 protected String getAuthorizationBaseUrl()
166 {
167 return "https://kauth.kakao.com/oauth/authorize";
168 }
169
170 /**
171 * 사용자 정보 요청 URL 반환 메서드
172 *
173 * @return [String] 사용자 정보 요청 URL
174 */
175 @Override
176 protected String getUserInfoEndPoint()
177 {
178 return "https://kapi.kakao.com/v2/user/me";
179 }
180}

정리한 전체 코드는 위와 같다.

정리 🔗

네이버의 인증 모듈 구현과 매우 흡사하다. 구현, 오버라이딩해야하는 메서드 모두 동일하다. AuthModule을 통해 공통 모듈로 대체함과 필요 시 오버라이딩을 함으로써 여러 플랫폼에 효과적으로 대응이 가능함을 느낄 수 있다. 객체지향이 왜 유지보수에 유용한지 새삼 깨달을 수 있었다.

이로써 Google 인증 모듈 구현이 완료됐다. 현재까지는 개발 중 단계라 정해진 아이디로만 사용할 수 있다. API 설정에서 테스트 계정을 등록해야 해당 계정으로 로그인 테스트가 가능하다. 심사 이후 애플리케이션이 승인되면 모든 아이디에서 로그인이 가능하다.