Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat/kakao_login]: 카카오 소셜 로그인, 닉네임 불러오기, 로그아웃 구현 #30

Merged
merged 36 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e24b7b1
[setting]: Fix Merge Error
kez-lab Dec 30, 2023
f0b04fe
[feat]: Feat Login Module
kez-lab Dec 30, 2023
cb31fcc
[feat]: Feat SampleClass
kez-lab Dec 30, 2023
9c0bafe
[setting]: 카카오 sdk 추가
kangyuri1114 Dec 30, 2023
deb2ece
[setting]: 카카오 sdk 추가
kangyuri1114 Dec 31, 2023
7c140d3
[feat]: 인터넷 권한 허용
kangyuri1114 Jan 1, 2024
9ec42cf
[fix]: Debug KeyStore
kez-lab Jan 1, 2024
e5ff432
Merge remote-tracking branch 'origin/feat/kakao_login' into feat/kaka…
kangyuri1114 Jan 1, 2024
b29e663
[feat]: LoginActivity 생성
kangyuri1114 Jan 1, 2024
6991738
[delete]: sampleClass 삭제
kangyuri1114 Jan 1, 2024
394008b
[feat]: LoginActivity viewBinding
kangyuri1114 Jan 1, 2024
afdd143
[feat]: local properties에 KAKAO_NATIVE_APP_KEY 추가
kangyuri1114 Jan 1, 2024
9c95a29
[feat]: 로그인 조합 예제 함수 삽입
kangyuri1114 Jan 1, 2024
84af8ad
[feat]: LoginViewModel 생성
kangyuri1114 Jan 1, 2024
46f8344
[feat]: 카카오 로그인 기능 구현 (앱, 웹)
kangyuri1114 Jan 2, 2024
2c56cdf
[feat]: 로그인 성공 시 유저 정보 반환 액티비티 생성 및 세팅
kangyuri1114 Jan 2, 2024
c3941f6
[feat]: 유저 닉네임 반환, 카카오 로그아웃 기능 구현
kangyuri1114 Jan 2, 2024
74c9e8f
[refact]: 카카오 로그인 코드 리팩토링
kangyuri1114 Jan 2, 2024
3bc8510
[fix]: Fix Navigation Resource
kez-lab Jan 2, 2024
3b8cbf4
[fix]: Fix Common Dependency
kez-lab Jan 2, 2024
27d78da
Merge remote-tracking branch 'origin/develop' into develop
kangyuri1114 Jan 2, 2024
c70cb1c
[fix]: resolve conflict
kangyuri1114 Jan 2, 2024
eb2e8f6
[fix]: resolve conflict
kangyuri1114 Jan 2, 2024
3e804a1
[feat]: viewModel, Livedata 세팅
kangyuri1114 Jan 2, 2024
dcb394e
[fix]: Fix Kakao Api Key
kez-lab Jan 3, 2024
8f61225
[feat]: 카카오 로그인 버튼 추가
kangyuri1114 Jan 3, 2024
cde45aa
[feat]: 로그인 컨셉설명 뷰페이저 UI 구현
kangyuri1114 Jan 3, 2024
74bf456
[delete]: 프래그먼트 파일 삭제
kangyuri1114 Jan 3, 2024
5b5c5b0
[feat]: 로그인 화면 뷰페이저 구현
kangyuri1114 Jan 3, 2024
cbcd38d
[fix]: Fix properties reference
kez-lab Jan 3, 2024
8b196a6
Merge branch 'feat/login_view' into feat/kakao_login
kangyuri1114 Jan 3, 2024
f39c00e
[fix]: indicator 색 변경
kangyuri1114 Jan 3, 2024
811a6d5
[fix]: id 명 변경
kangyuri1114 Jan 3, 2024
acb4224
[fix]: id 명 변경
kangyuri1114 Jan 3, 2024
66850b4
[fix]: 뷰페이저 함수화
kangyuri1114 Jan 3, 2024
75aa724
[fix]: 뷰페이저 함수화
kangyuri1114 Jan 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ render.experimental.xml

# Keystore files
*.jks
*.keystore

# Google Services (e.g. APIs or Firebase)
google-services.json
Expand Down
42 changes: 32 additions & 10 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties

plugins {
hmh("application")
hmh("compose")
Expand All @@ -14,11 +16,33 @@ android {
applicationId = "com.hmh.hamyeonham"
versionCode = libs.versions.versionCode.get().toInt()
versionName = libs.versions.appVersion.get()

buildConfigField(
"String",
"KAKAO_NATIVE_APP_KEY",
gradleLocalProperties(rootDir).getProperty("kakao.native.app.key"),
)
manifestPlaceholders["KAKAO_NATIVE_APP_KEY"] =
gradleLocalProperties(rootDir).getProperty("kakao.native.app.key")
}

signingConfigs {
getByName("debug") {
keyAlias = "android_debug_key"
keyPassword = "android"
storeFile = File("${project.rootDir.absolutePath}/keystore/debug.keystore")
storePassword = "android"
}
}

buildTypes {
debug {
isDebuggable = true
signingConfig = signingConfigs.getByName("debug")
}
release {
isMinifyEnabled = false
isDebuggable = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
Expand All @@ -28,21 +52,19 @@ android {
}

dependencies {
// Feature
implementation(projects.feature.onboarding)

// Core
implementation(projects.core.common)
implementation(projects.core.database)

// Feature
implementation(projects.feature.onboarding)
implementation(projects.feature.main)

// Firebase
implementation(platform(libs.firebase))
implementation(libs.bundles.firebase)

// Splash
implementation(libs.splash.screen)

// Features
implementation(projects.feature.login)
implementation(projects.feature.onboarding)
implementation(projects.feature.main)

// kakao
implementation(libs.kakao.login)
}
18 changes: 18 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

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

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -21,8 +23,24 @@
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>
</activity>

<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" />

<data android:host="oauth"
android:scheme="kakao${KAKAO_NATIVE_APP_KEY}" />
</intent-filter>
</activity>


</application>

</manifest>
11 changes: 9 additions & 2 deletions app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import android.view.animation.AnimationUtils
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.hmh.hamyeonham.feature.login.LoginActivity
import com.kakao.sdk.common.KakaoSdk
import com.hmh.hamyeonham.feature.main.MainActivity
import com.hmh.hamyeonham.feature.onboarding.OnBoardingActivity

class SampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -18,7 +19,13 @@ class SampleActivity : AppCompatActivity() {
initSplashAnimation(splashScreen)

setContentView(R.layout.activity_sample)
Intent(this, MainActivity::class.java).let(::startActivity)

initKakaoSdk()
Intent(this, LoginActivity::class.java).let(::startActivity)
}

private fun initKakaoSdk() {
KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY)
}

private fun initSplashAnimation(splashScreen: SplashScreen) {
Expand Down
1 change: 1 addition & 0 deletions feature/login/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
19 changes: 19 additions & 0 deletions feature/login/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
hmh("feature")
alias(libs.plugins.kotlin.android)
}

android {
namespace = "com.hmh.hamyeonham.feature.login"
}

dependencies {
implementation(projects.core.common)
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.constraintlayout)

// kakao
implementation(libs.kakao.login)
}
Empty file.
21 changes: 21 additions & 0 deletions feature/login/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
13 changes: 13 additions & 0 deletions feature/login/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application>
<activity
android:name=".UserInfoActivity"
android:exported="false" />
<activity
android:name=".LoginActivity"
android:exported="false" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.hmh.hamyeonham.feature.login

import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.hmh.hamyeonham.common.context.toast
import com.hmh.hamyeonham.feature.login.databinding.ActivityLoginBinding
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

class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding

private val loginViewModel: LoginViewModel by viewModels()

private val callback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
when {
error != null -> {
}

token != null -> {
moveToUserInfoActivity()
}
}
}

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

binding.btnLogin.setOnClickListener {
loginWithKakaoApp()
}
}

private fun loginWithKakaoApp() {
if (UserApiClient.instance.isKakaoTalkLoginAvailable(this)) {
UserApiClient.instance.loginWithKakaoTalk(this) { token, error ->
if (error != null) {
toast("카카오 로그인 실패")
if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
toast("다시 로그인 해주세요.")
return@loginWithKakaoTalk
}
loginWithKakaoAccount()
} else if (token != null) {
toast("카카오 로그인 성공")
moveToUserInfoActivity()
}
}
} else {
loginWithKakaoAccount()
}
}
Comment on lines +64 to +82
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기로직은 추후 관심사 분리가 필요하겠죠!?! 한번 확인해주시고 또 다른 예외상황이 있는지도 확인해주세요~!!!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵! 지금 뷰모델 분리하면서 조금 더 세분화 하고 있고, 예외의 경우도 공식문서에서는 유저가 임의로 로그인을 이탈하거나 취소하는 경우만 나와있어서 그 부분만 구현했는데, 또 다른 경우도 고려해보겠습니다!


private fun loginWithKakaoAccount() {
UserApiClient.instance.loginWithKakaoAccount(this, callback = callback)
}

private fun moveToUserInfoActivity() {
startActivity(Intent(this, UserInfoActivity::class.java))
finish()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.hmh.hamyeonham.feature.login

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class LoginViewModel : ViewModel(){

private val _kakaoLoginResult = MutableLiveData<Boolean>()
val kakaoLoginResult: LiveData<Boolean> get() = _kakaoLoginResult
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굿!@!@

Comment on lines +9 to +10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요 값이 왜 있는지 모르겠네욥?!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

뷰모델 분리하기 위해 세팅만 해두고 아직 코드가 완성되지 않아 올리지 못했습니다 ㅎㅎ.. kakaoLoginResult 라이브 데이터의 결과에 따라 로그인 액티비티에서 분기처리 진행할 예정입니다!


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.hmh.hamyeonham.feature.login

import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.hmh.hamyeonham.common.context.toast
import com.hmh.hamyeonham.feature.login.databinding.ActivityUserInfoBinding
import com.kakao.sdk.user.UserApiClient

class UserInfoActivity : AppCompatActivity() {
private lateinit var binding: ActivityUserInfoBinding

private val userInfoViewModel: UserInfoViewModel by viewModels()
Copy link
Collaborator

@jihyun0v0 jihyun0v0 Jan 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

viewModel을 사용한 곳이 안보이는데, 외부 API에서 사용되는 건가요?? 궁금하네요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아직 뷰모델 코드가 반영이 안되어 있습니답 ㅎ,, 릴리즈 위해서 기능 구현 우선 했습니다


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

getKakaoUserNickname()
binding.btnLoginLogout.setOnClickListener {
logoutFromKakao()
}
}

private fun getKakaoUserNickname() {
UserApiClient.instance.me { user, error ->
if (error != null) {
moveToLogin()
} else if (user != null) {
val kakaoNickname = user.kakaoAccount?.profile?.nickname
binding.tvLoginNickname.text = kakaoNickname
}
}
}

private fun logoutFromKakao() {
UserApiClient.instance.logout { error ->
if (error != null) {
toast("다시 로그아웃 해주세요.")
} else {
toast("로그아웃 되었습니다.")
moveToLogin()
}
}
}

private fun moveToLogin() {
startActivity(Intent(this, LoginActivity::class.java))
finish()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.hmh.hamyeonham.feature.login

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class UserInfoViewModel : ViewModel() {

private val _kakaoLogoutResult = MutableLiveData<Boolean>()
val kakaoLogoutResult: LiveData<Boolean> get() = _kakaoLogoutResult

private val _kakaoUserNickname = MutableLiveData<Boolean>()
val kakaoUserNickname: LiveData<Boolean> get() = _kakaoUserNickname
}
32 changes: 32 additions & 0 deletions feature/login/src/main/res/layout/activity_login.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?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=".LoginActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="Login"
android:textSize="30sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />


<Button
android:id="@+id/btn_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:text="Login"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

/>

</androidx.constraintlayout.widget.ConstraintLayout>
Loading
Loading