MVC 패턴을 적용시킨 TodoList

MVC 패턴을 적용 시킨 TodoList를 달달 외울 정도로 손에 익혀두자.

이를 기본으로 앞으로 애플리케이션을 만들때 두고두고 유용하게 쓰일 것이다.

HTML, CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html lang="ko">
<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 - TodoList</title>
<style>
.todo-list {
list-style-type: none;
padding-inline-start: 0;
}

.todo-filters {
display: flex;
gap: 15px;
list-style-type: none;
padding-inline-start: 0;
}

.todo-list input:checked + span {
text-decoration: line-through;
}

.active {
text-decoration: underline;
}
</style>
</head>

<body>
<input type="text" class="todo-input" />
<ul class="todo-list"></ul>
<ul class="todo-filters">
<li id="all" class="active">All</li>
<li id="completed">Completed</li>
<li id="active">Active</li>
</ul>
</body>
</html>
  1. 우선 정적인 요소와 동적인 요소를 구분지어서 HTML 구조를 설계한다.

동적인 todo 요소를 추가할 것이고 그 외에 항목들은 정적인 요소들이다.

Javascript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
let todos = [];

const $todoInput = document.querySelector(".todo-input");
const $todoList = document.querySelector(".todo-list");

const generateNextTodoId = () =>
Math.max(...todos.map((todo) => todo.id), 0) + 1;

const render = (todos) => {
$todoList.innerHTML = todos
.map(
({ id, content, completed }) =>
`<li id="${id}">
<input type="checkbox" ${completed ? "checked" : ""} />
<span>${content}</span>
<button>X</button>
</li>`
)
.join("");
};

const setTodos = (newTodos) => {
todos = newTodos;
render(todos);
};

const fetchTodos = () => {
setTodos([
{ id: 3, content: "일찍 일어나기", completed: false },
{ id: 2, content: "공부하기", completed: false },
{ id: 1, content: "저녁먹기", completed: false },
]);
};

const addTodo = (content) => {
todos = [{ id: generateNextTodoId(), content, completed: false }, ...todos];
setTodos(todos);
};

const toggleCompleted = (id) => {
todos = todos.map((todo) =>
todo.id === +id ? { ...todo, completed: !todo.completed } : todo
);
setTodos(todos);
};

const removeTodo = (id) => {
todos = todos.filter((todo) => todo.id !== +id);
setTodos(todos);
};

window.addEventListener("DOMContentLoaded", () => {
fetchTodos();
});

$todoInput.addEventListener("keyup", (e) => {
const content = $todoInput.value.trim();
if (e.key !== "Enter" || content === "") return;
addTodo(content);
$todoInput.value = "";
});

$todoList.addEventListener("change", (e) => {
toggleCompleted(e.target.parentNode.id);
});

$todoList.addEventListener("click", (e) => {
if (!e.target.matches("button")) return;

removeTodo(e.target.parentNode.id);
});
  1. 화면을 나타내는 부분을 View

  2. 상태(데이터)와 상태를 변경하는 메서드를 Model

  3. 화면으로 부터 받은 이벤틑 객체를 가지고 Model의 함수를 호출하여 상태를 변경하는 Controller

크게 3가지 역할로 구분하여 애플리케이션을 바라보자.

단, 너무 이 틀에 강박관념을 갖지 말길 바란다. 이러한 큰 틀이 있는 것일 뿐 100% 이 틀에 맞게 구분하는 것을 불가능하다.