Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[우창완] 챕터 8: 자바스크립트 MV* 패턴 #76

Merged
merged 1 commit into from
Dec 2, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 227 additions & 0 deletions 챕터_8/우창완.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# 자바스크립트 MV* 패턴



## 8.1 MVC 패턴

MVC 패턴에서 Model과 View가 Subject, Observer 관계로 결합도를 낮춘 형태

* Model

```js
// Model: 데이터 관리와 비즈니스 로직
class TodoModel {
constructor() {
this.todos = [];
this.observers = [];
}

addTodo = (text) => {
this.todos.push({ id: Date.now(), text, completed: false });
this.notify();
}

addObserver = (observer) => {
this.observers.push(observer);
}

notify = () => {
this.observers.forEach(observer => observer(this.todos));
}
}
```

* Controller

Controller에서 View에 함수를 binding, model에 observer를 추가한다.

```js
// Controller: Model과 View 연결
class TodoController {
constructor(model, view) {
this.model = model;
this.view = view;

// View의 이벤트를 Model과 연결
this.view.bindAddTodo(this.handleAddTodo);
this.model.addObserver(this.view.display);
}

handleAddTodo = (text) => {
this.model.addTodo(text);
}
}
```



* View

```js
// View: UI 표시와 사용자 입력 처리
class TodoView {
constructor() {
this.input = document.createElement("input");
this.button = document.createElement("button");
this.list = document.createElement("ul");
}

display = (todos) => {
this.list.innerHTML = "";
todos.forEach(todo => {
const li = document.createElement("li");
li.textContent = todo.text;
this.list.appendChild(li);
});
}

bindAddTodo = (handler) => {
this.button.addEventListener("click", () => {
if (this.input.value) {
handler(this.input.value);
this.input.value = "";
}
});
}
}
```





## MVP 패턴

MVC 패턴에서 model과 view 가 관찰자 패턴으로 커뮤니케이션 했다면, MVP패턴은 Presenter 계층이 중간에서 조정하는 역할을한다.

* MVC

```
Model ──(Observer 패턴)─→ View
↑ │
└─────── Controller ──────┘
```



* MVP

```
Model ←→ Presenter ←→ View
```



* 계층 별로 테스트 하기가 더 용이

* 개인적으로는 더 명시적이어서 개발 인지부하가 더 적을거 같다.

* 수동적인 VIew 계층 (MVC에서는 데이터 조작에 직접 관여, MVP에서는 관여 여지가 적음)











### MVVM 모델

Mvvm 모델로 오면서, UI 개발자와 서버 개발자가 분리될 수 있었음

코드를 살펴봤을 때는 MVC모델에서 Model의<->View의 관찰자 패턴의 ViewModel(presenter) <-> View의 관찰자 패턴으로 이동한 것 같다.



MVP에서 Model과 View의 분리의 장점을 가져가면서도, 단방향 데이터 흐름을 만들 수 있는 것이 `킥`이다



아래 코드에서 ViewModel이 View를 직접적으로 모르지만, `상태`가 업데이트되었음을 알리고, View에서 render() 를 통해 선언적으로 UI를 표현하는 방식이 react에도 많은 영향을 주지 않았나 싶다.

```js
class TodoViewModel {
private todos: string[] = [];
private subscribers: Array<() => void> = [];

// ... 중요한 로직들

subscribe(callback: () => void): void {
this.subscribers.push(callback);
}

private notifySubscribers(): void {
this.subscribers.forEach(callback => callback());
}
}

// View
class TodoViewMVVM {
private input: HTMLInputElement;
private list: HTMLUListElement;

constructor(private viewModel: TodoViewModel) {
// DOM 설정...

// 이벤트 바인딩
this.input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
const todo = this.input.value;
if (todo) {
this.viewModel.addTodo(todo);
this.input.value = '';
}
}
});

// ViewModel 구독
this.viewModel.subscribe(() => this.render());
}

private render(): void {
const todos = this.viewModel.getTodos();
this.list.innerHTML = '';
todos.forEach((todo, index) => {
const li = document.createElement('li');
li.textContent = todo;
li.addEventListener('click', () => this.viewModel.removeTodo(index));
this.list.appendChild(li);
});
}
}

```































Loading