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

#72 [feat] 카카오 로그인 구현 #82

Merged
merged 30 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c6b808a
#72 [add] login activity 생성
stellar-halo Jan 17, 2024
bb712af
#72 [add] kakao login을 위한 build.gradle 수정 및 추가
stellar-halo Jan 17, 2024
86e1ea6
#72 [chore] 불필요 파일 수정
stellar-halo Jan 17, 2024
0362ba2
#72 [add] 더블 클릭 시, 앱 종료 확장 함수
stellar-halo Jan 17, 2024
9a066b6
#72 [add] kakao login을 위한 setting.gradle maven 추가
stellar-halo Jan 17, 2024
fc11483
#72 [add] kakao login을 위한 com.kakao.sdk.auth.AuthCodeHandlerActivity 생성
stellar-halo Jan 17, 2024
7259e21
#72 [feat] auth레포지토리 생성 및 module에서 Impl binding
stellar-halo Jan 17, 2024
1e5a3fd
#72 [add] 터치 한 번만 가능한 singleClickListener
stellar-halo Jan 17, 2024
57c17a5
#72 [add] string 추가
stellar-halo Jan 17, 2024
7de912b
#72 [fix] SoftieApplication에 kakao native app key 설정
stellar-halo Jan 17, 2024
635918d
#72 [chore] 카카오 로그인 이미지, 곰 인사 로티 파일 추가
stellar-halo Jan 17, 2024
ef535d6
#72 [add] postLogin을 위한 data class 생성
stellar-halo Jan 17, 2024
e77b9fb
#72 [feat] AuthDataSource 생성 및 postLogin
stellar-halo Jan 17, 2024
f70f84b
#72 [feat] AuthService에 postLogin 추가
stellar-halo Jan 17, 2024
064d1c4
#72 [feat] kakaoLogin callback 클래스 생성
stellar-halo Jan 17, 2024
5bfc823
#72 [feat] postLoginUseCase
stellar-halo Jan 17, 2024
8d4c177
#72 [feat] token 저장하는 useCase 생성
stellar-halo Jan 17, 2024
afbaa62
#72 [feat] AuthRepositoryImpl 생성
stellar-halo Jan 17, 2024
0f39f50
#72 [feat] sharedPreference에 token값 저장하는 localDataSource 생성
stellar-halo Jan 17, 2024
e96dd1f
#72 [feat] 카카오 앱이 있으면, 앱으로 연결하고 없으면 계정 로그인으로 연결하는 KakaoLoginService 생성
stellar-halo Jan 17, 2024
2e7cee5
#72 [feat] 의존성 주입을 위한 SharedPrefModule 생성
stellar-halo Jan 17, 2024
092e39d
#72 [feat] 로그인 로직을 위한 activity, viewModel 생성
stellar-halo Jan 17, 2024
0cec856
#72 [fix] LocalDataSource에 저장된 access token을 기준으로 서버 통신
stellar-halo Jan 17, 2024
a7ba2bf
Merge remote-tracking branch 'origin/develop' into feature/#72-feat-k…
stellar-halo Jan 17, 2024
73da622
#72 [feat] sharedPreference에 회원가입한 사용자인지 아닌지 분기처리를 위한 UseCase
stellar-halo Jan 17, 2024
09714f7
#72 [feat] sharedPreference에 회원가입한 사용자인지 아닌지 boolean값 추가
stellar-halo Jan 17, 2024
5e3f356
#72 [feat] splash에서 회원가입한 사용자인지 아닌지 구분한 후에 온보딩으로 보낼지 main으로 보낼지 처리
stellar-halo Jan 17, 2024
371e981
#72 [fix] lint check
stellar-halo Jan 17, 2024
5dd25e2
#72 [fix] lint check
stellar-halo Jan 17, 2024
492a3b1
#72 [fix] 주석 삭제
stellar-halo Jan 17, 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
5 changes: 5 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "BASE_URL", getApiKey("BASE_URL"))
buildConfigField("String", "ACCESS_TOKEN", getApiKey("ACCESS_TOKEN"))
buildConfigField("String", "KAKAO_NATIVE_APP_KEY", getApiKey("KAKAO_NATIVE_APP_KEY"))
manifestPlaceholders["KAKAO_REDIRECT_SCHEME"] = getApiKey("KAKAO_REDIRECT_SCHEME")
}

buildTypes {
Expand Down Expand Up @@ -119,6 +121,9 @@ dependencies {
// Splash Screen
implementation("androidx.core:core-splashscreen:1.0.1")

// kakao
implementation("com.kakao.sdk:v2-user:2.12.1")

implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
Expand Down
26 changes: 22 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@

<application
android:name=".SoftieApplication"
android:allowBackup="true"
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:fullBackupContent="false"
android:fullBackupOnly="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Softie"
android:usesCleartextTraffic="true"
tools:targetApi="31">
tools:targetApi="33">

<activity
android:name=".ui.main.MainActivity"
android:exported="false"
Expand Down Expand Up @@ -65,7 +67,7 @@
android:screenOrientation="portrait" />

<activity
android:name=".ui.main.LoginActivity"
android:name=".ui.login.LoginActivity"
android:exported="false"
android:screenOrientation="portrait" />

Expand All @@ -79,6 +81,22 @@
android:exported="false"
android:screenOrientation="portrait" />

<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
android:exported="true"
android:launchMode="singleTask">
Copy link
Collaborator

Choose a reason for hiding this comment

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

singleTask가 어떤 역할인가요?

<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_REDIRECT_SCHEME}" />
</intent-filter>
</activity>

</application>

</manifest>
3 changes: 3 additions & 0 deletions app/src/main/java/com/sopetit/softie/SoftieApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package com.sopetit.softie

import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
import com.kakao.sdk.common.KakaoSdk
import com.sopetit.softie.BuildConfig.KAKAO_NATIVE_APP_KEY
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber

@HiltAndroidApp
class SoftieApplication : Application() {
override fun onCreate() {
super.onCreate()
KakaoSdk.init(this, KAKAO_NATIVE_APP_KEY)
Copy link
Member

Choose a reason for hiding this comment

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

코드에 카카오가 들어가 있는게 묘하게 깐지나네요..

if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sopetit.softie.data.entity.request

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class LoginRequest(
@SerialName("socialType")
val socialType: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.sopetit.softie.data.entity.response

import com.sopetit.softie.domain.entity.Token
import kotlinx.serialization.Serializable

@Serializable
data class LoginResponse(
val accessToken: String,
val refreshToken: String
) {
fun toToken(): Token = Token(
accessToken = this.accessToken,
refreshToken = this.refreshToken
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.sopetit.softie.data.repositoryImpl

import com.sopetit.softie.data.source.AuthDataSource
import com.sopetit.softie.data.source.LocalDataSource
import com.sopetit.softie.domain.entity.Token
import com.sopetit.softie.domain.repository.AuthRepository
import javax.inject.Inject

class AuthRepositoryImpl @Inject constructor(
private val authDataSource: AuthDataSource,
private val localDataSource: LocalDataSource
) : AuthRepository {
override suspend fun postLogin(socialPlatform: String): Result<Token> =
kotlin.runCatching { authDataSource.postLogin(socialPlatform) }.map { response ->
requireNotNull(response.data).toToken()
}

override fun initToken(accessToken: String, refreshToken: String) {
localDataSource.accessToken = accessToken
localDataSource.refreshToken = refreshToken
}

override fun initSignUpState(isSignUpState: Boolean) {
localDataSource.isUserSignUp = isSignUpState
}

override fun getSignedUp(): Boolean = localDataSource.isUserSignUp
}
14 changes: 14 additions & 0 deletions app/src/main/java/com/sopetit/softie/data/service/AuthService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.sopetit.softie.data.service

import com.sopetit.softie.data.entity.BaseResponse
import com.sopetit.softie.data.entity.request.LoginRequest
import com.sopetit.softie.data.entity.response.LoginResponse
import retrofit2.http.Body
import retrofit2.http.POST

interface AuthService {
@POST("api/v1/auth")
suspend fun postLogin(
@Body body: LoginRequest
): BaseResponse<LoginResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.sopetit.softie.data.service

import android.content.Context
import com.kakao.sdk.auth.model.OAuthToken
import com.kakao.sdk.user.UserApiClient
import dagger.hilt.android.qualifiers.ActivityContext
import javax.inject.Inject

class KakaoLoginService @Inject constructor(@ActivityContext private val context: Context) {
fun startKakaoLogin(kakaoLoginCallBack: (OAuthToken?, Throwable?) -> Unit) {
val kakaoLoginState =
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) KAKAO_APP_LOGIN
else KAKAO_ACCOUNT_LOGIN

when (kakaoLoginState) {
KAKAO_APP_LOGIN -> {
UserApiClient.instance.loginWithKakaoTalk(
context,
callback = kakaoLoginCallBack
)
}

KAKAO_ACCOUNT_LOGIN -> {
UserApiClient.instance.loginWithKakaoAccount(
context,
callback = kakaoLoginCallBack
)
}
}
}

companion object {
const val KAKAO_APP_LOGIN = 0
const val KAKAO_ACCOUNT_LOGIN = 1
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/com/sopetit/softie/data/source/AuthDataSource.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.sopetit.softie.data.source

import com.sopetit.softie.data.entity.BaseResponse
import com.sopetit.softie.data.entity.request.LoginRequest
import com.sopetit.softie.data.entity.response.LoginResponse
import com.sopetit.softie.data.service.AuthService
import javax.inject.Inject

class AuthDataSource @Inject constructor(
private val authService: AuthService
) {
suspend fun postLogin(socialType: String): BaseResponse<LoginResponse> =
authService.postLogin(LoginRequest(socialType))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.sopetit.softie.data.source

import android.content.SharedPreferences
import androidx.core.content.edit
import javax.inject.Inject

class LocalDataSource @Inject constructor(
private val prefs: SharedPreferences
) {
var accessToken: String
set(value) = prefs.edit { putString(ACCESS_TOKEN, value) }
get() = prefs.getString(ACCESS_TOKEN, "") ?: ""

var refreshToken: String
set(value) = prefs.edit { putString(REFRESH_TOKEN, value) }
get() = prefs.getString(REFRESH_TOKEN, "") ?: ""

var isUserSignUp: Boolean
set(value) = prefs.edit { putBoolean(USER, value) }
get() = prefs.getBoolean(USER, false)

companion object {
private const val ACCESS_TOKEN = "access_token"
private const val REFRESH_TOKEN = "refresh_token"
private const val USER = "user"
}
}
8 changes: 8 additions & 0 deletions app/src/main/java/com/sopetit/softie/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.sopetit.softie.di

import com.sopetit.softie.data.repositoryImpl.AuthRepositoryImpl
import com.sopetit.softie.data.repositoryImpl.DailyRoutineRepositoryImpl
import com.sopetit.softie.data.repositoryImpl.DollRepositoryImpl
import com.sopetit.softie.data.repositoryImpl.HappinessRoutineRepositoryImpl
import com.sopetit.softie.data.repositoryImpl.MemberRepositoryImpl
import com.sopetit.softie.domain.repository.AuthRepository
import com.sopetit.softie.domain.repository.DailyRoutineRepository
import com.sopetit.softie.domain.repository.DollRepository
import com.sopetit.softie.domain.repository.HappinessRoutineRepository
Expand All @@ -23,6 +25,12 @@ abstract class RepositoryModule {
memberRepositoryImpl: MemberRepositoryImpl
): MemberRepository

@Binds
@Singleton
abstract fun bindToAuthRepository(
authRepositoryImpl: AuthRepositoryImpl
): AuthRepository

@Binds
@Singleton
abstract fun bindToDailyRoutineRepository(
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/com/sopetit/softie/di/RetrofitModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.sopetit.softie.di

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import com.sopetit.softie.BuildConfig
import com.sopetit.softie.data.source.LocalDataSource
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand Down Expand Up @@ -32,13 +33,15 @@ object RetrofitModule {
@Provides
@Singleton
@SoftieType
fun providesSoftieInterceptor(): Interceptor = Interceptor { chain ->
fun providesSoftieInterceptor(
localDataSource: LocalDataSource
): Interceptor = Interceptor { chain ->
val request = chain.request()
var response = chain.proceed(
request
.newBuilder()
.addHeader(CONTENT_TYPE, APPLICATION_JSON)
.addHeader(AUTHORIZATION, BEARER + BuildConfig.ACCESS_TOKEN)
.addHeader(AUTHORIZATION, BEARER + localDataSource.accessToken)
.build()
)
response
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.sopetit.softie.di

import com.sopetit.softie.data.service.AuthService
import com.sopetit.softie.data.service.DailyRoutineService
import com.sopetit.softie.data.service.DollService
import com.sopetit.softie.data.service.HappinessRoutineService
Expand All @@ -17,6 +18,10 @@ object RetrofitServiceModule {
fun providesMemberService(@RetrofitModule.SoftieType retrofit: Retrofit): MemberService =
retrofit.create(MemberService::class.java)

@Provides
fun providesAuthService(@RetrofitModule.SoftieType retrofit: Retrofit): AuthService =
retrofit.create(AuthService::class.java)

@Provides
fun providesHappinessRoutineService(@RetrofitModule.SoftieType retrofit: Retrofit): HappinessRoutineService =
retrofit.create(HappinessRoutineService::class.java)
Expand Down
27 changes: 27 additions & 0 deletions app/src/main/java/com/sopetit/softie/di/SharedPrefModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.sopetit.softie.di

import android.content.Context
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object SharedPrefModule {
@Provides
@Singleton
fun providesLocalPreferences(@ApplicationContext context: Context): SharedPreferences =
EncryptedSharedPreferences.create(
context,
context.packageName,
MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(),
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
6 changes: 6 additions & 0 deletions app/src/main/java/com/sopetit/softie/domain/entity/Token.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sopetit.softie.domain.entity

data class Token(
val accessToken: String = "",
val refreshToken: String = ""
)
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
package com.sopetit.softie.domain.repository

interface AuthRepository
import com.sopetit.softie.domain.entity.Token

interface AuthRepository {
suspend fun postLogin(socialPlatform: String): Result<Token>
fun getSignedUp(): Boolean
fun initToken(accessToken: String, refreshToken: String)
fun initSignUpState(isSignUpState: Boolean)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sopetit.softie.domain.usecase

import com.sopetit.softie.domain.repository.AuthRepository
import javax.inject.Inject

class GetSignedUpUseCase @Inject constructor(
private val authRepository: AuthRepository
) {
operator fun invoke() = authRepository.getSignedUp()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.sopetit.softie.domain.usecase

import com.sopetit.softie.domain.repository.AuthRepository
import javax.inject.Inject

class InitSIgnUpStateUseCase @Inject constructor(
private val authRepository: AuthRepository
) {
operator fun invoke(isSignUpState: Boolean) =
authRepository.initSignUpState(isSignUpState)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.sopetit.softie.domain.usecase

import com.sopetit.softie.domain.repository.AuthRepository
import javax.inject.Inject

class InitTokenUseCase @Inject constructor(
private val authRepository: AuthRepository
) {
operator fun invoke(accessToken: String, refreshToken: String) =
authRepository.initToken(accessToken, refreshToken)
}
Loading