참고자료:
https://youtu.be/bjVAVm3t5cQ
https://beomy.tistory.com/43
우선 공통점부터. M=Model. V=View.
View는 사용자에게 보이는 UI,
Model은 뒤에서 데이터 처리하는 부분.
MV 뒤에 붙는 게 View와 Model 사이를 이어주는 것 - Controller or Presenter or View Model.
이 디자인 패턴에 따라 View와 Model이 하는 일도 조금씩 범위가 달라진다.
개념만으로는 이해가 잘 안 되길래 예제 코드를 함께 만들어봤다.
사용자가 이름을 입력하고 버튼을 누르면 이름과 글자수를 화면에 표시해 주는 웹 애플리케이션 코드를 각 디자인 패턴에 맞게 작성..해달라고 챗지피티한테 시켰다. 내가 짠 게 아니다.
(사담: 원래 안드로이드 스튜디오 배우는 수업에서 배웠던 내용인데, 몇 년 전에 들은 수업이라서 다시 보니 대충은 알겠지만 블로그에 포스팅해서 남에게 설명할 정도로는 못 하겠다. 그래서 예제를 자바스크립트로 바꿔봤다. )

class Model {
constructor() {
this.name = '';
this.length = 0;
}
setName(name) {
this.name = name;
this.length = name.length;
}
}
class View {
constructor() {
this.inputElement = document.getElementById('input');
this.buttonElement = document.getElementById('button');
this.textElement = document.getElementById('text');
}
getInputValue() {
return this.inputElement.value;
}
render(name, length) {
this.textElement.innerText = `Name: ${name}, Length: ${length}`;
}
}
class Controller {
constructor(model, view) {
this.model = model;
this.view = view;
this.view.buttonElement.addEventListener('click', () => this.handleSetName());
}
handleSetName() {
const name = this.view.getInputValue();
this.model.setName(name);
this.view.render(this.model.name, this.model.length);
}
}
let model = new Model();
let view = new View();
let controller = new Controller(model, view);
모델이 하는 일은 setName에서 글자 수 세는 정도고,
뷰가 하는 일은 input값이 뭔지 갖고 있다가 (컨트롤러가 물어보면) 알려주는 것과 화면 렌더링 정도다.
컨트롤러에는 모델과 뷰의 객체가 담겨있고 버튼 클릭 이벤트가 발생할 때마다 뷰에서 인풋이 뭐였는지 가져오고, 모델에 있는 setName() 함수를 실행시켜서, 그 결과물을 뷰에 있는 render() 함수에 파라미터로 집어넣는 것까지 다 직접 한다.
확실히 컨트롤러가 모든 일을 다 하고 있다.

class Model {
constructor() {
this.name = '';
this.length = 0;
}
setName(name) {
this.name = name;
this.length = name.length;
return {name: this.name, length: this.length};
}
}
class View {
constructor() {
this.input = document.getElementById('input');
this.button = document.getElementById('button');
this.text = document.getElementById('text');
}
bindSetName(handler) {
this.button.addEventListener('click', () => {
handler(this.input.value);
});
}
render(name, length) {
this.text.innerText = `Name: ${name}, Length: ${length}`;
}
}
class Presenter {
constructor(model, view) {
this.model = model;
this.view = view;
this.view.bindSetName(this.handleSetName.bind(this));
}
handleSetName(name) {
const {name: updatedName, length: updatedLength} = this.model.setName(name);
this.view.render(updatedName, updatedLength);
}
}
let model = new Model();
let view = new View();
let presenter = new Presenter(model, view);
모델에 return 코드가 생겼다. 이제 컨트롤러가 모델에 있는 함수를 실행시켜서 계산 결과를 직접 가져다 쓰는 게 아니라 모델이 리턴해준다.
뷰에는 이벤트 리스너가 생겼다. 이제 사용자의 입력을 컨트롤러가 아닌 뷰가 받는다.
프레젠터는 뷰에게 만약 버튼 클릭 이벤트가 발생하면 handleSetName() 함수를 실행하라고 파라미터로 넣어주고, handleSetName() 에서는 모델에게서 받아온 연산 결과를 뷰에게 렌더링하게 한다.
하지만 MVP 패턴의 단점은 이 웹 애플리케이션의 기능이 handleSetName() 뿐만 아니라 훨씬 많을 때 벌어진다. 프레젠터에 handleSetName() 같은 함수를 줄줄이 계속 만들어야 한다.

MVVM은 쌍방향 데이터 바인딩 기능이 없는 바닐라 JS에서는 제대로 예제를 만들기가 어렵다. 만들어봤자 MVP와 별 차이가 없다.
그래서 내가 Vue.js를 모르기는 하지만 ChatGPT가 Vue.js를 활용해서 예시를 보여줬다.
<template>
<div>
<input type="text" v-model="name" />
<button @click="setName">Submit</button>
<p>Name: {{ name }}, Length: {{ name.length }}</p>
</div>
</template>
<script>
export default {
data() {
return {
name: ''
}
},
methods: {
setName() {
// In this case, the name data property is already updated by two-way binding
// If there were any more logic to be done when setting the name, it could be done here
}
}
}
</script>
Vue.js에서는
v-model디렉티브를 사용하여 입력 필드와name데이터 속성을 양방향으로 바인딩합니다. 따라서 사용자가 입력 필드의 값을 변경하면name속성이 자동으로 업데이트됩니다. 그리고@click디렉티브를 사용하여 버튼 클릭 이벤트를setName메서드에 바인딩합니다.
이 예제에서는 별도의 Model과 View 클래스가 없습니다. Vue.js에서는 MVVM 패턴을 구현하기 위해 별도의 클래스를 만드는 대신 컴포넌트의data와methods옵션을 사용하여 모델과 뷰 모델의 역할을 수행합니다.data옵션은 모델의 역할을 하며,methods옵션은 뷰 모델의 역할을 합니다.
또한, Vue.js에서는 뷰와 뷰 모델 사이의 동기화를 자동으로 처리해주므로, 개발자는 데이터와 DOM 업데이트 간의 동기화에 신경 쓸 필요가 없습니다. 이는 Vue.js의 MVVM 패턴 구현의 주요 장점 중 하나입니다.
다만 이 코드는 MVVM의 모든 요소를 완전히 보여주지는 못합니다. MVVM 패턴은 일반적으로 뷰가 데이터 바인딩을 통해 ViewModel과 통신하고, ViewModel은 Model과 통신하는 구조를 가지며, 이로 인해 View와 Model 사이의 의존성이 제거됩니다. 하지만 위의 Vue 예제에서는 ViewModel이 명시적으로 나타나지 않습니다. 이는 Vue.js의 설계 방식 때문인데, Vue 컴포넌트 자체가 ViewModel의 역할을 하며, Vue 인스턴스의 데이터와 메소드가 Model의 역할을 수행하기 때문입니다.
내가 MVC랑 MVP는 JS 코드를 이해하니까 ChatGPT가 틀린 부분도 수정하고 했는데 얘는 Vue라서 무슨 말인지 모르겠다. 챗지피티가 말한 그대로 긁어왔다.
내가 리액트는 좀 아는데 혹시 리액트는 데이터 바인딩 안 되냐 물어봤더니 리액트는 부모->자식 일방향 소통이라서 안 된단다. 그 대답을 듣고서야 MVVM에 뭐가 필요하다는 건지 좀 감이 왔다.
MVVM은 당장 쓸 일 없을 듯하니 이 정도로만 이해하고 넘어가려 한다.