[Android/Kotlin]KaKao Login

์ตœ์ง€์›ยท2024๋…„ 1์›” 31์ผ
0

[Android/Kotlin]

๋ชฉ๋ก ๋ณด๊ธฐ
4/9

Kakao Login ์‚ฌ์šฉ ์‹ ์ฒญ

๐Ÿ”’์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ์‚ฌ์šฉ์ž ์ธ์ฆ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ฒฝ์šฐ์— ์ ํ•ฉํ•œ ๊ธฐ์ˆ ์ค‘ ํ•˜๋‚˜์ธ ์นด์นด์˜ค ๋กœ๊ทธ์ธ์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Kakao Developer ์ ‘์†

Kakao Developer์— ์ ‘์† ํ›„ ๋กœ๊ทธ์ธํ•ฉ๋‹ˆ๋‹ค.

๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ถ”๊ฐ€

  1. ์œ„ ํ™”๋ฉด์—์„œ ๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํด๋ฆญ.
  2. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ถ”๊ฐ€ํ•˜๊ธฐ ํด๋ฆญ.
  3. ์•ฑ ์ด๋ฆ„, ์‚ฌ์—…์ž๋ช…, ์นดํ…Œ๊ณ ๋ฆฌ ์ž‘์„ฑ ํ›„ ์ €์žฅ.

๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋ณธ ์„ค์ •

  1. ์•ฑ ์„ค์ • -> ํ”Œ๋žซํผ -> Android ํ”Œ๋žซํผ ๋“ฑ๋ก ํด๋ฆญ.
  2. ํŒจํ‚ค์ง€๋ช…, ํ•ด์‹œ ํ‚ค ์ž…๋ ฅ ํ›„ ์ €์žฅ.

๐Ÿ“Œ ํ•ด์‹œ ํ‚ค๊ฐ€ ๋ญ์ฃ ...?

์•ˆ๋“œ๋กœ์ด๋“œ ํ•ด์‹œ ํ‚ค๋Š” ์•ˆ์ „ํ•œ ํ†ต์‹ ์„ ๋ณด์žฅํ•˜๊ณ  ์•ฑ์˜ ๋ฌด๋‹จ ์‚ฌ์šฉ์„ ๋ฐฉ์ง€ํ•˜๋ฉฐ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ์˜ ๋ณด์•ˆ์„ ์œ ์ง€ํ•˜๋Š”๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฐ ์•ฑ์€ ๊ณ ์œ ํ•œ ์„œ๋ช… ํ‚ค๋ฅผ ๊ฐ€์ง€๋ฉฐ, ํŠน์ • ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์„ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ์นด์นด์˜ค๋Š” ๊ฐ ์•ฑ์„ ์‹๋ณ„ํ•˜๊ณ  ํŠน์ • ์•ฑ์— ๋Œ€ํ•œ ๋ณด์•ˆ ๋ฐ ๊ถŒํ•œ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ๊ทธ๋ž˜์„œ ์–ด๋–ป๊ฒŒ ์–ป๋Š”๊ฑฐ์ฃ ...?

private fun getAppKeyHash() {
        try {
            val info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
            for (signature in info.signatures) {
                val md: MessageDigest = MessageDigest.getInstance("SHA")
                md.update(signature.toByteArray())
                val hashKey = String(Base64.encode(md.digest(), 0))
                Log.e(TAG, "ํ•ด์‹œํ‚ค : $hashKey")
            }
        } catch (e: Exception) {
            Log.e(TAG, "ํ•ด์‹œํ‚ค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค : $e")
        }
    }

์ด ๋ฉ”์„œ๋“œ๋ฅผ onCreate()์—์„œ ํ˜ธ์ถœํ•ด์„œ logcat์„ ํ™•์ธํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  1. ์•ฑ ์„ค์ • -> ์š”์•ฝ ์ •๋ณด -> ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ ํ‚ค ๋ณต์‚ฌ

์นด์นด์˜ค ๋กœ๊ทธ์ธ ํ™œ์„ฑํ™”

  1. ์ œํ’ˆ ์„ค์ • -> ์นด์นด์˜ค ๋กœ๊ทธ์ธ -> on/off๋ฒ„ํŠผ ๋ˆŒ๋Ÿฌ์„œ ํ™œ์„ฑํ™”
  2. ์นด์นด์˜ค ๋กœ๊ทธ์ธ -> ๋™์˜ ํ•ญ๋ชฉ -> ๋‹‰๋„ค์ž„, ํ”„๋กœํ•„ ์‚ฌ์ง„ 'ํ•„์ˆ˜ ๋™์˜'๋กœ ์„ค์ •.

์•ˆ๋“œ๋กœ์ด๋“œ ์—์„œ ์นด์นด์˜ค SDK์‚ฌ์šฉ

AndroidManifest.xml

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

<activity
    android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
    android:exported="true">
<activity
    android:name=".MainActivity"
    android:exported="false">

</activity>
<activity android:name=".LoginActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
    <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="@string/kakao_redirection_scheme" />
    </intent-filter>
</activity>

๋งค๋‹ˆํŽ˜์ŠคํŠธ์— ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” ์นด์นด์˜ค ๋กœ๊ทธ์ธ์„ ์œ„ํ•ด ํ•„์š”ํ•œ ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์ •์˜ํ•˜๊ณ , ํ•ด๋‹น ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์–ด๋–ค ์ธํ…ํŠธ๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

build.gradle(:app)

implementation "com.kakao.sdk:v2-all:2.17.0"

settings.gradle(project)

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/'}
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<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/userName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textColor="@color/black"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/userThumbnail"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/userName" />
    
</androidx.constraintlayout.widget.ConstraintLayout>

activity_login.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/kakaoLogin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:background="@drawable/kakao_login_medium_wide"
        android:layout_margin="20dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

์—ฌ๊ธฐ์„œ ์นด์นด์˜ค ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ๋””์ž์ธ์€ ๋””์ž์ธ ๊ฐ€์ด๋“œ๋ฅผ ๋”ฐ๋ผ์„œ 'kakao_login_medium_wide'๋ฅผ ๋‹ค์šด ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

LoginActivity.kt

import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.kakaologin.databinding.ActivityLoginBinding
import com.kakao.sdk.auth.model.OAuthToken
import com.kakao.sdk.common.KakaoSdk
import com.kakao.sdk.user.UserApiClient

class LoginActivity: AppCompatActivity() {

    private lateinit var binding: ActivityLoginBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityLoginBinding.inflate(layoutInflater)
        setContentView(binding.root)

        KakaoSdk.init(this, getString(R.string.kakao_native_app_key))

        binding.kakaoLogin.setOnClickListener {
            login()
        }
    }

    private fun login(){
        if (UserApiClient.instance.isKakaoTalkLoginAvailable(this)) loginWithKakaoTalk()
        else loginWithKakaoAccount()
    }

    private fun loginWithKakaoTalk(){
        UserApiClient.instance.loginWithKakaoTalk(this){ token, error ->
            handleLoginResult(token, error)
        }
    }

    private fun loginWithKakaoAccount(){
        UserApiClient.instance.loginWithKakaoAccount(this) { token, error ->
            handleLoginResult(token, error)
        }
    }

    private fun handleLoginResult(token: OAuthToken?, error: Throwable?){
        if (error != null){
            Toast.makeText(this, "๋กœ๊ทธ์ธ ์‹คํŒจ!", Toast.LENGTH_SHORT).show()
        } else if (token != null){
            fetchUserInfo()
        }
    }

    private fun fetchUserInfo(){
        UserApiClient.instance.me { user, error ->
            if (error != null){
                Toast.makeText(this, "์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ ์‹คํŒจ!", Toast.LENGTH_SHORT).show()
            } else if (user != null){
                val userId = user.id
                val userName = user.kakaoAccount?.profile?.nickname
                val userThumbnail = user.kakaoAccount?.profile?.thumbnailImageUrl

                val intent = Intent(this@LoginActivity, MainActivity::class.java)
                intent.putExtra("userId", userId)
                intent.putExtra("userName", userName)
                intent.putExtra("userThumbnail", userThumbnail)

                startActivity(intent)
            }
        }
    }
}
  • KakaoSdk.init(this, getString(R.string.kakao_native_app_key)) : KakaoSdk ์ดˆ๊ธฐํ™”.
  • ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ login()๋ฉ”์„œ๋“œ ํ˜ธ์ถœ.
  • login() : ์นด์นด์˜คํ†ก์œผ๋กœ ๋กœ๊ทธ์ธ์ด ๊ฐ€๋Šฅํ•œ์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธ. ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ์นด์นด์˜คํ†ก์œผ๋กœ ๋กœ๊ทธ์ธ, ์•„๋‹ˆ๋ผ๋ฉด ์นด์นด์˜ค ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธ.
  • loginWithKakaoTalk() : KakaoTalk์„ ํ†ตํ•ด ๋กœ๊ทธ์ธํ•˜๋Š” ๋ฉ”์„œ๋“œ.
  • loginWithKakaoAccount() : Kakao ๊ณ„์ •์„ ํ†ตํ•ด ๋กœ๊ทธ์ธํ•˜๋Š” ๋ฉ”์„œ๋“œ.
  • handleLoginResult() : ๋กœ๊ทธ์ธ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ”์„œ๋“œ. ์„ฑ๊ณตํ•˜๋ฉด ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ด.
  • fetchUserInfo() : ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์„œ๋“œ. ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด MainActivity๋กœ ์ด๋™.

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val userId = intent.getStringExtra("userId")
        val userName = intent.getStringExtra("userName")
        val userThumbnail = intent.getStringExtra("userThumbnail")

        binding.userName.text = userName
        Glide.with(binding.userThumbnail)
            .load(userThumbnail)
            .into(binding.userThumbnail)
    }
}

์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ์ธ๋„ค์ผ์„ intent๋กœ ๊ฐ€์ ธ์˜จ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ธ๋„ค์ผ์„ค์ •์€ Glide๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

plugins {
    id 'kotlin-kapt'
}

dependencies {
    // Glide
    implementation 'com.github.bumptech.glide:glide:4.15.1'
    kapt 'com.github.bumptech.glide:compiler:4.15.1'
}

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

profile
์•ˆ๋“œ๋กœ์ด๋“œ, ํ”Œ๋Ÿฌํ„ฐ ์ฃผ๋‹ˆ์–ด์ž…๋‹ˆ๋‹ค

0๊ฐœ์˜ ๋Œ“๊ธ€