제목: Looking at Model-View-Controller in Cocoa

This article is copyright 2017 Matt Gallagher, https://cocoawithlove.com. The original English version is available here: Looking at Model-View-Controller in Cocoa. This translation is produced and hosted with permission of the original author.

애플의 문서에의하면, 코코아 어플리케이션에서 표준 패턴을 Model-View-Controller라 부른다. 그 이름에도 불구하고 이 패턴은 기존의 smalltalk-80의 Movel-View-Controller 정의와 완전히 다르다. 코코아의 앱 디자인 패턴은 실제로 원래의 Smalltalk의 용어보다 Taligent(1990년대부터 애플과 공동으로 개발해온 프로젝트)에서 만들어진 방법에 더 일반적으로 겹친다.

이 글에서는 코코아에서 주로 사용하는 앱 디자인 패턴의 뒷 배경과 약간의 이론을 보려한다. 나는 코코아의 Model-View-Controller 접근법의 주요 결점에대해 이야기해볼 것이다. 이런 결점을 해결하는데 실패한 애플의 노력과, 다음 메이저 개션으로부터 생기는 의문을 보게 될 것이다.

컨텐츠
  1. Smalltalk-80
  2. 코코아(AppKit/UIKit)
  3. Taligent
  4. 컨트롤러 문제
  5. 바인딩
  6. 새로운 무언가?
  7. 결론

Smalltalk-80
아마 UI 개발에서 널리 인용된 패턴은 Model-View-Controller(MVC)이다. 또한 많이들 잘못 인용되고 있다. 나는 MVC가 이것처럼 아무것도 아니게 되버렸다고 설명한 것을 보는데 시간가는줄 몰랐다 - Martin Fowler, GUI Architectures

나는 Martin Fowler가 사용한 정의로 위의 인용에서 Fowler가 말하고자하는바를 빠르게 알려주려한다. 코코아 앱 개발에 보통 사용되는 이 접근법은 Model-View-Controller가 아니다.

Smalltalk-80에서는 상호 소통하는 뷰가 완전히 분리된 두 오브젝트로 쪼개진다. 바로 뷰 오브젝트와 컨트롤러 오브젝트이다. 뷰 오브젝트는 화면 출력을 실행하나, 모든 클릭이나 인터렉션은 뷰 오브젝트에서 하지 않고, 대신 그 파트너인 컨트롤러 오브젝트에의해 디스패치된다. 중요하게 이해하고 넘어가야할것은 컨트롤러는 뷰를 불러오거나 셋업을 관리하지 않으며, 한 컨트롤러는 여러 뷰를 위한 동작을 다루지 않는다; 원래 Model-View-Controller 정의에서는 뷰와 컨트롤러는 간단하게 동작하고, 화면상의 하나의 조작면에서만 출력한다.

Smalltalk's version of Model-View-Controller
Smalltalk-80의 Model-View-Controller

Smalltalk-80의 Model-View-Controller의 다이어그램은 오브젝트 그래프의 중앙에 모델이 있고, 모델이 뷰나 컨트롤러와 직접 우선적으로 소통한다는 것을 보여준다.

이 명확한 패턴은 Smalltalk-80이 어떻게 사용자 입력을 처리했는지 반영하며, 현대의 프로그램에는 이 명확한 패턴을 사용하는데 조금만 필요로 한다. 이런 의미에서, 어떠한 현대의 프레임워크도 Model-View-Controller가 아니거나, 혹은 용어의 정의가 다른 의미로 바뀌어가고 있는 것이다.

코코아(AppKit/UIKit)
코코아가 Model-View-Controller를 논할때, 대부분 어플리케이션 설계에서 분리된 표시와 컨텐트의 개념을 일깨우려고 노력하고 있다(이 방법은 모델과 뷰가 분리되게 설계하고, 구성에서 느슨하게 연결되있는 것이다). 사실 코코아만 Model-View-Controller를 이런식으로 사용하는게 아니다. 현대의 많은 이 용어의 사용이 원래의 Smalltalk-80 정의보다는 분리된 표시를 전달하기 위함이다.

코코아가 실제로 쓰고있는 정확한 패턴을 보면서 애플의 코코아 참조 가이드가 사용하는 Model-View-Controller가 어떻게 생겼는지 보자.

Cocoa's version of Model-View-Controller
코코아의 Model-View-Controller

주의해야할 중요한 점은 컨트롤러가 오브젝트 그래프의 중앙에서 대부분 소통을 컨트롤러를통해 한다는 것이다. 모델이 그래프의 중앙에 왓던 Smalltalk-80와는 다르다.

코코아는 앱에서 이 패턴을 강요하진 않지만, 모든 어플리케이션 템플릿으로 강력히 내포하고 있다. NIB 파일로부터 불러오는 것은 NSWindowController/UIViewController 사용을 강력하게 지향한다. NSTableView/UITableView나 그 관련 클래스의 델리게이트 필요 조건은 전체 표시의 책임을 이해하는 조정자 클래스를 강하게 의미한다. UITabBarController와 UINavigationController 같은 클래스들은 뷰를 조정하기위해 명시적으로 UIViewController인스턴스를 필요로 한다.

Taligent
학술적 토론에서, 코코아가 Model-View-Controller로 부르는 그 패턴은 모통 Model-View-Presenter라 불린다. 이 두가지는 코코아가 컨트롤러라 부르는 것을 프레젠터라 부르는 것만 빼면 동일하다. "프레젠터"라는 이름은 화면을 셋업하고 동작을 중재하는 역할을 맡는다. 몇몇 케이스에서는 프레젠터 오브젝트를 "감독 컨트롤러(Supervising Controller)"라 부르기도 한다. (왜 Model-View-Supervising Controller가 Model-View-Controller로 다시 돌아오게 되었는지 이해할 수 있을 것이다)

Model-View-Presenter라는 용어는 Taligent에서 기원된다. 일반적으로 많이 인용된 논문은 1997년에나온 “MVP: Model-View-Presenter, The Taligent Programming Model for C++ and Java”인데, 이 모델을 구현하기위한 Taligent의 클래스들은 적어도 1995년만큼 이르게 문서화 되었다.

Taligent는 원래 코드명 "Pink"(그 방법에 사용된 색깔 색인 카드 이후)라는 프로젝트로 System 7(이것은 "Blue" 색인 카드에 해당함)를 대체하는 OS를 지원하기위해 애플 안에서 시작된 회사였다. 애플이 동등하게 운이다한 Copland 프로젝트에 집중하고 시선을 돌리는 동안, 이 프로젝트는 일련의 인기있는 개발과 관리 문제를 가졌었다. Taligent는 1998년에 막을 내리기 전까지, OS 대신에 일련의 어플리케이션 프레임워크로 CommonPoint라는 이름으로 IBM이 배포해왔다.

This Wired article from 1993 gives an interesting insight into Taligent and the apparent bloat and infighting that doomed it.

NeXTStep이 Taligent보다 앞서 나왔지만, AppKit(지금은 AppKit의 Model-View-Controller 디자인 패턴의 양상을 정의하고 있지만)에는 컨트롤러 클래스가 1996년에 NeXTStep 4 전까지 없었다(NeXTStep의 메이저 재설계와 NS가 접두에 붙는 첫번째 NeXTStep 버전이 오늘날까지도 macOS에 남아있다). NeXTStep이 Taligent의 것을 직접 빌렸는지는 모르겠다(이것이 한 점으로 수렴해버린 진화일 수도 있고, 여러 회사가 같은 인재 풀에서 고용을 했기 때문일 수도 있다).

The Taligent documentation, from 1995, is fascinating to read. The Guide to Designing Programs discusses many ideas relevant to application design, 22 years later. However, the Programming with the Presentation Framework tutorial is horrifically bad: baffling, over-technical and unapproachable.

컨트롤러 문제
코코아의 Mode-View-Controller를 Model-View-Presenter 패턴으로 이해하는것이 매우 중요한데, 이 패턴에서는 "컨트롤러 문제"라는 커다란 문제를 유발한다.

"메시브(Massive)/거대한(Huge)/자이언트(Giant) 컨트롤러"라고도 불리는 컨트롤러 문제는 코코아에 있는 컨트롤러가 여러 분리된 역할(특히 뷰가 들어있어 생기는데 기능적으로나 데이터 의존성에도 이럴 필요없다)을 가지면서 끔찍하게 커져버리는 경향의 문제이다. 많은 보통 프로젝트들은 2000줄 혹은 그 이상의 컨트롤러 하나를 가진다.

명확하게 해달라. 이 문제가 단순히 그 크기에만 있는것이 아니라, 컨트롤러가 다루는것이 커지게 되는 점이다. 코코아에서 컨트롤러는 관련된것 혹은 관련되지 않은 역할들의 한 집합체이다. 하나의 뷰 컨트롤러는 대여섯개 혹은 그 이상의 뷰의 역할이 있을테고, 각각은 구조, 구성, 데이터표시, 데이터갱신, 레이아웃, 애니메이션과 동작들을 가질것이며, 끝내 부모 뷰 컨트롤러가 된 상태 보존 역할이 있을 것이다.

상당한 규모로 이 독립적인 역할들과 상호으존적인 역할들의 집합체는 악몽의 유지보수이다. 많은 양의 코드 덩어리들은 실제 의존성을 만들고, 찾기 힘든 상호 의존적인 기능을 만든다. 컨트롤러는 항상 테스트하기 힘들지만 (커다란 앱과 고립시키기는 어렵게 만드는 묶인(bundle) 상태의 의존성 때문에) 규모와 반의존성(semi-depenency)의 문제는 모든것을 더 나쁘게 만든다.

컨트롤러 문제를 해결하는 유일한 방법은 커다란 뷰컨트롤러를 계속적으로 더 작고 간편한 컨트롤러로 리팩토링하는 것이다. 이것에는 뷰컨트롤러에서 빼내와, 여러 뷰컨트롤러를 가지도록하는 분별력있는 접근법을 설계하여 의존성을 빼내고 제거하기위해 데이터 구조를 다시 설계하고 다시 생각해야한다. 완료할 수 있을지라도 수많은 일이 있을 것이고, 각 변경마다 버그가 나오는 일반적인 위험을 가지며, 여전히 테스트하기 힘들고(모든 뷰컨트롤러가 연관되있기 때문에), 이 모든 것에도 불구하고 끝단 사용자에게는 기능을 추가해 주지도 못한다.

바인딩
애플은 그들의 Mac OS X 10.3의 코코아 바인딩을 소개함으로서 얼마동안은 컨트롤러 문제에대해 알고 있었다.

바인딩은 두 컴포넌트 사이에 경로가 만들어진 런타임이다. 이 컴포넌트들은 보통 데이터의 소스이거나 데이터의 옵저버이다. 바인딩은 명시적인 코드 경로가 필요없이 이 컴포넌트들이 변경사항을 소통할 수 있게 해준다. 대신에, 두 컴포넌트 사이에 경로는 데이터안에 정의되있다(코코아 바인딩의경우 "key-path"라 부른다). 컨트롤러를통해 뷰 상태를 조절하는 모델 프로퍼티까지 key-path를 명시함으로서, 바인딩은 컨트롤러를 통하는 코드 경로를 과감히 줄여주어서(제거하기도한다) 컨트롤러 문제를 개선시킬 수 있다.

Cocoa's Model-View-Controller with Bindings
컨트롤러를 통한 코드경로는 바인딩에의해 대체된다

그 소개 이후, 수십가지의 코코아 바인딩이 모두 잊혀진채 남아있다. AppKit에서 아직 바인딩을 사용할 수는 있지만(절때 디프리케이트 시키진 않는다) 절때 UIKit에 넣진 않았고, 뷰를 더 쉽게 프로그래밍하고자하는 그 목적은 완전히 이룰 수 없는 결과를 초래한다. 내 생각에는 직면해야할 문제를 마주하고 몇몇의 경우는 매우 잘 동작할 수 있지만(특히 NSTableView에 있는 NSArrayController로 결합되어있을때) 왜 Mac 프로그래밍을 대체하지 않았는지 이해할 수 있다.

(여러분의 뷰컨트롤러에서 더 적은 코드의) 코코아 바인딩의 핵심 이점은 인터페이스 빌더 인스팩터 패널에서 수많은 설정(configuration)을 할 수 있다는 것이다. 이것은 여러분 코드에서 기능을 찾아볼 때 혼란스러울 수 있고, (Xcode의 프로젝트 범위 검색이 XIB 파일을 검색할 수 있게 되었지만) 그래도 검색하기 어렵고, 디버깅에 힘들며(모델 데이터가 여러분의 코드를 따라가는 스택 추적 없이 변한다), 인스팩터 패널로 검색하고 싶지 않은 새로운 누군가에게 가르치는 것은 매우 힘들며, 시작할때 코드보다 더 보기 힘들어지고(XIB 파일에 주석을 달거나 재구성할 수 없다), 로컬라이제이션이 섞인(localization maxup)것과같은 인터페이스 빌더 이슈나 버전 컨트롤 머지 이슈의 늪에 빠져버릴 수 있다.

내 개인적인 견해로는 코코아 바인딩의 중대한 실패는 커스텀 변형과 커스텀 프로퍼티를 추가하는데 있어 어려움으로 남아있다. 이것들은 모두 완료될 수 있지만 변형자를 등록하고 바인딩 딕셔너리를 노출시키는 일은 지루한 일이다. 항상 바인딩 없이 뷰컨트롤러를 통해 데이터를 보내는것은 쉬워보인다. 이 의미는 바인딩이 가장 간단한 문제를 도울 수 있는 경향은 있지만 (크게 도움이 되진 않는다) 힘든 문제에는 큰 효과를 얻지 못한다.

새로운 무언가?
Mac OS X 10.3의 코코아 바인딩 이래로, 애플은 코코아 앱에 사용될 대안의 디자인 패턴을 찾는데 어떠한 명확한 시도도 해보지 않고 있다.

iOS5와 Mac OS X 10.10에서 스토리보드를 내놓았지만, 스토리보드는 현재 디자인 패턴을 용이하게 만드는 시도만큼 디자인 패턴을 바꾸기위한 시도가 아니다. 스토리보드는 NS/UIViewController 사용을 지향하면서 Model-View-Presenter 디자인 패턴을 보강한다. 스토리보드는 더 작고 더 초점이 가는 뷰 컨트롤러를 지향하고, 많은 셋업과 트랜지션의 "표현(Presentation)"을 아주 약간 줄여주는 역할이다. 그러나 인터페이스 빌더에서 구성될 수 있으므로 코코아 바인딩에 영향을 준 여러 이슈들을 보여준다.

어플리케이션 디자인 패턴에서 더 야심찬 무언가를 기대한 우리로써는, 스토리보드가 그 새로운것을 많이 제공하진 않았다.

어플리케이션 디자인에서 새로운 방법이 존재한다. 애플 바깥에는, 리엑티 프로그래밍(이것을 선택하면 많은 바인딩의 역할로 채울 수 있다), Model-View-ViewModel(변형된 섹션의 모델을 뷰에 가깝게하여 연결함으로서 컨트롤러의 작업을 줄인다), 상호 방향적 데이터흐름(unidirectional dataflow)(이것은 모든 데이터 변경을 앱 전체에 방송하고 리듀서(reducer)를 통해 데이터 변경을 함으로서 바인딩의 필요를 줄인다); 이 모든것들을 다른 사이클로 인기가 있다(원문: all of which are popular within different circles).

리엑트 네이티브(React Native)나 Swift-Elm같이 근본적으로 다른 프레임워크도 있다. 비록 스위프트나 코코아가 희생되는것이 전적으로 중요한 결점을 동반할지라도 말이다.

이런 어떤 것들이 공식적인 코코아 앱 개발에 어떤 영향을 줄지는 불명확하다. 애플은 이따금 과감하게 바꾸고 싶어 한다는 것을 스위프트가 증명했고, 스위프트는 언어면의 이점을 수용한 디자인 패턴이나 뷰 프레임워크 갈망이 점점 더 커진다는 견해가 있다. 그러나 애플은 스위프트만으로된 메이저 프레임워크를 소개하려하기 전까지는 시간이 좀 걸릴것 같다.

결론
코코아의 현재 Model-View-Controller 패턴의 원래 데이터로서 NeXTStep 4를 받아드린다면 올해(2017년)로 20년째이다. 망가지진 않았지만 결점을 가지고 있고, 한번 그렇게 했었던만큼 흥분되거나 능률적인것으로 보이진 않는다.

애플은 디자인패턴 개선을 위해 비교적 이르게 코코아 바인딩을 내놓았었다. 이런 수용은 섞였고 애플의 새 플랫폼에까지 도달하지 않아왔다(원문: Its reception was mixed and it has not been carried forward onto Apple’s newer platforms.).

AppKit 혹은 UIKit 팀에게서 내부적인 노력에대한 다른 정보는 없지만, 애플이 가까운 미래에 과감한 변화를 할 것 같지는 않아보인다. 코코아에서 어플리케이션 디자인 패턴 전반을 개선하는 목적의 써드파티 프레임워크를 쓸 수 있는 여러 디자인 패턴들이 있지만, 반드시 이중 하나가 앞으로의 방향이라는 합의는 없어보인다. 나는 이런 노력들이 어떤 종류의 개선에 관심을 반영한다고 생각한다.



이 블로그는 공부하고 공유하는 목적으로 운영되고 있습니다. 번역글에대한 피드백은 언제나 환영이며, 좋은글 추천도 함께 받고 있습니다. 피드백은 

으로 보내주시면 됩니다.



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,