VIPER 패턴의 각 요소의 역할

2014-06-07-viper-wireframe-76305b6d

MVC, MVP, MVVM과 같은 패턴들이 난무하는 세상에 VIPER라는 또 다른 개념의 패턴이 나오게 된 배경은 방대하게 커지는 View Controller를 막기 위한 수단이 아닐까 생각해본다. 기본적으로 모바일 어플리케이션에서 View Controller가 커지는 이유는 간단하다. 사용자의 이벤트 액션이 빈번하게 일어나면서 보다 느낌 좋은(?!) UI, UX를 제공하기 위함이 아니겠는가? 나는 MVC, MVP, MVVM을 실제 프로젝트에 적용해가면서 경험한 느낌 중 VIPER가 가장 깔끔한 인상을 받았다. 물론 하나라도 제대로 사용한다면 격게 될 경험일지도 모른다. 개인적인 경험상 그렇다는 것이다. 그렇다면 VIPER 패턴이 무엇인지 요소 하나하나 살펴보자.

 

Interactor

비즈니스 로직이 들어가는 클래스 종류이다. 각 클래스는 하나의 Use case에 해당하는 비즈니스 로직이 들어가야한다. NSObject를 상속받아 설계하면, iOS 프로젝트와 macOS 프로젝트에서 공유해서 함께 사용할 수 있다. 어떠한 UI와도 연관이 없어야 하며 독립적으로 존재해야 한다. Data Store에서 전달 받은 Entity는 절대 그대로 Presenter 클래스에 전달되어서는 안된다. Presenter에 전달 될 모델 역할의 클래스는 Behavior가 없는 간단한 데이터로 구성되어 있는 모델이어야 한다. 그리고 네트워킹을 사용하는 앱이라면 Interactor에서 DataManager나 DataStore와 같은 직접적으로 네트워킹 구현이 있는 클래스를 사용하여, 네트워크를 초기화하고 시작해야할 것이다. Interactor는 여러 Remote 자원에서 받은 데이터를 종합해서 정보를 Presenter에게 넘져주는 역할을 해야 할 것이다.

  • Data Store로 부터 데이터를 받아온다.
  • Entity를 조작 혹은 조합한다.
  • 조작된 Entity를 다시 Data store에 넣어 주거나, Presenter에게 화면에 뿌려주기 좋은 모양의 모델을 넘겨준다.

Entity

모델 클래스이다. Interactor 클래스에서 다루어지는 객체로써, 절대 그대로 Presenter 레이어로 넘겨서는 안된다. 그래고 만약 Core Data를 사용하고 있다면, ManagedObject로 부터 분리해서 생성되어야 한다.

Presenter

크게 두가지 역할이 있다. UI를 그려주고, Interactor 클래스에게 데이터를 요구하는 역할이다. 뿐만 아니라 VIPER의 약자중 Router에 해당하는 개념의 일부분을 담당한다. Wireframe 클래스는 다음 화면이 어딘지 안다면, Presenter는 언제 다음 화면이 나와야하는지 알고 있는 클래스이다. Presenter는 View 레이어 즉 UIViewController와 같은 클래스를 상속 받은 클래스에게 알맞는 데이터를 할당해주던가 원하는 UI 모습을 만들수 있는 클래스이다. Presenter는 View의 UILabel 이 존재하는지 UIButton이 존재하는지 알아서는 안된다. Presenter는 그려질 컨텐츠와 언제 그것이 View에 전달되어 화면에 뿌려져야하는 지만 알고 있으면 된다.

View

View는 수동적이여야 한다. Presenter가 보여줄 컨텐츠를 줄 때까지 기다려야 한다. 절대 Presenter에게 데이터를 달라고 요구해서는 안된다.  InterfaceProtocol 같은 것을 사용하여 Presenter가 View에 컨텐츠를 넣어 줄수 있게 해야한다. 비록 컨텐츠는 Presenter가 가지고 있지만 화면에 어떤 모습으로 그려져야하는지는 View가 알고 있다. View와 View Controller가 커지는 이유는 이벤트를 받고 그것을 처리하기 가장 편한 곳이기 때문이다. 하지만 View 레이어를 가볍게 만들기 위해서는 View에 관심을 가지고 있는 객체들에게 사용자가 특정 액션을 했을 때 그것을 알려주면 된다. 예를 ViewController가 이벤트 핸들러라는 이름으로 Presenter 객체를 프로퍼티로 가지고 있다고 가정하면, ‘취소’ 버튼 같은 특정 UI의 이벤트가 발생했을 때 Presentor의 메서드를 호출 하면된다. 이때 리액티브 관련 프래임워크의 힘을 빌리면 조금더 깔끔하게 코드를 짤 수 있다.

Routing

VIPER 패턴의 Routing은 Xcode의 스토리보드와 같은 역할을 하는 레이어이다. 구제적으로는 Wireframe 클래스와 Presenter 클래스가 그 역할을 한다. 먼저 Wireframe 클래스는 UIWindow, UINavigationController, UIViewController 같은 것을 소유하고 있으며, 담당 역할은 View나 ViewController를 생성하고 window에 설치하는 것이다. Presenter는 View와 인터랙션을 하고 있기 때문에 언제 새로운 창을 만들어야 하고 언제 화면전환이 이루어지는 지를 알고 있다. 반면 Wireframe은 어디로 가야하는 지를 알고 있다. 이 두 클래스를 통해서 Routing의 레이어를 완성 할 수 있다.

etc.

Data store

Data store는 Entity를 Interator에게 주는 역할을 한다. Entity를 받은 Interactor는 모델 데이터를 조작하여 알맞은 모양으로 Presenter에게 던진다.

Data Manager

Data Manager는 Data store와 비슷한 역할도 하지만, 추가로 fetch Request를 만든다든가, Query를 만들거나 하는 일을 한다. 이렇게 하므로써 Interactor는 Application 레벨의 비즈니스 로직에 더 집중 할 수 있게 해준다.

 

추가 2016.11.04

Module 간의 통신

위에서 간단히 설명한 VIPER의 구조는 iOS 프로젝트로 따지자면, 앱 기능의 의미를 갖는 ViewController를 중심으로 모듈화하여서 사용할 수 있을 것이다. 하지만 중요한 것은 모듈과 모듈 사이에 연결되는 고리를 고민해야 할 때가 온다. 예를 들어 Setting 모듈에서 적용된 작업을 List 모듈 같은 것에 적용하고 싶다면 Notification과 같은 옵져버 패턴을 사용하여 데이터 전달 혹은 메서드 호출을 할 수 있을 것이다. 하지만 더 VIPER 패턴스러운 방법은 Presenter에다가 delegate을 두고 사용하는 것이 깔끔하다. 예를 들어 Setting 모듈의 Presenter에 delegate을 선언하고, List 모듈의 presenter가 이 것을 구연하게 되면 Notification과 같은 API에 의존하지 않고 데이터 전달 및 메서드 호출이 가능해 질 것이다.