-
Notifications
You must be signed in to change notification settings - Fork 0
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
[Feat/#11] 7주차 과제 구현 #12
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
마지막 과제까지 너무 고생 많으셨습니다~!
is SignInContract.SignInUiEffect.ShowSuccessSnackBar -> { | ||
CoroutineScope(Dispatchers.Main).launch { | ||
SnackBarUtils.showSnackBar( | ||
message = context.getString(R.string.sign_in_snackbar_login_success), | ||
actionLabel = context.getString(R.string.sign_in_snackbar_action_close) | ||
) | ||
} | ||
navigateToMy() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
함수간 책임 분리가 필요해보입니다!!
스낵바를 보여주는 사이드이펙트에서 화면 전환 함수도 호출하는 것은 함수의 책임을 모호하게 한다고 생각해요.
두 개의 사이드이펙트로 분리하여 관리하는건 어떨까요??
} | ||
} | ||
|
||
fun signIn() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
접근제어자를 사용해서 UI단에서 불필요하게 호출하는 일이 없도록 관리해봅시다!
isLoading = true | ||
) | ||
) | ||
viewModelScope.launch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BaseViewModel에서 이벤트에 대한 코루틴 처리를 해주고 있는 것 같아요!
그렇다면 signIn
함수를 suspend 함수로 바꿔서 내부에 viewModelScope을 다시 사용하지 않도록 할 수 있을 것 같습니다!
screen.javaClass.canonicalName?.let { it1 -> popUpTo(it1) { inclusive = false } } | ||
launchSingleTop = true | ||
} | ||
screen.javaClass.canonicalName?.let { it1 -> popUpTo(it1) { inclusive = false } } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it1
같이 모호한 이름의 변수는 사용하지 않는게 좋을 것 같아요~
|
||
private fun loadHobby() { | ||
val token = preferenceUtil.getUserToken() | ||
Log.d("my**", token.toString()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로그 발견!
mainContentState = mainContent[idx % mainContent.size], | ||
onClick = onMainContentClicked, | ||
totalPage = mainContent.size, | ||
currentPage = idx % mainContent.size + 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
idx % mainContent.size
를 변수로 선언해서 사용하면 더 일관적이고 가독성이 좋은 코드가 될 것 같네요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
찰떡같이 잘하셨네요 ! 고생하셨어요 !
CoroutineScope(Dispatchers.Main).launch { | ||
SnackBarUtils.showSnackBar( | ||
message = context.getString(R.string.sign_in_snackbar_login_success), | ||
actionLabel = context.getString(R.string.sign_in_snackbar_action_close) | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서 CoroutineScope(Dispatchers.Main)을 사용한 이유가 있을까요?
} else { | ||
viewModel.errorMessageState.value?.let { message -> | ||
|
||
is SignInContract.SignInUiEffect.ShowErrorSnackBar -> { | ||
CoroutineScope(Dispatchers.Main).launch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요기도 궁금합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요~ 놀러왔습니다 ㅎㅎ
저번에 코리 남겨달라고 해서 남기러 왔는데 너무 잘해서 딱히 남길게 없네요...
코리 보시며 생각해보시고 Reply남기고 저 테그해주셔도 좋고, 따로 연락 주셔도 좋으니 편하게 질문주세요 :)
앱잼 화이팅~ 🚀
is BaseResult.Success -> { | ||
updateState( | ||
currentState.copy( | ||
isLoading = false | ||
) | ||
) | ||
preferenceUtil.saveUserToken(result.data.token) | ||
postEffect(SignInUiEffect.ShowSuccessSnackBar) | ||
postEffect(SignInUiEffect.NavigateToMy) | ||
} | ||
|
||
is BaseResult.Error -> { | ||
updateState( | ||
currentState.copy( | ||
isLoading = false, | ||
errorMessage = result.message | ||
) | ||
) | ||
postEffect(SignInUiEffect.ShowErrorSnackBar(result.message)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 제 스타일이긴 한데 아래처럼 성공, 실패에 상관 없이 진행되는 로직의 경우 when문 밖에 빼두고 같이 쓰는 편이에요
updateState(
currentState.copy(
isLoading = false
)
)
private fun validateUserName(username: String) = | ||
username.isNotBlank() && username.length <= 8 | ||
|
||
private fun validatePassword(password: String) = | ||
password.isNotBlank() && password.length <= 8 | ||
|
||
private fun validateHobby(hobby: String) = | ||
hobby.isNotBlank() && hobby.length <= 8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이런 숫자들은 상수로 빼주면 좋습니다.
앱잼을 하다보면 다른사람이 작성한 코드를 수정할 일도 생겨요. 그럴때 8 이라는 숫자로 있으면 이게 뭘 의미하는지 찾기 힘들 수 있어요.
val mainContents: List<HomeContent> = emptyList(), | ||
|
||
val commonContents: List<HomeCommonContent> = emptyList(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
List의 경우 불변 객체가 아닌, 읽기전용 객체에요. 그렇기 때문에 리컴포지션의 대상이 됩니다.
그렇기 때문에 @stable, @Immutble과 같은 어노테이션을 사용하거나, ImmutableList를 사용한다면 리컴포지션을 줄일 수 있어요. 단, 어노테이션을 붙일때는 여러 조건이 존재하기 때문에 꼭 찾아보시고 사용하시는걸 추천드려요
} | ||
|
||
sealed class HomeUiEffect : UiEffect | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
진짜진짜 사소한거긴 한데 저 빨간색 동그라미 거슬리지 않나요?
EOL(End Of Line)을 나타내는 에러표시에요. 파일의 마지막에 공백 엔터를 한줄 넣어서 컴파일러에게 "이 파일에 작성된 코드가 끝났다" 라는걸 알려주는 역할을 해요.
안쓰면 문제가 있냐구요? 적어도 kotlin 안드로이드에선 없어요. C언어 컴파일러인 gcc에선 없으면 에러난다고 하더라구요.
저처럼 앞으로 코드 작성하실때 신경쓰였으면 하는 마음에 코리 달았어요 ㅋㅋ
Text( | ||
text = commonContentState.mainTitle, | ||
text = commonContent.mainTitle, | ||
fontSize = 18.sp, | ||
color = White, | ||
fontWeight = FontWeight.Bold |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
text에 엄~청나게 긴 값을 한번 넣어보실래요?
그러면... 아이콘이 밀려서 사라질거에요
text자체에 weight를 넣어버리는 방법도 있고, horizontalArrangemnet를 사용하는 방법도 좋을 것 같습니다 :)
LaunchedEffect(Unit) { | ||
viewModel.sendEvent(MainContract.MainUiEvent.LoadUserToken) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LaunchedEffect
를 사용해서 viewModel에 있는 함수를 실행시키는 것과 viewModel에서 init
을 사용해서 함수를 실행시키는 것. 어떤 차이가 있을까요? 두 차이점을 찾아보고 상황에 맞게 사용한다면 좋을 것 같아요
여기선 큰 차이 없을거 같긴 하네요...
private val _event: MutableSharedFlow<Event> = MutableSharedFlow() | ||
val event = _event.asSharedFlow() | ||
|
||
private val _effect: Channel<Effect> = Channel() | ||
val effect = _effect.receiveAsFlow() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sharedFlow
와 channel
과 같은 두가지 방법을 사용하신 이유가 있나요??
차이점을 찾아보시고 명확한 이유가 있다면 좋을 것 같아요 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다른 분들이 너무 잘 달아주셔서 더 달게 없네용..ㅎㅎ 고생하셨습니다!
SignUpContract.Field.UserName, | ||
isFocused |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
field = SignUpContract.Field.UserName,
isFocused = isFocused
다른데는 다 되어 있는데 여긴 왜 없나요!?
SignUpContract.Field.Password, | ||
isFocused |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기두용
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다 ~
is SignUpContract.SignUpUiEffect.ShowSuccessToast -> { | ||
context.showToast(context.getString(R.string.sign_up_toast_success)) | ||
navigateToSignIn() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요기도 함수의 책임이 분리되면 좋겠네요
isValid = isValid && | ||
currentState.isPasswordValid && | ||
currentState.isHobbyValid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 값을 event의 인자로 넘겨줘도 좋을 것 같아요! 여기서는 상태를 업데이트 하는 로직만 담당할 수 있게요!
private val dummyHomeContentRepository: DummyHomeContentRepository | ||
) : BaseViewModel<HomeUiState, HomeUiEvent, HomeUiEffect>(HomeUiState()) { | ||
|
||
override fun reduceState(event: HomeUiEvent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 함수는 어떤 책임을 져야 할까요?
Related issue 🛠
Work Description ✏️
Screenshot 📸
Uncompleted Tasks 😅
To Reviewers 📢
preference 의존성 주입을 수정하고 MVI구조로 리팩했습니다.
MVI가 처음이라 잘한건지 모르겠네요 ..ㅜ
이상한 부분은 피드백 주세요 다들 화이팅!!