본문 바로가기
기타

[Android] MVVM과 MVI 패턴에 대한 생각

by 중곰 2025. 3. 14.

 

안녕하세요. 중곰입니다.

 

오늘은 MVVM과 MVI 패턴에 대한 생각을 정리하며,

그 내용을 정리해보려고 글을 써봅니다.

 

저의 생각을 정리하기 전에

간단하게 MVVM 패턴, MVI 패턴이란? 무엇이고, 데이터 흐름은 어떤지 간단하게 보고 

생각을 말해봅니다.

 

## 간단 요약 ##

* MVVM 패턴 ?!

  - Model-View-ViewModel 패턴으로, 안드로이드에서 UI 로직과 비즈니스 로직을 분리하는 목적으로 설계된 패턴

* MVI 패턴 ?!

  - Model-View-Intent 패턴으로, 단방향 데이터 흐름과 불변 상태에 중점으로 설계된 패턴

 

## 데이터 흐름 ##

* MVVM

  1. View: 사용자 입력을 ViewModel에 전달
  2. ViewModel: 비즈니스 로직 실행, Model에 데이터 요청/업데이트
  3. Model: 데이터 처리 후 ViewModel에 결과 반환
  4. ViewModel: 상태 업데이트
  5. View: 상태 관찰하여 UI 갱신

* MVI

  1. Intent: 사용자 액션이 Intent로 변환되어 ViewModel에 전달됨
  2. Processing: ViewModel이 Intent를 처리하고 상태 업데이트 또는 부작용 실행
  3. State: 상태가 업데이트되면 View에 전파되어 UI가 갱신됨
  4. Effect: 일회성 이벤트(토스트, 네비게이션 등)는 별도 채널로 처리

Jetpack Compose 를 쓰다보니 

Compose UI를 도입한다면 MVI 패턴이 더 효율적으로 사용 할 수 있다는 것을 다시 한번 느끼게 되었고,

그렇게 도입되어 사용한다는 것을 정리하다보니 생각하게 되었습니다.

 

그 이유를 조금 더 이야기 하자면,

1. 선언적으로 UI 패러다임이 일치

   // Compose의 선언적 UI 모델
   @Composable
   fun UserScreen(state: UserState, onEvent: (UserEvent) -> Unit) {
       // 상태에 따른 UI 선언 (MVI 철학과 일치)
       if (state.isLoading) {
           LoadingIndicator()
       } else {
           UserContent(
               user = state.user,
               onRefreshClick = { onEvent(UserEvent.RefreshRequested) }
           )
       }
   }

2. 단방향 데이터 흐름 일치

 * Compose : 상태 -> UI

 * MVI : 상태 -> 랜더링

 * 둘 다 예측 가능한 단방향 흐름을 촉진

3. 불변 상태 모델 공유

 * Compose : 불변 상태 기반 리컴포지션

 * MVI : 불변 상태 기반 상태 관리

 * 버그 발생 가능성을 줄임

 

이렇게 정리해볼 수 있습니다.

즉, Compose가 이벤트에 따른 리컴포지션이 일어나는데 단방향성이 있는 MVI 패턴을 가진다면,

예측이 가능하여 부수효과를 가지고 제어하고 여러번 리컴포지션이 일어나는 버그 확률을 줄 일 수 있다라고 볼 수 있기 때문입니다.

 

조금 예제 설명을 함께하여 MVVM 패턴과 MVI 패턴의 예시를 보여보겠습니다.

// MVVM 방식 (복잡한 상태 업데이트 시)
class UserViewModel : ViewModel() {
    // 여러 상태 스트림
    val userName = MutableStateFlow("")
    val isLoading = MutableStateFlow(false)
    val errorMessage = MutableStateFlow<String?>(null)
    
    // 컴포저블에서 각각 수집 필요
    fun updateUserName(name: String) {
        userName.value = name
        // 다른 상태에 미치는 영향이 명확하지 않음
    }
}

// MVI 방식 (통합된 상태 관리)
class UserViewModel : ViewModel() {
    // 단일 상태 객체
    private val _state = MutableStateFlow(UserState())
    val state = _state.asStateFlow()
    
    // 명확한 이벤트 처리
    fun handleIntent(intent: UserIntent) {
        when (intent) {
            is UserIntent.UpdateName -> {
                // 전체 상태 업데이트가 명시적
                _state.update { it.copy(userName = intent.name) }
            }
        }
    }
}

 

예시를 보면, 

MVVM 으로 하게 되면 여러 상태를 가지게 되고, 개별 StateFlow로 인해 불필요한 리컴포지션이 발생됩니다.

이 부분으로 인해 동일한 화면을 한번만 업데이틀 하면되는데 여러번 업데이트 되는 불상사가 발생했었습니다.

추가로, 상태 업데이트 로직 자체가 여러 메서드에 분산되다보니 디버깅 하기에도 어려운 점이 있었습니다.

 

MVI 를 보면 UserState로 통합 상태로 관리하여 필요한 부분만 명시적 update 를 하여 원하는 부분만 리컴포지션할 수 있고,

모든 상태 변환을 관리하여 업데이트 포인트가 현저하게 줄어들어 디버깅 하기에 수월하게 되었습니다.

 

그래서 MVI 패턴이 Compose 도입에서 더 수월하게 관리할 수 있다는 것을 

끝으로 마지막 저의 생각을 정리해봅니다.

 

 

반응형