[iOS] MVI 패턴 알아보기

Jaeho Yoo
6 min readJun 26, 2022

--

일주일 전에, iOS 개발자로 첫 출근을 했습니다.

좋은 회사, 좋은 팀원들을 만났습니다. 제게 과분할 정도로 잘해주셔서 너무나 감사한 마음입니다.

제게 주어진 첫 미션은, 회사 앱에 적용된 디자인 패턴에 관해 공부하고, 코드를 천천히 읽어보면서 전체적인 동작 방식을 이해하는 것이었습니다.

회사의 서브 앱은 배포 타겟 iOS 14.5 이고 SwiftUI 프레임워크와 MVI 패턴으로 만들어졌습니다.

MVI 패턴은 iOS 보다는, 안드로이드 진영에서 많이 사용되는 패턴인데요. 실제로 구글링을 해보면, 상위 검색 결과에 모두 안드로이드 키워드가 보이는 걸 확인할 수 있습니다.

MVI 패턴 검색 결과

단방향 아키텍처

저는 취업 전까지는, 양방향 아키텍처인 MVC, MVVM 패턴만 경험해봤습니다.

그래서 단방향 데이터 흐름(Unidirectional Data-Flow)의 개념과 장단점을 알아봐야 했습니다.

전통적인 UIKit + RxSwift 를 사용한 앱은 단방향 데이터 흐름을 위해 ReactorKit 을 사용한다는 걸 들은 적이 있습니다. 전수열 님이 만든 프레임워크인데요.

ReactorKit is a framework for a reactive and unidirectional Swift application architecture.

ReactorKit 은 RxSwift 의존성이 높고, SwiftUI 와는 함께 사용할 수 없는 것으로 보였습니다.

반면, SwiftUI 에서는 최신 디자인 패턴인 TCA 패턴(The Composable Architecture)이 개발자들의 주목을 받고 있는데요.

TCA 라이브러리는 RxSwift 가 아니라 애플의 Combine 의존성이 높아서 iOS 13 버전 이상부터 적용할 수 있습니다.

The Composable Architecture (TCA, for short) is a library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.

정리하자면, ReactorKit, TCA 둘 다 단방향 데이터 흐름을 위한 라이브러리이지만, UIKit / SwiftUI 프레임워크 중 어떤 것을 사용하는 지에 따라 구분되는 것 같습니다.

  • SwiftUI + Combine + TCA
  • UIKit + RxSwift + ReactorKit

그러면 MVI 패턴은?

MVI 는 TCA 와 동일하게 단방향 데이터 흐름을 추구하는 디자인 패턴이지만, 패턴 구현을 위한 별도의 라이브러리가 필요하지 않습니다.

TCA 패턴을 위해선 라이브러리 import 가 필요합니다

TCA 패턴을 라이브러리 의존성 없이 간소화한 것이 MVI 패턴이라고 이해하면 될 것 같습니다. 물론 ‘간소화’라고는 하지만, 처음 접하는 제 입장에선 둘 다 어렵긴 하더라구요. 😅

본격적으로 MVI 패턴에 대해 알아보겠습니다.

Model — View — Intent(”의도”) 로 구성된 단방향(Uni-Directional) 아키텍처
MVI 는 뒤를 돌아보지 않는 단방향 — 상남자의 아키텍처
말하듯이 코드를 작성할 수 있다.

MVI 패턴 도식화

위 이미지를 통해 이해해보겠습니다.

  1. View 에서 액션(UI events)을 입력 받으면
  2. Intent 에서 Model(State) 의 상태 변화를 일으키고
  3. 변화된 상태의 모델이 View 에 전달돼서 화면이 바뀌는 것!

MVI 존재 이유

기존 MVVM 패턴으로 해결하지 못하는 문제가 2가지 있습니다.

  • 상태 문제 — State

상태를 관리하기 힘들어지고, 의도하지 않은 방향으로 제어가 된다면, 이걸 상태 문제라고 부릅니다.

  • 부수 효과 — Side Effect

어떤 결과를 얻을지 예상할 수 없으며, 그에 따른 상태 변경의 어려움을 말합니다.

MVI 패턴이 선택한 방법

MVI 의 특징 중에서 가장 중요한 것 2가지를 살펴보겠습니다.

  • Pure Cycle
  • Side Effect Cycle

🧼 Pure Cycle

MVI 패턴에서 상태(State)는 불변입니다.

위 이미지를 보시면

  1. View 업데이트를 위해, Intent 를 사용해 이벤트를 발생시키고
  2. 이벤트에 따라 새로운 상태(State)가 생성되며
  3. View 가 업데이트 됩니다.

즉, 이벤트가 상태를 변경하기 위한 유일한 방법입니다.

상태는 불변성을 갖기 때문에, 항상 예측 가능한 값을 얻을 수 있습니다. 이는 함수형 프로그래밍이 갖는 장점인데요.

// 함수 x()의 인자로 y를 넘기면 항상 결과는 z이다x(y) == z
// 똑같은 event에 대한 결과는 항상 state 이다intent(event) == state

마치 순수함수처럼, 인자가 같다면 항상 같은 값이 반환됩니다. 그래서 Pure Cycle이라고 부릅니다.

Pure Cycle은 상태관리에서 가장 중요한 역할을 합니다. 예상 가능한 값을 반환하기 때문에, 멋진 테스트 코드를 작성하는 데도 유리합니다.

SwiftUI 본연의 데이터 흐름(Data-Flow)

Pure Cycle 이미지를 보면, SwiftUI 본연의 데이터 흐름과 닮아있습니다.

User Interaction 이 들어오면, 상태(State)가 변경되고, 화면(View)이 업데이트 되는 구조죠. 본질적으로는 똑같습니다.

♻️ Side Effect Cycle

MVI 패턴은 우아한 방식으로 Side Effect 를 제어합니다.

위 이미지를 보시면, 기존 순환에 새롭게 Side Effect 가 추가된 걸 볼 수 있는데요.

intent() 를 사용해 새로운 상태를 생성함과 동시에, side effect 를 실행(dispatch)합니다.

실행이 끝났다면, 결과를 바로 render() 하는 게 아니라, 다시 Event 로서 결과를 전달한다는 게 중요합니다.

MVI 패턴의 장단점

장점

  • 유지 관리가 용이하고, 확장 가능한 앱을 만들 수 있습니다.
  • View 생명주기 동안 일관성 있는 상태(state)를 가집니다.
  • 모델이 불변하기 때문에, 큰 앱에서 멀티 스레드 안정성이 높습니다.

단점

  • 처음에 패턴을 제대로 이해할 때 까지 러닝 커브가 높습니다.
  • RxSwift, Combine 같은 비동기 프레임워크 주제에 대한 상당한 지식이 필요합니다.

--

--