Nuke Olaf - Log Store

[Android] 카카오 로그인 API 사용하기 (feat. Kotlin) 본문

Android

[Android] 카카오 로그인 API 사용하기 (feat. Kotlin)

NukeOlaf 2020. 3. 26. 15:05

1. 카카오 API 란?

카카오 플랫폼을 기반으로 하여 앱 개발에 필요한 기능을 제공하는 API 이다.
현재 사용자 관리, 푸시 알림, 앱 로그 분석, 지도, 번역 등 다양한 기능을 제공하고 있다.

우리가 사용하려는 카카오 로그인 API 는 카카오 계정으로 로그인하고, 로그인 한 계정에서 이메일이나 계정 이름 등의 정보를 가져오는 API 이다.

Kakao Developers 사이트에 접속하면 좀 더 자세한 내용을 확인할 수 있다.

https://developers.kakao.com/features/platform

 

2. 우리 앱에서 카카오 로그인을 사용해야하는 이유?

사용자 관리를 위해 회원 가입시 사용자의 개인정보를 받아와야 한다. 그런데 이게 개인 정보다 보니 법적으로 문제가 없도록 회원가입 약관을 만들고 처리해야 하는데, 이 과정이 번거롭다는 얘기가 있었다. 또한, 회원가입 과정이 어렵고 귀찮은 경우, 가입을 포기하는 사용자가 생길 수 있다. 그리고 아이디 비밀번호를 통해 로그인하는 경우, 아이디와 비밀번호를 기억하는 것이 사용자에게 귀찮게 느껴질 수 있다.

이러한 문제를 해결하기 위해 Metaler 프로젝트에서는 소셜 로그인 기능만을 사용하여 사용자를 관리하기로 결정했다.

 

3. 카카오 API 사용하기

3.1 개발환경 구성

(1) build.gradle(Project) 에 해당 코드를 추가한다

subprojects {
    repositories {
        mavenCentral()
        maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' }
    }
}

(2) build.gradle(Module: app) 의  dependencies 에 아래 내용을 추가한다

2020년 3월 27일 시점 가장 최신 버전인 1.28.0 을 추가했다.

dependencies {
	// 카카오 로그인 sdk를 사용하기 위해 필요.
	implementation 'com.kakao.sdk:usermgmt:1.28.0'
}

implementation group: 'com.kakao.sdk', name: 'usermgmt', version: project.KAKAO_SDK_VERSION

(3) values/strings.xml 폴더에 kakao_api_key 를 넣어준다.

<resources>
    <string name="app_name">kakao-login-example</string>
    <string name="kakao_app_key">AAAAAAAAAAAAAAAAAAAAAA</string>
</resources>

(4) Manifest 파일에 인터넷 권한추가

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

(5) Manifest <application> 태그 안에 네이티브 앱 키 입력

<meta-data
            android:name="com.kakao.sdk.AppKey"
            android:value="@string/kakao_app_key" />

 

3.2 레이아웃에 카카오 로그인 버튼 추가

xml 레이아웃 파일에 카카오 로그인 버튼을 추가해준다.

카카오 로그인 API 에서 기본적으로 제공해주는 카카오 로그인 버튼을 사용하면 된다.
물론, 커스텀한 버튼에 카카오 로그인을 연동해도 괜찮다.

<com.kakao.usermgmt.LoginButton
        android:id="@+id/com_kakao_login"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_marginBottom="30dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp" />

 

3.3 GloablApplication 클래스를 작성하고 Manifest name 에 추가한다

KakaoSDK를 사용하기 위해선 SDK와 Application을 연결해 주어야 하며, 이때 사용되는 객체는 KakaoAdapter이다. KakaoAdapter를 통해서 SDK에 필요한 정보를 제공해 주어야 하며 각각 IApplicationConfig ISessionConfig를 구현해 주어야 한다.

class GlobalApplication : Application() {

    private object KakaoSDKAdapter : KakaoAdapter() {
        /**
         * Session Config에 대해서는 default값들이 존재한다.
         * 필요한 상황에서만 override해서 사용하면 됨.
         * @return Session의 설정값.
         */
        override fun getSessionConfig(): ISessionConfig {
            return object : ISessionConfig {
                override fun getAuthTypes(): Array<AuthType> {
                    return arrayOf(AuthType.KAKAO_LOGIN_ALL)
                    // 로그인을 하는 방식을 지정하는 부분. 
                    // AuthType 로는 다음 네 가지 방식이 있다.
                    // KAKAO_TALK: 
                    // 카카오톡으로 로그인, KAKAO_STORY: 카카오스토리로 로그인
                    // KAKAO_ACCOUNT: 
                    // 웹뷰를 통한 로그인,
                    // KAKAO_TALK_EXCLUDE_NATIVE_LOGIN: 
                    // 카카오톡으로만 로그인+계정 없으면 계정생성 버튼 제공
                    // KAKAO_LOGIN_ALL: 
                    // 모든 로그인방식 사용 가능. 
                    // 정확히는, 카카오톡이나 카카오스토리가 있으면 그 쪽으로 로그인 기능을 제공하고, 
                    // 둘 다 없으면 웹뷰를 통한 로그인을 제공한다
                }

                override fun isUsingWebviewTimer(): Boolean {
                    return false
                    // SDK 로그인시 사용되는 WebView 에서 
                    // pause 와 resume 시에 Timer 를 설정하여 CPU소모를 절약한다.
                    // true 를 리턴할경우 webview 로그인을 사용하는 화면에서 
                    // 모든 webview 에 onPause 와 onResume 시에 Timer 를 설정해 주어야 한다.
                    // 지정하지 않을 시 false 로 설정된다.
                }

                override fun isSecureMode(): Boolean {
                    return false
                    // 로그인시 access token과 refresh token을 저장할 때의 암호화 여부를 결정한다.
                }

                override fun getApprovalType(): ApprovalType? {
                    return ApprovalType.INDIVIDUAL
                    // 일반 사용자가 아닌 Kakao와 제휴된 앱에서만 사용되는 값으로, 
                    // 값을 채워주지 않을경우 ApprovalType.INDIVIDUAL 값을 사용하게 된다.
                }

                override fun isSaveFormData(): Boolean {
                    return true
                    // Kakao SDK 에서 사용되는 WebView에서 
                    // email 입력폼의 데이터를 저장할지 여부를 결정한다.
                    // true일 경우, 다음번에 다시 로그인 시 email 폼을 누르면 
                    // 이전에 입력했던 이메일이 나타난다.
                }
            }
        }

        override fun getApplicationConfig(): IApplicationConfig {
            return IApplicationConfig { getGlobalApplicationContext()?.applicationContext!! }
        }
    }

    companion object {
        var instance: GlobalApplication? = null

        fun getGlobalApplicationContext() : GlobalApplication? {
            checkNotNull(this) { "this application does not inherit com.kakao.GlobalApplication" }
            return instance
        }
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
        KakaoSDK.init(KakaoSDKAdapter)
    }

    override fun onTerminate() {
        super.onTerminate()
        instance = null
    }
}

GloabalApplication 을 작성하고 난 뒤, Mainfest application 태그에 추가해준다.

<application
        android:name=".GlobalApplication"
        android:allowBackup="true"
        ...

 

3.4 로그인 Activity 구현

* 앱의 해쉬 키 얻는 코드를 통해 해쉬 키를 얻어 카카오 developer 사이트에 등록해주어야 한다.

class LoginActivity : AppCompatActivity() {

    private lateinit var callback: SessionCallback

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        callback = SessionCallback()
        Session.getCurrentSession().addCallback(callback)
        Session.getCurrentSession().checkAndImplicitOpen()

    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

        if (Session.getCurrentSession().handleActivityResult(requestCode, resultCode, data)) {
            return
        }
        super.onActivityResult(requestCode, resultCode, data)
    }

    override fun onDestroy() {
        super.onDestroy()
        Session.getCurrentSession().removeCallback(callback)
    }

    // 앱의 해쉬 키 얻는 함수
    private fun getAppKeyHash() {
        try {
            val info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
            for (signature in info.signatures) {
                val md: MessageDigest
                md = MessageDigest.getInstance("SHA")
                md.update(signature.toByteArray())
                val something = String(Base64.encode(md.digest(), 0))
                Log.e("Hash key", something)
            }
        } catch (e: Exception) {
            // TODO Auto-generated catch block
            Log.e("name not found", e.toString())
        }

    }

    private inner class SessionCallback : ISessionCallback {
        override fun onSessionOpened() {
            // 로그인 세션이 열렸을 때
            UserManagement.getInstance().me( object : MeV2ResponseCallback() {
                override fun onSuccess(result: MeV2Response?) {
                    // 로그인이 성공했을 때
                    var intent = Intent(this@LoginActivity, MainActivity::class.java)
                    intent.putExtra("name", result!!.getNickname())
                    intent.putExtra("profile", result!!.getProfileImagePath())
                    startActivity(intent)
                    finish()
                }

                override fun onSessionClosed(errorResult: ErrorResult?) {
                    // 로그인 도중 세션이 비정상적인 이유로 닫혔을 때
                    Toast.makeText(
                        this@LoginActivity,
                        "세션이 닫혔습니다. 다시 시도해주세요 : ${errorResult.toString()}",
                        Toast.LENGTH_SHORT).show()
                }
            })
        }
        override fun onSessionOpenFailed(exception: KakaoException?) {
            // 로그인 세션이 정상적으로 열리지 않았을 때
            if (exception != null) {
                com.kakao.util.helper.log.Logger.e(exception)
                Toast.makeText(
                    this@LoginActivity,
                    "로그인 도중 오류가 발생했습니다. 인터넷 연결을 확인해주세요 : $exception",
                    Toast.LENGTH_SHORT).show()
            }
        }

    }

    private fun redirectSignupActivity() {
        val intent = Intent(this, LoginActivity::class.java)
        startActivity(intent)
        finish()
    }

}

 

3.5 MainActivity 구현

메인 액티비티에서는 카카오 로그인을 통해 받아온 사용자 정보를 화면에 보여주는 기능을 한다

activity_main 레이아웃 리소스

<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="25dp"
        android:layout_marginTop="25dp"
        android:textSize="20sp"
        android:text="닉네임: "
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tvNickname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="15dp"
        android:layout_marginTop="25dp"
        android:textSize="20sp"
        android:text="..."
        app:layout_constraintStart_toEndOf="@+id/textView"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="25dp"
        android:layout_marginTop="20dp"
        android:textSize="15sp"
        android:text="프로필사진: "
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <TextView
        android:id="@+id/tvProfile"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:textSize="14sp"
        android:text="..."
        app:layout_constraintTop_toTopOf="@+id/textView3"
        app:layout_constraintStart_toEndOf="@+id/textView3" />

    <ImageView
        android:id="@+id/imgProfile"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        tvNickname.text = intent.getStringExtra("name")
        tvProfile.text = intent.getStringExtra("profile")

        Glide.with(this)
            .load(intent.getStringExtra("profile"))
            .into(imgProfile)

    }
}

 

참고>>>

KakaoDevelopers : https://developers.kakao.com/docs/android

https://developers.kakao.com/docs/android/user-management

https://m.blog.naver.com/woo171tm/221459351482

카카오 로그인 api 예제 : https://github.com/Candykick/KakaoLogin

Comments