[Vue] Vue3 TodoList

0 0 0

Vue 3로 TodoList를 만들었습니다.

Vue 3는 여기까지 정리하고, 다음 글부터는 Vue 2에 TypeScript를 적용하는 방법을 공부할 예정입니다.


gh-pages : https://kimdongeun12.github.io/vue3-practice/ github : https://github.com/kimdongeun12/vue3-practice

폴더 구조

├─ src
│  ├─ App.vue
│  ├─ main.ts
│  └─ views
│     └─ todo
│        ├─ TodoList.vue // Todo의 View
│        ├─ TodoListWrap.vue // Todo의 List를 담고 있음
│        └─ TodoListWrite.vue // 리스트 작성 , 수정을 담당

폴더 구조는 위와 같습니다.

TodoList.vue

<template>
  <v-container>
    <TodoListWrap
      :Todolists="Todolists"
      @isChecked="isChecked"
      @deleteTodo="deleteTodo"
      @showDialog="showDialog"
    />
    <v-btn
      class="floating"
      icon="mdi-pencil"
      size="small"
      @click="showDialog()"
    />
    <v-dialog v-model="dialog">
      <TodoListWrite
        :dialog="dialog"
        :edits="edits"
        :editTodo="edits.edit ? Todolists[edits.idx] : {}"
        @hideDialog="hideDialog"
        @submitDialog="submitDialog"
        @editSubmitDialog="editSubmitDialog"
      />
    </v-dialog>
  </v-container>
</template>

export default defineComponent({
  ...
  methods: {
    showDialog(idx?: number) {
      this.dialog = true;
      if (idx !== undefined) {
        this.edits.idx = idx;
        this.edits.edit = true;
      } else {
        this.edits.idx = 0;
        this.edits.edit = false;
      }
    }
  },
  ...
});
</script>

<style lang="scss" scoped>
.floating {
  position: fixed;
  right: 24px;
  bottom: 24px;
}
</style>

TodoListWrap.vue

<template>
  <div class="listsWrap">
    <v-card
      class="mx-auto my-list"
      v-for="(item, idx) in Todolists"
      :key="idx"
      :class="{ 'mx-auto active': item?.checked }"
      width="400"
    >
      <template v-slot:title>
        <v-btn
          variant="text"
          icon="mdi-check"
          size="small"
          @click="$emit('isChecked', idx)"
        />
        {{ item?.title }}
      </template>

      <v-card-text> {{ item?.contents }} </v-card-text>
      <v-card-actions class="justify-end">
        <v-btn variant="outlined" @click="$emit('showDialog', idx)">
          수정
        </v-btn>
        <v-btn variant="outlined" @click="$emit('deleteTodo', idx)">
          삭제
        </v-btn>
      </v-card-actions>
    </v-card>
    <div class="text-center" v-if="Todolists?.length === 0">
      할 일 목록이 없습니다.<br />
      우측 아래 버튼을 클릭하여 추가해주세요.
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  name: "TodoListWrap",
  components: {},
  props: {
    Todolists: Object,
  },
  data() {
    return {};
  },
});
</script>
<style lang="scss" scoped>
.listsWrap {
  display: block;
  & > div + div {
    margin-top: 20px;
  }
  .active:deep .v-card-title .v-btn__content {
    color: #ff0000;
  }
}
</style>

작업하면서 느낀 점이 두 가지 있습니다. 첫째, 스타일은 대부분 scoped로 작성하는데 Vuetify 컴포넌트에는 스타일이 적용되지 않을 때가 있습니다. 이때 :deep을 사용하면 해결됩니다. 저는 v-card 안의 체크 아이콘 색상을 바꿀 때 사용했습니다.

둘째, props는 React와 비슷하면서도 다릅니다. :속성명으로 바인딩해 props를 전달하고, 이벤트처럼 함수를 넘길 때는 @이벤트명으로 전달합니다. 자식에서는 emit('이벤트명', 전달할 값)으로 부모에게 알립니다.

아직 양방향 바인딩의 장점을 깊이 체감하지는 못했지만, 단방향으로만 데이터를 전달하는 React보다 처음 입문하기에는 Vue가 더 편하다고 느꼈습니다.

전체 코드는 위 GitHub 저장소에서 확인할 수 있습니다.

댓글 0