관리 메뉴

열심히 일한 당신 떠나라

코딩 자율학습단(Vue.js 프런트엔드 개발 입문) 8일차 본문

정보교육/프런트엔드

코딩 자율학습단(Vue.js 프런트엔드 개발 입문) 8일차

@thiskorea 2024. 6. 27. 12:31

6장 실습: 할 일 관리 앱 만들기

o 할 일 관리 앱의 ui 구성

HTML 코드 작성, css 코드 작성, 컴포넌트 분리

o 할 일 관리 앱의 기능 구현

- 할 일 목록 입력받기

데이터 정의, v-model 디렉티브로 입력창과 양방향 데이터 바인딩

버튼 클릭이나 엔터를 통해 사용자가 어떤 값을 입력했는지 확인 이벤트 등록

입력한 값을 부모 컴포넌트로 전달

- 할 일 목록 필터링하기

할 일 목록을 '전체'와 '완료'로 필터링

current 데이터를 초기값으로 'all' updateTab을 이용해서 이벤트 발신

filter를 통해서 completed라면 속성 값이 true인 것만 보여줌. 

- 할 일 목록 출력하기

computedTodo 데이터를 props 옵션 속성으로 받는다.

배열이므로 v-for 데릭티브로 할 일 목록을 반복

delete, update 함수 연결

// App.vue

<script>
import TodoHeader from "@/components/TodoHeader.vue";
import TodoList from "@/components/TodoList.vue";
import TodoInput from "@/components/TodoInput.vue";
export default {
  components: {
    TodoHeader,
    TodoList,
    TodoInput,
  },
  data() {
    return {
      todo: [],
      current: "all",
    };
  },
  computed: {
    computedTodo() {
      if (this.current === "all") {
        return this.todo;
      } else {
        return this.todo.filter((v) => v.completed);
      }
    },
  },
  methods: {
    addTodo(inputMsg) {
      const item = {
        id: Math.random(), // 고유한 값
        msg: inputMsg, // 할 일 텍스트
        completed: false, // 할 일 완료 여부
      };
      this.todo.push(item);
    },
    updateTab(tab) {
      this.current = tab;
    },
    deleteTodo(id) {
      this.todo = this.todo.filter((v) => v.id !== id);
    },
    updateTodo(id) {
      this.todo = this.todo.map((v) =>
        v.id === id ? { ...v, completed: !v.completed } : v
      );
    },
  },
};
</script>
<template>
  <div class="todo">
    <TodoHeader :current @update-tab="updateTab" />
    <TodoList
      :computed-todo="computedTodo"
      @delete-todo="deleteTodo"
      @update-todo="updateTodo"
    />
    <TodoInput @add-todo="addTodo" />
  </div>
</template>
// TodoHeader.vue

<script>
export default {
  props: {
    current: {
      type: String,
      default() {
        return "all";
      },
    },
  },
  emits: ["update-tab"],
  methods: {
    updateTab(tab) {
      this.$emit("update-tab", tab);
    },
  },
};
</script>

<template>
  <div class="todo__title">
    <h1 class="todo__text">Todo List</h1>
    <ul class="todo__tab">
      <li
        :class="{ 'todo__tab--active': current === 'all' }"
        @click="updateTab('all')"
      >
        전체
      </li>
      <li
        :class="{ 'todo__tab--active': current === 'completed' }"
        @click="updateTab('completed')"
      >
        완료
      </li>
    </ul>
  </div>
</template>
// TodoInput.vue

<script>
export default {
  data() {
    return {
      inputMsg: "", // 데이터 정의
    };
  },
  emits: ["addTodo"],
  methods: {
    addTodo() {
      this.$emit("addTodo", this.inputMsg); // 부모 컴포넌트 이벤트 호출
      this.inputMsg = ""; // 입력 데이터 초기화
    },
  },
};
</script>
<template>
  <div class="todo__input">
    <input
      v-model="inputMsg"
      type="text"
      class="todo__input-text"
      placeholder="할 일을 입력하세요."
      @keydown.enter="addTodo"
    />
    <button class="todo__input-btn" @click="addTodo">등록</button>
  </div>
</template>
// TodoList.vue

<script>
export default {
  props: {
    computedTodo: {
      type: Array,
      default() {
        return [];
      },
    },
  },
  methods: {
    deleteTodo(id) {
      this.$emit("delete-todo", id);
    },
    updateTodo(id) {
      this.$emit("update-todo", id);
    },
  },
};
</script>
<template>
  <div class="todo__list">
    <div
      v-for="item in computedTodo"
      :key="item.id"
      class="todo__item"
      :class="{ 'todo__item--completed': item.completed }"
    >
      <input
        type="checkbox"
        :id="`chk${item.id.toString()}`"
        :checked="item.completed"
        @click="updateTodo(item.id)"
      />
      <label
        :for="`chk${item.id.toString()}`"
        class="todo__checkbox-label"
      ></label>
      <span class="todo__item-text">{{ item.msg }}</span>
      <span
        class="material-symbols-outlined todo__delete-icon"
        @click="deleteTodo(item.id)"
        >delete</span
      >
    </div>
    <div v-if="computedTodo.length === 0" class="todo__item--no">
      <p>할 일이 없습니다.</p>
    </div>
  </div>
</template>