Compose 와 잘 어울리는 MVI Architecture 하지만 저는 너무 어렵게 느껴졌습니다.
처음에 MVI Architecture 를 사용했을 때 Redux Style 의 MVI 를 사용했습니다.
RxJava 를사용 하여 구현했는데.... 결국 다른 사람들이 따라오지를 못하여 MVVM 으로 변경하는 사태가 벌어졌습니다.
저만 MVVM 으로 하자고 하고 나머지 분들은 다 MVI로 하자고 했는데 말이죠....? (아직까지 생각해봐도 조금 억울....)
어려워서 엎었던 기억이 있습니다.
https://play.google.com/store/apps/details?id=com.danchoo.tagalbum&hl=ko
하지만!!
MvRx 로 알려진 MVVM+ Style 의 MVI Architecture 는 Redux Style 보다 쉽고 간단했습니다.
그때 이걸로 할껄.... 후회되는 1인 입니다.
개발을 할때 사전조사가 많이 필요한 이유를 다시 한번 깨닳게 되었습니다
반성....
MVI
M : Model
V: View
I : Intent -> Android 의 Intent 가 아닙니다!!! Action 혹은 Command 입니다.
MVI 는 단방향으로 이벤트가 이동하는 Architecture 입니다.
아래 그림을 보면 동그랗게 순환이 되어 이벤트가 흘러가게 됩니다.
User 가 Action or Event 을 발생시키면 (Intent 발생)
Model 에 상태가 변경 됩니다.
View는 Model의 상태에 따라 유저에게 해당 상태를 표현해줍니다.
https://smartstore.naver.com/happysiso
아이고 어렵다...
Redux Style 의 MVI는 난이도가 높습니다...ㅎㅎ
하지만 MVVM+ Style 은 간단 합니다. 복잡하지 않습니다.
단순히 단방향으로 이벤트가 흘러간다고 생각만 하면 구현하기 쉽진 않지만...... 쉽다고 생각하면 편합니다.
Smaple 코드에서 Model, View, Intent 를 가지고 왔습니다.
하기 코드는 버튼을 클릭 했을 때 count가 증가 하는 것을 보여줍니다.
MVI 의 Model
/**
* MVI의 M : Model
*
* view의 상태이기 때문에 ViewState로 정의해 줬다.
* 편의를 위해 default 값을 정해준다.
*/
data class SampleViewState(
val text: String = "Click Button",
val count: Int = 0,
val buttonText: String = "Button",
val title: String = "Mvi Sample"
)
MVI 의 View
/**
* MVI의 V : View
* Statefull composable
*/
@Composable
fun MainScreen(
viewModel: SampleViewModel = viewModel()
) {
val viewState = viewModel.viewState.value
/**
* LaunchedEffect key1을 Unit 으로 설정하여 한번만 생성이 되도록 한다.
*/
LaunchedEffect(key1 = Unit) {
/**
* viewState 받아오기 위하여 subscribeToIntent를 호출해 준다.
*/
viewModel.subscribeToIntent()
/**
* sideEffect 를 collect 하여 1회성 이벤트를 처리해 준다.
*/
viewModel.sideEffect
.onEach {
when (it) {
is SampleSideEffect.CheckButtonClick -> {
Log.d("SideEffect", "CheckButtonClick")
}
is SampleSideEffect.ButtonClickComplete -> {
Log.d("SideEffect", it.message)
}
}
}.collect()
}
SampleScreenImpl(
modifier = Modifier,
viewState = viewState,
onClickButton = {
/**
* SampleIntent.OnClickButton viewModel에 전달
*/
viewModel.setIntent(SampleIntent.OnClickButton)
}
)
}
MVI 의 Intent
/**
* MVI의 I : Intent
* ViewModel로 전달하는 Action or Command or Event
*/
sealed class SampleIntent {
object OnClickButton : SampleIntent()
}
ViewModel 에서 Intent 처리
fun subscribeToIntent() {
viewModelScope.launch {
_intent.collect { handleIntent(it) }
}
}
/**
* 전달 받은 Intent 를 처리
*/
private suspend fun handleIntent(intent: SampleIntent) {
when (intent) {
is SampleIntent.OnClickButton -> {
_sideEffect.send(SampleSideEffect.CheckButtonClick)
/**
* _viewState.value 를 copy 하여 사용한다.
* copy 하여 사용 했을경우
* 변경하지 않는 데이터가 보존된다.
* 또한, Create 하는 것보단 Copy 하는것이 좋다고 한다.
*/
_viewState.value = _viewState.value.copy(
count = _viewState.value.count + 1
)
_sideEffect.send(SampleSideEffect.ButtonClickComplete("onClick button success"))
}
}
}
/**
* view에서 전달 받은 Intent를 sharedFlow에 전달 해준다.
*/
fun setIntent(intent: SampleIntent) {
viewModelScope.launch {
_intent.emit(intent)
}
}
전체 코드는 github에 있습니다.
https://github.com/danchoo21/mvi-sample
1. 유저가 버튼을 누릅니다 ( Intent 발생)
2. ViewModel 에 Commnad or Action (Intent)을 보냅니다. (A Button 눌렀음 )
3. 해당 Commnad or Action (Intent) 을 수행 합니다. (Network 처리, DB R/W 기타 등등....) -> sample 에서는 count 증가
4. 수행한 해당 data 를 Model에 넣어줍니다.
5. LiveData 혹은 Rx subscrive, Flow를 통하여 View에서 Observe 하고 있는 Model의 상태의 변경을 감지 합니다.
6. View 를 업데이트 합니다.
참고 : https://appmattus.medium.com/top-android-mvi-libraries-in-2021-de1afe890f27
https://github.com/airbnb/mavericks
https://blog.mindorks.com/mvi-architecture-android-tutorial-for-beginners-step-by-step-guide
'Android > Kotlin' 카테고리의 다른 글
[Compose] Compose Image center crop not working (0) | 2023.05.15 |
---|---|
[Android] Compose Dialog Full Screen (Full Width Screen) (0) | 2023.05.10 |
[Android] Compose CompositionLocal (0) | 2022.03.30 |
[Android] Compose lifecycle (0) | 2022.03.26 |
[Android] Compose State, Statefull, Stateless, State Hoisting (0) | 2022.03.26 |
댓글