본문 바로가기

Dev.Project/Todolist_project

[TodoMVC] Step 7. ECMAScript2015 Refactoring

Step 7. ECMAScript 2015 로 Refactoring하기

Intro
ES6 문법이 자리를 잡아가면서, 그 필요성이 증대됬습니다.
이를 연습하기 위해 VanilaJS TodoMVC를 ES6 문법에 맞게 Refactoring 해봤습니다.
이전의 포스팅 단계로 봤을 때, Step 3. RemoveItem 에 해당하는 단계까지만 진행해봤습니다.
그 다음 단계는 똑같은 refactoring의 반복이기에 생략했습니다.
기존의 코드와 비교하면서 보시면 더 이해가 잘 되실겁니다.
문법적은 내용에 대한 설명은 이 포스팅에서는 생략하겠습니다.
아직 ES6 문법이 익숙하지 않으신 분들은 아래 포스팅을 통해 확인하는 것도 좋을 것 같습니다.




0단계. 개발환경 Setting



1단계. app.js 파일을 작성한다.
1-1. 모듈로 model, view, controller, template, storage 파일 import 하기
code>
1
2
3
4
5
import Storage from './storage.js';
import Model from './model.js';
import Template from './template.js';
import View from './view.js';
import Controller from './controller.js';
cs


1-2. App class 생성하고 constructor에 import한 클래스들 생성하기
code>
1
2
3
4
5
6
7
8
9
10
class App{
    constructor() {
        this.storage = new Storage();
        this.model = new Model(this.storage);
        this.template = new Template();
        this.view = new View(this.template);
        this.controller = new Controller(this.model, this.view);
    }
}
new App();
cs




2단계. controller.js 파일을 작성한다.
2-1. Controller 클래스 만들고 constructor 생성하기
code>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default class Controller {
    constructor(model, view) {
        this.model = model;
        this.view = view;
 
        this.view.bind('newTodo', (title) => {
            this.addItem(title);
        });
 
        this.view.bind('itemRemove', (item) => {
            this.removeItem(item.id);
        });
 
        this.showAll();
    }
}
cs


2-2. 각종 메소드 생성하기
code>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
showAll(){
    this.model.read((data) => {
        this.view.render('showEntries', data);
    });
}
 
addItem(title){
    if(title.trim() === ''){
        return;
    }
    this.model.create(title, () => {
        this.view.render('clearNewTodo', title);
    });
    this.showAll();
}
 
removeItem(id){
    this.model.remove(id, () => {
        this.view.render('removeItem', id);
    });
}
cs




3단계. model.js 파일을 작성한다.
3-1. Model 클래스 만들고 constructor 생성하기
code>
1
2
3
4
5
export default class Model {
    constructor(storage) {
        this.storage = storage;
    }
}
cs

3-2. 각종 메소드 생성하기
code>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
create(title = '', callback = function(){}){
    let newItem = {
        title: title.trim(),
        completed: false
    };
    this.storage.save(newItem, null ,callback);
}
 
read(callback){
    return this.storage.findAll(callback);
}
 
remove(id, callback){
    this.storage.remove(id, callback);
}
cs



4단계. storage.js 파일을 작성한다.
4-1. Storage 클래스 만들고 constructor 생성하기
code>
1
2
3
4
5
6
7
8
9
10
11
export default class Storage {
    constructor(name){
        this._dbName = name;
        if(!localStorage[name]){
            let data = {
                todos: []
            };
            localStorage[name= JSON.stringify(data);
        }
    }
}
cs


4-2. 각종 메소드 생성하기
code>
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
findAll(callback = function(){}){
    callback.call(this, JSON.parse(localStorage[this._dbName]).todos);
}
 
save(updateData, id, callback = function(){}){
    let data = JSON.parse(localStorage[this._dbName]);
    let todos = data.todos;
 
    if(id){
        for(var i = 0; i < todos.length; i++){
            if(todos[i].id === id){
                for(var key in updateData){
                    todos[i][key] = updateData[key];
                }
                break;
            }
        }
        localStorage[this._dbName] = JSON.stringify(data);
        callback.call(this, todos);
    } else {
        updateData.id = new Date().getTime();
 
        todos.push(updateData);
        localStorage[this._dbName] = JSON.stringify(data);
        callback.call(this, [updateData]);
    }
}
 
remove(id, callback){
    let data = JSON.parse(localStorage[this._dbName]);
    let todos = data.todos;
 
    for(var i = 0; i < todos.length; i++){
        if(todos[i].id === id){
            todos.splice(i, 1);
            break;
        }
    }
    localStorage[this._dbName] = JSON.stringify(data);
    callback.call(this, todos);
}
cs




5단계. view.js 파일을 작성한다.
5-1. View 클래스 만들고 constructor 생성하기
code>
1
2
3
4
5
6
7
8
export default class View {
    constructor(template) {
        this.template = template;
 
        this.$todoList = document.getElementById('todo-list');
        this.$newTodo = document.getElementById('new-todo');
    }
}
cs


5-2. 각종 메소드 생성하기
code>
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
render(viewCmd, data){
    let viewCommands = {
        showEntries : () => {
            this._addItem(data);
        },
        clearNewTodo : () => {
            this.$newTodo.value = '';
        },
        removeItem : () => {
            this._removeItem(data);
        }
    };
    viewCommands[viewCmd]();
}
 
bind(event, handler){
    if(event === 'newTodo'){
        let temp = this.$newTodo;
        temp.addEventListener('change', () => {
            handler(this.$newTodo.value);
        });
    } else if(event === 'itemRemove'){
        let todo = this.$todoList;
        todo.addEventListener('click', (event=> {
            let target = event.target;
            if(target.className === 'destroy'){
                handler({id:this._getItemId(target.parentNode, 'li')});
            }
        });
 
    }
}
 
_addItem(id){
    this.$todoList.innerHTML = this.template.insert(id);
}
 
_removeItem(id){
    let elem = document.querySelector('[data-id="' + id + '"]');
    if(elem){
        this.$todoList.removeChild(elem);
    }
}
 
_getItemId(element, tagName){
    let li;
    if(element.parentNode.tagName.toLowerCase() === tagName.toLowerCase()){
        li = element.parentNode;
    }
    return parseInt(li.dataset.id, 10);
}
cs


6단계. template.js 파일을 작성한다.
2-1. Template 클래스 만들기

code>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default class Template{
    constructor(){
        this.defaultTemplate =
            '<li data-id="{{id}}" class="{{completed}}">' +
                '<div class="view">' +
                    '<input class="toggle" type="checkbox" {{checked}}>' +
                    '<label>{{title}}</label>' +
                    '<button class="destroy"></button>' +
                '</div>' +
            '</li>';
 
 
    }
}
cs


2-2. constructor 와 각종 메소드 생성하기

code>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
insert(data){
    let view = '';
    for(var i = 0; i < data.length; i++){
        let template = this.defaultTemplate;
        let completed = '';
        let checked = '';
 
        if(data[i].completed){ //data[i].completed's default value = false
            completed = 'completed';
            checked = 'checked';
        }
 
        template = template.replace('{{id}}', data[i].id);
        template = template.replace('{{title}}', data[i].title);
        template = template.replace('{{completed}}', completed);
        template = template.replace('{{checked}}', checked);
 
        view = view + template;
    }
    return view;
}
cs




-end-


완성된 코드

>> Gitbub Address >>