Computer Science/디자인 패턴

MVC 패턴을 다시 공부해보자

sh1mj1 2023. 10. 22. 22:39

우아한 테크 코스의 프리코스에서 숫자 야구 게임 구현 단계를 진행하고 있었습니다. 이 때 따로 GUI 는 없고 콘솔을 통해서 입력과 출력을 받고 있었습니다. 저는 먼저 한 클래스에 모든 구현을 때려 넣은 후에 리팩토링하면서 각 객체에게 역할과 책임을 분리했습니다. 그런데 프리코스 커뮤니티에서 MVC 패턴에 대한 이야기가 나오더라구요..?

 

저는 안드로이드 프로젝트만 해왔기 때문에 당연히 따로 View 가 있는 패턴(MVC 나 MVVM 과 같이)을 사용할 생각을 못했었는데 말이죠. 

 

그래서 MVC 패턴을 적용해서 구현한 코드를 리팩토링하려고 했습니다. 그런데 갑자기 조금씩 헷갈리더라군요..

그래서 MVC 패턴을 어떤 프레임워크에 국한되지 않도록, 조금만 더 자세히 알아보려고 합니다.

(사실 모름)

참고로 이전에 MVC, MVP, MVVM 패턴에 대해 글을 쓴 적이 있습니다.

https://sh1mj1-log.tistory.com/172

 

MVC, MVP, MVVM 봐도 봐도 조금씩 헷갈리면 모르는 거임

안드로이드에서 유명한 패턴인데 봐도 봐도 조금씩 헷갈리는 것 같아서 제대로 정리를 할 필요성을 느껴 정리합니다. MVC(Model - View - Controller) 패턴 MVC 패턴은 다른 소프트웨어 개발에서 많이 사

sh1mj1-log.tistory.com

 

MVC(Model-View-Controller)

MVC 는 사용자 인터페이스, 데이터 및 논리 제어를 구현하는데 널리 사용되는 소프트웨어 디자인 패턴입니다. 모바일 앱을 개발할 때 뿐만 아니라, 백엔드 등 여러 분야에서도 사용되는 패턴입니다.

당연히 MVC 패턴을 구성하는 주요 요소는 `Model`, `View`, `Controller` 입니다.

 

MVC 패턴의 등장

굉장히 옛날에는 소스 코드가 하나의 파일에 뭉쳐있었다고 합니다. 그리고 객체지향 패러다임이 시장에 널리 퍼지게 되면서 개발자들은 객체의 책임과 역할을 분리하려고 했었죠. 

 

대부분의 소프트웨어 시스템은 비즈니스 로직이 돌아가는 것과, 데이터를 UI 로 표시하는 부분(그게 GUI 든, CLI든)으로 나뉘어 있고 그러한 소프트웨어를 개발하다보니, '어떻게 하면 코드 구성이 편한지' 에 대한 규칙성, 즉, 패턴이 보이기 시작한 것입니다. 

비즈니스 로직(Business logic): 컴퓨터 프로그램에서의 실세계의 규칙에 따라 데이터를 생성, 표시, 저장, 변경하는 부분.
데이터베이스, 출력 장치 등 프로그램의 다른 부분과 대조되는 개념.

 

그러한 패턴을 한 대학원생이 1979 년에 논문을 작성해서  발표하게 됩니다.  이 때 당시에는 지금의 Model, View, Controller 가 아니라 `Thing-Model-View-Editor` 의 요소로 이루어져 있었습니다. 

 

그리고 1988년에는 Smalltalk 에서 'A CookBook for Using the MVC User Interface Paradigm in Smalltalk-80'라는 글을 발표합니다. 이 때 당시에는 모델과 뷰의 분리에 대한 내용이 담겨 있습니다. 최근에 사용하는 MVC 의 정의와 비슷하게 정의되어 있지만, Model 과 View 의 직접적인 결합이 있습니다.

1988 년 SmallTalk 의 MVC 패턴 - https://beza1e1.tuxen.de/model_view_controller.html

 

 

그리고 나중에 애플(apple) 사에서는 Cocoa MVC 패턴을 발표합니다. 참고로 Cocoa 는 ios 설계를 위해서 사용되는 라이브러리입니다. 이 MVC 패턴이 현재까지의 MVC 패턴으로 이어지고 있습니다. 사실상 이 Cocoa MVC 패턴이 현대식 MVC 라고 할 수 있죠.

왼쪽: Compound pattern 을 사용하는 전통적인 MVC 패턴, 오른쪽: Compound pattern 을 사용하는 Cocoa MVC 패턴 - https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html

Compound pattern 은 여러가지 디자인 패턴을 조합한 패턴입니다.  `Observer`, `Strategy`, `Composite` 패턴을 쓰고 있습니다.
Observer 패턴: Model 이 `Observable`, Model 로부터 정보를 받고 싶어하는 애들이 `Observer` 로 작동.
Strategy 패턴: Controller 가 View 의 행동에 해당하고 만약 View 가 다른 행동을 원한다면 다른 Controller 로 교환하기만 하면 됨. 
Composite 패턴: View 내부에서 이 패턴을 사용해서 Controller 가 View 에게 화면을 변경하라고 요청하면, 최상위 View 구성요소한테만 요청하면 됨. 
이 패턴을 통해 각 요소 간의 독립이 가능해집니다. 더 자세한 내용은 `헤드 퍼스트` 라는 책을 참고하면 됩니다.

 

MVC 를 사용하는 이유

MVC 패턴을 도입하면 도메인(비즈니스 로직)영역과 UI 영역이 분리됩니다. 두 영역이 서로 영향을 주지 않게 되는 것이죠.

이렇게 '관심사의 분리' 가 수행되면, 더 나은 업무의 분리와 향상된 관리가 가능해집니다. 프로그램의 확장성과 유연성이 높아지는 것입니다. 즉, 유지, 보수가 편리해지는 것입니다.

 

사실 다른 디자인 패턴인 MVP 나 MVVM, MVW 도 결국에는 MVC 패턴에서 파생된 것입니다. 이 패턴들도 결국에는 더 나은 유지, 보수를 위해서 사용하는 것이죠. 

안드로이드에서는 MVVM 을 일반적으로 사용하는 것 같은데 백엔드에서는 일반적으로 MVC 패턴을 사용하는 것 같습니다. 특히 Spring 진영에서는 Spring MVC 라는 패턴을 사용합니다. 아래 그림처럼요.

 

Spring MVC 패턴 구조 - '스프링 MVC 1편 - 인프런 김영한님 강의'

 

객체지향의 권위자인 마틴 파울러는 아래처럼 말했습니다.

MVC 의 핵심 개념은 뷰를 모델에서 분리하고 컨트롤러를 뷰에서 분리하는 두가지 분리이다!

이를 먼저 기억해두고, Model 과 View, Controller 에 대해 하나하나 천천히 봅시다. 

 

Model

Model데이터와 비즈니스 로직을 관리합니다. 데이터 가공을 책임지는 component 입니다.

  • 앱의 데이터(데이터베이스나 초기화된 상수나 값, 변수 등)가 무엇인지를 정의한다. 이 데이터의 상태를 저장, 업데이트한다.
  • 때로는 Model 의 변경사항을 옵저빙하는 방법을 제공하기도 한다.
  • 비즈니스 로직을 처리하고 Model 의 변경 사항을 Controller 와 View 에 전달한다.
  • View 나 Controller 에 의존하면 안된다. (코드 상의 의존이 없어야 함.)

데이터의 상태가 변경되면 Model 은 일반적으로 Controller 에게 알립니다. View 에게 알려서 필요한 대로 화면을 변경할 수도 있죠. 

그런데 이상합니다. 분명, 'MVC 에서는 뷰를 모델에서 분리' 해야 한다고 했는데 말이죠..?

 

View 에게 알리는 경우, `databinding` 이나 이벤트 핸들링를 통해서 구현될 수 있습니다. 하지만 이 경우에도 Model 과 View 는 직접적으로 의존하지는 않습니다. 다른 중간 계층을 통해서 상호작용하는 것이죠.

 

코드 상의 의존: 어떤 객체가 어떤 역할을 수행할 때 다른 객체에게 자신이 하지 못하는 역할을 위임하는 것.
보통 메시지를 보낸다고 한다. 클래스를 가지고 설명하면 더 쉽다.
A 라는 클래스 내부에서 B 클래스의 메소드를 호출하면 A 가 B 에게 의존성을 가지게 되는 것이다.
`B클래스의 인스턴스.method` 의 형태로 작성되면 의존성을 가지는 것이다.

 

View

View레이아웃과 화면을 처리합니다. 앱의 데이터를 보여주는 방식을 정의하는 component 입니다.

  • View 가 가진 데이터나, Model 의 상태를 표시하고 사용자 입력을 처리한다.
  • 사용자가 화면에 표시된 내용을 변경하게 되면 Model 에게 전달해서 Model 을 변경해야 한다.
  • Model 이 가지고 있는 정보를 따로 저장해서는 안 된다.
  • View 는 Model 로부터 데이터를 읽을 수는 있지만, Model 을 직접 수정해서는 안 된다.
  • Model 의 상태 변경에 대해 동적으로 업데이트되어야 한다. 변경에 대한 처리 방법을 구현해야 한다. (옵저버 패턴, 이벤트 핸들링, 데이터 바인딩 등)
  • Model 이나 Controller 에 의존하면 안된다. (코드 상의 의존이 없어야 함.)

여기서도 이상한 부분이 보일 수 있습니다.

"아니, 'MVC 에서는 뷰를 모델에서 분리' 해야 한다며?! 그런데 왜 View 가 Model 로부터 데이터를 읽을 수 있지?"

사실 데이터를 읽는 것도 직접 읽을 수 있는 것이 아닙니다. 위에서도 설명했듯이, 옵저버 패턴, `databinding`, 이벤트 핸들링 등의 방법을 통해 읽을 수 있는 것입니다.

 

Controller

Model 과 View 사이의 중개자 역할을 하며 Model 과 View 로 명령을 전달하는 component 입니다.

  • View 로부터 받은 앱 사용자로부터의 입력에 대한 응답으로 Model 및 View를 업데이트하는 로직을 가진다. 

 

여기서 예를 들어봅시다. 쇼핑 관련 앱이 있다고 합시다.

쇼핑 리스트는 항목을 추가하거나 제거할 수 있게 해주는 입력 폼과 버튼(View)을 가집니다. 이 입력과 버튼을 통한 `액션` 은 컨트롤러에게 전송됩니다. 그러면 Controller 는 Model 에게 데이터 처리를 위임합니다. Model 에서 처리 동작을 한후 Controller 는 업데이트된 데이터를 View 로 전송합니다. View 는 데이터를 사용자게에 보여주죠.

단순히 데이터를 다른 형태로 나타내기 위해 View 를 업데이트하고 싶을 수도 있습니다. 이런 경우에 Controller 는 Model 을 업데이트할 필요없이 바로 처리할 수도 있습니다.

 

동작을 그림으로 보면 대략 아래와 같습니다.

https://junhyunny.github.io/information/design-pattern/mvc-pattern/

위 왼쪽 그림에서는 Model  이 View 를 업데이트하고 있습니다. 그림에서는 간단히 표현하기 위해 이렇게 나타나 있지만 사실 여기서 UPDATES 될 때 중간 계층(Controller 등)이 있는 것입니다. 오른쪽 그림에서처럼 Model 과 View 는 서로를 아예 모르는 것이 맞습니다.

 

MVC 패턴의 핵심

이제 다시 마틴 파울러의 말을 곱씹어 봅시다.

MVC 의 핵심 개념은 View 를 Model 에서 분리하고 Controller 를 View 에서 분리하는 두가지 분리이다!

 

그렇다면 이 두 가지 분리의 중요성에 대해서 설명해 보겠습니다.

 

View 를 Model 에서 분리하는 것

  1. View 와 Model 은 서로 다른 관심사이다.
    • View: 사용자 인터페이스를 고려한다.
    • Model: 비즈니스 로직이나 데이터베이스 상호작용 등을 고려한다.
  2. 재사용
    • 동일한 Model 로 다양한 뷰(콘솔, 웹 등)에 적용이 가능하다.
  3. 테스트의 용이성
    • 시각적인 요소를 배제하고 도메인 로직에 대한 테스트만 작성이 가능하다.

 

Controller 를 View 에서 분리하는 것

  1. Controller 와 View 역시 서로 다른 관심사이다.
    • View: 사용자 인터페이스를 고려한다.
    • Controller: 비즈니스 로직(모델을 통해, 혹은 간단한 형태 변환)을 처리한다.
  2.  재사용
    • 다양한 View 를 동일한 Controller 와 함께 사용할 수 있다.
    • 반대로 동일한 View 를 여러 Controller 와 결합하여 재사용할 수 있다.

 

그런데 이렇게만 알고 있으면 MVC 을 구현할 때 초보자들은 정확히 어떻게 해야 할지 감이 안 올 수 있습니다.

 

실전에서는 어떻게 하면 MVC 를 잘 지키면서 코딩할 수 있는지에 대한 몇가지 원칙을 위 내용을 정리하면서 소개하겠습니다. 

 

MVC 를 지키면서 코딩하는 방법

  • Model 은 Controller 와 View 에 의존해서는 안된다. 즉, 데이터에 대한 순수 로직만 존재해야 한다.
  • View 는 Model 에만 의존해야 한다. 
    • 여기서의 의존은 코드의 의존성이 아닌, 도메인 데이터에 대한 의존입니다. Model 의 데이터를 보여준다는 의미, View 에서 사용되는 값은 Model 에서 전달된 값이어야 한다는 것입니다.
    • 하지만, View 코드에서 Model 에 직접 접근해서 Model 의 데이터를 수정하거나 비즈니스 로직을 수정해서는 안됩니다.
  • View 는 Model 로부터 사용자마다 다르게 변경되는 데이터만 받아야 한다.
    • 사용자마다 다른 data 만을 Model 로부터 받아야 합니다. 즉, 공통으로 처리되는 부분에 대해서는 View 자체에서 처리되어야 합니다.
  • View 가 Model 로부터 데이터를 받을 때 Controller를 통해 받아야 한다. 구현에 따라 Controller 가 아닌 다른 중간 계층으로부터 받을 수도 있다. (Controller 를 어떻게 정의하냐에 따라 그 중간 계층도 Controller 라고 볼 수도 있습니다.)

 

이렇게 크게 다섯까지의 원칙이 있을 수 있습니다.

 

정리

  • MVC 패턴은 결국 확장성, 유연성, 재사용성, 유지보수성, 테스트 용이성을 위해 사용한다.
  • MVC 패턴으로 도메인 로직 영역과 UI 로직이 분리된다. 
  • Model 은 데이터와 비즈니스 로직을 처리한다.
  • View 는 레이아웃과 화면을 표시한다.
  • Controller 는 Model 과 View 의 중개자이며, 명령을 내린다.
  • MVC 를 지키면서 코딩하는 방법을 참고하면 최소한의 안티 패턴을 피할 수 있다.

 

 

 

 

Reference

https://www.youtube.com/watch?v=ogaXW6KPc8I

https://developer.mozilla.org/ko/docs/Glossary/MVC

https://junhyunny.github.io/information/design-pattern/mvc-pattern/

https://murphymoon.tistory.com/entry/%EC%9A%B0%EC%95%84%ED%95%9C-%ED%85%8C%ED%81%AC-MVC-%EB%A6%AC%EB%B7%B0-%EB%A0%88%EC%9D%B4%EC%96%B4-MVC-%ED%8C%A8%ED%84%B4-5%EB%A0%88%EC%9D%B4%EC%96%B4

https://luckygg.tistory.com/182

https://beza1e1.tuxen.de/model_view_controller.html

https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html