diff --git a/app/src/main/java/com/msg/bitgoeul_android/MainActivity.kt b/app/src/main/java/com/msg/bitgoeul_android/MainActivity.kt index 6903684a..e3ae66e1 100644 --- a/app/src/main/java/com/msg/bitgoeul_android/MainActivity.kt +++ b/app/src/main/java/com/msg/bitgoeul_android/MainActivity.kt @@ -16,13 +16,11 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - com.msg.design_system.theme.BitgoeulAndroidTheme { colors, typography -> - // A surface container using the 'background' color from the theme + BitgoeulAndroidTheme { colors, typography -> Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - Greeting("Android") } } } @@ -40,7 +38,7 @@ fun Greeting(name: String, modifier: Modifier = Modifier) { @Preview(showBackground = true) @Composable fun GreetingPreview() { - com.msg.design_system.theme.BitgoeulAndroidTheme { colors, typography -> + BitgoeulAndroidTheme { colors, typography -> Greeting("Android") } -} \ No newline at end of file +} diff --git a/core/domain/src/main/java/com/msg/domain/exception/HttpException.kt b/core/domain/src/main/java/com/msg/domain/exception/HttpException.kt new file mode 100644 index 00000000..376f8e5e --- /dev/null +++ b/core/domain/src/main/java/com/msg/domain/exception/HttpException.kt @@ -0,0 +1,42 @@ +package com.msg.domain.exception + +class BadRequestException( + override val message: String? +) : RuntimeException() + +class UnauthorizedException( + override val message: String? +) : RuntimeException() + +class ForBiddenException( + override val message: String? +) : RuntimeException() + +class NotFoundException( + override val message: String? +) : RuntimeException() + +class NotAcceptableException( + override val message: String? +) : RuntimeException() + +class ConflictException( + override val message: String? +) : RuntimeException() + +class TimeOutException( + override val message: String? +) : RuntimeException() + +class ServerException( + override val message: String? +) : RuntimeException() + +class OtherException( + override val message: String?, + val code: Int +) : RuntimeException() + +class UnknownException( + override val message: String? +) : RuntimeException() \ No newline at end of file diff --git a/core/domain/src/main/java/com/msg/domain/exception/NeedLoginException.kt b/core/domain/src/main/java/com/msg/domain/exception/NeedLoginException.kt new file mode 100644 index 00000000..c5a7b3df --- /dev/null +++ b/core/domain/src/main/java/com/msg/domain/exception/NeedLoginException.kt @@ -0,0 +1,8 @@ +package com.msg.domain.exception + +import java.io.IOException + +class NeedLoginException: IOException() { + override val message: String + get() = "토큰이 만료되었습니다. 다시 로그인 해주세요" +} \ No newline at end of file diff --git a/feature/login/src/main/java/com/bitgoeul/login/LoginScreen.kt b/feature/login/src/main/java/com/bitgoeul/login/LoginScreen.kt index 60331cfd..b873f440 100644 --- a/feature/login/src/main/java/com/bitgoeul/login/LoginScreen.kt +++ b/feature/login/src/main/java/com/bitgoeul/login/LoginScreen.kt @@ -19,6 +19,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import com.bitgoeul.login.viewmodel.AuthViewModel import com.msg.design_system.R import com.msg.design_system.component.button.BitgoeulButton import com.msg.design_system.component.button.ButtonState @@ -26,13 +28,17 @@ import com.msg.design_system.component.textfield.DefaultTextField import com.msg.design_system.component.textfield.LinkText import com.msg.design_system.component.textfield.PasswordTextField import com.msg.design_system.theme.BitgoeulAndroidTheme +import com.msg.model.remote.request.auth.LoginRequest @Composable fun LoginScreen() { - val isEmailErrorStatus = remember { mutableStateOf(false)} - val isPasswordErrorStatus = remember { mutableStateOf(false)} + val isEmailErrorStatus = remember { mutableStateOf(false) } + val isPasswordErrorStatus = remember { mutableStateOf(false) } val isErrorTextShow = remember { mutableStateOf(false) } var isTextStatus = "" + var emailState = remember { mutableStateOf("") } + val passwordState = remember { mutableStateOf("") } + val authViewModel: AuthViewModel = hiltViewModel() BitgoeulAndroidTheme { color, type -> Box { Column( @@ -71,6 +77,7 @@ fun LoginScreen() { errorText = stringResource(id = R.string.error_text_email), onValueChange = { isTextStatus = it + emailState.value = it }, isError = isEmailErrorStatus.value, onClickButton = { @@ -101,7 +108,9 @@ fun LoginScreen() { .height(54.dp), placeholder = stringResource(id = R.string.password), errorText = stringResource(id = R.string.wrong_password), - onValueChange = {}, + onValueChange = { + passwordState.value = it + }, onClickLink = {}, isError = isPasswordErrorStatus.value, isLinked = true, @@ -124,7 +133,12 @@ fun LoginScreen() { .height(52.dp), state = ButtonState.Disable, ) { - // Action + authViewModel.login( + body = LoginRequest( + email = emailState.value, + password = passwordState.value + ) + ) } } @@ -136,7 +150,7 @@ fun LoginScreen() { style = type.labelMedium, color = color.G1, fontSize = 14.sp, - ) + ) Spacer(modifier = Modifier.height(2.dp)) diff --git a/feature/login/src/main/java/com/bitgoeul/login/viewmodel/AuthViewModel.kt b/feature/login/src/main/java/com/bitgoeul/login/viewmodel/AuthViewModel.kt new file mode 100644 index 00000000..9ee62ab7 --- /dev/null +++ b/feature/login/src/main/java/com/bitgoeul/login/viewmodel/AuthViewModel.kt @@ -0,0 +1,46 @@ +package com.bitgoeul.login.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.bitgoeul.login.viewmodel.util.Event +import com.bitgoeul.login.viewmodel.util.errorHandling +import com.msg.domain.auth.LoginUseCase +import com.msg.domain.auth.LogoutUseCase +import com.msg.domain.auth.SaveTokenUseCase +import com.msg.domain.auth.WithdrawUseCase +import com.msg.model.remote.model.auth.AuthTokenModel +import com.msg.model.remote.request.auth.LoginRequest +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class AuthViewModel @Inject constructor( + private val loginUseCase: LoginUseCase, + private val logoutUseCase: LogoutUseCase, + private val saveTokenUseCase: SaveTokenUseCase, + private val withdrawUseCase: WithdrawUseCase, +) : ViewModel() { + private val _saveTokenRequest = MutableLiveData>() + val saveTokenRequest: LiveData> get() = _saveTokenRequest + + private val _loginRequest = MutableLiveData>() + val loginRequest: LiveData> get() = _loginRequest + + fun login(body: LoginRequest) = viewModelScope.launch { + loginUseCase( + body = body + ).onSuccess { + it.catch { remoteError -> + _loginRequest.value = remoteError.errorHandling() + }.collect { response -> + _loginRequest.value = Event.Success(data = response) + } + }.onFailure { + _loginRequest.value = it.errorHandling() + } + } +} diff --git a/feature/login/src/main/java/com/bitgoeul/login/viewmodel/util/Event.kt b/feature/login/src/main/java/com/bitgoeul/login/viewmodel/util/Event.kt new file mode 100644 index 00000000..62dc6c1a --- /dev/null +++ b/feature/login/src/main/java/com/bitgoeul/login/viewmodel/util/Event.kt @@ -0,0 +1,59 @@ +package com.bitgoeul.login.viewmodel.util + +sealed class Event( + val data: T? = null +) { + + object Loading : Event() + + /** + * 성공 + */ + class Success(data: T? = null) : Event(data = data) + + /** + * 400번 요청이 올바르지 않은 경우 + */ + object BadRequest : Event() + + /** + * 401번 비인증 요청 + */ + object Unauthorized : Event() + + /** + * 403번 권한이 없음 + */ + object ForBidden : Event() + + /** + * 404 찾을 수 없는 경우 + */ + object NotFound : Event() + + /** + * 406 맞는 규격이 없는 경우 + */ + object NotAcceptable : Event() + + /** + * 408 요청이 너무 오래 걸리는 경우 + */ + object TimeOut : Event() + + /** + * 409 권한이 없을 때 + */ + object Conflict : Event() + + /** + * 50X 서버에러 + */ + object Server : Event() + + /** + * 예상치 못한 에러 + */ + object UnKnown : Event() + +} \ No newline at end of file diff --git a/feature/login/src/main/java/com/bitgoeul/login/viewmodel/util/errorHandling.kt b/feature/login/src/main/java/com/bitgoeul/login/viewmodel/util/errorHandling.kt new file mode 100644 index 00000000..84ccb782 --- /dev/null +++ b/feature/login/src/main/java/com/bitgoeul/login/viewmodel/util/errorHandling.kt @@ -0,0 +1,67 @@ +package com.bitgoeul.login.viewmodel.util + +import android.util.Log +import com.msg.domain.exception.* + +suspend fun Throwable.errorHandling( + badRequestAction: suspend () -> Unit = {}, + unauthorizedAction: suspend () -> Unit = {}, + forBiddenAction: suspend () -> Unit = {}, + notFoundAction: suspend () -> Unit = {}, + notAcceptableAction: suspend () -> Unit = {}, + timeOutAction: suspend () -> Unit = {}, + conflictAction: suspend () -> Unit = {}, + serverAction: suspend () -> Unit = {}, + unknownAction: suspend () -> Unit = {}, +): Event = + when (this) { + is BadRequestException -> { + errorLog("BadRequestException", message) + badRequestAction() + Event.BadRequest + } + is UnauthorizedException, is NeedLoginException -> { + errorLog("UnauthorizedException", message) + unauthorizedAction() + Event.Unauthorized + } + is ForBiddenException -> { + errorLog("ForBiddenException", message) + forBiddenAction() + Event.ForBidden + } + is NotFoundException -> { + errorLog("NotFoundException", message) + notFoundAction() + Event.NotFound + } + is NotAcceptableException -> { + errorLog("NotAcceptableException", message) + notAcceptableAction() + Event.NotAcceptable + } + is TimeOutException -> { + errorLog("TimeOutException", message) + timeOutAction() + Event.TimeOut + } + is ConflictException -> { + errorLog("ConflictException", message) + conflictAction() + Event.Conflict + } + is ServerException -> { + errorLog("ServerException", message) + serverAction() + Event.Server + } + else -> { + errorLog("UnKnownException", message) + unknownAction() + Event.UnKnown + } + } + +private fun errorLog(tag: String, msg: String?) { + Log.d("ErrorHandling-$tag", msg ?: "알 수 없는 오류") +} \ No newline at end of file