Skip to content

Commit 49e6c78

Browse files
committed
Feat: 카카오 로그인 기능 추가
- LoginViewModel 추가 및 카카오 로그인 로직 구현 - LoginScreen에 임시 카카오 로그인 UI 및 이벤트 핸들링 추가
1 parent a623843 commit 49e6c78

3 files changed

Lines changed: 124 additions & 19 deletions

File tree

app/src/main/java/com/threegap/bitnagil/MainNavHost.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import androidx.compose.ui.Modifier
55
import androidx.navigation.compose.NavHost
66
import androidx.navigation.compose.composable
77
import com.threegap.bitnagil.presentation.home.HomeScreen
8-
import com.threegap.bitnagil.presentation.login.LoginScreen
8+
import com.threegap.bitnagil.presentation.login.LoginScreenContainer
99

1010
@Composable
1111
fun MainNavHost(
@@ -18,9 +18,7 @@ fun MainNavHost(
1818
modifier = modifier,
1919
) {
2020
composable<Route.Login> {
21-
LoginScreen(
22-
onLoginClick = { navigator.navController.navigate(Route.Home) },
23-
)
21+
LoginScreenContainer()
2422
}
2523

2624
composable<Route.Home> {
Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,81 @@
11
package com.threegap.bitnagil.presentation.login
22

33
import androidx.compose.foundation.background
4-
import androidx.compose.foundation.layout.Arrangement
5-
import androidx.compose.foundation.layout.Column
6-
import androidx.compose.foundation.layout.Spacer
4+
import androidx.compose.foundation.layout.Box
75
import androidx.compose.foundation.layout.fillMaxSize
8-
import androidx.compose.foundation.layout.height
6+
import androidx.compose.foundation.layout.padding
97
import androidx.compose.material3.Button
108
import androidx.compose.material3.Text
119
import androidx.compose.runtime.Composable
1210
import androidx.compose.ui.Alignment
1311
import androidx.compose.ui.Modifier
1412
import androidx.compose.ui.graphics.Color
13+
import androidx.compose.ui.platform.LocalContext
1514
import androidx.compose.ui.tooling.preview.Preview
1615
import androidx.compose.ui.unit.dp
16+
import androidx.hilt.navigation.compose.hiltViewModel
17+
import com.kakao.sdk.user.UserApiClient
1718
import com.threegap.bitnagil.designsystem.BitnagilTheme
19+
import com.threegap.bitnagil.presentation.login.model.LoginIntent
20+
import com.threegap.bitnagil.presentation.login.model.LoginSideEffect
21+
import org.orbitmvi.orbit.compose.collectSideEffect
1822

1923
@Composable
20-
fun LoginScreen(
21-
onLoginClick: () -> Unit,
24+
fun LoginScreenContainer(viewModel: LoginViewModel = hiltViewModel()) {
25+
val context = LocalContext.current
26+
val client = UserApiClient.instance
27+
28+
viewModel.collectSideEffect { sideEffect ->
29+
when (sideEffect) {
30+
is LoginSideEffect.RequestKakaoTalkLogin -> {
31+
client.loginWithKakaoTalk(context) { token, error ->
32+
viewModel.sendIntent(LoginIntent.OnKakaoLoginResult(token, error))
33+
}
34+
}
35+
36+
is LoginSideEffect.RequestKakaoAccountLogin -> {
37+
client.loginWithKakaoAccount(context) { token, error ->
38+
viewModel.sendIntent(LoginIntent.OnKakaoLoginResult(token, error))
39+
}
40+
}
41+
}
42+
}
43+
44+
LoginScreen(
45+
onKakaoLoginClick = {
46+
viewModel.sendIntent(
47+
LoginIntent.OnKakaoLoginClick(client.isKakaoTalkLoginAvailable(context)),
48+
)
49+
},
50+
)
51+
}
52+
53+
@Composable
54+
private fun LoginScreen(
55+
onKakaoLoginClick: () -> Unit,
2256
modifier: Modifier = Modifier,
2357
) {
24-
Column(
25-
verticalArrangement = Arrangement.Center,
26-
horizontalAlignment = Alignment.CenterHorizontally,
58+
Box(
2759
modifier =
2860
modifier
2961
.fillMaxSize()
3062
.background(Color.White),
3163
) {
32-
Text(text = "여긴 로그인 화면")
33-
34-
Spacer(modifier = Modifier.height(10.dp))
64+
Text(
65+
text = "빛나길 로고",
66+
modifier = Modifier.align(Alignment.Center),
67+
)
3568

3669
Button(
37-
onClick = onLoginClick,
70+
onClick = onKakaoLoginClick,
71+
modifier =
72+
Modifier
73+
.align(Alignment.BottomCenter)
74+
.padding(20.dp),
3875
) {
39-
Text("로그인 버튼 눌러보던가")
76+
Text(
77+
text = "카카오 로그인버튼",
78+
)
4079
}
4180
}
4281
}
@@ -46,7 +85,7 @@ fun LoginScreen(
4685
private fun LoginScreenPreview() {
4786
BitnagilTheme {
4887
LoginScreen(
49-
onLoginClick = {},
88+
onKakaoLoginClick = {},
5089
)
5190
}
5291
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.threegap.bitnagil.presentation.login
2+
3+
import android.util.Log
4+
import androidx.lifecycle.SavedStateHandle
5+
import com.kakao.sdk.common.model.ClientError
6+
import com.kakao.sdk.common.model.ClientErrorCause
7+
import com.kakao.sdk.user.UserApiClient
8+
import com.threegap.bitnagil.presentation.common.mviviewmodel.MviViewModel
9+
import com.threegap.bitnagil.presentation.login.model.LoginIntent
10+
import com.threegap.bitnagil.presentation.login.model.LoginSideEffect
11+
import com.threegap.bitnagil.presentation.login.model.LoginState
12+
import dagger.hilt.android.lifecycle.HiltViewModel
13+
import org.orbitmvi.orbit.syntax.simple.SimpleSyntax
14+
import javax.inject.Inject
15+
16+
@HiltViewModel
17+
class LoginViewModel
18+
@Inject
19+
constructor(
20+
private val savedStateHandle: SavedStateHandle,
21+
) : MviViewModel<LoginState, LoginSideEffect, LoginIntent>(
22+
initState = LoginState(),
23+
savedStateHandle = savedStateHandle,
24+
) {
25+
override suspend fun SimpleSyntax<LoginState, LoginSideEffect>.reduceState(
26+
intent: LoginIntent,
27+
state: LoginState,
28+
): LoginState? =
29+
when (intent) {
30+
is LoginIntent.OnKakaoLoginClick -> {
31+
if (!intent.onKakaoTalkLoginAvailable) {
32+
sendSideEffect(LoginSideEffect.RequestKakaoAccountLogin)
33+
} else {
34+
sendSideEffect(LoginSideEffect.RequestKakaoTalkLogin)
35+
}
36+
null
37+
}
38+
39+
is LoginIntent.OnKakaoLoginResult -> {
40+
when {
41+
intent.token != null -> {
42+
Log.i("KakaoLogin", "로그인 성공 ${intent.token.accessToken}")
43+
UserApiClient.instance.me { user, error ->
44+
if (error != null) {
45+
Log.e("KakaoLogin", "사용자 정보 요청 실패", error)
46+
} else if (user != null) {
47+
Log.i(
48+
"KakaoLogin",
49+
"사용자 정보 요청 성공" +
50+
"\n이메일: ${user.kakaoAccount?.email}" +
51+
"\n닉네임: ${user.kakaoAccount?.profile?.nickname}",
52+
)
53+
}
54+
}
55+
}
56+
57+
intent.error is ClientError && intent.error.reason == ClientErrorCause.Cancelled -> {
58+
Log.e("KakaoLogin", "로그인 취소", intent.error)
59+
}
60+
61+
intent.error != null -> {
62+
Log.e("KakaoLogin", "로그인 실패", intent.error)
63+
}
64+
}
65+
null
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)