본문 바로가기

Android/Tech

안드로이드의 MVI 와 Reducer

Unsplas, Nicholas Ng.

동기

신규 프로젝트에 단독 투입되어 안드로이드 앱 개발을 수행하게 되었습니다. Jetpack Compose 와 

MVI Architecture 로 앱을 구현했고, 이후 사수분께서 코드에 대한 설명을 요청하셨습니다. 

Screen-Level-Composable 에서 받은 UiEvent 를 ViewModel 로 전달하는 코드의 구현에 대한 설명을 드렸고, 참고 하시라고 Reducer 관련 컬럼을 몇 개 전달했는데, 저에게도 많은 도움이 되어 이에 대해 정리하고자 합니다.

 


Reducer

Reducer 는 React 애플리케이션의 상태 관리 프로세스로 잘 알려져 있습니다. Reducer 는 현재 상태와 액션을 결합하여 새로운 애플리케이션의 상태를 반환하는 순수 함수를 일컫습니다. 즉, 함수형 프로그래밍이 가능한 언어에서 사용하기 적절합니다.

const reducer = (state, action) => {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    default:
      return state;
  }
};

 

Reducer 의 장점은 상태 전체를 반환하는 순수 함수 형태로 구현 되기 때문에 상태의 불변성이 보장된다는 것입니다. Reducer 를 거치지 않는 이상 상태를 업데이트 할 수 없기 때문에, 휴먼 에러 발생의 위험성을 대폭 감소시킬 수 있습니다.

 

개인적인 생각이지만, Reducer 와 같은 방식으로 상태 관리 체계를 구축하면 어떤 액션에 따라 상태가 어떻게 변경되는지에 대한 청사진이 그려진다는 점도 매우 큰 장점이라 생각합니다. 코드를 작성하는 일은 결국 사람이 하기 때문에, 긴 소스 코드에 대한 인지 상태를 지속하는 것이 매우 중요한데 이와 같은 방식으로 상태를 관리하면 소스 코드의 극히 일부분만을 보더라도 해당 화면에 어떤 기능이 존재하고, 또 그 기능이 상태를 어떻게 업데이트하는 지에 대한 판단이 굉장히 간편해집니다. 

 

즉, 코드의 미시적 가독성 개선이 아닌 거시적 가독성 개선이 가능합니다. 기능 오류나 국소적 리팩토링에 있어서도 유의미한 생산성 향상이 있다고 느끼고요. 제가 MVI Architecture 를 좋아하는 이유도 여기에 있습니다.


MVI Architecture

MVI Architecture 는 Redux 의 Reducer 패턴을 적용한 MVVM Architecture 라고 해석할 수 있습니다.

 

 

간단히 도식화하면 위와 같은데요. 이는 굉장히 잘 알려진 그림입니다. 아키텍처에 관심이 많은 분들은 이해가 쉽겠지만, 그렇지 않은 분들은 조금 난해할 수도 있다고 사료됩니다. 이를 안드로이드 프로그래밍에 대입하자면 다음과 같은 그림이 될 겁니다.

 

 

유저 인터랙션이 Intent 라는 이름의 일회성 객체로 ViewModel Reducer 에 전달이 되고, 개발자의 의도에 따라 별도로 데이터가 필요하지 않은 경우에는 즉시 상태를 업데이트할 수 있습니다. 데이터가 필요하다면  DataSource(DB 접근, 네트워크 통신 등) 를 통해 데이터를 획득하고 이를 활용하여 상태를 업데이트하게 되고요.

 

UI 갱신은 사용하는 UI 구현 방식에 따라 다른데, Android View System 이라면 Observer 패턴을 이용하거나 Databinding 을 통해 이루어질 수 있고, Jetpack Compose 를 사용한다면 collectAsState() 와 같은 Flow<T> 메서드를 이용해서 이루어지게 됩니다.

 


개인적인 생각

절대적 사견이라 잘못된 내용이 많을 수 있습니다.

 

저는 사용해 본 적이 없어서 잘 알지 못하지만, Mavericks 나 Orbit 같은 라이브러리를 통해서도 

MVI Architecture 를 보다 간단히 구현할 수 있는 것으로 압니다. 생산성 향상에 굉장히 큰 도움이 될 겁니다.

이러한 라이브러리들은 Reducer 의 본질에 집중함과 동시에 다양한 기능을 제공합니다.

조금 서치해보니, SavedStateHandle 도 자동으로 사용할 수 있도록 준비해주고, Activity Lifecycle 에 안전하게 구성해준다고 합니다. 나중에 한 번쯤 사용해보면 좋을 것 같습니다.

 

사실 DataSource 나 UseCase 를 거쳐 데이터를 획득하고, 이를 활용해 UI 를 업데이트하는 방식은 우리에게 이미 충분히 친숙합니다. 다만, 그 과정에서 발생할 수 있는 휴먼 에러를 줄이고, 애플리케이션의 상태를 보다 예측 가능한 형태로 유지하기 위해 Reducer 라는 개념이 추가된 것일 뿐이지요.

 

작은 변화를 통해 휴먼 에러의 위험을 방지하고, 거시적 가독성 개선까지 이룰 수 있어서 저는 정말 좋아하는 구조입니다. 최근 들어 MVI Architecture 를 채택하는 기업들도 더 많아진 것 같더군요. 앞으로도 안드로이드 생태계에 MVI Architecture 를 보다 발전시키는 움직임이 더 많아졌으면 좋겠습니다. 

 

이후에 또 어떤 새로운 구조가 등장할지는 모르겠지만, 갈수록 좋아지는 것 같습니다. 새로운 구조가 나오면 또 학습해서 적용해보고 싶네요.