KakaoAuthViewModel.kt
import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import com.kakao.sdk.auth.AuthApiClient
import com.kakao.sdk.auth.model.OAuthToken
import com.kakao.sdk.common.model.ClientError
import com.kakao.sdk.common.model.ClientErrorCause
import com.kakao.sdk.user.UserApiClient
import com.kakao.sdk.auth.model.Prompt
import com.kakao.sdk.common.model.KakaoSdkError
const val CUSTOM_PROPERTY_VALUE = “custom_value”
const val CUSTOM_PROPERTY_KEY = “custom_key”
const val TAG = “com.example.test111.KakaoAuthViewModel”
class KakaoAuthViewModel(application: Application) : AndroidViewModel(application) {
private val context = application.applicationContext
fun handleKakaoLogin() {
val callback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
if (error != null) {
Log.e(TAG, "카카오 로그인 실패", error)
} else if (token != null) {
Log.i(TAG, "카카오 로그인 성공 ${token.accessToken}")
}
}
val serviceTerms = listOf("service")
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
UserApiClient.instance.loginWithKakaoAccount(context, prompts = listOf(Prompt.SELECT_ACCOUNT)) { token, error ->
if (error != null) {
Log.e(TAG, "로그인 실패", error)
}
else if (token != null) {
Log.i(TAG, "로그인 성공 ${token.accessToken}")
}
}
UserApiClient.instance.loginWithKakaoTalk(context, serviceTerms = serviceTerms) { token, error ->
if (error != null) {
Log.e(TAG, "카카오톡으로 로그인 실패", error)
if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
return@loginWithKakaoTalk
}
UserApiClient.instance.loginWithKakaoAccount(context, callback = callback)
} else if (token != null) {
Log.i(TAG, "카카오톡으로 로그인 성공 ${token.accessToken}")
}
}
} else {
UserApiClient.instance.loginWithKakaoAccount(context, prompts = listOf(Prompt.LOGIN), callback = callback)
}
// 추가 동의 받기
UserApiClient.instance.me { user, error ->
if (error != null) {
Log.e(TAG, "사용자 정보 요청 실패", error)
} else if (user != null) {
var scopes = mutableListOf<String>()
if (user.kakaoAccount?.emailNeedsAgreement == true) { scopes.add("account_email") }
if (user.kakaoAccount?.birthdayNeedsAgreement == true) { scopes.add("birthday") }
if (user.kakaoAccount?.birthyearNeedsAgreement == true) { scopes.add("birthyear") }
if (user.kakaoAccount?.genderNeedsAgreement == true) { scopes.add("gender") }
if (user.kakaoAccount?.phoneNumberNeedsAgreement == true) { scopes.add("phone_number") }
if (user.kakaoAccount?.profileNeedsAgreement == true) { scopes.add("profile") }
if (user.kakaoAccount?.ageRangeNeedsAgreement == true) { scopes.add("age_range") }
if (user.kakaoAccount?.ciNeedsAgreement == true) { scopes.add("account_ci") }
if (scopes.count() > 0) {
Log.d(TAG, "사용자에게 추가 동의를 받아야 합니다.")
UserApiClient.instance.loginWithNewScopes(context, scopes) { token, error ->
if (error != null) {
Log.e(TAG, "사용자 추가 동의 실패", error)
} else {
Log.d(TAG, "allowed scopes: ${token!!.scopes}")
// 사용자 정보 요청 재시도
UserApiClient.instance.me { user, error ->
if (error != null) {
Log.e(TAG, "사용자 정보 요청 실패", error)
} else if (user != null) {
Log.i(TAG, "사용자 정보 요청 성공")
// 여기서 로그인 완료 처리 등을 할 수 있음
Log.i(TAG, "사용자 정보 요청 성공" +
"\n회원번호: ${user.id}" +
"\n이메일: ${user.kakaoAccount?.email}" +
"\n닉네임: ${user.kakaoAccount?.profile?.nickname}" +
"\n프로필사진: ${user.kakaoAccount?.profile?.thumbnailImageUrl}")
}
}
}
}
}
}
}
// 사용자 정보 저장 부분 추가
val properties = mapOf("${CUSTOM_PROPERTY_KEY}" to "${CUSTOM_PROPERTY_VALUE}")
UserApiClient.instance.updateProfile(properties) { error ->
if (error != null) {
Log.e(TAG, "사용자 정보 저장 실패", error)
}
else {
Log.i(TAG, "사용자 정보 저장 성공")
}
}
UserApiClient.instance.loginWithKakaoAccount(context, prompts = listOf(Prompt.CREATE), callback = callback)
if (AuthApiClient.instance.hasToken()) {
UserApiClient.instance.accessTokenInfo { tokenInfo, error ->
if (error != null) {
Log.e(TAG, "토큰 정보 보기 실패", error)
}
else if (tokenInfo != null) {
Log.i(TAG, "토큰 정보 보기 성공" +
"\n회원번호: ${tokenInfo.id}" +
"\n만료시간: ${tokenInfo.expiresIn} 초")
}
}
} else {
Log.i(TAG, "로그인 필요")
}
UserApiClient.instance.logout { error ->
if (error != null) {
Log.e(TAG, "로그아웃 실패. SDK에서 토큰 삭제됨", error)
}
else {
Log.i(TAG, "로그아웃 성공. SDK에서 토큰 삭제됨")
}
}
UserApiClient.instance.unlink { error ->
if (error != null) {
Log.e(TAG, "연결 끊기 실패", error)
} else {
Log.i(TAG, "연결 끊기 성공. SDK에서 토큰 삭제 됨")
}
}
}
}
MainActivity.kt
package com.example.test111
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
}
MyApplication.kt
package com.example.test111
import android.app.Application
import com.kakao.sdk.common.KakaoSdk
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 다른 초기화 코드들
// Kakao SDK 초기화
KakaoSdk.init(this, "{kakao889595c19cf42c64b1e6b101ed398cbe}")
}
}
build.gradle.kts(Module ;app)
import java.util.Properties
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
}
android {
namespace = “com.example.test111”
compileSdk = 34
defaultConfig {
applicationId = "com.example.test111"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
implementation("com.kakao.sdk:v2-all:2.20.1") // 전체 모듈 설치, 2.11.0 버전부터 지원
implementation("com.kakao.sdk:v2-user:2.20.1") // 카카오 로그인 API 모듈
implementation("com.kakao.sdk:v2-share:2.20.1") // 카카오톡 공유 API 모듈
implementation("com.kakao.sdk:v2-talk:2.20.1") // 카카오톡 채널, 카카오톡 소셜, 카카오톡 메시지 API 모듈
implementation("com.kakao.sdk:v2-friend:2.20.1") // 피커 API 모듈
implementation("com.kakao.sdk:v2-navi:2.20.1") // 카카오내비 API 모듈
implementation("com.kakao.sdk:v2-cert:2.20.1")// 카카오톡 인증 서비스 API 모듈
}
settings.gradle.kts
pluginManagement {
repositories {
google {
content {
includeGroupByRegex(“com\.android.")
includeGroupByRegex("com\.google.”)
includeGroupByRegex(“androidx.*”)
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = java.net.URI(“Repository - Nexus Repository Manager”) }
}
}
rootProject.name = “test111”
include(“:app”)
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:id=“@+id/main”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=“.MainActivity”>
<!-- 카카오톡 버튼 1 - 상단 배치 -->
<ImageButton
android:id="@+id/kakaoButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/kakao_login_medium_narrow"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
지금 이렇게 되어있습니다.
카카오 로그인 버튼을 누르면 로그인이 구현이 되게끔 할려고 계획을 세우고 있습니다.
하지만 문서 페이지에 올라온 예시들 토대로 했으나 코틀린으로 앱 만드는게 익숙치 않아 계속 실패를 합니다.
어떤 것을 수정해야 하는지 알 수 있을까요,?