Skip to content

Commit d657c09

Browse files
committed
chapter 8
1 parent a61ed94 commit d657c09

File tree

1 file changed

+227
-0
lines changed

1 file changed

+227
-0
lines changed

챕터_8/우창완.md

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
# 자바스크립트 MV* 패턴
2+
3+
4+
5+
## 8.1 MVC 패턴
6+
7+
MVC 패턴에서 Model과 View가 Subject, Observer 관계로 결합도를 낮춘 형태
8+
9+
* Model
10+
11+
```js
12+
// Model: 데이터 관리와 비즈니스 로직
13+
class TodoModel {
14+
constructor() {
15+
this.todos = [];
16+
this.observers = [];
17+
}
18+
19+
addTodo = (text) => {
20+
this.todos.push({ id: Date.now(), text, completed: false });
21+
this.notify();
22+
}
23+
24+
addObserver = (observer) => {
25+
this.observers.push(observer);
26+
}
27+
28+
notify = () => {
29+
this.observers.forEach(observer => observer(this.todos));
30+
}
31+
}
32+
```
33+
34+
* Controller
35+
36+
Controller에서 View에 함수를 binding, model에 observer를 추가한다.
37+
38+
```js
39+
// Controller: Model과 View 연결
40+
class TodoController {
41+
constructor(model, view) {
42+
this.model = model;
43+
this.view = view;
44+
45+
// View의 이벤트를 Model과 연결
46+
this.view.bindAddTodo(this.handleAddTodo);
47+
this.model.addObserver(this.view.display);
48+
}
49+
50+
handleAddTodo = (text) => {
51+
this.model.addTodo(text);
52+
}
53+
}
54+
```
55+
56+
57+
58+
* View
59+
60+
```js
61+
// View: UI 표시와 사용자 입력 처리
62+
class TodoView {
63+
constructor() {
64+
this.input = document.createElement("input");
65+
this.button = document.createElement("button");
66+
this.list = document.createElement("ul");
67+
}
68+
69+
display = (todos) => {
70+
this.list.innerHTML = "";
71+
todos.forEach(todo => {
72+
const li = document.createElement("li");
73+
li.textContent = todo.text;
74+
this.list.appendChild(li);
75+
});
76+
}
77+
78+
bindAddTodo = (handler) => {
79+
this.button.addEventListener("click", () => {
80+
if (this.input.value) {
81+
handler(this.input.value);
82+
this.input.value = "";
83+
}
84+
});
85+
}
86+
}
87+
```
88+
89+
90+
91+
92+
93+
## MVP 패턴
94+
95+
MVC 패턴에서 model과 view 가 관찰자 패턴으로 커뮤니케이션 했다면, MVP패턴은 Presenter 계층이 중간에서 조정하는 역할을한다.
96+
97+
* MVC
98+
99+
```
100+
Model ──(Observer 패턴)─→ View
101+
↑ │
102+
└─────── Controller ──────┘
103+
```
104+
105+
106+
107+
* MVP
108+
109+
```
110+
Model ←→ Presenter ←→ View
111+
```
112+
113+
114+
115+
* 계층 별로 테스트 하기가 더 용이
116+
117+
* 개인적으로는 더 명시적이어서 개발 인지부하가 더 적을거 같다.
118+
119+
* 수동적인 VIew 계층 (MVC에서는 데이터 조작에 직접 관여, MVP에서는 관여 여지가 적음)
120+
121+
122+
123+
124+
125+
126+
127+
128+
129+
130+
131+
### MVVM 모델
132+
133+
Mvvm 모델로 오면서, UI 개발자와 서버 개발자가 분리될 수 있었음
134+
135+
코드를 살펴봤을 때는 MVC모델에서 Model의<->View의 관찰자 패턴의 ViewModel(presenter) <-> View의 관찰자 패턴으로 이동한 것 같다.
136+
137+
138+
139+
MVP에서 Model과 View의 분리의 장점을 가져가면서도, 단방향 데이터 흐름을 만들 수 있는 것이 ``이다
140+
141+
142+
143+
아래 코드에서 ViewModel이 View를 직접적으로 모르지만, `상태`가 업데이트되었음을 알리고, View에서 render() 를 통해 선언적으로 UI를 표현하는 방식이 react에도 많은 영향을 주지 않았나 싶다.
144+
145+
```js
146+
class TodoViewModel {
147+
private todos: string[] = [];
148+
private subscribers: Array<() => void> = [];
149+
150+
// ... 중요한 로직들
151+
152+
subscribe(callback: () => void): void {
153+
this.subscribers.push(callback);
154+
}
155+
156+
private notifySubscribers(): void {
157+
this.subscribers.forEach(callback => callback());
158+
}
159+
}
160+
161+
// View
162+
class TodoViewMVVM {
163+
private input: HTMLInputElement;
164+
private list: HTMLUListElement;
165+
166+
constructor(private viewModel: TodoViewModel) {
167+
// DOM 설정...
168+
169+
// 이벤트 바인딩
170+
this.input.addEventListener('keypress', (e) => {
171+
if (e.key === 'Enter') {
172+
const todo = this.input.value;
173+
if (todo) {
174+
this.viewModel.addTodo(todo);
175+
this.input.value = '';
176+
}
177+
}
178+
});
179+
180+
// ViewModel 구독
181+
this.viewModel.subscribe(() => this.render());
182+
}
183+
184+
private render(): void {
185+
const todos = this.viewModel.getTodos();
186+
this.list.innerHTML = '';
187+
todos.forEach((todo, index) => {
188+
const li = document.createElement('li');
189+
li.textContent = todo;
190+
li.addEventListener('click', () => this.viewModel.removeTodo(index));
191+
this.list.appendChild(li);
192+
});
193+
}
194+
}
195+
196+
```
197+
198+
199+
200+
201+
202+
203+
204+
205+
206+
207+
208+
209+
210+
211+
212+
213+
214+
215+
216+
217+
218+
219+
220+
221+
222+
223+
224+
225+
226+
227+

0 commit comments

Comments
 (0)