From 16e75b82504993ed56ccda8d968af00a3806ea1d Mon Sep 17 00:00:00 2001 From: oungsi2000 Date: Thu, 20 Aug 2026 13:54:39 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=86=8C=EC=85=9C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EB=B2=84=ED=8A=BC=EB=B3=84=20=EB=A1=9C?= =?UTF-8?q?=EB=94=A9=20=EC=83=81=ED=83=9C=20=ED=91=9C=EC=8B=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인 요청 시 어떤 소셜 로그인 제공자가 선택되었는지 추적하고, 해당 버튼 내부에 로딩 인디케이터를 표시하여 사용자 피드백을 개선합니다. - `LoginScreen`: 현재 로딩 중인 제공자를 추적하기 위한 `loadingProvider` 상태 추가 및 `LaunchedEffect`를 통한 상태 초기화 로직 구현 - `SocialLoginButton`: `isLoading` 매개변수를 추가하고, 로딩 중일 때 기존 아이콘과 텍스트 대신 `CircularProgressIndicator`를 표시하도록 수정 - 버튼 클릭 시 클릭된 제공자 정보를 `loadingProvider`에 저장하여 특정 버튼에만 로딩 애니메이션이 적용되도록 처리 --- .../auth/component/SocialLoginButton.kt | 32 ++++++++++++------- .../com/kikidan/auth/screen/LoginScreen.kt | 15 ++++++++- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/feature/auth/src/main/java/com/kikidan/auth/component/SocialLoginButton.kt b/feature/auth/src/main/java/com/kikidan/auth/component/SocialLoginButton.kt index a3a4f346..8436459e 100644 --- a/feature/auth/src/main/java/com/kikidan/auth/component/SocialLoginButton.kt +++ b/feature/auth/src/main/java/com/kikidan/auth/component/SocialLoginButton.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.material3.ripple import androidx.compose.runtime.Composable @@ -58,6 +59,7 @@ internal fun SocialLoginButton( provider: SocialLoginProvider, onClick: () -> Unit, modifier: Modifier = Modifier, + isLoading: Boolean = false, ) { Box( modifier = @@ -74,20 +76,28 @@ internal fun SocialLoginButton( ), contentAlignment = Alignment.Center, ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - Image( - painter = painterResource(id = provider.iconRes), - contentDescription = null, + if (isLoading) { + CircularProgressIndicator( modifier = Modifier.size(20.dp), - ) - Text( - text = stringResource(id = provider.labelRes), - style = TodakunTypography.body2Medium, color = provider.contentColor, + strokeWidth = 2.dp, ) + } else { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Image( + painter = painterResource(id = provider.iconRes), + contentDescription = null, + modifier = Modifier.size(20.dp), + ) + Text( + text = stringResource(id = provider.labelRes), + style = TodakunTypography.body2Medium, + color = provider.contentColor, + ) + } } } } diff --git a/feature/auth/src/main/java/com/kikidan/auth/screen/LoginScreen.kt b/feature/auth/src/main/java/com/kikidan/auth/screen/LoginScreen.kt index a391f07f..4e2259f7 100644 --- a/feature/auth/src/main/java/com/kikidan/auth/screen/LoginScreen.kt +++ b/feature/auth/src/main/java/com/kikidan/auth/screen/LoginScreen.kt @@ -13,6 +13,11 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -34,6 +39,10 @@ internal fun LoginScreen( modifier: Modifier = Modifier, ) { val loginEnabled = state is LoginState.Idle || state is LoginState.Failure + var loadingProvider by remember { mutableStateOf(null) } + LaunchedEffect(state) { + if (state !is LoginState.Loading) loadingProvider = null + } Box( modifier = modifier @@ -77,8 +86,12 @@ internal fun LoginScreen( SocialLoginProvider.entries.forEach { provider -> SocialLoginButton( enabled = loginEnabled, + isLoading = state is LoginState.Loading && loadingProvider == provider, provider = provider, - onClick = { onProviderClick(provider) }, + onClick = { + loadingProvider = provider + onProviderClick(provider) + }, ) } } From e273fc447475cf31ea838c220586bfa7fbe62053 Mon Sep 17 00:00:00 2001 From: oungsi2000 Date: Thu, 20 Aug 2026 13:59:02 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EC=98=A8=EB=B3=B4=EB=94=A9=20?= =?UTF-8?q?=EA=B3=BC=EC=A0=95=20=EC=97=90=EB=9F=AC=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EB=B0=B1(=EC=8A=A4=EB=82=B5=EB=B0=94)=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 온보딩 및 회원가입 과정에서 발생하는 예외 상황을 사용자에게 알리기 위해 `SnackbarHostState`를 연동하고, 사이드 이펙트에 따른 에러 메시지 표시 로직을 추가합니다. - `OnboardingRoute`: `OnboardingSideEffect`(`InvalidInput`, `Failure`) 발생 시 `snackbarHostState`를 통해 에러 메시지를 노출하도록 로직 구현 - `TodakunApp`: 온보딩 화면 진입 시 메인 UI의 `snackbarHostState`를 전달하도록 파라미터 추가 - `strings.xml`: 회원가입 실패 및 입력 정보 오류에 대한 안내 문구 리소스 추가 (`onboarding_signup_error`, `onboarding_invalid_input_error`) --- .../main/java/com/kikidan/todakun/TodakunApp.kt | 1 + core/designsystem/src/main/res/values/strings.xml | 2 ++ .../java/com/kikidan/onboarding/OnboardingRoute.kt | 14 +++++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/kikidan/todakun/TodakunApp.kt b/app/src/main/java/com/kikidan/todakun/TodakunApp.kt index 901d878f..2b3afbc1 100644 --- a/app/src/main/java/com/kikidan/todakun/TodakunApp.kt +++ b/app/src/main/java/com/kikidan/todakun/TodakunApp.kt @@ -145,6 +145,7 @@ fun TodakunApp( onFinish = { navigator.resetTo(TodakunRoute.Home) }, onNavigateTerm = { navigator.goBack() }, onboardingToken = OnboardingToken(route.onboardingToken), + snackbarHostState = snackbarHostState, ) } diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index c6051eb1..f7605165 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -314,6 +314,8 @@ 다음 시작하기 확인 + 회원가입에 실패했어요. 잠시 후 다시 시도해주세요 + 입력한 정보를 다시 확인해주세요. 약관 동의 diff --git a/feature/onboarding/src/main/java/com/kikidan/onboarding/OnboardingRoute.kt b/feature/onboarding/src/main/java/com/kikidan/onboarding/OnboardingRoute.kt index 586058b3..db38745a 100644 --- a/feature/onboarding/src/main/java/com/kikidan/onboarding/OnboardingRoute.kt +++ b/feature/onboarding/src/main/java/com/kikidan/onboarding/OnboardingRoute.kt @@ -5,6 +5,7 @@ import android.os.Build import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -12,7 +13,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel +import com.kikidan.designsystem.R import com.kikidan.designsystem.component.wheelpicker.BirthDateState import com.kikidan.designsystem.component.wheelpicker.BirthDateWheelPicker import com.kikidan.designsystem.component.wheelpicker.SajuBirthTimeWheelPicker @@ -34,6 +37,7 @@ fun OnboardingRoute( onFinish: () -> Unit, onNavigateTerm: () -> Unit, onboardingToken: OnboardingToken, + snackbarHostState: SnackbarHostState, modifier: Modifier = Modifier, viewModel: OnboardingViewModel = hiltViewModel(), ) { @@ -48,6 +52,8 @@ fun OnboardingRoute( // 권한을 허락하지 않더라도 앱 진입 permissionHandled = true } + val signupErrorMessage = stringResource(R.string.onboarding_signup_error) + val invalidInputErrorMessage = stringResource(R.string.onboarding_invalid_input_error) viewModel.collectSideEffect { sideEffect -> when (sideEffect) { @@ -67,7 +73,13 @@ fun OnboardingRoute( } } - else -> { /* TODO 에러 처리 스낵바 또는 다이얼로그 */ } + OnboardingSideEffect.InvalidInput -> { + snackbarHostState.showSnackbar(invalidInputErrorMessage) + } + + is OnboardingSideEffect.Failure -> { + snackbarHostState.showSnackbar(signupErrorMessage) + } } }