MVI는 단방향 데이터 흐름을 갖는 아키텍처 패턴이다.
State를 중심으로 UI를 구성하며 모든 상태 변화는 사용자의 명시적인 Intent를 통해 일어난다.
여기서 Intent는 안드로이드의 Intent와는 달리 사용자의 UI 액션이나 이벤트를 표현하는 객체이다.
- ViewState (Model)
UI에서 표현할 상태 데이터를 의미하며 상태는 불변 객체로 관리된다.
ex)로딩 중 여부, 입력 값, 에러 메시지 등- View
UI를 구성하는 계층으로 ViewState의 변화를 관찰하고 화면을 업데이트한다.
또한, 사용자의 입력을 Intent로 변환해 전달한다.- Intent
사용자 또는 시스템에 의해 발생하는 이벤트 또는 액션을 의미하며 ViewState를 변경하기 위한 트리거로 사용된다.
ex) 버튼 클릭, 텍스트 변경, 스크롤 이벤트 등
- View → Intent
사용자 이벤트를 Intent로 변환해 ViewModel로 전달- Intent → State
ViewModel이 Intent를 처리해 ViewState를 생성- State → View
View는 ViewState를 관찰하고 UI를 갱신
항목 | MVI | MVVM |
---|---|---|
데이터 흐름 | 완전한 단방향 흐름 (Intent → State → View) | 과거엔 양방향 데이터 바인딩도 가능했으나 Compose 기반은 단방향이 대세 |
UI 상태 관리 | 단일 불변 State 객체로 전체 UI 상태를 관리 | 보통 여러 LiveData/StateFlow 사용하며 최근에는 단일 상태 객체 관리 추세 |
변경 추적 | Reducer 함수로 명확하게 상태 변화 관리 | 보통 ViewModel 내부에서 상태 직접 갱신 |
디버깅/예측 가능성 | 높은 예측 가능성 (불변 상태 + 단일 상태 + Intent 기반) | 설계에 따라 다르나 상태가 여러 흐름에 나뉘면 예측과 디버깅 어려울 수 있음 |
복잡도 | 상태가 많아지면 구조가 복잡해질 수 있음 | 상대적으로 단순하고 익숙한 구조 |
대표 사용처 | Jetpack Compose, 리액티브 UI | 전통적인 XML 기반, Compose에도 사용 가능 |
대표 라이브러리/예시 | Orbit MVI, MviKotlin, Redux 스타일 | Jetpack ViewModel, LiveData, StateFlow 등 |
상황 | 설명 |
---|---|
상태(State)가 복잡한 화면 | 여러 UI 요소가 동시에 상태 영향을 받을 때 (로그인, 장바구니, 대시보드 등) |
UI 상태를 명확하게 추적하고 싶을 때 | 모든 상태 변화가 Intent를 통해 발생하므로 디버깅과 로깅에 유리 |
불변 상태 기반의 정제된 설계를 원할 때 | 단일 상태(State)를 기준으로 화면을 구성 |
단방향 흐름이 강하게 요구될 때 | UI 동기화 문제를 방지하고자 할 때 |
상황 | 설명 |
---|---|
화면 구조가 단순하거나 CRUD 중심일 때 | 리스트, 입력 폼, 디테일 페이지 등 |
구현 속도와 생산성이 중요할 때 | Boilerplate 적고 익숙한 패턴으로 빠른 개발 가능 |
구현 복잡도를 낮추고 싶을 때 | MVI보다 학습 비용과 설계 난이도가 낮음 |
기존 프로젝트가 MVVM 기반일 때 | 유지보수성과 팀 내 일관성 고려 |
질문 | 답변 |
---|---|
언제 MVI를 써야 하나요? | 상태가 복잡하고 명확한 상태 추적과 불변 상태 관리가 필요한 화면에 적합 |
언제 MVVM을 써야 하나요? | 단순 UI나 CRUD, 빠른 개발 및 기존 MVVM 프로젝트 유지보수에 적합 |
기능별로 나눠 써도 되나요? | 가능하지만 팀 내 아키텍처 일관성과 합의가 매우 중요 |
전체를 하나로 통일해야 하나요? | 규모가 크고 여러 팀이 함께 작업할 경우, 통일된 아키텍처 유지가 유지보수에 유리 |
Jetpack Compose와의 높은 궁합
Compose는 선언적 UI → 상태가 곧 UI
MVI는 단일 상태와 단방향 흐름으로 구성 → Compose와 찰떡
→ UI 갱신이 예측 가능하고 일관성 있게 작동
단방향 흐름으로 상태 꼬임 최소화
MVVM은 상태 변경 경로가 다양해 복잡한 화면에서 꼬일 수 있음
MVI는 Intent → Reducer → State 흐름이 명확
→ 디버깅, 추적, 유지보수에 유리
상태 관리 및 추적이 쉬움
하나의 ViewState로 모든 UI 상태를 통합
→ 저장, 복원, 테스트, 시뮬레이션이 용이
→ 특히 복잡한 화면일수록 신뢰성과 유지보수성 향상
실시간 반응형 UI에 적합
빠른 상태 전이와 피드백이 필요한 앱에 적합
→ 복잡한 상태 흐름을 명확히 모델링 가능
아키텍처 일관성과 테스트 용이성
흐름이 명확하여 SideEffect 분리, 유닛 테스트 구조화 쉬움
→ 비즈니스 로직과 UI 코드가 잘 분리됨
단, MVI가 항상 MVVM보다 우월한 건 아니며 화면이 단순하거나 전환 비용이 클 경우엔 MVVM도 여전히 유효하다.
결국 상황에 맞게 선택하는 것이 중요하다.
사이드 프로젝트를 진행하면서 MVI 패턴을 적용해볼까 고민 중이다.
기존에는 MVVM 패턴만 사용했지만, MVI 패턴도 새롭게 알게 되어 이번 기회에 직접 적용해보며 학습하면 좋을 것 같아 정리해봤다.
Compose + MVI가 좋은 선택일까?
비교적 MVVM보다 초기 설계와 학습에 시간이 더 많이 들고 코드 구조가 복잡해질 수도 있지만..
Compose의 선언적 UI 특성상 상태 기반 렌더링에 최적화되어 있어
MVI의 단방향 데이터 흐름과 단일 불변 상태 관리 방식이 Compose와 잘 어울린다.
덕분에 UI 상태 관리가 훨씬 명확해진다는 점이 큰 장점이다.
그래서 이번 프로젝트에서 MVI를 경험하며 배우는 것이 좋은 선택일 것 같다.