이 노트의 목적은 "목적(thing)-모델-뷰-편집기"라는 메타포를 일관된 예제들로 탐구하는 것이다.

이 예제들은 모두 내 계획 시스템(planning system)에서 가져온 것으로, 위의 네 가지 개념을 설명한다. 모든 예제는 구현되었지만, 여기에 설명된 깔끔한 클래스 구조 내에서는 구현되지 않았다. 

이러한 메타포는 A note on DynaBook requirements에서 제안된 현실 세계-모델-뷰-도구(Thing-Model-View-Tool)와 대응된다.


-1979년 노르웨이 수학자 Trygve Reenskaug의 노트에서


아키텍쳐란 무엇인가?


사실 한국 중국, 미국 서양 사례를 보면 대부분의 코드 구조를 아키텍쳐로 혼용하면서 용어를 사용한다. 하지만 그렇다보니 초보자는 이 프로그래밍 아키텍쳐에 혼란을 느끼기 쉽다.


관심사의 범위와 초점이 다름에도 불구하고, 모두 같은 레벨에서 혼용되기때문에 혼동이 발생한다.

즉 구조 아키텍쳐, UI 아키텍쳐, 흐름 제어 등 다른 관심사와 범위를 가진 개념들이 동등한 것처럼 이야기되기때문이다.


이건 한국,중국,서양, 일본 할 것도 없이 경력자라고 하는 사람들에게서 자주 나타나는 현상이다. 

그렇다면 이해하기 쉽게 간단하게

여기서 아키텍쳐란 무엇이고 간단하게 정리하겠다




범주설명대표 아키텍처
전체 시스템 구조계층 간 의존성 방향, 도메인 격리, 비즈니스 중심 구조Clean Architecture, Hexagonal (Ports & Adapters), Onion, Layered (3-tier)
UI 구성 및 View 흐름 아키텍처 (View Architecture)View-Logic-Model 간 데이터 흐름, 사용자 이벤트 처리MVC, MVP,MVVM, MVI, VIPER, Redux
내부 로직 제어 구조 (Behavior/Control Architecture[Model])FSM, Event Sourcing, Actor Model 등 상태 전이/메시지 기반 제어FSM,Saga,Workflow,RuleEngine
배포 및 인프라 중심 아키텍처 (Deployment Architecture)물리적 배포/통신 구조Microservices,Monolith,Serverless,Service Mesh


각 아키텍쳐의 목적은 일반적으로 다음과 같다


시스템 구조 아키텍쳐 -> 의존성 방향 통제, 테스트 용이성, 비즈니스 핵심 보호


UI 구조 아키텍쳐 -> 사용자 입력->상태 변경->렌더링 흐름 정리


행위 제어 아키텍쳐-> 내부 상태 프로세스 흐름 제어


배포 아키텍쳐 -> 네트워크/서비스 분산에 따른 물리 구조 설계


이 글은 이중에서 UI 구조, 즉 View Architecture에 관한 글이다.


View Architecture란 무엇인가?


주로 어떻게 View와 로직을 연결할 것인가에 초점을 둔 로컬한 아키텍쳐 패턴이다.


실제 도메인, 인프라, 배포와는 무관하며, 전체 구조 시스템 구조의 일부분이다.


이 계열은 UI를 기계처럼 다루는 시도에서 시작했으며, 상태의 단 방향 흐름을 어떻게 이해하느냐가 이해의 핵심이다.


(Trygve Reenskaug)


MVC(Model - View - Controller)


뷰 아키텍쳐(이하 아키텍쳐라고하겠다)의 시작은 노르웨이 수학자 Trygve Reenskaug가 제록스 PARC에서 스몰토크의 GUI 시스템을 만들며 처음 제안한다.

목적은 사용자 인터페이스 UI 로직과 도메인 로직을 분리였다.

시작은 TMVE라고 표기됐으나


( Patterns of Enterprise Application Arhitecture)


이후 많은 변형이 이루어지는데 MVC가 된다. 그리고 이 MVC는

마틴 파울러의 기라성 같은 서적 Patterns of Enterprise Application Arhitecture등으로 퍼진다.(최초 창안자라기보다는 많이 퍼뜨리는데 큰 공로가 있다)



MVC의 구성요소는 다음과 같다.


구성 요소역할설명
Model데이터 상태 관리애플리케이션의 핵심 로직과 상태를 담고 있음. DB, 메모리 상태, 비즈니스 규칙 등을 포함. View와 Controller는 이 Model을 참조하거나 조작함.
View시각적 표시Model의 상태를 사용자에게 보여주는 UI 요소. 화면에 보여지는 정보, 레이아웃, 스타일 등을 담당하며, 자체적인 로직은 최소화됨.
Controller사용자 입력 처리사용자 입력(버튼 클릭, 키보드 입력 등)을 받아 적절한 Model을 조작하고 View에 업데이트를 요청. View와 Model 사이의 중재자 역할을 수행.


MVC가 가장 크게 영향력을 가진 것은


자바 기반의 Spring(2002~)일 것이다.


여전히 많이 쓰이지만, 사실 꽤 많은 MVC로 시작한 프레임워크들은 과거의 형태와는 다른 형태로 변했다.


당장 Spring을 MVC라고 하지만, 현재 Spring은 많은게 변했고, View를 자바로 보지 않으며, View 없이 컨트롤러만 남았는데


컨트롤러도 사실상 고전적인 의미에서 컨트롤러가 아닌 HTTP 핸들러처럼 변했기때문에 MVC가 일종의 브랜드화 됐다.


이외에도 AngularJS, React등이 MVC로 분류되긴했으나, 전통적인 MVC가 아니고 변형판에 가깝다.


AngulerJS는 전반적으로 MVVM에 가깝고 컨트롤러가 뷰모델 역할을 하고, 양방향 바인딩을 지원해서 View와 모델간의 경계가 흐릿하고


React는 MVC가 아닌 단일 View를 기반으로 전통적인 MVC와는 거리가 멀다라고 할 수 있다.


그렇다면 MVC는 언제 쓸 것인가?

MVC는 구조가 단순하고, 뷰와 로직이 비교적 명확하게 분리 된 경우 웹 서버 렌더링 중심의 구조에 적합하다.

현대에는 아키텍쳐 설계가 많은 방향에 발전이 이루어짐에 따라서, 사실 대규모 구조보다는 소규모 개인용에 적합하다.




전통적인 스몰토크(Smalltalk)의 컨트롤러 기능을 나타내지만, 애플리케이션 수준으로 격상된 형태로, 

중간 과정의 선택(selection), 명령(command), 상호작용(interactor) 개념들을 고려한 것이다. 이러한 차이를 포착하기 위해 우리는 이러한 종류의 컨트롤러를 

"프레젠터(presenter)"라고 부른다. 그 결과, 우리는 이 프로그래밍 모델을 전체적으로 모델-뷰-프레젠터(Model-View-Presenter, MVP)라고 부르며, 

이는 MVC의 일반화된 형태로 간주됨을 인정하는 것이다.

-1996 Taligent 문서에서-


MVP (Model - View - Presenter)


MVC는 하나의 문제가 있었다. Controller가 View 이벤트와 모델을 양쪽으로 과도하게 처리가 되는 일명 Fat Cotroller라는 문제가 있었다.


그리고 View와 컨트롤러는 결합되어, 이벤트 루프 내에서 UI 프레임워크가 결합되 테스트가 어렵기도 했다.


결국 여기서 


Presenter라는 중재자 역할을 분리하고, View를 인터페이스로 추상화 한 후에 Presenter에서 테스트 하게 만든다.




MVP의 구성요소는 다음과 같다.



구성요소역할설명
Model상태 및 데이터 처리

애플리케이션의 상태와 데이터를 처리하는 역할을 한다. 이는 데이터 저장, 비즈니스 규칙 적용, 외부 API 호출 등 순수한 비즈니스 로직을 담당하는 계층으로, UI와는 분리되어 있다

ViewUI 표현 및 사용자 입력 전달사용자에게 UI를 표시하고 입력을 전달하는 역할을 한다. 이때 View는 가능한 한 Dumb View로 설계되어야 하며, 복잡한 로직이나 상태 처리를 포함하지 않는다. 일반적으로 인터페이스를 통해 Presenter와 연결된다.
PresenterView 이벤트 수신 → Model 조작 → 결과 다시 View에 반영View로부터 발생한 사용자 이벤트를 수신하여 Model을 조작하고, 그 결과를 다시 View에 전달하는 중재자 역할을 수행한다. View와 Model 사이에서 데이터를 조정하고 흐름을 관리하며, 단위 테스트가 용이하도록 View에 대한 의존은 인터페이스를 통해 느슨하게 유지한다.


MVP는 2010년대 초반 안드로이드에서 MVP를 요구했다.


이유는 Activity/Fragment가 너무 많은 책임을 가지던 구조였기때문에, 베스트 프랙티스로 Presenter로 분리한 것이다.

View: Activity/Fragment (View Interface 구현)

Presenter: View에 결과 전달

Model: Repository, UseCase 등

이런식으로

MVP를 채택한 구조가 많았다.

다만 현재 .NET 의 WinForm을 쓸때 정도나 MVP를 채택하는 경향이 크다.

왜냐하면 UI 이벤트 루프가 강하게 UI 오브젝트에 묶여있다보니 추상화로 유닛 테스트 하기 편한 선택지로 자주 선택된다.


주로 클라이언트 앱에서 사용되지만, 이후 MVVM으로 많은 프레임워크가 넘어가게된다.


그렇다면 MVP는 언제 쓸 것인가?


복잡한 UI 이벤트 처리가 중요하고 테스트 용이성이 중요한 모바일 앱에서 많이 썼다.

클라이언트에서 많이 사용되고 있으며, 상태 관리가 간단한 클라이언트 앱의 경우 MVP를 많이 추천한다.




사람들이 소프트웨어 사용자 인터페이스를 만들기 시작한 이후로 이 작업을 수월하게 하기 위한 인기 있는 디자인 패턴이 있었습니다. 

예를 들어 MVP(Model-View-Presenter) 패턴은 다양한 UI 프로그래밍 플랫폼에서 인기를 누렸습니다. 

MVP는 수십 년간 사용된 Model-View-Controller 패턴의 변형판입니다. MVP 패턴을 사용해 보지 않은 독자를 위해 간단히 설명하자면 화면에 보이는 것은 뷰이고 뷰가 표시하는 데이터는 모델이며 이 둘을 서로 연결하는 것이 프레젠터입니다. 뷰는 모델 데이터로 뷰를 채우고, 사용자 입력에 반응하며, 입력 유효성 검사를 제공(예를 들어 모델에 위임하여)하고 이러한 다른 작업을 위해 프레젠터를 사용합니다. 

-MSDN 매거진 이슈 09


(John Grossman)

(필자는 개인적으로 WPF를 위시한 C# 유저이기때문에 개인적으로 프로그래머인데 개인적으로 언젠가 싸인받아보고 싶은 사람 중 한명이 John Grossman이다. 사실 마소키즈의 우상이라고 할 수 있지 않을까.)


MVVM(Model-View-ViewModel)


2005년 John Gossman은 자신의 블로그에서 MVVM에 대한 글을 쓴다.

MVP는 분명 View-로직 분리, 테스트 용이성, 의존성을 제어한다는 면에서 매우 효과적 패턴이었다.

하지만 구조적/생산성/ 바인딩 측면의 한계에서 MVVM으로 자연스럽게 넘어가게된다.


View와 Presenter 간의 수동 연결은 보일러 플레이트를 발생시키고, View와 프레젠터 간의 전달과정에서 많은 반복과정을 작성하게 된다.


또한 MVP는 데이터 바인딩이 부재하기때문에 프레젠터 호출이 잦아 오버헤드가 컸다. 하지만 MVVM에서는 ViewModel이 상태를 바인딩하고, 자동갱신, 즉 Reactive하게된다.


결국 현대적 UI 프레임워크는 MVVM으로 많이 넘어갔고, 마우이, WPF 등등 데이터 바인딩 기반 UI에 가장 적합한 선택일 것이다.




MVVM의 구성요소는 다음과 같다.



구성 요소역할설명
Model데이터 상태 관리애플리케이션의 핵심 데이터와 비즈니스 로직을 담당한다. 서버 API, 데이터베이스, 비즈니스 규칙 등이 포함되며, ViewModel과 직접 데이터를 주고받는다.
ViewUI 표시 및 바인딩사용자 인터페이스(UI)를 시각적으로 표현한다. View는 ViewModel에 바인딩되어 상태가 변경되면 자동으로 갱신된다. 자체 로직은 없으며, 선언적으로 구성된다.
ViewModel상태 보유 및 중재자View에서 발생한 사용자 입력을 수신하고, Model과 상호작용하여 상태를 갱신하며, 그 상태를 View에 바인딩 가능한 형태로 제공한다. ViewModel은 View와는 분리되어 테스트가 용이하다.


실제로 C#을 사용하는 대부분의 개발 환경에서는 WPF, Xamarin, MAUI 등에서 MVVM이 기본 아키텍처로 채택된다.

이는 단지 컨벤션의 문제를 넘어서, 프레임워크 자체가 XAML과 데이터 바인딩 기반의 UI 선언 방식을 전제하고 있기 때문이다.


따라서 C# 생태계에서 UI와 로직의 분리, 상태 동기화 자동화, 테스트 용이성 확보를 고려하면 MVVM은 거의 "기본값"에 가깝다.

개발자들도 자연스럽게 ViewModel이라는 중간 계층에 익숙해지게 되며, MVVM은 C# 개발자에게 가장 널리 알려진 UI 패턴 중 하나가 되었다.


그렇다면 MVVM는 언제 쓸 것인가?


XAML 기반 UI를 사용하는 경우에 특히 강하다.

WPF, MAUI, Xamarin.Forms 등에서는 데이터 바인딩을 전제로 한 UI 구조가 자연스럽게 MVVM으로 귀결된다.


상태가 자주 변경되고, UI에 그 상태를 실시간 반영해야 할 때

예: 폼 상태, 필터 조건, 네트워크 응답 결과, 유효성 검사 등일때도 추천된다.


테스트 가능한 ViewModel 계층을 통해 비즈니스 로직을 검증하고 싶을 때


디자이너와 개발자의 작업이 분리되어야 할 때

View(XAML)와 ViewModel(C#)이 명확히 분리되기 때문에, 협업 효율이 높다.


물론 항상 MVVM이 항상 최적은 아니다. 화면이 매우 단순하고 상태가 1~2가지로 단순한 경우 오히려, 코드 비하인드나 MVP 구조가 훨씬 단순 할 수 있다.


MVVM은 매우 좋은 패턴임이 분명하나, 간단한 유틸 앱에서는 과한 복잡성을 가질 수도 있는 양날의 칼이다.





MVI(Model – View – Intent) 

(André Staltz)

André Staltz가 JSConf Budapest에서 Cycle.js를 발표한다. 


2015년 Andre Staltz는 2015년에 Cycle.js를 발표한다.


MVI는 단방향 상태 흐름을 중심으로 구성된 현대적인 UI 아키텍처이며, 특히 Cycle.js는 MVI 개념을 구조적 수준으로 끌어올린 대표적인 프레임워크다.

Cycle.js는 Intent → Model → View → Intent 순환 구조를 스트림 기반으로 완전 추상화하고, Side Effect를 Driver로 분리하여 함수형 UI의 실질적 구현을 시도했다.

MVI는 단순한 패턴이 아니라, 프로그래밍 모델 자체를 변화시킨 역사적 진화 경로의 일부라고 할 수 있다.



MVI의 구성요소는 다음과 같다.



구성 요소역할설명
Model (State)UI의 단일 상태(Single Source of Truth)View의 현재 상태를 나타내는 단일 불변 객체. 모든 UI 상태는 이 State에 포함되며, 상태는 항상 완전한 형태로 유지된다.
View상태를 기반으로 렌더링상태(State)를 받아 UI를 그리며, 사용자의 인터랙션을 Intent로 변환하여 전달한다. View는 stateless하고 선언적으로 동작한다.
Intent사용자 이벤트 정의사용자의 입력(클릭, 입력 등)을 의미론적으로 표현한 객체. 예: AddTodoClicked, SearchTextChanged, RetryButtonPressed
Reducer상태 전이 함수이전 상태 + Intent를 받아 새로운 상태를 반환하는 순수 함수. side effect 없이 상태만 계산한다.
Effect / Side Effect (선택적)외부 시스템 호출 등 비결정적 작업API 호출, 파일 I/O, DB 접근 등 순수하지 않은 작업은 별도로 분리하여 처리한다. Redux나 Elm에서는 Effect라는 구조로 명시됨.



이렇게 보면 복잡하지만


사실 오히려 더 단순하게 생각할 수 있다.


Intent → Reducer → State → View → Intent 구조


즉 단방향으로 상태를 전이함으로써 경로를 예측하기 쉬워 디버깅이 편하다는 것이다.


Swift, 코틀린 스테이트 플로우, JetPack Compose도 유명하지만, 역시 가장 중요한 것은 Flutter에서 자주 사용되는 기술일 것이다.


MVI는 MVVM 이후 등장한 단방향 상태 흐름 기반의 선언형 UI 아키텍처이다.


모든 UI 상태를 단일 불변 객체로 관리하며, 사용자의 의도(Intent)를 기반으로 명확한 상태 전이를 수행한다.


테스트 가능성, 상태 예측성, Side Effect 분리를 강조하는 구조이며, 현대의 선언형 UI 프레임워크에서는 사실상 표준 구조로 채택되고 있다.


그렇다면 MVI는 언제 쓸 것인가?


UI 상태가 복잡하고 상태 추적이 힘들때, 상태 자체를 단일 객체로 정의하고, 상태 전이를 '의도(Intent)'로 통제하기때문에 디버깅이 용이하다.

즉 복잡한 디버깅이 용이하다.


사이드 이펙트를 분리해서 비결정적 작업을 상태 전이와 구분하기 쉬워 디버깅이 쉽다.


이 장점들을 보면 알다시피 UI가 단순하고, 함수 지향 프로그래밍(FP)를 바탕으로 생각하지 않으면 까다로운 경향이 있다.


즉 구조 자체가 일정 복잡도를 가장하고, 러닝커브가 높기때문에 생각보다 함부러 도입하기가 어려운 경향성이 있음이 틀림없다.



단순한 것은 단순해야 하고, 복잡한 것은 가능케 해야 한다 -Alan Kay


글을 끝맺으며


아키텍처는 문법 없는 언어와 같다. 

우리가 그것을 선택한다기보다는, 사용하는 기술, 우리가 해결하려는 문제, 우리가 속한 조직의 철학이 우리에게 구조를 요구한다.

MVC, MVP, MVVM, MVI, 최근에는 VIPER, RIBs 등등 수 많은 아키텍쳐가 있고

이 모든 것은 결국 “어떻게 인간의 의도를 코드에 잘 반영할 수 있을까”라는 오래된 질문에 대한 나름의 답일뿐이다.


이 글은 단순히 아키텍쳐가 단순하게 적용될 것이 아니고, 필요성에 의해서 변형되어왔음을 보여주기 위해 서술되었다.


프로그래밍은 단순히 답을 암기하며 복사 붙여넣기 하는 사람이 아니다.


프로그래밍은 사고방식의 일종이며 언제나 더 나은 대답을 추구하는 사람이여야한다.

아키텍쳐는 이론이 아니라, 단순히 현장의 문제를 해결하는 도구일뿐이며,

오늘 내가 선택한 구조에 대해서 내일 바꾸더라도, 그 판단이 맥락을 고려한 결정이었다면 그건 좋은 선택이다.


이 글이 그 판단의 기준점이 세우는데 조금이라도 도움이 되었기를 빈다.