최근 스터디를 진행하면서 기술 블로그를 탐방하다 MVI Pattern을 보았다.
이전에 디자인 패턴에 대하여 공부할 때 확인했던 패턴인데, 그 당시에는 기존의 디자인 패턴과는 좀 다른 모습을 보이고, 이해하기에 어려움이 있는 것으로 보여서 패스했던 기억이 있다.
이제야 다시 한번 MVI Pettern에 대하여 스터디를 진행했고, 이해한 것들을 토대로 글을 한번 작성해보고자 한다.
MVI Pattern에 대하여 스터디하기 위해 여러 게시글을 보면 항상 나오는 그림이 있다.
그리고, 이런 것도 말이다.
View ( Model ( Intent( ) ) )
필자가 클린 아키텍처에 대한 글을 작성했을 때도 그랬지만, 맨땅에 보면 이해하기가 쉽지 않다.
MVI Pattern에 대하여 이해하고 해당 Flow를 확인하면 "그렇지 이렇게 흘러가지"라고 납득할 수 있지만 말이다.
그래서 이번에도,
필자가 공부하고 예제를 만들어 보면서 이해한 대로 정리해보려고 한다.
필자가 이해한대로 작성했기 때문에, 이해하는 것에 위의 이미지가 필요한 경우 언급했으니 그때에 맞춰 확인하면 이해가 더 쉬울 것이다.
우선,
MVI Pattern은 무엇인가?
Model, View, Intent로 이루어진 Design Pattern
여기서 말하는 Intent는 우리가 흔히 개발을 진행하면서 봐왔던 android.content.Intent에서 사용되는 Intent가 아닌,
앱의 상태를 바꾸라는 요청을 Intent라고 부른다.
즉, 우리가 알고있던 Intent와 MVI에서의 Intent는 다른 것이며, 우선은 앱의 상태를 변경하는 Event라고 생각하고 넘어가도록 하자.
그러면 여기서 각 Model, View, Intent가 어떠한 역할을 하는지 확인해보자.
View : "User"가 볼 수 있는 화면. Activity, Fragment가 이곳에 포함된다.
Model : 앱의 유일한 상태와 데이터를 가지고 있는 불변 객체.
Intent : 앱의 상태를 변경하는 요청.
자, 벌써 Model에서 알 수 없는 말이 나오기 시작한다. Model은 불변 객체라고 한다.
이것은 MVI Pattern의 핵심 중 하나로, Model을 불변의 객체로 만들어서 사용함으로써 MVI Pattern을 사용할 때 얻을 수 있는 이점들을 얻을 수 있다.
불변 객체라는 것은 변할 수 없는 단일 상태의 객체 라는 것이다.
단순하게, MVI 패턴에서는 한 가지 화면을 보여주기 위해서 한 가지의 Model을 사용하고,
Toast가 뜬다던지, 화면이 이동된다던지 등의 UI Event가 발생하게 되면 그때그때 대응되는 별도의 Model을 새로 만들어서 1:1 매칭으로 Model을 사용한다고 생각하면 된다.
변화에 따라 만들어진 Model을 변경하는 것이 아니라, 별도로 Model을 만들어서 사용하기 때문에 하나의 Model은 하나의 상태만 보여줄 수 있게 되는 것이다.
Intent는 앱의 상태를 바꾸라는 "요청" 자체를 말하는 것으로, 쉽게 생각해서 User의 행동이 Intent라고 볼 수 있다.
예로 들어서, A라는 버튼을 누르면 Toast가 뜬다고 생각해보자.
User가 A라는 버튼을 누른다.라는 것은 Toast를 띄우라는 "요청"이 되는 것이다.
즉, App의 상태를 바꾸려는 User의 Event를 받아서, 해당 동작을 위하여 다른 부분을 호출하는 것을 통틀어서 Intent라고 한다.
Intent를 앱의 상태를 변경하는 user의 Event라고 생각하고 위의 이미지를 한번 보자.
User가 어떠한 동작을 하여
> Intent(Event)가 발생하면
> Model에서 필요한 데이터를 만들고
> View에서 Model을 토대로 UI를 갱신하여
> User가 보게 된다.
라는 식의 flow가 되는 것이다.
그렇다면,
MVI Pattern을 사용하여 얻을 수 있는 장점은 무엇인가?
- 단방향 및 순환의 데이터 흐름을 보인다.
- 불변성
- 디버깅이 용이하다.
- 로직이 분리되어있다.
- State Problem이 없다.
위에 작성한 내용들 외에도 테스트가 용이하다던지 다양한 장점이 있지만, 우선 필자가 지금까지 공부하면서 공감할 수 있는 장점에 대하여 작성해 보았다.
이 중, State Problem이 없어지는 것은 MVI Pattern을 사용하는 가장 큰 이유라고 볼 수 있다.
MVI Pattern을 사용하게 되면, 화면 당 하나의 Model을 사용하기 때문에 상태 문제(State Problem)가 발생할 수가 없는 구조가 된다.
가장 흔하게 예로드는 상태 문제는, 모든 List의 로딩이 끝났음에도 불구하고 Progress가 돌고 있는 상태이다.
API 통신을 통해 데이터를 가져와서 UI로 뿌려주는 작업은 완료했지만, 어떠한 이유로 인해서 Progress를 invisible 시키는 작업이 동작하지 않는 상태라고 볼 수 있다.
하지만 MVI 패턴에서는 프로그레스가 도는 것, Progress가 멈추고 List가 보이는 것이 각각 다른 Model을 사용하기 때문에 이러한 상태 문제가 발생할 수 없게 되는 것이다.
다시 한번 맨 처음에 작성했던 이미지를 보자.
위의 설명을 통해 해당 이미지가 어느 정도 이해가 될 것이라고 생각한다.
그러면, 조금 더 확장된 것을 보도록 하자.
Side Effect라는 항목이 추가되었다.
앞서 표현한 4가지의 항목을 가지고 생각하면 좋겠지만, 사용자의 입력에 따라 Intent가 발생하고 Model을 컨트롤하여 View에 보여준다는 단순한 모습으로는 표현될 수 없는 것들이 있다.
따라서, MVI Pattern에서는 Side Effect라는 항목으로 정의하여 별도의 작업들을 관리할 수 있도록 하였다.
주로 상대적으로 시간이 많이 걸리는 Background 작업들, API 통신, I/O 작업, Activity 화면의 전환 등이 여기에 포함될 수 있다.
이러한 Intent가 들어오게 되면 Model로 값을 전달함과 동시에 Side Effect에도 값을 전달하여 해당하는 작업을 실행시킨다.
해당 작업이 끝나게 되면 Intent와 마찬가지로 Model로 값을 던져서 동일한 프로세스를 탈 수도 있고, Side Effect에서 사이클이 끝날 수도, 다른 Intent를 호출시킬 수도 있다.
중요한 점은, Intent에서 호출 된 일부 작업들을 Side Effect로 정의된 틀에서 따로 관리할 수 있도록 빼놓은 것이라는 것이다.
MVI Pattern에 대하여 공부하면서 여러 가지 글을 확인해 보면, MVI FrameWork를 사용하는 것을 종종 확인할 수 있었다.
mobius, mavericks, orbit, stateMachine, MVICore 등등 상당히 많다.
하지만 필자는 해당 프레임워크를 사용하지 않고 예제를 만들어 보면서 스터디를 진행하였다.
제대로 어떠한 흐름으로 동작하는지, 어떻게 구현을 해야 하는지 모르는 상태에서 프레임워크를 써봤자 제대로 이해를 할 수 없다고 생각했기 때문이다.
애초에 지금 이 글을 정리하고있는 지금에도 이해하지 못한 부분들이 있는데 말이다.
공부를 했을 때, MVI Pattern은 MVVM과 함께 사용하는 경우가 많았다.
따라서 필자는 보다 익숙한 MVVM에 MVI를 붙여서 예제를 만들어서 공부를 진행하였다.
다음 글에서는 MVI를 적용시킨 샘플 코드를 확인하면서 기본적인 사용 방법에 대해서 작성해보고자 한다.
'Android > Architecture' 카테고리의 다른 글
[Android] MVP Pattern을 적용해보자. (0) | 2022.05.05 |
---|---|
[Android] MVI Pattern을 적용해보자. (2) | 2022.05.03 |
[Android] Repository Pattern (15) | 2022.03.21 |
[Android] Modular Architecture 예제 (0) | 2022.02.20 |
[Android] Modular Architecture 개념 정리 (0) | 2022.02.16 |