MVI
- 모델, 뷰, 인텐트의 3가지 컴포넌트를 사용하는 아키텍처 패턴
- 양방향 데이터 흐름이 가능한 MVVM 과 달리, 단방향 데이터 흐름 형태로 데이터가 이동한다.
- MVVM 패턴은 충분히 이상적인 패턴이지만, 화면에 대한 요구사항이 커지고 상태가 늘어남에 따라 복잡한 데이터 흐름, 상태 충돌, 스레드 안전성이란 문제에 대해 한계가 존재한다.
- MVVM 패턴은 데이터 바인딩으로 뷰가 데이터를 구독할 수 있지만 뷰 안에서 스스로 바인딩하는 경우가 있고, 뷰와 뷰모델 간의 핑퐁으로 로직을 처리하면 복잡한 데이터 흐름으로 파악하기 힘들 때도 있다.
- 즉, 뷰와 뷰모델의 양방향 참조가 가능해 생기는 문제가 있다.
- 데이터 흐름을 제어하지 못하는 게 문제였기 때문에, MVI 는 단일 상태 관리, 단방향 데이터 흐름을 통해 MVVM 의 문제를 해결하려고 했다.
- UI 에서 뷰모델로의 직접적인 호출이 아니라 인텐트에 기반하기 때문에 좀 더 느슨한 결합을 유지하게 된다는 장점이 있다.
- 데이터는 항상 유저로부터 시작해서 인텐트를 통해 유저에게 전달된다. 이 반대일 수는 없어서 단방향 아키텍처라고 한다.
- 유저가 한 번 더 작업을 수행하면 같은 사이클이 반복되므로 순환형 아키텍처다.
- Model 은 단일한 상태를 나타낸다.
- 아키텍처의 다른 레이어와의 단방향을 보장하기 위해 변경이 불가능한 불변성을 보장해야 한다.
- Intent 는 앱이나 사용자가 취하는 행위를 나타내기 위한 의도이다.
- View 는 Intent 를 받고 ViewModel 은 Intent 를 옵저빙하여 Model 은 그에 따라 새로운 상태로 변환한다.
- Composable 구성요소들은 State 가 변경되는 Recomposition 이 일어나면서 새로 생성된다.
- 하지만 ViewModel 은 종속된 Activity 나 Fragment 가 완전히 종료될 때까지 동일한 인스턴스를 호출한다.
- 그래서 만약 ViewModel 에서 특정 Composable 의 생명주기에 의존하는 값이나 메서드를 가지고 있을 경우, 의도치 않은 다른 결과를 불러일으킬 수도 있다.
class GreetingActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Column {
GreetingScreen("user1")
GreetingScreen("user2")
}
}
}
}
}
@Composable
fun GreetingScreen(
userId: String,
viewModel: GreetingViewModel = viewModel(
factory = GreetingViewModelFactory(userId)
)
) {
val messageUser by viewModel.message.observeAsState("")
Text(messageUser)
}
class GreetingViewModel(private val userId: String) : ViewModel() {
private val _message = MutableLiveData("Hi $userId")
val message: LiveData<String> = _message
}
class GreetingViewModelFactory(private val userId: String) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return GreetingViewModel(userId) as T
}
}
- 위처럼 GreetingScreen 이 서로 다른 userId 값을 갖고 2번 호출되어도 GreetingViewModel 은 GreetingActivity 가 완전히 종료되기 전까지 동일한 인스턴스를 반환하기 때문에 2번의 GreetingScreen 은 모두 "user1"에 대한 인사말을 표시하게 된다.
- MVI 의 Intent 는 사용자 액션 및 시스템 이벤트에 따른 결과라고 해석할 수 있다.
- MVI 에서도 여전히 상태 관리를 위해 ViewModel 을 사용하며 기존의 MVVM 에서 Compose 의 상태 관리를 좀 더 수월하게 하기 위해 나왔다.
- UiEvent : 사용자의 행동을 나타낸다.
- 사용자의 이벤트를 모델링한 컴포넌트를 의미한다.
- 해당 컴포넌트를 통해 하나의 화면에서 사용자가 어떤 이벤트를 트리거했는지 알 수 있다.
- UiState : View 의 상태를 나타낸다.
- 사용자가 UiEvent 를 통해 앱 이벤트를 트리거한다.
- 앱은 일련의 비즈니스 로직을 실행시키고 최종적으로 UI 를 갱신하게 된다.
- 갱신되는 UI 의 경우의 수를 정의한 컴포넌트이다.
- UiEffect : 에러 메시지 표시와 같이 단 한 번만 보여주고자 하는 *Side Effect 를 나타낸다.
- Side Effect : 백그라운드 작업, 액티비티 전환과 같은 부수적인 작업 또는, Android 하단 알림바 Toast
- 많은 보일러플레이트 코드가 발생한다.
- 많은 객체를 생성해야 하기 때문에, 높은 메모리 관리가 필요하다.
[Android] MVI 패턴이란?
MVVM 패턴 이후 MVI라는 새 패턴이 등장했다. MVI 패턴을 다룬 글은 2019년에도 있어서 등장한지 꽤 오래됐다고 생각된다.각설하고 MVI 패턴은 무엇인지 먼저 확인한다. https://medium.com/@mohammedkhudair57/m
onlyfor-me-blog.tistory.com
[Android] MVI Pattern - (1)
Compose의 복잡한 State 관리를 좀 더 쉽게
velog.io
Android Architecture 패턴: MV 형제들, 옆에서 볼까 앞에서 볼까? : NHN Cloud Meetup
Android Architecture 패턴: MV 형제들, 옆에서 볼까 앞에서 볼까?
meetup.nhncloud.com
'안드로이드 > Android' 카테고리의 다른 글
[Android] Clean Architecture (0) | 2025.01.22 |
---|---|
[Android] LiveData, Flow (0) | 2024.12.22 |
[Android] 프로그램(Program), 프로세스(Process), 스레드(Thread) (0) | 2024.12.11 |