๐Ÿpinia์™€ ๐Ÿ—‚LocalStorage ์‚ฌ์šฉํ•œ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ

dyeon-devยท2023๋…„ 9์›” 10์ผ
1

์ง„ํ–‰ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์—์„œ LocalStorage๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ ๊ตฌ์ถ•์„ ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

๋˜ํ•œ vue.js๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ pinia๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

// ModbusMasterEthernet.vue
const setNetwork = () => {
  networkData.value = {
    protocol: selectedProtocol.value,
    ip: inputedIp.value,
    port: inputedPort.value,
    transactionDelay: inputedTD.value,
    timeout: inputedTO.value,
  };
  console.log(networkData.value);
};

ModbusMasterEthernet.vue์—์„œ setNetwork๋ณ€์ˆ˜์˜ ์ต๋ช…ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋์„ ๋•Œ networkData.value์˜ ๊ฐ’์„ ์„ค์ •ํ•œ๋‹ค. ์ด ๊ฐ’์„ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” networkData๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•˜๋‹ค.

๐Ÿ pinia ์ƒํƒœ๊ด€๋ฆฌ

vuex ํ˜น์€ pinia ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
Vue3์—์„œ ์ถ”์ฒœํ•˜๋Š” ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Pinia๋Š” ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ฐ€ ์ง์ ‘ ์ถ”์ฒœํ•˜๋Š” ์ƒํƒœ๊ด€๋ฆฌ ํˆด์ด๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ๋ฒ•์„ ์ตํ˜€๋ณด์•˜๋‹ค. ์‚ฌ์šฉํ•˜๋‹ค๋ณด๋ฉด Composition API ์Šคํƒ€์ผ๋กœ ์ž‘์—…ํ•  ๋•Œ ์ž˜ ์–ด์šธ๋ฆฌ๊ณ  Vuex๋ณด๋‹จ ์ž‘์—…๋Ÿ‰์ด ์ ๋‹ค๋Š” ๋Š๋‚Œ์ด ๋“ ๋‹ค.

1. pinia ์„ค์น˜

npm install pinia

2. Pinia Store ๋งŒ๋“ค๊ธฐ

// store.ts

import { createPinia, defineStore } from 'pinia';

const pinia  = createPinia();

// Define store
export const useMyStore = defineStore('myStore', {
  state: () => ({
    networkMasterData: {} as NetworkMasterData,
  }),
  actions: {
    setNetworkMasterData(this: NetworkMasterData, data: NetworkMasterData) {
      this.networkMasterData = data;
    },
  },
});

export { pinia };

// NetworkMasterData type
export interface NetworkMasterData {
  networkMasterData: NetworkMasterData;
  protocol?: string;
  ip?: string;
  port?: number;
  transactionDelay?: number;
  timeout?: number;
}

3. main.ts์— Pinia Store ๋“ฑ๋ก

// main.ts

import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);
app.use(pinia);
app.mount('#app');

4. ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ฌ ๊ณณ์— Pinia Store ์—…๋ฐ์ดํŠธ

// ModbusMasterEthernet.vue

   import { useMyStore } from '../store'; 

   const myStore = useMyStore();

    const setNetwork = () => {
      const data = {
        protocol: selectedProtocol.value,
        ip: inputedIp.value,
        port: inputedPort.value,
        transactionDelay: inputedTD.value,
        timeout: inputedTO.value,
      };

      myStore.setNetworkData(data); 
      console.log(myStore.networkMasterData); 

์—ฌ๊ธฐ๊นŒ์ง€ ํ•˜๋ฉด ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ data๋ฅผ ์“ธ ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋œ๋‹ค !

๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ๋˜๋Š” data ๊ฐ’์„ ์ €์žฅ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ localstorage๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.
๋˜, ์ €์žฅ๋˜๋Š” ๊ฐ’์€ ๋ฐฐ์—ด์˜ ๊ฐ์ฒด๋กœ ์ €์žฅ์‹œํ‚ฌ ๊ฒƒ์ด๋‹ค.

5. ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ปดํฌ๋„ŒํŠธ์— Pinia์™€ Localstorage๋ฅผ ์„ค์ •

// MME-Recent.vue

<script setup lang="ts">
import { onMounted, watchEffect } from 'vue';
import { useMyStore } from './store'; 

const myStore = useMyStore();
let savedData = ref<SettingType[]>([]);

// pinia store์˜ networkData ๋ณ€๊ฒฝ ์‚ฌํ•ญ ๊ฐ์‹œํ•˜๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด localStorage์— ์ €์žฅ
watchEffect(() => {
   // ํ˜„์žฌ pinia store์— ์žˆ๋Š” networkMasterData ๊ฐ’์˜ ๋ณ€์ˆ˜
  const currentData = myStore.networkMasterData;
	// currentData์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด์— ํ‘ธ์‹œํ•˜๊ณ  localStorage์— ์ €์žฅํ•˜๊ธฐ ์ „์— ๋นˆ ๊ฐ์ฒด์ธ์ง€ ํ™•์ธ (์ƒˆ๋กœ๊ณ ์นจํ•  ๋•Œ localStorage ํ•ญ๋ชฉ์— ๋นˆ ๊ฐ์ฒด๊ฐ€ ์ถ”๊ฐ€๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€)
    if (Object.keys(currentData).length > 0) {
        try {
            // string ํƒ€์ž…์ด๋ฉด networkData ๊ฐ’์„ JSON์œผ๋กœ ํŒŒ์‹ฑํ•˜๊ณ , string์ด ์•„๋‹ˆ๋ผ๋ฉด ๋นˆ ๋ฐฐ์—ด์„ ํ• ๋‹น
            const savedDataJSON = localStorage.getItem('networkMasterData');
            savedData = typeof savedDataJSON === 'string' ? JSON.parse(savedDataJSON) : [];
            console.log('savedData:', savedData.value);
            // ์ถ”๊ฐ€๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฐ์—ด์˜ ๋งจ ์•ž์— ์ถ”๊ฐ€
            savedData.unshift(currentData);
            // ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ฌธ์ž์—ด๋กœ ์ง๋ ฌํ™”
            localStorage.setItem('networkMasterData', JSON.stringify(savedData));
        } catch (error) {
            // ์—๋Ÿฌ์ฒ˜๋ฆฌ
            console.error('Error saving data to localStorage:', error);
        }
    }
});
</script>

6. ์žฌ๋กœ๋”ฉ ์‹œ ์Šคํ† ๋ฆฌ์ง€ ์—…๋ฐ์ดํŠธ

์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹Œ, ํŽ˜์ด์ง€๋ฅผ ์žฌ๋กœ๋”ฉํ•ด๋„ ํ…œํ”Œ๋ฆฟ์ด ํ™”๋ฉด์— ๋‚จ์•„์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ์Šคํ† ๋ฆฌ์ง€์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ํŽ˜์ด์ง€๋ฅผ ๋กœ๋“œํ•  ๋•Œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
loadData ํ•จ์ˆ˜์˜ ๋กœ์ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
1. ๋ฐ์ดํ„ฐ ๋กœ๋“œ: ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋  ๋•Œ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ํ•œ๋‹ค.
2. ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐํ™”: ๊ฒ€์ƒ‰๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋ณ€์ˆ˜์— ํ• ๋‹นํ•œ๋‹ค.
3. ํ…œํ”Œ๋ฆฟ์— ๋ฐ์ดํ„ฐ ํ‘œ์‹œ: ๋ณ€์ˆ˜์— ํ• ๋‹น๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ…œํ”Œ๋ฆฟ์—์„œ ์‚ฌ์šฉํ•˜์—ฌ ํ™”๋ฉด์— ํ‘œ์‹œํ•œ๋‹ค.

<script setup lang="ts">
// ๋ฐ์ดํ„ฐ ๋กœ๋“œ ํ•จ์ˆ˜
const loadData = () => {
    try {
        const savedDataJSON = localStorage.getItem('networkMasterData');
        if (savedDataJSON) {
            savedData.value = JSON.parse(savedDataJSON);
        }
    } catch (error) {
        console.error('Error loading data from localStorage:', error);
    }
};
// ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋  ๋•Œ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹คํ–‰
onMounted(() => {
    loadData();
});
</script>
<template>
<div v-for="(data, i) in savedData" :key="i" v-if="savedData && savedData.length > 0">
    <q-card class="my-card bg-secondary text-white">
        <q-card-section>
            <div class="text-h6">{{ data.protocol }}</div>
            <div class="text-subtitle2">{{ data.ip }}</div>
            <div class="text-subtitle2">[Port] {{ data.port }}</div>
            <div class="text-subtitle2">[Transaction Delay] {{ data.transactionDelay }}</div>
            <div class="text-subtitle2">[Timeout] {{ data.timeout }}</div>
        </q-card-section>
 </template>
        

ํ…์ŠคํŠธ๋กœ์ปฌ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅ๋œ ํ™”๋ฉด !
๋ฐฐ์—ด์— ๊ฐ์ฒด๋กœ ์ž˜ ์Œ“์ธ๋‹น.

์ง€๊ธˆ๊นŒ์ง€ ์ตœ๊ทผ ํ•ญ๋ชฉ ์ €์žฅ์„ ์œ„ํ•ด์„œ ์—ด์‹ฌํžˆ ํ™”๋ฉด์— ๋ฟŒ๋ ค์ฃผ์—ˆ๋‹ค !!

7. DELETE ๋ฐ OPEN ๊ธฐ๋Šฅ ์ถ”๊ฐ€

<script setup lang="ts">
import { ref, watchEffect, onMounted } from 'vue';
import { useMyStore } from '../store/store';
import { useToggleStore } from '../store/modules/settingtoggle';

const deleteCard = (index: number) => {
        // savedData ๋ฐฐ์—ด์—์„œ ์„ ํƒ๋œ ํ•ญ๋ชฉ์œผ๋กœ๋ถ€ํ„ฐ 1๊ฐœ ์‚ญ์ œ
        savedData.value.splice(index, 1);

        // ํ•ด๋‹น ๋กœ์ปฌ ์ €์žฅ์†Œ ๊ฐ’๋„ ์ œ๊ฑฐ
        const savedDataJSON = localStorage.getItem('networkMasterData');
        if (savedDataJSON) {
            const parsedData = JSON.parse(savedDataJSON) as SettingType[];
            parsedData.splice(index, 1);
            localStorage.setItem('networkMasterData', JSON.stringify(parsedData));
        }
    
};

const openCard = () => {
    const toggleStore = useToggleStore();
    toggleStore.falseSetting();
}
</script>

<template>
    <div v-for="(data, i) in savedData" :key="i">
        <q-card class="my-card bg-secondary text-white">
            <q-card-section>
                <div class="text-h6">{{ data.protocol }}</div>
                <div class="text-subtitle2">{{ data.ip }}</div>
                <div class="text-subtitle2">[Port] {{ data.port }}</div>
                <div class="text-subtitle2">[Transaction Delay] {{ data.transactionDelay }}</div>
                <div class="text-subtitle2">[Timeout] {{ data.timeout }}</div>
            </q-card-section>

            <q-separator dark />

            <q-card-actions>
                <q-btn flat @click="deleteCard(i)">Delete</q-btn>
                <router-link to="/Modbus/MasterEthernet"><q-btn flat @click="openCard">Open</q-btn></router-link>
            </q-card-actions>
        </q-card>
    </div>
</template>

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