본문 바로가기
Dev/Vue.js

[Vue.js] Todo App 만들기

by dev_jsk 2020. 9. 10.
728x90
반응형

Todo App

간단한 일정관리 App으로 기본 CRUD를 구현하기 적합하다.

 

구조

<Root>
    <App>
        <TodoHeader>
        <TodoInput>
        <TodoList>
        <TodoFooter>

컴포넌트 생성 및 등록하기

1. src/components에 필요한 컴포넌트용 vue파일 생성

2. App.vue에 컴포넌트용 vue파일 import

// App.vue

<template>
  <div id="app">
    <TodoHeader></TodoHeader>
    <TodoInput></TodoInput>
    <TodoList></TodoList>
    <TodoFooter></TodoFooter>
  </div>
</template>

<script>
import TodoHeader from './components/TodoHeader.vue'
import TodoInput from './components/TodoInput.vue'
import TodoList from './components/TodoList.vue'
import TodoFooter from './components/TodoFooter.vue'

export default {
  components: {
    'TodoHeader': TodoHeader,
    'TodoInput': TodoInput,
    'TodoList': TodoList,
    'TodoFooter': TodoFooter
  }
}
</script>

<style>
body {
  text-align: center;
  background-color: #f6f6f6;
}
input {
  border-style: groove;
  width: 200px;
}
button {
  border-style: groove;
}
.shadow {
  box-shadow: 5px 10px 10px rgba(0, 0, 0, 0.03);
}
</style>

파비콘, 반응형 웹, 폰트, 아이콘 태그 설정하기

// index.html

...
<head>
    <!-- 반응형 태그 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- favicon 설정 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- fontawesome -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css"/>
    <!-- google font -->
    <link href="https://fonts.googleapis.com/css2?family=Ubuntu&display=swap" rel="stylesheet">
</head>
...

TodoHeader 구현

기능

  • App 제목
// TodoHeader.vue

<template>
  <div>
      <h1>TODO it!</h1>
  </div>
</template>

<style scoped>
/* scoped 선언 시 해당 컴포넌트에서만 유효하다는 선언 */
h1 {
    color: #2F3B52;
    font-weight: 900;
    margin: 2.5rem 0 1.5rem;
}
</style>

TodoInput 구현

기능

  • 할 일 추가 (버튼 클릭, 엔터키 이벤트)
  • localStorage에 객체로 할 일, 완료여부 저장
  • 저장 후 입력란 비우기
// TodoInput.vue

<template>
  <div class="inputBox shadow">
      <!-- enter 입력 시 addTodo 함수 실행 -->
      <input type="text" v-model="newTodoItem" v-on:keyup.enter="addTodo">
      <!-- 클릭 시 addTodo 함수 실행 -->
      <span class="addContainer" v-on:click="addTodo">
          <i class="fas fa-plus addBtn"></i>
      </span>
  </div>
</template>

<script>
export default {
    data: function() {
        return {
            newTodoItem: ''
        }
    },
    methods: {
        addTodo: function() {
            // 입력값이 공백이 아닐 때
            if(this.newTodoItem !== '') {
                // 완료여부, 아이템 포함 객체
                var obj = {completed: false, item: this.newTodoItem};
                // 저장하는 로직 수행
                // logcalStorage.setItem(key, value);
                // JSON.stringify() 객체를 String으로 변환
                localStorage.setItem(this.newTodoItem, JSON.stringify(obj));
                this.clearInput();
            }
        },
        clearInput: function() {
            // input 값 비우기
            this.newTodoItem = '';
        }
    }
}
</script>

<style scoped>
input:focus {
    outline: none;
}
.inputBox {
    background: white;
    height: 50px;
    line-height: 50px;
    border-radius: 5px;
}
.inputBox input {
    border-style: none;
    font-size: 0.9rem;
}
.addContainer {
    float: right;
    background: linear-gradient(to right, #6478fb, #8763fb);
    display: block;
    width: 3rem;
    border-radius: 0 5px 5px 0;
}
.addBtn {
    color: white;
    vertical-align: middle;
}
</style>

LocalStorage 확인

TodoList 구현

기능

  • localStorage에 저장된 할 일 목록 표시
  • 체크 클릭 시 완료여부 변경
  • 휴지통 클릭 시 할 일 삭제 (localStorage와 화면 스크립트 둘 다)
// TodoList.vue

<template>
  <div>
      <ul>
          <!-- v-for 사용 시 v-bind:key 사용하지 않아도 무방(VSCode에서는 에러문구 출력) -->
          <li v-for="(todoItem, index) in todoItems" v-bind:key="todoItem.item" class="shadow">
              <!-- v-bind:class="{checkBtnCompleted: todoItem.completed}"는 todoItem.completed의 true/false에 따라 동적으로 class 추가/삭제 기능 -->
              <i class="checkBtn fas fa-check" v-bind:class="{checkBtnCompleted: todoItem.completed}" v-on:click="toggleComplete(todoItem)"></i>
              <span v-bind:class="{textCompleted: todoItem.completed}">{{ todoItem.item }}</span>
              <span class="removeBtn" v-on:click="removeTodo(todoItem, index)">
                  <i class="fas fa-trash"></i>
              </span>
          </li>
      </ul>
  </div>
</template>

<script>
export default {
    data: function() {
        return {
            todoItems: []
        }
    },
    methods: {
        removeTodo: function(todoItem, index) {
            // localStorage에 저장된 값 지우기
            // localStorage.removeItem(key);
            localStorage.removeItem(todoItem);

            // localStroage영역만 지우는게 아니라 화면 스크립트 영역도 지워야 한다
            // splice(배열의 변경을 시작할 인덱스, 배열에서 제거할 요소의 수, [배열에 추가할 요소])
            this.todoItems.splice(index, 1);
        },
        toggleComplete: function(todoItem) {
            // completed 변경
            todoItem.completed = !todoItem.completed;
            // localStorage에 update가 없기 때문에
            // 기존걸 지우고
            localStorage.removeItem(todoItem.item);
            // 변경된 정보로 다시 저장
            localStorage.setItem(todoItem.item, JSON.stringify(todoItem));
        }
    },
    // 뷰 라이프사이클 설정
    created: function() {
        // localStorage에 저장된 값 꺼내 todoItems에 넣기
        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>

<style scoped>
ul {
    list-style-type: none;
    padding-left: 0px;
    margin-top: 0;
    text-align: left;
}
li {
    display: flex;
    min-height: 50px;
    height: 50px;
    line-height: 50px;
    margin: 0.5rem 0;
    padding: 0 0.9rem;
    background: white;
    border-radius: 5px;
}
.removeBtn {
    margin-left: auto;
    color: #de4343;
}
.checkBtn {
    line-height: 45px;
    color: #62acde;
    margin-right: 5px;
}
.checkBtnCompleted {
    color: #b3adad;
}
.textCompleted {
    text-decoration: line-through;
    color: #b3adad;
}
</style>

TodoFooter 구현

기능

  • localStorage에 저장된 할 일 전체 제거
// TodoFooter.vue

<template>
  <div class="clearAllContainer">
      <span class="clearAllBtn" v-on:click="clearTodo">Clear All</span>
  </div>
</template>

<script>
export default {
    methods: {
        clearTodo: function() {
            // localStorage 비우기
            localStorage.clear();
        }
    }
}
</script>

<style scoped>
.clearAllContainer {
    width: 8.5rem;
    height: 50px;
    line-height: 50px;
    background-color: white;
    border-radius: 5px;
    margin: 0 auto;
}
.clearAllBtn {
    color: #e20303;
    display: block;
}
</style>

완료 화면

728x90
반응형

'Dev > Vue.js' 카테고리의 다른 글

[Vue.js] Todo App 사용자 경험 개선하기  (0) 2020.09.10
[Vue.js] Todo App 개선하기  (0) 2020.09.10
[Vue.js] 싱글 파일 컴포넌트  (0) 2020.09.09
[Vue.js] Vue CLI  (0) 2020.09.09
[Vue.js] 템플릿 문법 - 실전  (0) 2020.09.08

댓글