Meme's IT

[Vue] Pinia 써보기 - Todo List 만들기 본문

FrontEnd/Vue

[Vue] Pinia 써보기 - Todo List 만들기

Memez 2023. 11. 13. 13:48

0. 틀만들기

TodoListItem.vue, TodoList.vue, TodoForm.vue 컴포넌트 만들고

TodoList.vue만 다음과 같이 작성, 나머지는 기본틀에 template에 파일 제목만

// TodoList.vue
<template>
    <div>
        <TodoListItem />
    </div>
</template>

<script setup>
import TodoListItem from '@/components/TodoListItem.vue';
</script>

<style scoped>

</style>
// App.vue
<template>
  <div>
    <h1>Todo PJT</h1>
    <TodoForm/>
    <TodoList/>
  </div>
</template>

<script setup>
import TodoForm from './components/TodoForm.vue';
import TodoList from './components/TodoList.vue';
</script>


<style scoped>
</style>

 

완료 화면

 

counters.js도 수정해준다

// counters.js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  let id = 0
  const todos = ref([
    {id: id ++, text: 'todo 1', isDone: false},
    {id: id ++, text: 'todo 2', isDone: false},
  ])

  return { todos }
})

 


1. 조회

이 todos를 누가 가져가는게 맞는지 생각해보자

app에는 todoform에서 사용하지 않으므로 굳이 필요없음.

TodoList에서 가져가자

<template>
    <div>
        {{ store.todos }}
        <TodoListItem 
        v-for="todo in store.todos"
        :key="todo.id"
        :todo-data="todo"
        />
    </div>
</template>

<script setup>
import TodoListItem from '@/components/TodoListItem.vue'
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()
</script>

그리고, TodoListItem에서 받아서 써보자

<template>
    <div>
        {{ todoData.text }}
    </div>
</template>

<script setup>
defineProps({
    todoData : Object
})
</script>

 

2. Todo 생성하기

state(todo)에 데이터를 생성하는 것 → action으로 하자

그래서 counter.js에 addTodo라는 action을 추가해줌

const addTodo = function () {
    todos.value.push({
      id: id++,
      // 여기에 사용자 입력 데이터 → TodoForm
      text: todoText,
      isDone: false
    })
  }

 

그리고 TodoForm.vue에서 입력을 받을 form을 만들어줌

<template>
    <div>
        <p>todoForm</p>
        <form @submit.prevent="createTodo()" ref="formElement">
            <input type="text" v-model="todoText">
            <input type="submit">
        </form>
    </div>
</template>

<script setup>
import { useCounterStore } from '@/stores/counter'
import { ref } from 'vue'

const todoText = ref('')
const store = useCounterStore()
const formElement = ref(null)	// form 입력 후 초기화용 (다른 값 들어가도 상관없음)

const createTodo = function () {
    store.addTodo(todoText.value)
    formElement.value.reset()
}
</script>

새롭게 생성 가능

 

3. 삭제하기

todos 조회해서 어떤걸 삭제했는지 찾고, 지우고 지운것을 다시 todos에 할당

todoListItem에서 우선 delete버튼 하나씩 만들어주고

<template>
    <div>
        {{ todoData.text }}
        <button @click="store.deleteTodo(todoData.id)">delete</button>
    </div>
</template>

<script setup>
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()

defineProps({
    todoData : Object
})
</script>

그리고 counter.js에서 지우는 action 만들어주기

const deleteTodo = function (todoId) {
    // todos에서 몇번째가 삭제되었는지 검색
    const idx = todos.value.findIndex((todo) => todo.id === todoId)
    // 배열에서 요소 제거 후 업데이트
    todos.value.splice(idx, 1)
  }

버튼 완성 후 지우기도 가능

 

4. 업데이트

todo의 완료 유무를 표현해보자

항목을 클릭하면 완료를 표시하기 위해 span을 줘서 click에 대한 v-on과 

스타일을 주기위한 class v-bind를 적용

// todoListItem.vue
<template>
    <div>
        <!-- 클릭했을 때 updateTodo함수를 통해 isDone을 바꿔주고,
        v-bind를 통해 클래스로 완료 스타일을 적용 -->
        <span 
        @click="store.updateTodo(todoData.id)"
        :class="{ 'is-done' : todoData.isDone }">
            {{ todoData.text }}
        </span>
        <button @click="store.deleteTodo(todoData.id)">delete</button>
    </div>
</template>

<script setup>
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()

defineProps({
    todoData : Object
})
</script>

<style scoped>
.is-done {
    text-decoration: line-through;
}
</style>

그리고 counter.js에 todoUpdate action을 만들어준다.

const updateTodo = function(todoId) {
    todos.value = todos.value.map((todo) =>{
      if ( todo.id === todoId) {
        todo.isDone = !todo.isDone
      }
      return todo
})

 

 

# computed를 통해 완료된 todo 개수 계산

counter.js에서 완료된 todo의 갯수를 계산해주는 함수를 작성

const updateTodo = function(todoId) {
    todos.value = todos.value.map((todo) =>{
      if ( todo.id === todoId) {
        todo.isDone = !todo.isDone
      }
      return todo
    })
}

그리고 App.vue에서 출력

<h2>완료된 Todo: {{ store.doneTodosCount }}</h2>


# Plugin - persistedstate

Pinia의 플러그인(plugin) 중에 하나

웹 애플리케이션의 상태(state)를 브라우저의 local storage나 session storage에

영구적으로 저장하고 복원하는 기능

 

명령어를 통해 설치

npm i pinia-plugin-persistedstate

 

 

// vue파일에서 import하기
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
// counter.js에서 return 뒤에 ,이후에 추가

	return { ... }
}, { persist: true })

 

이렇게 추가해주면 이제 새로고침해도 안 없어짐!!!

'FrontEnd > Vue' 카테고리의 다른 글

[Vue] Pinia의 구성 요소  (0) 2023.11.13
[Vue] State Management  (0) 2023.11.13
[Vue] Axios  (1) 2023.11.10
[Vue] Router(3) Navigation Guard  (0) 2023.11.09
[Vue] Router(2) 프로그래밍 방식 네비게이션  (0) 2023.11.09