[Vue.js] Todo App - 어플리케이션 구조 개선

Yujin Lee·2021년 5월 20일
0

Vue.js

목록 보기
16/18
post-thumbnail

1. 현재 애플리케이션 구조의 문제점

문제점

각각의 컴포넌트에서만 사용할 수 있는 뷰 데이터 속성(newTodoItem, todoItems)을 갖고 있다.
만약 모든 컴포넌트가 '같은 데이터 속성'을 조작한다면 화면을 매번 새로 고침해야 하는 문제점을 해결할 수 있다.

같은 데이터 속성을 사용하기 위해 최상위(루트) 컴포넌트인 App 컴포넌트에 todoItems라는 데이터를 정의하고(App을 컨테이너처럼 생각하자), 하위 컴포넌트 TodoListprops로 전달한다.




2. [리팩토링] 할 일 목록 표시 기능

1)

App.vue가 생성되자 마자 배열을 담을 수 있도록
TodoList.vue -> App.vue로 옮김

App.vue

<template>
  <div id="app">
  ...생략...
    <!-- <TodoList v-bind:내려보낼 프롭스 속성이름="현재 위치의 컴포넌트 데이터 속성"></TodoList> -->
    <TodoList v-bind:propsdata="todoItems"></TodoList>
  </div>
</template>

<script>
export default {
  data: function() { //옮김
    return {
      todoItems: []
    }
  },
  created: function() { //옮김
    if (localStorage.length > 0) {
      for (var i = 0; i < localStorage.length; i++) {
        if (localStorage.key(i) !== 'loglevel:webpack-dev-server') {
          this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
        }
      }
    }
  },
  components: {
    'TodoHeader': TodoHeader,
    'TodoInput': TodoInput,
    'TodoList': TodoList,
    'TodoFooter': TodoFooter
  }
}
</script>

2)

TodoList로 데이터를 내려보내기 위한 propsdata 생성

TodoList.vue

<template>
  <section>
    <ul>
      <li v-for="(todoItem, index) in propsdata" class="shadow" v-bind:key="todoItem.item">
      <!-- propsdata로 변경 -->
...생략...
</template>

<script>
export default {
  props: ['propsdata'],
  methods: {
    removeTodo: function(todoItem, index) {
      this.todoItems.splice(index, 1);
      localStorage.removeItem(todoItem);
    },
    toggleComplete: function(todoItem) {
      todoItem.completed = !todoItem.completed;
      localStorage.removeItem(todoItem.item);
      localStorage.setItem(todoItem.item, JSON.stringify(todoItem));
    }
  }
}
</script>

원랜 localhost:8080을 들어갔을 때 localstorage에 들어 있는 내용이 바로 화면에 띄워지지않았다.
하지만 이젠 localhost:8080를 들어가자 마자 항목을 띄운다.




3. [리팩토링] 할 일 추가 기능

할 일을 추가해도 화면에 바로 띄워지지 않고 새로 고침을 해야하는 문제가 있다.

1)

addOneItem라는 이름으로 method를 하나 생성하고
App.vue

<template>
  <div id="app">
  ...생략...
  <!-- <TodoInput v-on:하위 컴포넌트에서 발생시킨 이벤트 이름="현재 컴포넌트에서 메서드 명"></TodoInput> -->
    <TodoInput v-on:addTodoItem="addOneItem"></TodoInput> 
  </div>
</template>

2)

TodoInput.vue 안에 있던 내용을 App.vue로 옮긴다.

App.vue

<script>
...
export default {
  ...,
  methods: {
    addOneItem: function(todoItem) {
      var obj = {completed: false, item: todoItem};
      localStorage.setItem(todoItem, JSON.stringify(obj));
      this.todoItems.push(obj);
    }
  },
  created: function() {
    if (localStorage.length > 0) {
      for (var i = 0; i < localStorage.length; i++) {
        if (localStorage.key(i) !== 'loglevel:webpack-dev-server') {
          this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
        }
      }
    }
  },
  ...
}
</script>

3)

맨 위 코드에서 하위 컴포넌트에서 발생시킨 이벤트 이름addTodoItem이라고 명명했으니

TodoInput.vue의 메서드 내용을 다음과 같이 매칭한다.

TodoInput.vue

<script>
export default {
  ...,
  
  methods: {
    addTodo: function() {
      if (this.newTodoItem !== '') {
        // this.$emit('이벤트 이름', 인자1, 인자2, ...);
        this.$emit('addTodoItem', this.newTodoItem)
        this.clearInput();
      }
    },
    ...
  }
}
</script>

이제 새로운 항목을 추가하는 버튼을 누르면 새로 고침하지 않아도 바로 화면에 뜬다.




4. [리팩토링] 할 일 삭제 기능

App.vue

<template>
  <div id="app">
    ...
    <TodoList v-bind:propsdata="todoItems" v-on:removeItem="removeOneItem"></TodoList>
    ...
  </div>
</template>

<script>
export default {
  ...,
  methods: {
    addOneItem: function(todoItem) {
     ...
    },
    removeOneItem: function(todoItem, index) {
      this.todoItems.splice(index, 1); //특정 index에서 하나를 지울 수 있음
      localStorage.removeItem(todoItem.item);
    }
  },
  ...
</script>

TodoList.vue에서 App.vue로 옮기는 코드

this.todoItems.splice(index, 1); //특정 index에서 하나를 지울 수 있음
localStorage.removeItem(todoItem);

TodoList.vue

<script>
export default {
  ...,
  methods: {
    removeTodo: function(todoItem, index) { 
      this.$emit('removeItem', todoItem, index);
    },
  ...
</script>

이젠 휴지통버튼을 누르면 localstorage에서 내용이 삭제되고 화면에서도 바로 사라진다.




5. [리팩토링] 할 일 완료 기능

TodoList.vue에서 App.vue로 옮길 코드

todoItem.completed = !todoItem.completed;
localStorage.removeItem(todoItem.item);
localStorage.setItem(todoItem.item, JSON.stringify(todoItem));

App.vue

<template>
  <div id="app">
  ...
    <!-- <TodoList v-bind:내려보낼 프롭스 속성이름="현재 위치의 컴포넌트 데이터 속성"></TodoList> -->
    <TodoList v-bind:propsdata="todoItems" v-on:removeItem="removeOneItem" v-on:toggleItem="toggleOneItem"></TodoList>
    <TodoFooter></TodoFooter>
  </div>
</template>

<script>
export default {
  ...,
  methods: {
    addOneItem: function(todoItem) {
      ...
    },
    removeOneItem: function(todoItem, index) {
      ...
    },
    toggleOneItem: function(todoItem, index) {
      //todoItem.completed = !todoItem.completed;
      this.todoItems[index].completed = !this.todoItems[index].completed;
      localStorage.removeItem(todoItem.item);
      localStorage.setItem(todoItem.item, JSON.stringify(todoItem));
    }
  },
  ...
}
</script>

TodoList.vue

<script>
export default {
  ...,
  methods: {
    removeTodo: function(todoItem, index) { 
      ...
    },
    toggleComplete: function(todoItem) {
      this.$emit('toggleEvent', todoItem, index);
    }
  }
}
</script>




6. [리팩토링] 할 일 모두 삭제 기능

TodoFooter.vue에서 App.vue로 옮길 코드

localStorage.clear();

App.vue

<script>
export default {
  ...,
  methods: {
    addOneItem: function(todoItem) {
    ...
    },
    removeOneItem: function(todoItem, index) {
    ...
    },
    toggleOneItem: function(todoItem, index) {
    ...
    },
    clearAllItems: function() {
      localStorage.clear();
      this.todoItems = []; //다시 빈 배열로 만들기
    }
  },
  ...
}
</script>

TodoFooter.vue

<script>
export default {
  methods: {
    clearTodo: function() {
      this.$emit('clearAll');
    }
  }
}
</script>
profile
I can be your Genie🧞‍♀️ How ‘bout Aladdin? 🧞‍♂️

0개의 댓글