[Android] MVI 패턴

kimgwon·2024년 10월 22일

Android

목록 보기
3/7

🫧 MVI (Model-View-Intent)

UI를 중심의 관심사 분리를 목표로 한다. 세 가지 구성 요소로 이루어진다.

  • M (Model) - UI의 상태
  • V (View) - UI (ex. View, Compose 등)
  • I (Intent) - 사용자의 액션 또는 이벤트

✏️ 순수 함수 기반

MVI의 가장 큰 특징은 순수 함수 기반의 사이클을 따른다는 것이다.

  1. Intent()의 호출 결과가 Model()의 인자로 전달된다.
  2. Model()의 호출 결과가 View()의 인자로 전달된다.

순수 함수
다음 두 가지 조건을 만족하는 함수이다.
1. 함수의 입력이 같다면 항상 같은 출력을 반환한다.
2. Side Effect가 없다. (외부 상태를 변경하거나 의존하지 않는다.)


✏️ 단방향 데이터 흐름

MVI는 데이터가 한 방향으로만 흐르는 단방향 데이터 흐름을 갖는다.

예를 들어,
1. 사용자가 View에서 목록을 클릭하면, 데이터를 가져오려는 Intent가 생성된다.
2. 해당 Intent가 처리되어 Model에서 새로운 데이터를 생성한다.
3. 생성된 데이터는 View에 반영된다.

이러한 단방향 데이터 흐름은, Compose의 철학과도 부합한다.


✏️ MVVM과의 관계

MVI는 MVVM과 완전히 별개의 패턴이 아닌, MVVM에서 발전된 형태로 볼 수 있다.
즉, MVVM 내에서 구현될 수 있는 하위 패턴으로 볼 수 있다.



🫧 State Reducer

MVI에서 Intent 호출 결과는 항상 새로운 상태(Model)를 생성한다.
MVI는 순수함수 사이클을 지향하기에 상태를 불변하게 관리하며, 상태를 변경하기 위해 항상 새로운 상태를 생성한다.

State Reducer는 주어진 현재 상태와 이벤트를 기반으로 새로운 상태를 생성하는 역할을 한다.

이 방식은 모든 상태를 한 곳에서 관리하기 때문에 디버깅이 용이해진다.


✏️ 예제

// Intent에 해당
sealed class Event {
	object Increment: Evnet()
    object Decrement: Event()
}

// Model에 해당
data class State(val counter: Int = 0)


class ViewModel {
    // Event 처리 순서를 보장한다.
    private val events = Channel<Event>()
    
    // 새로운 State를 생성한다.
    val state = events.receiveAsFlow()
    	.runningFold(State(), ::reduceState) // 이벤트와 현재 상태를 통해 새로운 상태를 생성하는 State Reducer
        .staeln(viewModelScope, Eagerly, State())
        
    fun handleEvent(event: Event) {
    	when(event) {
        	is Increment -> state.update { it.copy(counter = it.counter + 1) }
            is Decrement -> state.update { it.copy(counter = it.counter + 1) }
        }
    }
}


🫧 장단점

✏️ 장점

  • 상태 관리 용이: 모든 상태를 중앙에서 관리하여 관리가 용이해진다.
  • 단방향 데이터 흐름: 데이터 흐름이 명확하여 디버깅과 유지보수에 용이하다.
  • 스레드 안전성: 순수 함수 기반으로 설계되어 멀티스레드 환경에서도 안정적이다.
  • 테스트 용이: 순수 함수와 단방향 흐름으로 테스트 작성이 간단하다.

✏️ 단점

  • 학습 곡선: 패턴 이해와 적용에 시간이 걸린다.
  • 보일러플레이트 코드: 초기 설정과 코드 작성이 많아질 수 있다.
  • 파일 및 메모리 관리 복잡성: 상태를 지속적으로 생성하기에 메모리 사용량이 증가할 수 있다.


🫧 Side Effect

순수함수로만 앱을 구성하기는 어렵다. 그렇기에 Side Effect를 통해 보완할 수 있다.

Side Effect는 Intent가 반드시 UI 상태에 반영되지 않아도 될 때 사용할 수 있다.
예를 들어,
버튼 클릭 시 상태를 변경하지 않고 사용자에게 간단한 알림(토스트)을 보여주거나, 특정 동작을 로깅할 때 Side Effect를 통해 처리할 수 있다.



Reference

패스트캠퍼스 - 의존성 주입 완전 정복 by Hilt

0개의 댓글