본문 바로가기

iOS (스파르타)/앱 개발 숙련

MVC 패턴 (Model, View, Controller)

MVC 패턴은 Model-View-Controller 패턴의 줄임말으로, 이름 그대로 세 가지 계층으로 각 코드의 책임과 역할을 나눈다. 계층은 각각 Model, View, Controller로 나뉜다.

 

Model은 앱의 데이터와 비즈니스 로직을 갖고 있다.

View는 사용자에게 데이터를 보여주거나 UI를 담당한다.

Controller는 Model과 View의 중간다리 역할로 View로부터 사용자의 action을 받아 Model에게 어떤 작업을 해야 하는지 알려주거나, Model의 데이터 변화를 View에게 전달하여 View를 어떻게 업데이트할지 알려준다.

 

Model

Model은 데이터와 관련된 내용을 담고 있다. 그리고 데이터를 관리하는 로직도 포함하고 있다. 네트워크를 통해 받아온 DTO 구조체와 네트워크에 접근하는 로직, 파일로 따로 저장해야하는 Persistance한 데이터를 로드한다든가, 아니면 필요한 구조체를 만드는 경우 해당 내용들은 모두 Model에 포함된다.

 

Model은 UI와 직접적으로 연결되지 않는다. 당연히 불가능한 것은 아니지만 MVC 패턴을 제대로 활용하기 위해서는 Model은 받아온 데이터를 그에 맞춰 저장할 형태를 만드는 것이 중요하지 UI에서 어떻게 보일지는 신경쓰지 않는 것이 좋다. 예를 들면, Person이라는 구조체에 생일을 저장하는 것은 괜찮지만, 여기서 이 생일 날짜 문자열을 어떤 식으로 파싱해서 어떤 식으로 화면에 띄워줄지는 고민하지 않아도 된다.

 

Model에는 대부분 이런 코드들이 포함된다.

  • 데이터로 사용하는 구조체: 위에서 예시로 든 Person 구조체 같은거라고 생각할 수 있을 것 같다. struct Person {let name, birthDate} 이런 느낌.
  • 네트워크 로직: 네트워크 요청을 하고, 그 결과를 받아오는 기본적인 기능을 담은 네트워크 로직이 포함된다.
  • Persistance 로직: 메모리에 저장되는 데이터를 로드 및 세이브하는 로직이 포함된다.
  • 데이터 파싱 로직: 네트워크로 받아오든 내부 파일에서 받아오든, JSON 같은 데이터가 왔을 때 이를 파싱하는 로직도 포함한다.
  • Manager 객체(shared 객체): 구조체를 만들어두고 필요한 경우에는 어디서든 접근해 사용할 수 있도록 따로 Manager를 만드는 경우도 Model에 포함된다.
  • Util, Extension, Constant: 앱으로 따지면 우리가 정의하는 Color나, String의 추가적인 기능, 혹은 특정 사이즈에 대한 정보를 util, extension, constant로 많이 만들게 되는데, 이런 경우도 Model에 포함된다.

 

View

View는 흔히 말하는 UI다. 유저들에게 데이터를 보여주고, 어떻게 보여줄지 화면을 구성하는 코드들이 포함되어있다. 또한 View 계층은 직접 유저와 상호작용을 해야 하는 곳이다보니, 상호작용을 Controller 계층으로 전달하는 역할도 한다.

 

기본적으로 View는 Controller로부터 정리된 데이터를 받아서 화면에 보여주는 역할을 하나, 무조건 데이터를 저장해서 안되는 것은 아니다. 간단한 정보들은 View단에서도 정의해서 사용할 수 있다. 상태와 관련된 변수를 View에서 정의해 사용하는 경우도 많다.

 

View를 작성할 때는 재사용성이 강조된다. 화면에 들어가는 여러 요소들을 어떻게 잘 나누어서 앱 전반에서 재활용할 수 있도록 할지가 중요하다. 예를 들면, 앱 전반에서 일관적으로 사용되는 디자인의 버튼이 있다고 치자. 이때 필요할 때마다 해당 디자인을 다시 코드로 작성하는 것이 아니라, 재사용할 수 있도록 MyButton 이런 덩어리를 따로 만들어서 관리하면 보다 코드가 깔끔해질 것이다.

 

View에는 주로 아래의 코드들이 포함된다.

  • 주로 UIView를 상속해 만들어진 subclass
  • Core Animation
  • Core Graphics

 

Controller

Controller는 앱의 핵심 로직을 담고 있는 계층이다. Controller는 View, Model에 연결되어 그 중간의 역할을 하고 있다. MVVM 패턴에서 ViewModel이 하고 있는 역할과 비슷하다고 볼 수 있다.

 

View에서 보여주기 위한 데이터를 이 Controller가 보내주면서 View를 refresh하고, 그 데이터를 Model으로부터 가져오는 기능을 한다. 물론 View로부터 유저와의 상호작용에 대한 정보를 받고, 그 정보를 바탕으로 해당된 로직을 실행하고 Model의 정보를 업데이트하는 기능도 있다.

 

Controller는 해당 View마다 하나씩 붙어서 그 View에 맞는 로직을 포함하고 있기 때문에, 재사용성은 View보다 훨씬 떨어진다. 재사용성이 적다보니 코드가 길어지는 경우가 많다.

 

 


 

 

 

MVC 패턴에서 각 영역끼리 소통?

Controller는 Model과 View에 직접 지시를 할 수 있지만 Model과 View는 Controller에 직접적으로 알릴 수 없다.
그렇다면 만약 Model의 데이터가 변경된 것을 알리거나, View에서 사용자의 action이 발생하였을 때 Controller에 어떻게 알릴까?

 

Vew to Controller

먼저 View의 경우

Controller는 View에서 발생할 수 있는 action에 대한 target을 만들어둔다.
그래서 View에서 유저의 action이 발생할 경우 Controller에 있는 target이 이를 받아들이고 작업을 수행한다.
또한 View는 delegate 패턴의 delegate datasource를 이용하여 Controller에게 어떤 작업을 수행해야하는지 알리기도 한다.
대표적인 예로 UITableView의 UITableViewDelegate와 UITableViewDatasource를 들 수 있다.

Model은 Observer 패턴( Observer 패턴에 관한 내용은 다른 글에서 따로 정리하도록 하겠다. )의 Notification KVO(Key Value Observation)를 통해 Controller에게 알린다.
Notification과 KVO는 일을 수행하는 객체(publisher)가 진행하던 작업이 끝나면 자신들을 구독 중인 객체들(subscribers)에게 신호를 보내는 방식이다.
간단하게 설명하자면, 작업이 완료됐을 때 라디오 센터에서 전파를 보내는 것처럼 Controller에게 신호를 보낸다.

 

 


 

MVC 패턴의 장점 및 단점

장점

다른 패턴에 비해 코드량이 적다.
또한 애플에서 기본적으로 지원하고 있는 패턴이기 때문에 쉽게 접근할 수 있다.
많은 개발자들에게 친숙한 패턴이기 때문에 개발자들이 쉽게 유지보수 할 수 있다.
개발 속도가 빠르기 때문에 아키텍처가 중요하지 않을 때 사용하거나 규모가 작은 프로젝트에서 사용하기 좋다.

 

단점

위의 사진과 같이 View와 Controller가 너무 밀접하게 연결되어 있다.
ViewController에서 볼 수 있듯이, View와 Controller가 붙어 있으며, Controller가 View의 Life Cycle까지 관리하기 때문에 View와 Controller를 분리하기 어렵다.
이렇게 되면 재사용성이 떨어지고, 유닛 테스트를 진행하기 힘들어진다.
또한 대부분의 코드가 Controller에 밀집될 수 있다.
Life Cycle 관리 뿐만아니라, delegate나 datasource 관리, 네트워크 요청, DB에 데이터 요청 등 많은 코드가 Controller에 작성되면 Controller의 크기는 비대해지고 내부 구조는 복잡해지게 된다.
이런 상황을 비유해 많은 사람들이 Massive View Controller라고 부르기도 한다.
이렇게 복잡해진 코드는 프로젝트 규모가 커질수록 유지보수하기 힘들게 만든다.