From d657c095a26ff2bda6678efeb60f5b378bbad0c7 Mon Sep 17 00:00:00 2001 From: WooWan Date: Sun, 17 Nov 2024 21:24:50 +0900 Subject: [PATCH] chapter 8 --- .../\354\232\260\354\260\275\354\231\204.md" | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 "\354\261\225\355\204\260_8/\354\232\260\354\260\275\354\231\204.md" diff --git "a/\354\261\225\355\204\260_8/\354\232\260\354\260\275\354\231\204.md" "b/\354\261\225\355\204\260_8/\354\232\260\354\260\275\354\231\204.md" new file mode 100644 index 0000000..70b3cb9 --- /dev/null +++ "b/\354\261\225\355\204\260_8/\354\232\260\354\260\275\354\231\204.md" @@ -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); + }); + } + } + + ``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +