[프로그래밍 일반] MVC, MVP 웹 디자인 패턴에 대해
디자인 패턴이란?
디자인 패턴이란 프로그램을 개발할 때 발생할 수 있는 문제를 방지하고자 만든 규약이다. 프로젝트가 커짐에 따라 코드 간에 복잡하게 얽혀 있으면 무언가를 추가하거나 수정할 때 예상치 못한 문제가 발생하기 쉽다. 따라서 서로 간에 역할을 분리하고 각자의 역할에 집중하기 위한 규칙을 정한다.
프로젝트에 따라 정하는 자체적인 약속이므로 문법이나 예약어처럼 강제성은 없다. 그리고 상황에 따라 구현 방식이 달라지기도 한다. 하지만, 일반적으로 사용되는 공통 패턴이 몇 가지 있는데, 그 중에서 MVC, MVP패턴에 대해 살펴보자.
MVC
mvc 패턴은 작업들을 model - view - controler로 구분한다.
간단한 역할
model
- 사이트에 필요한 모든 데이터, 상수를 저장한다.
- 데이터를 CRUD 할 수 있는 주요 로직을 제공한다.
view
- 사용자가 보는 페이지를 담당한다.
- 데이터를 받아 html 템플릿에 할당하여 페이지를 렌더링한다,
- 사용자가 form, button 등으로 상호작용을 하면서 데이터를 요청하면 이를 외부로 전달한다.
controler
- model과 view간의 중개 역할이다.
- view가 무언가를 요청하면 이를 적절한 작업으로 중개하고, 이는 다시 model로 작업을 요청한다.
- model이 보내는 데이터나 작업 결과를 view로 반환한다.
작동 흐름의 예시1: 페이지 요청
- 유저가 사이트의 "/"라우트 요청
- "/" 라우트는 controller 중 home 서브 컨트롤러로 요청 전달
- home 서브 컨트롤러 함수가 호출된다. 여기서 view에 필요한 데이터를 불러오기 위해 model에 작업을 요청함
- model에서 home에 필요한 데이터를 쿼리하여 controller로 반환함.
- cotroller에서는 모델의 데이터를 받아 view로 전송함
- view는 전달받은 데이터를 템플릿에 할당하여서 유저에게 렌더링
작동 흐름의 예시2: 데이터 수정 요청
- 회원가입 페이지에서 유저가 id, password, name, email 등의 정보를 form으로 제출
- 유저가 제출한 데이터는 "/signUp"에 post 메소드로 전송
- "/signUp" 라우트는 메소드에 맞게 요청을 스위칭해서 signUpPost 컨트롤러 함수 호출 후 유저의 데이터를 전달
- signUpPost 함수에서 유저 데이터를 model로 전달하며 유저 계정의 추가 요청
- model은 db와 통신하며 유저 계정 데이터를 추가 후, 성공이나 실패 여부의 결과를 signUpPost으로 반환
- signUpPost는 결과에 따라 다른 로직(redirect, 에러 메시지 전송)을 view로 전송
- view에서 받은 응답을 유저에게 전달
왜 필요한가.
mvc 패턴이 정형화되어 있지 않던 시절에는 주로 model - (view+control) 패턴을 사용했다. 이는 view와 control의 로직이 동일한 영역에서 관리되며 바로 model과 통신하는 방식이었다. 이 방식은 프로젝트를 여러 영역으로 쪼개지 않으므로 시작이 간단했지만 두 역할 간에 구분이 명확하지 않고 view가 model에 의존하게 되어서 유지 보수가 어렵다는 문제가 있었다. 그 이유는 view나 model이 통신을 위해 상대의 정보를 저장하는 경우가 필요했고, 이렇게 되면 다른 view나 model 간에 조합을 하는데 사이드 이펙트가 발생할 수 있기 때문이었다.
컨트롤러의 분리
mvc과 mvp 패턴의 핵심은 view와 model이 통신할 때 필요한 정보를 서로 분리해 controller에 담는다는 것이다. 이러면 각 view와 model은 서로에 대한 정보를 담지도 않고 직접 가공하지도 않는 순수한 상태가 된다. 이러면 view1과 통신하던 model1이 view2나 view3와 통신하더라도 view1에게 예상치 못한 부작용을 주지 않게 된다.
또한, view의 로직이 model과의 통신 로직과 분리되므로 코드 관리의 측면에서 보기 좋아진다.
올바른 작동의 위한 규칙
- 모델과 뷰는 서로에 대해 모르는 순수한 상태여야 한다. 즉, 상대에 대한 정보를 미리 저장해놨다가 사용하는 방식이면 안되고 오로지 입력 받은 정보를 가공하여 처리하고 출력하는 형태로만 구현되어야 한다.
- 모델과 뷰는 데이터를 변경하거나 갱신할 시에 이를 처리하고 발신할 인터페이스를 구현해야 한다.
- 컨트롤러는 양 측의 요청을 받으면 다른 한쪽에 전달하는 역할을 한다. 모델이나 뷰의 요청을 처리하기 위해 view나 모델의 정보를 저장할 수 있다.
MVP
mvp는 mvc 디자인의 변형으로, model과 view의 분리한다는 원칙과 중개 영역에서 필요한 정보를 저장하고 각 영역의 요청을 다른 영역으로 전달하여 처리한다는 점은 동일하다. 차이는 중개 영역이 controller 대신 presenter라는 점이다.
controller와 presenter의 차이점
mvc의 컨트롤러는 하나의 컨트롤러가 여러 뷰의 요청을 담당하는 1대 n의 관계이다.
이런 방식은 개발 초반에는 컨트롤러의 재사용성이 높으니 편리해보일 수 있다. 하지만, view와 모델이 늘어남에 따라서 컨트롤러 하나가 담당하는 view가 많아지고 연계된 모델 로직이 많아질수록 코드가 복잡해질 수 밖에 없다. 이는 곧 유지 보수와 확장의 불편성으로 이어진다.
이럴 때, "이럴 바에는 그냥 하나의 컨트롤러는 하나의 view만 담당하자" 라는 생각이 들고 그것이 바로 mvp 패턴이다.
mvp에서 presenter란 하나의 view만 담당하는 1대1 관계의 컨트롤러이다. 이는 하나의 view를 수정하기 위해 하나의 presenter만 관리하면 되므로 단일 책임 원칙에 따른 유지보수가 편리해진다. 그러나, view가 하나씩 추가될 때마다 presenter도 하나 씩 추가되므로 코드가 많아지고 생산성이 좋지 않다는 단점이 존재한다.
정리
mvp와 mvc 디자인은 추상적인 수준의 디자인 패턴 정의이므로 사람마다 세부적인 구현방식이 달라진다. 그리고 양자 중에 뭐가 옳냐의 정답도 없다. 이는 프로젝트의 규모에 따라 결정된다. 두 디자인 패턴의 핵심은 다음과 같다.
- 왜 필요한가 | 과거 model - view 턴은 model과 view 간의 의존성이 높아서 model은 특정 view의 정보를 저장하여 사용하고, view는 model의 특정 로직에 의존했다. 이는 재사용하기 어렵고 유지보수 및 확장이 불편했다.
- 중개자 | model과 view를 분리하기 위해 중개자로서 controller를 도입한다. 그렇게 되면 model이나 view는 추상적인 수준에서 요청을 할 수 있고 구체적인 로직은 controller에서 구현하므로 model과 view에서 하는 일이 한 눈에 들어온다.
- model과 view의 상호 순수성 | controller의 구체적 로직에 필요한 각 model과 view의 정보는 controller에 보관함으로써 각 model과 view는 입력된 데이터만 가공하는 순수한 형태가 될 수 있다.
- 다음 문제는 controller가 view와 1대 n의 관계를 맺는지, 1대1의 관계를 맺는지이고, 후자를 MVP 패턴이라 부른다.
- MVP 패턴은 MVC에 비해서 대규모 웹의 유지보수에 유리하다. 하나의 view는 하나의 중개자(presenter)가 담당하므로 각자의 책임이 더 세분화되고 명확해지기 때문이다.
- MVP 패턴의 단점은 view 마다 presenter을 이어주어야 하므로 코드가 더 많아진다는 점이다.