MVC
패턴이란Model
,View
,Controller
라는 계층 구조로 나뉘어진 패턴입니다.
애플리케이션의 데이터와 비즈니스 로직을 담당하며, 데이터베이스와 상호작용하여 데이터를 저장하고, 데이터를 처리하는 역할을 합니다.
Modal의 규칙
사용자에게 데이터를 표시하는 역할을 하며, 사용자가 볼 수 있는 인터페이스를 제공합니다.
View의 규칙
Modal View 사이의 상호작용을 관리하며, 사용자의 입력을 처리하고, 모델을 업데이트 및 업데이트된 모델을 기반으로 뷰를 다시 렌더링합니다.
Controller의 규칙
MVC 패턴의 작동 원리는 다음과 같습니다:
todoList-model.ts
애플리케이션의 데이터와 비즈니스 로직을 관리합니다.
Todo 항목을 추가, 삭제하거나 완료 상태를 토글하는 기능을 제공합니다.
export type Todo = {
id: string;
content: string;
isDone: boolean;
};
export class TodoListModel {
todoList: Todo[];
constructor() {
this.todoList = [];
}
addTodo(content: string) {
const newTodo: Todo = {
id: new Date().getTime().toString(),
content,
isDone: false,
};
this.todoList.push(newTodo);
}
removeTodo(id: string) {
this.todoList = this.todoList.filter((todo) => todo.id !== id);
}
toggleDone(id: string) {
const todo = this.todoList.find((todo) => todo.id === id);
if (todo) {
todo.isDone = !todo.isDone;
}
}
}
todoList-view.ts
사용자에게 화면에 보여지는 View를 담당합니다.
TodoList를 렌더링하고, 사용자 입력을 처리하는 이벤트를 모델이나 컨트롤러에 전달합니다.
import { Todo } from './todoList-model';
export class TodoListView {
formElement: HTMLFormElement;
todoListElement: HTMLUListElement;
constructor() {
this.formElement = document.getElementById('todo-form')! as HTMLFormElement;
this.todoListElement = document.getElementById(
'todos-ul',
)! as HTMLUListElement;
}
bindAddTodo(handler: (content: string) => void) {
this.formElement.addEventListener('submit', (e) => {
e.preventDefault();
const input = this.formElement.querySelector('input') as HTMLInputElement;
const inputValue = input.value;
if (inputValue.trim()) {
handler(inputValue);
input.value = '';
}
});
}
bindRemoveTodo(handler: (id: string) => void) {
this.todoListElement.addEventListener('click', (event) => {
const target = event.target as HTMLButtonElement;
if (target && target.classList.contains('delete')) {
const id = target.dataset.id!;
handler(id);
}
});
}
bindToggleDone(handler: (id: string) => void) {
this.todoListElement.addEventListener('click', (event) => {
const target = event.target as HTMLInputElement;
if (target && target.classList.contains('check-box')) {
const id = target.dataset.id!;
handler(id);
}
});
}
renderTodoList(todoList: Todo[]) {
this.todoListElement.innerHTML = '';
todoList.forEach((todo) => {
const li = `
<li id=${todo.id}} style=display:flex;align-items:center;gap:8px>
<input class=check-box ${todo.isDone && 'checked'} data-id=${todo.id} type=checkbox />
<p style=${todo.isDone && 'text-decoration:line-through;opacity:0.5;'}>${todo.content}</p>
<button class=delete data-id=${todo.id}>remove</button>
</li>
`;
this.todoListElement.insertAdjacentHTML('afterbegin', li);
});
}
}
todoList-controller.ts
Model과 View 사이의 중개 역할을 합니다.
사용자의 입력을 받아서 Model을 업데이트하고, 업데이트된 Model의 상태를 View에 반영합니다.
import { TodoListModel } from './todoList-model';
import { TodoListView } from './todoList-view';
export class TodoListController {
model: TodoListModel; // 모델 인스턴스
view: TodoListView; // 뷰 인스턴스
constructor(model: TodoListModel, view: TodoListView) {
this.model = model;
this.view = view;
// 뷰에서 발생한 이벤트를 처리하기 위해 핸들러를 바인딩
this.view.bindAddTodo(this.handleAddTodo);
this.view.bindRemoveTodo(this.handleRemoveTodo);
this.view.bindToggleDone(this.handleToggleDone);
// 초기 렌더링
this.view.renderTodoList(this.model.todoList);
}
handleAddTodo = (content: string) => {
// 새로운 Todo 항목을 추가하고 리스트를 다시 렌더링
this.model.addTodo(content);
this.view.renderTodoList(this.model.todoList);
};
handleRemoveTodo = (id: string) => {
// 특정 Todo 항목을 삭제하고 리스트를 다시 렌더링
this.model.removeTodo(id);
this.view.renderTodoList(this.model.todoList);
};
handleToggleDone = (id: string) => {
// 특정 Todo 항목의 완료 상태를 토글하고 리스트를 다시 렌더링
this.model.toggleDone(id);
this.view.renderTodoList(this.model.todoList);
};
}
index.html
<!doctype html>
<html lang="ko-KR">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MVC-ToodoList</title>
</head>
<body>
<div id="app">
<form id="todo-form">
<input id="todo-input" placeholder="Please enter the task." />
<button id="submit-btn" type="submit">Add</button>
</form>
<ul id="todos-ul"></ul>
</div>
</body>
</html>
index.ts
import { TodoListModel } from './src/todoList-model';
import { TodoListView } from './src/todoList-view';
import { TodoListController } from './src/todolist-controller';
document.addEventListener('DOMContentLoaded', () => {
// model 생성
const model = new TodoListModel();
// view 생성
const view = new TodoListView();
// controller 생성 인자 값으로 model 인스턴스와 view 인스턴스 전달
new TodoListController(model, view);
});
MVC 패턴을 이용하여 간단한 TodoList를 구현해 보았습니다.
위의 TodoList 예시에서 알 수 있듯이, 프론트엔드에서 MVC 패턴을 사용할 경우 코드의 복잡성이 증가하고, 컨트롤러가 비대해지며, 데이터와 UI 간의 동기화를 효율적으로 처리하기 어려울 수 있습니다. 특히, 현대적인 프레임워크들이 등장하면서 MVC 패턴의 단점을 보완하고, 더 직관적이고 효율적인 방법으로 프론트엔드 애플리케이션을 구축할 수 있게 되었습니다. 따라서, 오늘날 프론트엔드 개발에서는 MVC 패턴 대신 컴포넌트 기반 아키텍처, 단방향 데이터 흐름 패턴(Flux 패턴) 등이 더 많이 사용됩니다.
MVC 패턴은 Model, View, Controller 계층 구조로 나뉘어진 패턴입니다.
Model
은 데이터 및 비즈니스 로직을 관리하며, 데이터의 상태를 유지하고, 데이터 변경 시 뷰에 통지하는 역할도 포함됩니다.
View
는 사용자에게 제공할 화면을 표시하며, 사용자의 입력을 받을 수 있는 UI 요소를 포함합니다.
Controller
는 사용자와 상호작용하며, 모델과 뷰 간의 데이터 흐름을 조정하는 역할을 합니다. 사용자 입력에 따라 모델을 업데이트하고, 변경된 데이터를 뷰에 반영합니다.
MVC 패턴은 유지보수성, 재사용성, 유연성 등의 장점이 존재하지만, 현재의 프론트엔드에서는 Model
과 View
의 복잡성 증가 및 데이터 흐름 파악이 어려워 MVC 패턴을 잘 사용하지 않으며, 대신 컴포넌트 기반 아키텍처, 단방향 데이터 흐름 패턴(Flux 패턴) 등이 더 많이 사용됩니다.