Skip to content

Commit

Permalink
refactor: EditText의 검증 상태를 관리하는 공통 interface를 각 항목에 맞게 분리
Browse files Browse the repository at this point in the history
팀원 분의 코드리뷰 반영
6a028a6#r140847470
  • Loading branch information
rlaxodud214 committed Apr 11, 2024
1 parent c42085b commit 98ea7e5
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 99 deletions.
1 change: 0 additions & 1 deletion app/src/main/java/com/example/introduce/SignInActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.example.introduce.databinding.SignInActivityBinding
import com.example.introduce.databinding.SignUpActivityBinding
import com.example.introduce.domain.UserData

class SignInActivity : AppCompatActivity() {
Expand Down
40 changes: 19 additions & 21 deletions app/src/main/java/com/example/introduce/SignUpActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class SignUpActivity : AppCompatActivity() {
editTexts.forEach { editText ->
editText.addTextChangedListener {
editText.checkValidElement()
viewModel.checkAllValid()
}
}
}
Expand All @@ -56,54 +57,51 @@ class SignUpActivity : AppCompatActivity() {
exitActivity(user)
}

errorUiState.observe(this@SignUpActivity) { errorUiState ->
errorState.observe(this@SignUpActivity) { errorState ->
with(binding) {
// 이름
tvNameError.setText(
when (errorUiState.name) {
SignUpValidUiState.NameBlank -> getString(R.string.sign_up_error_empty_name)
when (errorState.name) {
CommonValidState.Blank -> getString(R.string.sign_up_error_empty_name)
else -> ""
}
)

// 이메일
tvEmailError.setText(
when (errorUiState.email) {
SignUpValidUiState.EmailBlank -> getString(R.string.sign_up_email_error_blank)
SignUpValidUiState.EmailIncludeAt -> getString(R.string.sign_up_email_error_include)
SignUpValidUiState.EmailTypeNotAllowed -> getString(R.string.sign_up_email_error_type)
when (errorState.email) {
CommonValidState.Blank -> getString(R.string.sign_up_email_error_blank)
EmailValidState.IncludeAt -> getString(R.string.sign_up_email_error_include)
EmailValidState.TypeNotAllowed -> getString(R.string.sign_up_email_error_type)
else -> ""
}
)

// 비밀번호
val isNotPasswordBlank = errorUiState.passwordInput != SignUpValidUiState.PasswordInputBlank
val isNotPasswordBlank = (errorState.passwordInput != CommonValidState.Blank)
tvPasswordInputError.isEnabled = isNotPasswordBlank

tvPasswordInputError.setText(
when (errorUiState.passwordInput) {
SignUpValidUiState.PasswordInputBlank -> getString(R.string.sign_up_password_hint_guide)
SignUpValidUiState.PasswordInputLength -> getString(R.string.sign_up_password_error_length)
SignUpValidUiState.PasswordInputSpecialChar -> getString(R.string.sign_up_password_error_special_char)
SignUpValidUiState.PasswordInputUpperCase -> getString(R.string.sign_up_password_error_uppercase)
when (errorState.passwordInput) {
CommonValidState.Blank -> getString(R.string.sign_up_password_hint_guide)
PasswordInputValidState.Length -> getString(R.string.sign_up_password_error_length)
PasswordInputValidState.SpecialChar -> getString(R.string.sign_up_password_error_special_char)
PasswordInputValidState.UpperCase -> getString(R.string.sign_up_password_error_uppercase)
else -> ""
}
)

// 비밀번호 재확인
tvPasswordConfirmError.setText(
when (errorUiState.passwordConfirm) {
SignUpValidUiState.PasswordConfirmBlank -> getString(R.string.sign_up_password_hint)
SignUpValidUiState.PasswordConfirmEquals -> getString(R.string.sign_up_password_confirm_error_equal)
when (errorState.passwordConfirm) {
CommonValidState.Blank -> getString(R.string.sign_up_password_error_blank)
PssswordConfirmValidState.Equals -> getString(R.string.sign_up_password_confirm_error_equal)
else -> ""
}
)

// 4가지 항목에 대해서 유효성 검사를 통과했는지 체크
checkAllValid()

// 회원가입 버튼 활성화 / 비활성화
btnSignUp.isEnabled = errorUiState.enabled
btnSignUp.isEnabled = errorState.enabled
}
}
}
Expand Down Expand Up @@ -147,5 +145,5 @@ class SignUpActivity : AppCompatActivity() {
}
}

fun EditText.getTextElement() = this.text.toString()
private fun EditText.getTextElement() = this.text.toString()
}
46 changes: 0 additions & 46 deletions app/src/main/java/com/example/introduce/SignUpErrorUiState.kt

This file was deleted.

44 changes: 44 additions & 0 deletions app/src/main/java/com/example/introduce/SignUpInputErrorState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.example.introduce

data class SignUpInputErrorState(

This comment has been minimized.

Copy link
@agvber

agvber Apr 11, 2024

https://github.com/android/nowinandroid/blob/main/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/OnboardingUiState.kt

보통 상태를 담을 때에는 에러 상태도 함께 담아요!
왜냐하면 성공, 로딩, 에러는 동시에 나타나서는 안되기 때문입니다.
현재 코드에서는 에러 상태일 때와 적합 상태가 동시에 올 수 있기 때문에 코드상으로 헷갈릴 수 있을 것 같습니다

예를들어서 아래 코드와 같습니다

sealed interface PssswordConfirmValidState {
data object Equals : PssswordConfirmValidState
data object Unequal: PssswordConfirmValidState
}

fun function1(passwordConfirmValidState: PssswordConfirmValidState) {

when ( passwordConfirmValidState ) {
    Equals -> {
            // 패스워드와 동일할 때의 action 
    }
    Unequal -> {
            // 패스워드와 동일 하지 않을때 action 
    }
}

}

물론 제 말이 정답은 아니니 이렇게 생각할 수도 있다고 봐주세요~~~
또한 팀플 때 열심히 제 코드 리뷰해 주신 거 이렇게 갚습니다 😊😊

This comment has been minimized.

Copy link
@agvber

agvber Apr 11, 2024

더 이상은 피드백 하지 않겠습니다 너무 잘하셔서 조금 아쉬워서 자꾸 짚고 가네요 🥺🥺

This comment has been minimized.

Copy link
@rlaxodud214

rlaxodud214 Apr 17, 2024

Author Owner

코드 리뷰를 늦게 봤네요 ㅜㅜ 코드 리뷰 감사합니다!!

상태를 담을 때, 에러 상태도 함께 담는다고 말씀해주셨는데

현재는 전체 통과(Valid), case별 오류 상태 object (Length, SpecialChar, UpperCase)
만을 포함하고 있어 아래와 같이 수정 하는 게 맞을까요?!

말씀 주신 내용을 제대로 이해한 게 맞을지 궁금합니다.

확실히 알려주신 링크를 참고하니 테스트 코드에서도 쓰기 좋고, 로직을 좀 더 구체화할 수 있는 방안이 될 수 있을 것 같네요 :)

알려주셔서 감사합니다 ㅎㅎㅎ

기존 코드

sealed interface PasswordInputValidState : CommonValidState {
    data object LengthError : PasswordInputValidState
    data object SpecialCharError : PasswordInputValidState
    data object UpperCaseError : PasswordInputValidState
}

수정된 코드

sealed interface PasswordInputValidState : CommonValidState {
    data object LengthValid : PasswordInputValidState
    data object LengthError : PasswordInputValidState
    data object SpecialCharValid : PasswordInputValidState
    data object SpecialCharError : PasswordInputValidState
    data object UpperCaseValid : PasswordInputValidState
    data object UpperCaseError : PasswordInputValidState
}

This comment has been minimized.

Copy link
@rlaxodud214

rlaxodud214 Apr 17, 2024

Author Owner

코드로는 이해했지만,,,

왜냐하면 성공, 로딩, 에러는 동시에 나타나서는 안되기 때문입니다.
현재 코드에서는 에러 상태일 때와 적합 상태가 동시에 올 수 있기 때문에 코드상으로 헷갈릴 수 있을 것 같습니다

말씀 주신 이 부분은 경험 부족으로 인해 이해가 어렵네요 ㅎㅎ,,ㅠㅠ

val name: CommonValidState,
val email: CommonValidState,
val passwordInput: CommonValidState,
val passwordConfirm: CommonValidState,
val enabled: Boolean,
) {
companion object {
fun init() = SignUpInputErrorState(
name = CommonValidState.Init,
email = CommonValidState.Init,
passwordInput = CommonValidState.Init,
passwordConfirm = CommonValidState.Init,
enabled = false
)
}
}

sealed interface CommonValidState {
data object Init : CommonValidState // 초기 상태
data object Blank : CommonValidState // 비어 있음
data object Valid : CommonValidState // 적합
}

sealed interface NameValidState : CommonValidState {
data object Length : NameValidState
}

sealed interface EmailValidState : CommonValidState {
data object IncludeAt : EmailValidState
data object TypeNotAllowed : EmailValidState
}

sealed interface PasswordInputValidState : CommonValidState {
data object Length : PasswordInputValidState
data object SpecialChar : PasswordInputValidState
data object UpperCase : PasswordInputValidState
}

sealed interface PssswordConfirmValidState : CommonValidState {
data object Equals : PssswordConfirmValidState
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ object SignUpValidExtension {
*/
fun String.isValidateEmail() =
Regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\$")
.matches(this)
.matches(this)

/**
* 특수 문자 포함
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ package com.example.introduce.viewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.introduce.SignUpErrorUiState
import com.example.introduce.EmailValidState
import com.example.introduce.PasswordInputValidState
import com.example.introduce.PssswordConfirmValidState
import com.example.introduce.SignUpInputErrorState
import com.example.introduce.SignUpValidExtension.includeAt
import com.example.introduce.SignUpValidExtension.includeSpecialCharacters
import com.example.introduce.SignUpValidExtension.includeUpperCase
import com.example.introduce.SignUpValidExtension.isValidateEmail
import com.example.introduce.SignUpValidUiState
import com.example.introduce.CommonValidState
import com.example.introduce.domain.UserData

class SignUpViewModel : ViewModel() {
// user
private val _errorUiState: MutableLiveData<SignUpErrorUiState> =
MutableLiveData(SignUpErrorUiState.init())
val errorUiState: LiveData<SignUpErrorUiState> get() = _errorUiState
private val _errorState: MutableLiveData<SignUpInputErrorState> =
MutableLiveData(SignUpInputErrorState.init())
val errorState: LiveData<SignUpInputErrorState> get() = _errorState

private val _userData: MutableLiveData<UserData> = MutableLiveData()
val userData: LiveData<UserData> get() = _userData
Expand All @@ -24,67 +27,69 @@ class SignUpViewModel : ViewModel() {
_userData.value = userData
}

// 이 로직을 호출하는 위치 때문에 렉이 있는 것 같다. 연구가 필요함.
/**
* 4가지 항목에 대해서 유효성 검사를 통과했는지 체크
*/
fun checkAllValid() {
errorUiState.value?.run {
val isAllValid = (name == SignUpValidUiState.Valid)
&& email == SignUpValidUiState.Valid
&& passwordInput == SignUpValidUiState.Valid
&& passwordConfirm == SignUpValidUiState.Valid
errorState.value?.run {
val isAllValid = (name == CommonValidState.Valid)
&& email == CommonValidState.Valid
&& passwordInput == CommonValidState.Valid
&& passwordConfirm == CommonValidState.Valid

// 값이 다를 때만, 변경 해야한다. 아니면 무한 루프 발생함
if (enabled != isAllValid) {
_errorUiState.value = errorUiState.value?.copy(
_errorState.value = errorState.value?.copy(
enabled = isAllValid
)
}
}
}

fun checkValidName(value: String) {
_errorUiState.value = errorUiState.value?.copy(
_errorState.value = errorState.value?.copy(
name = when {
value.isBlank() -> SignUpValidUiState.NameBlank
else -> SignUpValidUiState.Valid
value.isBlank() -> CommonValidState.Blank
else -> CommonValidState.Valid
}
)
}

fun checkValidEmail(value: String) {
_errorUiState.value = errorUiState.value?.copy(
_errorState.value = errorState.value?.copy(
email = when {
value.isBlank() -> SignUpValidUiState.EmailBlank
value.includeAt().not() -> SignUpValidUiState.EmailIncludeAt
value.isValidateEmail().not() -> SignUpValidUiState.EmailTypeNotAllowed
else -> SignUpValidUiState.Valid
value.isBlank() -> CommonValidState.Blank
value.includeAt().not() -> EmailValidState.IncludeAt
value.isValidateEmail().not() -> EmailValidState.TypeNotAllowed
else -> CommonValidState.Valid
}
)
}

fun checkValidPassword(value: String) {
_errorUiState.value = errorUiState.value?.copy(
_errorState.value = errorState.value?.copy(
passwordInput = when {
value.isBlank() -> SignUpValidUiState.PasswordInputBlank
value.length < 10 -> SignUpValidUiState.PasswordInputLength
value.isBlank() -> CommonValidState.Blank
value.length < 10 -> PasswordInputValidState.Length

value.includeSpecialCharacters().not() ->
SignUpValidUiState.PasswordInputSpecialChar
PasswordInputValidState.SpecialChar

value.includeUpperCase().not() ->
SignUpValidUiState.PasswordInputUpperCase
PasswordInputValidState.UpperCase

else -> SignUpValidUiState.Valid
else -> CommonValidState.Valid
}
)

}

fun checkValidPasswordConfirm(passwordConfirm: String, passwordInput: String) {
_errorUiState.value = errorUiState.value?.copy(
_errorState.value = errorState.value?.copy(
passwordConfirm = when {
passwordConfirm.isBlank() -> SignUpValidUiState.PasswordConfirmBlank
passwordConfirm != passwordInput -> SignUpValidUiState.PasswordConfirmEquals
else -> SignUpValidUiState.Valid
passwordConfirm.isBlank() -> CommonValidState.Blank
passwordConfirm != passwordInput -> PssswordConfirmValidState.Equals
else -> CommonValidState.Valid
}
)
}
Expand Down

0 comments on commit 98ea7e5

Please sign in to comment.