Skip to content

MVI를 뿌셔보자!

JunJangE edited this page Nov 1, 2024 · 1 revision

남녀노소 안드로이드 팀은 위와 같은 이유와 비디오 스트리밍 및 비디오 업로드 페이지의 특성으로, 사용자의 제스처나 터치가 빈번하게 발생할 수 있다고 생각했다.

MVVM에서는 이 모든 동작을 VM에서 구현하게 되는데, 이때 코드가 복잡해지고 각 기능이 어떻게 동작하는지 쉽게 이해하기 어려울 것으로 판단했다. 이에 반해, MVI에서는 Intent를 사용하여 사용자 작업의 모든 행위를 명시하고 관리함으로써 코드를 간결하게 유지할 수 있다고 생각했다.

이러한 이유로 MVI 디자인 패턴을 도입하여 프로젝트를 진행하게 되었다.

우리는 Compose 기반 Android 앱에서 MVI 아키텍처를 구현하기 위한 유틸리티 함수를 정의했다.

@Composable
inline fun <reified STATE, EVENT, EFFECT> use(
    viewModel: BaseContract<STATE, EVENT, EFFECT>
): StateDispatchEffect<STATE, EVENT, EFFECT> {
    val state by viewModel.state.collectAsStateWithLifecycle()

    val dispatch: (EVENT) -> Unit = { event ->
        viewModel.event(event)
    }

    return StateDispatchEffect(
        state = state,
        effectFlow = viewModel.effect,
        dispatch = dispatch,
    )
}

data class StateDispatchEffect<STATE, EVENT, EFFECT>(
    val state: STATE,
    val dispatch: (EVENT) -> Unit,
    val effectFlow: SharedFlow<EFFECT>,
)

use 함수는 주어진 viewModel을 기반으로 상태(State), 이벤트(Event), 이펙트(Effect)를 다루기 위한 Compose-friendly한 유틸리티 함수이다.

  • STATE는 상태를 나타내는 타입으로, 해당 상태는 viewModel의 state 플로우를 통해 수집된다.
  • EVENT는 사용자의 액션 또는 이벤트를 나타내는 타입입니다. viewModel의 event 메서드를 호출하여 이벤트를 처리한다.
  • EFFECT는 비동기적인 작업의 결과 또는 부가적인 효과를 나타내는 타입으로, viewModel의 effect 플로우를 통해 수집된다.
  • StateDispatchEffect는 use 함수에서 반환하는 데이터 클래스로, 현재 상태, 이벤트 디스패치 함수, 이펙트 플로우를 포함합니다.

이렇게 정의된 use 함수와 StateDispatchEffect 데이터 클래스는 Compose 기반 앱에서 MVI 아키텍처를 쉽게 구현하고 사용할 수 있도록 도와주는 유틸리티이다.

interface BaseContract<STATE, EVENT, EFFECT> {
    val state: StateFlow<STATE>
    val effect: SharedFlow<EFFECT>
    fun event(event: EVENT)
}

위 MVI 아키텍처에서 사용되는 기본적인 계약을 정의한 인터페이스이다. state는 현재 뷰의 상태를 나타내는 StateFlow로, 뷰는 해당 상태를 수신하여 UI를 업데이트한다. effect는 비동기 작업의 결과나 부가적인 효과를 나타내는 SharedFlow로, 뷰는 해당 이펙트를 수신하여 추가적인 작업을 수행한다. event는 사용자의 액션 또는 이벤트를 처리하는 메서드로, 뷰에서 발생한 이벤트를 받아서 처리하고, 필요한 경우 상태를 업데이트하고 이펙트를 발생시킨다.

이 인터페이스를 구현하는 구현체는 ViewModel로, 상태, 이벤트 처리, 이펙트에 관한 기본적인 계약을 제공하게 된다. 이를 통해 일관된 방식으로 MVI 아키텍처를 구현하고 사용할 수 있다.

MVI를 뿌셔보면서,,,

위 설명과 같이 MVI를 뿌셔보았다.

Contract를 통해 Screen Composable에서 사용할 state, event, effect를 명확하게 정의하고, 해당 Contract를 구현하는 ViewModel에서는 비즈니스 로직을 담당하도록 설계했다. Screen에서는 use 함수를 호출하여 UI에서 뷰모델의 상태를 간단히 수신하고, 사용자 액션을 발생시키며, 뷰모델이 생성하는 이펙트를 간편하게 처리할 수 있도록 구성했다.

Screen에서는 주로 상태를 UI에 렌더링하고 사용자 액션을 event로 트리거한다. 이벤트가 발생하면 ViewModel에서 해당 액션이 어떤 것인지 확인하고, 그에 따라 상태를 업데이트하거나 이펙트를 발생시켜 상태 업데이트 이외의 행위를 실행시킨다. 특히, effect의 경우 Screen에서 정의한 LaunchedEffect에서 해당 이펙트를 감시하다가 발생하면 그에 맞는 동작을 수행한다.

이렇게 함으로써 MVI 패턴을 통한 상태 관리와 UI 로직을 효율적으로 다룰 수 있었다. 이 구조는 코드의 가독성과 유지 보수성을 높이며, 각 구성 요소가 명확한 역할을 수행할 수 있도록 도와준다. 개인적으로 상태 중심의 패턴으로 사용자 액션을 처리하고, 그에 따른 상태 변화를 업데이트하는 방식을 선호하여 이 구조는 꽤나 만족스러웠다. 더 나아가 시간이 허락한다면 이 구조를 더 개발하고 개선하는 것이 목표이다.

👪 남녀노소 Tech Blog

🤔 우리는 왜 이런 선택을 했는가?

🗣 Ground Rule

💃 MoveMove

🌱 Daily Scrum

1주차
2주차
3주차
4주차
5주차
6주차

✏️ MoveMove 개발 일지

김민조

장지호

조준장

장민석

하채리

🐣 개인회고

김민조
장지호
조준장
장민석
하채리

🐥 팀회고

Clone this wiki locally