Android/study

[Android/ReactNative] 카카오 로그인 SDK v2 적용기 (+ Native 모듈 적용)

written by yunwon 2021. 12. 9. 15:17

 

 

 

현재 진행 중인 프로젝트는 하이브리드 앱으로, 내부 웹뷰를 네이트브로 감싸고 있다.

네이티브 쪽에서 로그인 프로세스를 진행하고 이제 웹뷰와 소통하는 방식으로

소셜 로그인이나 프로젝트 자체 API 를 적용하고자 했다.

 

처음에는 react-native-seoul 에서 제공하는 라이브러리를 사용하려 했지만,

웹뷰가 아닌 앱으로 이동되는 UX 를 충족시켜주기 위해서 actbase 라이브러리를 참고했다.

(위 라이브러리도 추가 처리를 해주면 된다는데 왜인지 계속 오류 지옥 ..  🤦‍♀️)

 

정리하자면,

1. 안드로이드 네이티브에서 React 모듈 (bridge) 생성

2. 모듈 내 카카오 로그인 SDK 관련 코드 작성

3. ReactNative (js) 코드 상에 안드로이드 Module 주입

 

 

 


 

 

카카오 로그인

카카오 공식문서의 말을 빌려오면,

 

카카오 로그인은 카카오 계정과 앱을 연결하고 토큰을 발급받아 카카오 API를 사용할 수 있도록 하는 기능이다.

토큰은 사용자를 인증하고 카카오 API 호출 권한을 부여하는 액세스 토큰(Access Token)

액세스 토큰을 갱신하는 데 쓰는 리프레시 토큰(Refresh Token)이 있다.

 

해당 서비스를 이용해서 로그인, 연결, 로그아웃, 연결끊기 등을 구현할 수 있다.

하지만, 카카오 API 가 제공되지 않는 가입ㆍ탈퇴 등 회원 정보에 대한 처리는 자체적으로 구현해야 한다!

 

안드로이드 SDK 의 경우에 카카오톡 설치 유저일 시, 카카오톡에 연결된 카카오계정 정보를 이용하고

미설치 유저 시 카카오 계정 입력 웹 페이지 (새창) 을 이용한다.

 

카카오톡 설치 유저 로그인 프로세스

 

 

지금부터 차근차근 카카오 로그인 SDK 를 적용해보자 💫

 

(1) 기본 설정

  • 카카오 개발자 페이지 내 애플리케이션 등록
  • [제품 설정 -> 카카오 로그인] 활성화 설정 ON & Redirect URI 등록
  • [제품 설정 -> 카카오 로그인 -> 동의항목] 서비스 시작 시 받아올 항목 설정
  • [앱 설정 -> 플랫폼] 키 해시 등록
import com.kakao.sdk.common.util.Utility
var keyHash = Utility.getKeyHash(this)

 

(2) 코드 설정

- build.gradle(Project)

allprojects {    
    repositories {        
        google()        
        jcenter()        
        maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/'}    
    }
}

- build.gradle(Module)

dependencies {
  implementation "com.kakao.sdk:v2-user:2.8.3" // 카카오 로그인
  implementation "com.kakao.sdk:v2-talk:2.8.3" // 친구, 메시지(카카오톡)
  implementation "com.kakao.sdk:v2-story:2.8.3" // 카카오스토리
  implementation "com.kakao.sdk:v2-link:2.8.3" // 메시지(카카오링크)
  implementation "com.kakao.sdk:v2-navi:2.8.3" // 카카오내비
}

- 인터넷 권한 설정 (AndroidManifest.xml)

<uses-permission android:name="android.permission.INTERNET" />

- Java 8 사용 설정 (build.gradle-Module)

android {

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

- 초기화

class GlobalApplication : Application() {
  override fun onCreate() {
    super.onCreate()
    // 다른 초기화 코드들

    // Kakao SDK 초기화
    KakaoSdk.init(this, "{NATIVE_APP_KEY}")
  }
}

 

(3) Redirect URI 설정

카카오 로그인 기능을 구현하기 위해서는 Redirection 을 통해 인가 코드를 받아야 한다.

이를 위해 AndroidManifest.xml 에 액티비티 설정을 해야 한다.

네이티브 키 입력란에 "kakao{abcde}" 이 아닌 "kakaoabcde" 와 같은 형식으로 작성해주어야 한다.

<activity 
    android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        
        <!-- Redirect URI: "kakao{NATIVE_APP_KEY}://oauth" -->
        <data android:host="oauth"
                android:scheme="kakao{NATIVE_APP_KEY}" />
    </intent-filter>
</activity>

 

(4) 카카오 로그인 코드 작성

- 카카오톡으로 로그인

ReactNative 코드 상에서 login 함수 호출 시 동작하는 메서드

카카오 공식 문서에 코틀린으로 작성되어있기에 전부 자바로 바꿔주었다.

 

isKakaoTalkLoginAvailable 을 통해 카카오톡 앱 여부를 확인한 후,

설치자만 카카오톡 앱으로 바로 연결될 수 있도록 작성해주었다.

@ReactMethod
    public void login(final Promise promise) {
        if(!UserApiClient.getInstance().isKakaoTalkLoginAvailable(context)){
            loginWithKakaoAccount(promise);
            return;
        }

        UserApiClient.getInstance().loginWithKakaoTalk(context.getCurrentActivity(), (token, error) -> {
            try {
                if(error != null) {
                    throw error;
                }

                WritableMap map = Arguments.createMap();
                map.putString("accessToken", token.getAccessToken());
                map.putString("refreshToken", token.getRefreshToken());
                promise.resolve(map);
            } catch (Throwable ex) {
            	promise.reject(ex);
            }
            return null;
        });
    }

 

대표적으로 로그인 함수만 작성해보았고, 나머지 메서드들도 유사하게 사용하면 된다.

코틀린으로 작성된 코드는 카카오 공식 문서📜 에서 확인할 수 있다.

 

 

 

 


 

 

안드로이드 네이티브 모듈 생성

(1) 파일 생성

네이티브 모듈을 생성하기 위해서는 모듈 파일과 패키지 파일을 생성해주어야 한다.

android/app/src/main/java/com/your-app-name/ folder

위 경로에 "..Module.java", "..Package.java" 의 형태로 파일을 추가한다.

 

(2) 파일 작성

-Module.java

getName 과 같은 기본 메서드를 작성해주고, 자바스크립트에서 접근이 가능하도록

@ReactMethod 어노테이션을 추가하여 사용하고자 하는 메서드를 작성한다.

package com;

import android.util.Log;

import com.facebook.react.bridge.*;
import com.kakao.sdk.user.UserApiClient;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

public class KakaoLoginModule extends ReactContextBaseJavaModule {
    private static ReactApplicationContext context;

    KakaoLoginModule(ReactApplicationContext context) {
        super(context);
        this.context = context;
    }

    @Override
    public String getName() {
        return "KakaoLoginModule";
    }

    @ReactMethod
    public void login(final Promise promise) {
        if(!UserApiClient.getInstance().isKakaoTalkLoginAvailable(context)){
        	// 카카오톡 미설치 시, "카카오 계정으로 로그인"
            loginWithKakaoAccount(promise);
            return;
        }

        UserApiClient.getInstance().loginWithKakaoTalk(context.getCurrentActivity(), (token, error) -> {
            try {
                if(error != null) {
                    throw error;
                }

		// 네이티브 객체를 전송하는 경우, WritableMapㆍWritableArray 사용
                WritableMap map = Arguments.createMap();
                map.putString("accessToken", token.getAccessToken());
                map.putString("refreshToken", token.getRefreshToken());

                WritableArray scopes = Arguments.createArray();
                List<String> givenScopes = token.getScopes();

                if (givenScopes != null) {
                    for (String scope : givenScopes) {
                        scopes.pushString(scope);
                    }
                }

                map.putArray("scopes", scopes);
                promise.resolve(map);
            } catch (Throwable ex) {
            	promise.reject(ex);
            }
            return null;
        });
}

 

-Package.java

package com;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class KakaoLoginPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new KakaoLoginModule(reactContext));
        return modules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

 

(3) 네이티브 모듈 Link 하기

MainApplication.java (implemets)에 생성한 Native Module 을 연결한다. (14줄)

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost =
      new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
          return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          packages.add(new KakaoLoginPackage());
          return packages;
        }

        @Override
        protected String getJSMainModuleName() {
          return "index";
        }
  };

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
    initializeFlipper(this, getReactNativeHost().getReactInstanceManager());

    KakaoSdk.init(this, getString(R.string.kakao_app_key)); // Kakao SDK 초기화
    Log.d("uunwon", Utility.INSTANCE.getKeyHash(this)); // 키 해시값 출력
  }
}

 

(4) RN (Javascript) 에서 네이티브 모듈 사용하기

import {  NativeModules } from "react-native";

const { KakaoLoginModule } = NativeModules;

function KakaoScreen(props) {
  const signInWithKakao = async () => {
    setResultText("로그인 중..");
    console.log('start login');
    const token = await KakaoLoginModule.login();

    setResultText(JSON.stringify(token));
  };
}

 

(5) Rebuild

# npx react-native run-android

 

 

 

생각보다 구현은 오래 걸리지 않았는데,

어떻게 해야 하는지 그 방식을 찾느라 시간이 조금 걸렸다.

안드로이드는 아무쪼록 잘 마쳤고 .. 마음의 큰 짐인 iOS 가보자고 .. 🏄‍♀️

 

 

 

 

 

© 참고

https://github.com/actbase/react-kakaosdk

https://reactnative.dev/docs/native-modules-android

https://leonkong.cc/posts/react-native-android-bridge.html

 

GitHub - actbase/react-kakaosdk: KakaoSDK Bridge for React, React-Native, RNW

KakaoSDK Bridge for React, React-Native, RNW. Contribute to actbase/react-kakaosdk development by creating an account on GitHub.

github.com