Frontend/Vue.js

[Vue CLI] 할 일 관리 앱 만들기(1)

gamzaggang7 2024. 6. 5. 16:48
728x90

프로젝트 생성 및 초기 설정

vue create [프로젝트명]으로 프로젝트 생성

 

awesome icon 사용 (index.html)

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer" />

 

컴포넌트 생성

할 일 관리 앱의 컴포넌트 구조는 헤더, 입력, 목록, 푸터로 구성된다.

 

컴포넌트 폴더에 TodoHeader.vue, TodoInput.vue, TodoList.vue, TodoFooter.vue 파일 생성

<template>
  <div>Header</div>
</template>

<script>
export default {
 
}
</script>

<style>

</style>

 

App.vue에 컴포넌트 등록

import TodoFooter from './components/TodoFooter.vue'
import TodoHeade from './components/TodoHeader.vue'
import TodoInput from './components/TodoInput.vue'
import TodoList from './components/TodoList.vue'

export default {
  components: {
    'Header': TodoHeade,
    'Input': TodoInput,
    'List': TodoList,
    'Footer': TodoFooter
  }
}

 

컴포넌트 태그들을 div#app 태그 안에 추가

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

 

기본 스타일 적용

body {
  text-align: center;
  background-color: #f6f6f8;
}

 

TodoHeader.vue

헤더에는 앱의 이름을 표시한다.

<template>
  <header>
    <h1>TODO</h1>
  </header>
</template>
<style scoped>
h1 {
  font-weight: 200;
}
</style>

scoped는 스타일을 해당 컴포넌트에만 적용하겠다는 의미이다.

 

TodoInput.vue

입력을 받기 위한 input 태그와 텍스트 값을 저장하기 위한 button 태그를 추가한다. 인풋 박스에 텍스트를 입력했을 때 뷰 인스턴스에서 텍스트 값을 인식할 수 있게 v-model 디렉티브와 데이터 속성 newTodoItem을 추가한다.

<template>
  <input type="text" v-model="newTodoItem">
  <button>add</button>
</template>

<script>
export default {
  data() {
    return {
      newTodoItem: ''
    }
  }
}
</script>

인풋 박스에 입력한 값이 뷰에서 잘 인식된다. 이제 입력된 값을 데이터 저장소인 로컬 스토리지에 저장해야 한다. add 버튼을 클릭하면 특정 동작을 수행할 수 있도록 v-on:click에 버튼 이벤트 addTodo를 추가한다. 버튼 이벤트 로직은 methods에서 정의한다.

<template>
  <input type="text" v-model="newTodoItem">
  <button v-on:click="addTodo">add</button>
</template>

<script>
export default {
  data() {
    return {
      newTodoItem: ''
    }
  },
  methods: {
    addTodo() {
      console.log(this.newTodoItem);
    }
  }
}
</script>

버튼이 잘 작동하는지 확인하기 위해 데이터 값을 콘솔로 찍어본다.

버튼 이벤트가 잘 작동하므로 입력값을 로컬 스토리지에 저장한다. 로컬 스토리지에 데이터를 추가하는 setItem() API를 사용한다.

  methods: {
    addTodo() {
      localStorage.setItem(this.newTodoItem, this.newTodoItem)
    }
  }

키와 값 모두 입력값으로 했다.

Application -> Storage -> Local Storage -> http://localhost:8080 에서 로컬 스토리지에 데이터가 저장된 것을 확인한다.

 

입력값이 없을 경우 로컬 스토리지에 데이터가 저장되지 않도록 예외 처리 코드를 추가한다.

methods: {
    addTodo() {
      if (this.newTodoItem !== '') {
        var value = this.newTodoItem && this.newTodoItem.trim()
        localStorage.setItem(value, value)
        this.clearInput()
      }
    },
    clearInput() {
      this.newTodoItem = ''
    }
  }

 

다듬기)

<div class="inputBox">
    <input
      type="text"
      v-model="newTodoItem"
      placeholder="Type what you have to do"
      v-on:keyup.enter="addTodo"
    />
    <span class="addContainer" v-on:click="addTodo">
      <i class="addBtn fas fa-plus" aria-hidden="true"></i>
    </span>
  </div>

- 엔터키를 눌렀을 때로 입력값이 저장되도록 한다.

- <i class = "fas fa-plus">: 어썸 아이콘의 + 아이콘

<style>
input:focus {
  outline: none;
}
.inputBox {
  background-color: #fff;
  height: 45px;
  line-height: 45px;
  border-radius: 5px;
}
.inputBox input {
  border-style: none;
  font-size: 0.9rem;
}
.addContainer {
  float: right;
  width: 3rem;
  background-color: rgb(202, 168, 168);
  border-radius: 0 5px 5px 0;
}
.addBtn {
  color: #fff;
  vertical-align: middle;
}
</style>

728x90

 

TodoList.vue

현재 로컬 스토리지에 저장된 데이터 개수만큼 목록에 추가해 표시한다.

로컬 스토리지의 데이터를 담을 todoItems 데이터 속성을 빈 배열로 선언한다. 그리고 created() 라이프 사이클에 로컬 스토리지의 모든 데이터를 todoItems에 담는 로직을 작성한다. 로컬 스토리지 데이터를 뷰 데이터에 저장한다.

export default {
  data() {
    return {
      todoItems: [],
    };
  },
  created() {
    if (localStorage.length > 0) {
      for (var i = 0; i < localStorage.length; i++) {
        this.todoItems.push(localStorage.key(i));
      }
    }
  },
};

 

v-for 디렉티브로 뷰 데이터 아이템 개수만큼 화면에 표시한다.

  <section>
    <ul>
      <li v-for="(todoItem, index) in todoItems" :key="todoItem">
        {{ todoItem }}
      </li>
    </ul>
  </section>

다듬기)

<section>
    <ul>
      <li v-for="(todoItem, index) in todoItems" :key="todoItem">
        <i class="checkBtn fas fa-check" aria-hidden="true"></i>
        {{ todoItem }}
        <span class="removeBtn" type="button" @click="removeTodo(todoItem, index)">
          <i class="far fa-trash-alt" aria-hidden="true"></i>
        </span>
      </li>
    </ul>
  </section>

@click은 v-on:click과 동일하게 동작한다.

ul {
  list-style-type: none;
  padding-left: 0;
  margin: 0;
  text-align: left;
}
li {
  display: flex;
  min-height: 50px;
  height: 50px;
  line-height: 50px;
  margin-top: 0.5rem;
  padding: 0 0.9rem;
  background: white;
  border-radius: 5px;
}
.checkBtn {
  line-height: 45px;
  color: rgb(49, 132, 63);
  margin-right: 0.9rem;
}
.removeBtn {
  margin-left: auto;
  color: rgb(205, 0, 0);
}

 

할 일 삭제 기능을 추가한다. 할 일 삭제 기능은 선택한 일을 뷰에서 인식 -> 로컬 스토리지에서 삭제 -> 뷰 데이터에서 삭제로 동작한다. 

휴지통의 클릭 이벤트가 잘 동작하고 선택한 일을 뷰에서 제대로 인식하는지 확인한다.

  methods: {
    removeTodo(todoItem, index) {
      console.log(todoItem, index);
    }
  }

이제 선택한 항목을 로컬 스토리지와 뷰 데이터에서 삭제한다.

  methods: {
    removeTodo(todoItem, index) {
      localStorage.removeItem(todoItem)
      this.todoItems.splice(index, 1)
    }
  }

휴지통 아이콘을 클릭하면 해당 항목은 로컬 스토리지에서도 삭제되고 todoItems 배열에서도 삭제된다. 데이터 속성이 변하면 화면에 즉시 반영하는 뷰의 반응성 때문에 배열 요소가 삭제되자마자 뷰에서 자동으로 화면을 다시 갱신한다. 

 

TodoFooter.vue

이제 전체 삭제 버튼을 추가한다.

<template>
  <div class="clearAllContainer">
    <span class="clearAllBtn" @click="clearTodo">Clear All</span>
  </div>
</template>

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

<style scoped>
.clearAllContainer {
  width: 8.5rem;
  height: 50px;
  line-height: 50px;
  background-color: #fff;
  border-radius: 5px;
  margin: 0.5rem auto;
}
</style>

버튼을 클릭하면 로컬 스토리지 데이터는 삭제되지만 화면은 자동으로 갱신되지 않는다. 로컬 스토리지의 데이터만 지우고 목록에 표시되는 데이터를 제거하지 않았기 때문이다. 목록 데이터를 제거하기 위해서는 TodoList 컴포넌트의 데이터에 접근해야 한다.

728x90