모델(Model), 뷰(View), 인텐트(Intent)의 세 가지 구성 요소로 이루어져 있습니다.
- Model : 상태 처리 로직, 비지니스 로직
- View : 상태를 바탕으로 사용자 인터페이스 구성, intent를 model로 전달
- Intent : 사용자의 행동(+ 시스템 이벤트) - 상태 변화 트리거
MVI 패턴의 핵심은 단방향 데이터 흐름(Unidirectional Data Flow)

예시 :
class VoteMainContract {
data class VoteMainViewState(
val loadState: LoadState = LoadState.SUCCESS,
val groupId: Long = 0L,
val voteList: List<AgendaDetailInfoModel> = listOf(),
val voteDetail: AgendaInfoListModel? =null,
val groupName: String = "",
val groupNameList : List<ShareGroupNameInfoModel> = listOf()
) : ViewState
sealed class VoteMainSideEffect : ViewSideEffect {
object NaviAgendaAdd :VoteMainSideEffect()
object NaviBack : VoteMainSideEffect()
data class NaviVoteDetail(val agendaId: Long) : VoteMainSideEffect()
}
sealed class VoteMainEvent : ViewEvent {
object InitVoteMainScreen : VoteMainEvent()
object onAddAgendaInBoxClicked : VoteMainEvent()
object OnBackClicked :VoteMainEvent()
object OnPagingVoteList : VoteMainEvent()
data class OnAgendaItemClicked(val agendaId : Long) :VoteMainEvent()
data class OnClickDropBoxItem(val member: ShareGroupNameInfoModel) : VoteMainEvent()
}
}
VoteMainScreen.kt
@Composable
fun VoteMainScreen(
//**//
) {
//state 받기
val viewState by viewModel.viewState.collectAsState()
//**//
//sideEffect 받기
LaunchedEffect(key1 = viewModel.effect) {
viewModel.effect.collect { effect ->
when (effect) {
VoteMainContract.VoteMainSideEffect.NaviAgendaAdd -> {
navigationAgenda(viewState.groupId)
}
VoteMainContract.VoteMainSideEffect.NaviBack -> {
navigationBack()
}
is VoteMainContract.VoteMainSideEffect.NaviVoteDetail -> {
navigationVoteDetail(effect.agendaId)
}
}
}
}
//state 데이토로 ui 분기
when (viewState.loadState) {
LoadState.LOADING -> {
StateLoadingScreen()
}
//..//
viewState.groupNameList.forEachIndexed { _, member ->
androidx.compose.material3.DropdownMenuItem(
text = {
Text(
member.name,
fontSize = 16.sp,
modifier = Modifier.fillMaxWidth()
)
},
//event 보내기
onClick = {
viewModel.setEvent(
VoteMainContract.VoteMainEvent.OnClickDropBoxItem(
member = member
)
)
//..//
@HiltViewModel
class VoteMainViewModel @Inject constructor(
private val agendaInfoListUsecase: AgendaInfoListUsecase,
private val savedStateHandle: SavedStateHandle,
private val checkSpecificGroupUsecase: CheckSpecificGroupUsecase,
private val shareGroupNameListUsecase: ShareGroupNameListUsecase
) : BaseViewModel<VoteMainContract.VoteMainViewState, VoteMainContract.VoteMainSideEffect, VoteMainContract.VoteMainEvent>(
VoteMainContract.VoteMainViewState()
) {
//..//
init {
updateState { copy(groupId = savedStateHandle[KEY_GROUP_ID] ?: 0L) }
fetchGroupNameList()
}
//event 분기 처리
override fun handleEvents(event: VoteMainContract.VoteMainEvent) {
when (event) {
is VoteMainContract.VoteMainEvent.InitVoteMainScreen -> {
//비지니스 로직
showRefreshVoteList()
}
is VoteMainContract.VoteMainEvent.onAddAgendaInBoxClicked -> {
sendEffect({ VoteMainContract.VoteMainSideEffect.NaviAgendaAdd })
}
is VoteMainContract.VoteMainEvent.OnClickDropBoxItem -> {
//..//
//상태 업데이트
updateState {
copy(
groupId = event.member.shareGroupId,
groupName = event.member.name,
voteList = listOf()
)
}
setEvent(VoteMainContract.VoteMainEvent.OnPagingVoteList)
}
//..//
코드 더보기 :
나o만 GitHub Repository
MVI 패턴을 적용해서 가독성이 좋아서 코드를 리뷰하기 매우매우매우매우 편하다. 협업하기에 좋은 패턴이라고 생각한다. 😎✌️