Pinia

์ด๋™์–ธยท2024๋…„ 8์›” 23์ผ

new world

๋ชฉ๋ก ๋ณด๊ธฐ
32/62
post-thumbnail

8.23 (๊ธˆ)

1. Loading ๋ชจ๋‹ฌ

๐Ÿ‘‰ ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด api ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ ์‹œ๊ฐ„์ด๊ฑธ๋ฆฌ๋Š”๊ฒƒ์— ๋Œ€ํ•ด loading์ฐฝ์„ ๋„์šฐ๊ธฐ

TodoListComponent.vue

const loading = ref(false) // api๋ฅผ ๊ฐ€์ ธ์˜ฌ๋•Œ ๋กœ๋”ฉ๋ชจ๋‹ฌ

๐Ÿ‘‰ loading ๋ณ€์ˆ˜๋ฅผ ๋ฐ˜์‘ํ˜•์œผ๋กœ ๋งŒ๋“ ๋‹ค

const todoList = ref([]) // ์–ธ์ œ ์˜ฌ์ง€ ๋ชจ๋ฅด๋‹ˆ๊นŒ ref๋กœ ๋งŒ๋“ ๋‹ค

    loading.value = true // api๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋กœ๋”ฉ์ž‘์—…์ด ์‹œ์ž‘๋˜๋ฉด
    const loadPageData = async(page) =>{// api์—์„œ ๋ฐ›์€ res.data์ค‘์—์„œ content๋ผ๋Š” ์†์„ฑ์„ todoList.value์— ์ง‘์–ด๋„ฃ๋Š”๋‹ค. 
    const data = await getList(page)
    todoList.value = data.content //data์˜ content์†์„ฑ์„ ๊ฐ€์ ธ์˜จ๋‹ค.
    loading.value = false // ๋กœ๋”ฉ์ž‘์—…์ด ๋๋‚˜๋ฉด
}

๐Ÿ‘‰ api์„œ๋ฒ„ ํ†ต์‹ ํ•˜๋Š” ์ฝ”๋“œ ๋‚ด๋ถ€์—์„œ loading.value๊ฐ’์„ true -> false๋กœ ๋ณ€๊ฒฝํ•ด์ค€๋‹ค.

<div v-if="loading" class="loadingDiv">
         <h1>loading.......</h1>
</div>

๐Ÿ‘‰ loading์ด true์ผ๋•Œ๋Š” loading์ด๋ผ๋Š” ํ™”๋ฉด์ด ์ž ๊น ์ถœ๋ ฅ๋œ๋‹ค.




2. Pinia

๐Ÿ‘‰ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
๐Ÿ‘‰ npm install pinia
๐Ÿ‘‰ Pinia์˜ store์— ์ €์žฅํ•˜์—ฌ ์‚ฌ์šฉํ–ˆ์„๋•Œ ์ด์ ์€ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๊ฐ„์— ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ œ์•ฝ์—†์ด ์ด์šฉํ• ์ˆ˜ ์žˆ๋‹ค๋Š”๊ฒƒ์ด๋‹ค.

์ƒํƒœ๊ด€๋ฆฌ๊ธฐ์ค€

  • ์ปดํฌ๋„ŒํŠธ๊ฐ„ ๊ฑฐ๋ฆฌ๊ฐ€ ๋จผ ๋ฐ์ดํ„ฐ
  • ์™ธ๋ถ€์—์„œ ๋ณ€๊ฒฝ๊ฐ€๋Šฅ์„ฑ ์—†๋Š” ๋ฐ์ดํ„ฐ = ์„œ๋ฒ„์—์„œ ๋ณ€๊ฒฝ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ
    ex) ํ‚ค์˜ค์Šคํฌ -> ์žฅ๋ฐ”๊ตฌ๋‹ˆ
    ๐Ÿ‘‰ ํ”„๋กœ์ ํŠธ์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋“ค์„ store๋กœ ๋นผ์ฃผ๋Š”๊ฒƒ์ด ์•„๋‹Œ, ์ด๋Ÿฌํ•œ ์ƒํƒœ๊ด€๋ฆฌ๊ธฐ์ค€์— ์ ํ•ฉํ•œ ๋ฐ์ดํ„ฐ๋“ค๋งŒ store๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ๊ด€๋ฆฌ๋ฐ์ดํ„ฐ๋กœ ๋นผ์ฃผ๋Š”๊ฒƒ.




3. Store๋ฅผ ์ด์šฉํ•œ Login

3-0. useMember.js

import { defineStore } from "pinia";
import { ref } from "vue";

const useMember = defineStore('memberStore', ()=>{ //memberStore๋Š” ์Šคํ† ์–ด์˜ ๊ณ ์œ ID

    const member = ref({mid:null}) // ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์˜ ๊ฐ’์„ ๋ฐ˜์‘ํ˜•์œผ๋กœ ์ €์žฅ

    const login = (valueId) => {
        
        console.log('login..... '+valueId)

        member.value.mid = valueId  // pinia store์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธ

        localStorage.setItem('mid',valueId) // ๋กœ์ปฌ์Šคํ† ๋ฆฌ์ง€์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธ

    }

    const logout = () => {
        console.log('logout.........')

        member.value.mid = null

        localStorage.removeItem('mid')
    }

    return{member,login, logout} // ํ•ด๋‹น return๊ฐ’์„ ํ†ตํ•ด ๋‚˜์ค‘์— useMember๋ฅผ ํ˜ธ์ถœํ•˜๊ณ ,(member,login, logout) ์›ํ•˜๋Š” ๋ณ€์ˆ˜ ๋ฐ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋‹ค.

})

export default useMember

๐Ÿ‘‰ pania๋ฅผ ์ด์šฉํ•˜์—ฌ useMember๋ผ๋Š” store๋ฅผ ๋งŒ๋“ค๊ณ , member๋ผ๋Š” ๋ฐ˜์‘ํ˜• ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋กœ๊ทธ์ธ์„ ํ–ˆ๋Š”์ง€ ์•Œ์ˆ˜์žˆ๋„๋ก ๋งŒ๋“ ๋‹ค.
๐Ÿ‘‰ login ๋ฉ”์†Œ๋“œ์—์„œ๋Š” loginPage.vue์—์„œ input์„ ํ†ตํ•ด midInput์ด๋ผ๋Š” ๊ฐ’์„ ๋„ฃ๊ณ , ๊ทธ ๋ณ€์ˆ˜๊ฐ’์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋“ค์–ด์˜ค๊ฒŒ ๋œ๋‹ค.
๐Ÿ‘‰ localStoralge๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธํ•œ ๊ธฐ๋ก์„ ๋‚จ๊ธฐ๊ฒŒ๋˜์–ด ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•˜๋”๋ผ๋„ ๋กœ๊ทธ์ธ๊ธฐ๋ก์ด ๋‚จ์•„์žˆ์–ด ์žฌ๋กœ๊ทธ์ธ์„ ํ•  ๋ฒˆ๊ฑฐ๋กœ์›€์„ ์ค„์ธ๋‹ค.
๐Ÿ‘‰ useMember์˜ return๊ฐ’์€ useMember๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์›ํ•˜๋Š” ๋ฉ”์†Œ๋“œ ๋ฐ ๋ณ€์ˆ˜๋ฅผ ์–ด๋А ์ปดํฌ๋„ŒํŠธ์—์„œ๋“  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. (ํ•˜์ง€๋งŒ import๋Š” ํ•„์ˆ˜)



3-1. LoginPage.vue

<template>
    <div>
        <h1>Login Page</h1>
        <input type="text" v-model="midInput">
        <button @click="handleClick">Login</button>
    </div>
</template>

<script setup>
import { ref } from 'vue';
import useMember from '../stores/useMember';

const {login} = useMember() // useMember.js์˜ login ๊ธฐ๋Šฅ ์‚ฌ์šฉ

const midInput = ref('') // input๋˜๋Š” id๊ฐ’๊ณผ ์—ฐ๋™๋˜๋„๋ก v-model์–‘๋ฐฉํ–ฅ์„ ์‚ฌ์šฉ

const handleClick = () => {

    const mid = midInput.value // input text ๋กœ ์ž…๋ ฅํ•œ id ๊ฐ’์„ mid๋ณ€์ˆ˜์— ๋„ฃ๊ณ 

    login(mid) // store์— ์ €์žฅ๋œ login๋ฉ”์†Œ๋“œ์— mid ์ธ์ž๋ฅผ ๋„ฃ์–ด ํ˜ธ์ถฃ
}

</script>

<style lang="scss" scoped>

</style>

๐Ÿ‘‰ useMember store๋ฅผ importํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š”๋ชจ์Šต




3-2. todo.js

import useMember from '../stores/useMember'

const IndexPage = () => import('../pages/todo/IndexPage.vue')
const TodoListPage = () => import('../pages/todo/TodoListPage.vue')
const TodoAddPage = () => import('../pages/todo/TodoAddPage.vue')
const TodoReadPage = () => import('../pages/todo/TodoReadPage.vue')


const moveToLogin = (to, from, next) => { // ์ด๋Ÿฐ์‹์œผ๋กœ login์„ ํ•˜๊ณ  ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐˆ์ˆ˜์žˆ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๋นผ๋†“์œผ๋ฉด ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ๊ฐˆ๋•Œ ๋กœ๊ทธ์ธ์„ ํ•œ ์‚ฌ๋žŒ๋งŒ ํ• ์ˆ˜์žˆ๋„๋ก filter๊ธฐ๋Šฅ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

    const checkLogin = () => { // checkLogin ๋ฉ”์†Œ๋“œ๋Š” ๋กœ๊ทธ์ธ ํ•œ ๊ธฐ๋ก์ด ์žˆ๋‹ค๋ฉด true / ์•„๋‹ˆ๋ฉด false
        const {member} = useMember() // useMember.js์—์„œ member๋ผ๋Š” ๋ฐ˜์‘ํ˜• ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•จ

        return member.mid ? true : false 
    }

    console.log('move to add page '+ checkLogin())

    if(checkLogin()){
        next() // ๋กœ๊ทธ์ธ ๊ธฐ๋ก์ด ์žˆ์œผ๋ฉด ๋‹ค๋ฅธ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ• ์ˆ˜ ์žˆ๋‹ค.
    }else{
        next('/login') // ๋กœ๊ทธ์ธ ๊ธฐ๋ก์ด ์—†์œผ๋ฉด login ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐ„๋‹ค.
    }
}

const todoRouting = {path: "/todo",
            component: IndexPage, // Index๊ฐ€ ๊ฐ€์žฅ์œ„์—์žˆ๊ณ , ๊ทธ ์•„๋ž˜์— todo list / todoadd๊ฐ€ ์žˆ๋Š”๊ฒƒ์ด๋‹ค.
            children: [
                {path: "list", component: TodoListPage, beforeEnter: moveToLogin}, // ์‹ค์ œ๋กœ๋Š” path: /todo/list, component๋Š” ์œ„์— const TodoListPage๋กœ ์ธํ•ด์„œ import์—ฐ๊ฒฐ์ด ๋˜์–ด์žˆ๋Š”๊ฒƒ.
                {path: "add", component: TodoAddPage, beforeEnter: moveToLogin},
                {path: "", redirect: '/todo/list'}, // ์ด๊ฒƒ๋•Œ๋ฌธ์— Indexpage์˜ ๊ฐ€์žฅ ๊ธฐ์ดˆํŽ˜์ด์ง€๊ฐ€ listํŽ˜์ด์ง€๊ฐ€ ๋˜๋Š”๊ฒƒ.
                {path: "read/:mno", component: TodoReadPage, beforeEnter: moveToLogin},
               
            ]
        }

export default todoRouting //๋‹ค๋ฅธํŒŒ์ผ์—์„œ todoRouting์„ ์‰ฝ๊ฒŒ import




3-3. App.vue

<script setup>
import { RouterLink, RouterView } from 'vue-router';
import useMember from './stores/useMember';

const {member, logout} = useMember() //useMember.js ์—์„œ member๋ณ€์ˆ˜์™€ logout๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค.
</script>

<template>
  
  <div>
    <span><RouterLink to="/">Main</RouterLink></span> <!--Aํƒœ๊ทธ ์ฒ˜๋Ÿผ ์ƒ๊ธด ํ™”๋ฉด์ด๋™-->
    <span><RouterLink to="/about">About</RouterLink></span>
    <span><RouterLink to="/todo">Todo</RouterLink></span>
    <span v-if="member.mid === null"><RouterLink to="/login">Login</RouterLink></span> <!--์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ํ•œ ๊ธฐ๋ก์ด ์—†์„๋•Œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์ด ๋ณด์ž„-->

    <span v-if="member.mid !== null"><button @click="logout">Logout</button></span> <!--์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ํ•œ ๊ธฐ๋ก์ด ์žˆ์„๋•Œ ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ์ด ๋ณด์ž„-->
  </div>

  <div>
    <hr/>
    MemberID : {{ member.mid }}
    <hr/>
  </div>

  <RouterView></RouterView> <!--๋ผ์šฐํ„ฐ๋กœ ์ด์šฉ๋œ ํŽ˜์ด์ง€๋ฅผ ์ถœ๋ ฅ-->
</template>


<style scoped>

span{
  margin-left: 1em;
}

</style>




3-4. main.js

import { createApp } from 'vue'
import App from './App.vue'
import routeConfig from './routers'
import { createPinia } from 'pinia'




createApp(App).
use(routeConfig).
use(createPinia()). // pinia์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ•„์ˆ˜
mount('#app') // use(routeConfig)์œผ๋กœ ์„ค์ •ํ•œ ๋ผ์šฐํ„ฐ๋กœ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค

0๊ฐœ์˜ ๋Œ“๊ธ€