개요
모던 자바스크립트의 구조를 학습하고 컴포넌트 기반의 코드를 작성한다.
목표
순수 Javascript로 작성된 Todo List 어플리케이션을 작성한다.
(이 글은 Next Step에서 진행한 js-todo-list-step을 따라 작성되었습니다.)
전체 코드는 여기에서 보실 수 있습니다.
여정
삭제 기능 만들기
조회 기능과 다르게
삭제 기능은 버튼을 눌렀을 때 이벤트로 동작한다.
아래와 같이 이벤트를 정의해주고 Reducer를 호출해준다.
// TodoList.js
setEvent () {
this.onClickDeleteButton();
}
onDoubleClickTodoItem() {
this.addEvent('dblclick', 'li', (event) => {
const todoItem = this.getTargetTodoItem(event);
todoItem.classList.add(TODO_STATUS.EDITING);
})
}
deleteTodoItem(todoItem) { store.dispatch(deleteTodo(todoItem.dataset.id)); }
getTargetTodoItem(event) { return event.target.closest('li'); } // 편의상 만든 함수
그간 고생해서 작성한 컴포넌트와 Reducer에게
책임을 넘겨줬기 때문에 간결하게 작성될 것이다!
user에서 작성한 Reducer와 크게 다르지 않기 때문에
Reducer 코드는 생략한다.
다음은 이번 편의 하이라이트인 편집 기능이다!
편집 기능 만들기
편집 기능을 적용하기 위해서는 HTML 템플릿에 한가지 트릭이 필요하다.
현재 입력된 label 태그와 동일한 모양의 Input 태그를 숨겨놓아야 한다.
그리고 편집 시작 이벤트(=더블 클릭)가 발생하면
label을 숨김 처리, Input을 노출시키면 된다.
또한, 편집을 완료하거나, 취소할 수도 있어야 한다.
이를 대략적으로 표현하면 아래와 같다.
// TodoList.js
const TodoItem = (todo) => {
const { id, content, completed } = todo;
return `
<li data-id="${id}">
<div class="view">
...
</div>
<input class="edit" value=${content} /> // <<<< 이부분이 추가됨
</li>
`;
}
...
setEvent () {
...
this.onDoubleClickTodoItem(); // 편집시작 이벤트 정의
this.onEditTodoItem(); // 편집중일때 이벤트 정의
}
...
onDoubleClickTodoItem(){
...
}
onEditTodoItem() {
...
}
먼저, 편집시작 이벤트(=더블클릭)를 좀 더 자세히 살펴보자.
// TodoList.js
onDoubleClickTodoItem() {
this.addEvent('dblclick', 'li', (event) => {
const todoItem = this.getTargetTodoItem(event);
todoItem.classList.add(TODO_STATUS.EDITING);
})
}
로직은 간단하다.
타겟이 되는 TodoItem을 찾아서
클래스에 편집 상태(EDITING)를 부여하는 일이다.
참고로 편집 상태는 이런식으로 정의해놓았다.
// constans.js
const TODO_STATUS = Object.freeze({
UPDATABLE: 'updatable',
EDITING: 'editing',
COMPLETED: 'completed'
})
그러면 CSS에 정의되어 있는 클래스중
editing에 해당하는 상태가 되어,
label을 숨김처리, input은 노출처리를 할 수 있다.
// css.js
...
.todo-list li.editing .edit { // input에 붙은 클래스
display: block;
width: calc(100% - 43px);
padding: 12px 16px;
margin: 0 0 0 43px;
}
.todo-list li.editing .view { // label에 붙은 클래스
display: none;
}
클래스 부여만 하면 되니 어렵지 않았다!
다음은 편집중일 때 이벤트를 구현해보자.
onEditTodoItem() {
this.addEvent('keydown', '.edit', (event) => {
const todoItem = this.getTargetTodoItem(event);
const editingTodoItem = event.target;
const originValue = editingTodoItem.dataset.origin;
switch (event.key) {
case 'Escape':
editingTodoItem.value = originValue;
todoItem.classList.remove(TODO_STATUS.EDITING);
break;
case 'Enter':
this.updateTodoContent(todoItem, editingTodoItem.value);
todoItem.classList.remove(TODO_STATUS.EDITING);
this.render(); // 데이터가 변경되었으니 다시 그려준다!
break;
}
});
}
자.. 코드가 길어진다!
큰 그림만 보면 이런식이다.
- 타겟 TodoItem을 찾아서
- 원래값을 저장하고
- 엔터면, 새 값으로 Update → Render
- ESC면, 원래값으로 Rollback
- 편집 상태를 변경
연산에 필요한 Reducer 함수는
TodoItem에 새로운 값을 덮어씌우는 책임을 맡게 된다.
때문에, 대상의 ID와 덮어씌울 값이 필요했다.
따라서, 새로 필요한 Reducer 함수는 이런 모양이 될 것이다.
// todo/creator.js
const updateTodoContent = (id, content) => {
return {
type: UPDATE_TODO_CONTENT,
payload: {
id,
content,
},
}
}
// reducer.js
const reducer = (state = initialState, action = {}) => {
switch (action.type) {
...
case UPDATE_TODO_CONTENT:
return {
...state,
todos: state.todos.map(todo => {
if (todo.id === action.payload.id) {
todo.content = action.payload.content;
}
return todo;
})
}
...
자, 이것으로 편집 기능도 완성했다.
마무리
한번 컴포넌트를 작성하고 나면,
다른 기능을 작성하는 일은 비슷한 결을 지니는 것 같다.
아무리 복잡한 기능이라도, 하나씩 만들어나가면 결국 해낼 수 있다.
다음은 Todo 컴포넌트의 마지막 기능인 Count, Filtering을 작성해보도록 하자
'Frontend' 카테고리의 다른 글
모던 자바스크립트 TodoList - User별 TodoList (0) | 2023.05.21 |
---|---|
모던 자바스크립트 TodoList - Todo Component (3) (0) | 2023.05.08 |
모던 자바스크립트 TodoList - Todo Component (1) (0) | 2023.05.02 |
모던 자바스크립트 TodoList - User Component (0) | 2023.04.21 |
모던 자바스크립트 TodoList - Store & Reducer (0) | 2023.04.17 |