Vuex

Vuex๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ ๊ด€๋ฆฌ ํŒจํ„ด + ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ๋‹ค๋ฅธ ์ƒํƒœ๊ด€๋ฆฌ ํŒจํ„ด + ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๋น„๊ตํ–ˆ์„๋•Œ Vue Reactivity ์ฒด๊ณ„๋ฅผ ํšจ์œจ์ ์œผ๋กœ ํ™œ์šฉํ•ด ํ™”๋ฉด ์—…๋ฐ์ดํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

Vue Reactivity

๋ทฐ๊ฐ€ ๋ฐ์ดํ„ฐ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ์ž๋™์œผ๋กœ ํ™”๋ฉด์„ ๊ฐฑ์‹ ํ•˜๋Š” ํŠน์„ฑ

์ƒํƒœ๊ด€๋ฆฌ ๊ตฌ์„ฑ์š”์†Œ

๊ตฌ์„ฑ์š”์†Œ

์„ค๋ช…

state

์ปดํฌ๋„ŒํŠธ๊ฐ„ ๊ณต์œ ํ•˜๋Š” data

view

๋ฐ์ดํ„ฐ๊ฐ€ ํ‘œํ˜„๋  template

actions

์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์— ๋”ฐ๋ผ ๋ฐ˜์‘ํ•  methods

new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    <div>{{ count }}</div>
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})

์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ์ด์œ 

  1. ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•  ๋•Œ ๊ทธ ์‚ฌ์ด์— ์ˆ˜๋งŽ์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด, props๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ฒŒ ๋˜๋ฉด ๊ทธ ์‚ฌ์ด์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์— props๋ฅผ ์„ค์ •ํ•ด์ค˜์•ผํ•˜๋ฉฐ, ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํž˜๋“ค์–ด์ง„๋‹ค.

  2. ์ด๋ฒคํŠธ ๋ฒ„์Šค๋ฅผ ํ™œ์šฉํ•˜๊ฒŒ๋˜๋ฉด ์ƒ-ํ•˜์œ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๊ณ ๋„ ํ•œ ๋ฒˆ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด ์•„๋‹Œ ์…€ ์ˆ˜ ์—†์ด ๋งŽ์€ ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด ๋œ๋‹ค๋Š” ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค.

์ฆ‰, ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฐ ์ด๋ฒคํŠธ ํ†ต์‹ ๋“ฑ์˜ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ ๊ด€๊ณ„๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๊ตฌ์กฐํ™” ํ•˜๋Š” ๊ฒƒ(์ค‘์•™ ์ง‘์ค‘์‹ ์ €์žฅ์†Œ)์ด ์ƒํƒœ๊ด€๋ฆฌ(state management)์ด๋‹ค.

Basic

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')
// store/index.js 
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
});

vuex์˜ ์ค‘์‹ฌ์—๋Š” store๊ฐ€ ์žˆ๋‹ค. "์ €์žฅ์†Œ"๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ(state)๋ฅผ ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” ์ปจํ…Œ์ด๋„ˆ์ด๋‹ค. Vuex ์ €์žฅ์†Œ๋Š” ๋‹ค๋ฅธ ์ „์—ญ ๊ฐ์ฒด์™€ ๋‘ ๊ฐ€์ง€ ์ฐจ์ด์ ์ด ์žˆ๋‹ค.

  1. Vuex store๋Š” ๋ฐ˜์‘ํ˜•์ด๋‹ค.

  2. store์˜ state๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค. state๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ ๋ช…์‹œ์ ์ธ commit์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

Getters

์ค‘์•™ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์‹ ๊ตฌ์กฐ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์  ์ค‘ ํ•˜๋‚˜๋Š” ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ Vuex์˜ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ๋•Œ ์ค‘๋ณต๋œ ์ฝ”๋“œ๋ฅผ ๋ฐ˜๋ณต ํ˜ธ์ถœํ•˜๊ฒŒ ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

// App.vue
computed: {
  getCounter() {
    return this.$store.state.counter;
  }
},
// Child.vue
computed: {
  getCounter() {
    return this.$store.state.counter;
  }
},

Vuex์˜ state ๋ณ€๊ฒฝ์„ ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด Vuex์—์„œ ์ˆ˜ํ–‰ํ•˜๋„๋กํ•˜๊ณ , ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ˆ˜ํ–‰ ๋กœ์ง์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
      counter: 0
  },
  getters: {
      getCounter( state ) {
          return state.counter;
      }
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
});
// App.vue
computed: {
  parentCounter() {
    this.$store.getters.getCounter;
  }
},
// Child.vue
computed: {
  childCounter() {
    this.$store.getters.getCounter;
  }
},

mapGetters

Vuex์— ๋‚ด์žฅ๋œ helperํ•จ์ˆ˜์ด๋‹ค. mapGetters๋กœ ๋” ์ง๊ด€์ ์ด๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

<!-- App.vue -->
<div id="app">
  Parent counter : {{ parentCounter }}
</div>
// App.vue
import { mapGetters } from 'vuex'

export default({
  computed: mapGetters({
    parentCounter: 'getCounter'
  })
});

๋˜๋Š” Vuex์˜ getters ์†์„ฑ ์ด๋ฆ„๊ณผ ์ปดํฌ๋„ŒํŠธ์˜ computed ์†์„ฑ์„ ๋™์ผํ•˜๊ฒŒ ํ•ด ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋‹ค.

<!-- App.vue -->
<div id="app">
  Parent counter : {{ getCounter }}
</div>
// App.vue
import { mapGetters } from 'vuex'

export default({
  computed: mapGetters([
    'getCounter'
  ]),
});

์œ„ ๋ฐฉ๋ฒ•๋“ค์€ ์ปดํฌ๋„ŒํŠธ ์ž์ฒด์—์„œ ์‚ฌ์šฉํ•  computed ์†์„ฑ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ES6์˜ ... ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

// App.vue
import { mapGetters } from 'vuex'

computed: {
  ...mapGetters([
    'getCounter'
  ]),
  anotherCounter() {
    // ...
  }
}

๋‹ค๋งŒ ... ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด babel stage-2 ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ๋ฐ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค.

Mutations

Vuex์˜ state(๋ฐ์ดํ„ฐ) ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋กœ์ง์„ ์˜๋ฏธํ•œ๋‹ค.

vs Getters

  • ์ธ์ž๋ฅผ ๋ฐ›์•„ Vuex์— ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋‹ค.

  • computed๊ฐ€ ์•„๋‹Œ methods์— ๋“ฑ๋กํ•œ๋‹ค.

vs Actions

  • Mutations๋Š” ๋™๊ธฐ ๋กœ์ง์„ ์ •์˜ํ•œ๋‹ค.

  • Actions๋Š” ๋น„๋™๊ธฐ ๋กœ์ง์„ ์ •์˜ํ•œ๋‹ค.

Mutations์˜ ์„ฑ๊ฒฉ์ƒ ์ •์˜ํ•œ ๋กœ์ง๋“ค์ด ์ˆœ์ฐจ์ ์œผ๋กœ ์ผ์–ด๋‚˜์•ผ ๊ฐ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฐ˜์˜ ์—ฌ๋ถ€๋ฅผ ์ œ๋Œ€๋กœ ์ถ”์ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

Mutations์„ ์ด์šฉํ•ด ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•จ์œผ๋กœ์จ testing, debugging, Vue์˜ Reactive ์„ฑ์งˆ ์ค€์ˆ˜๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด commit ์œผ๋กœ state๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    counter: 0
  },
  getters: {
    getCounter(state) {
      return state.counter;
    }
  },
  mutations: {
    addCounter(state, payload) {
      return state.counter++;
    }
  }
});
export default {
  methods: {
    addCounter() {
      this.$store.commit('addCounter');
    },
    subCounter() {
      this.$store.commit('subCounter');
    }
  },
  ...
};

.commit ์„ ์ด์šฉํ•˜์—ฌ mutations ์ด๋ฒคํŠธ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ƒํƒœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

์ธ์ž ๊ฐ’ ๋„˜๊ธฐ๊ธฐ

this.$store.commit('subCounter', 10);
this.$store.commit('subCounter', {
  value: 10,
  arr: ["1", "3"]
});

ํŠน์ •ํ•œ ๊ฐ’์„ ๋„˜๊ธฐ๊ณ  ์‹ถ์œผ๋ฉด ๋‘๋ฒˆ์งธ ์ธ์ž๋ฅผ ์ถ”๊ฐ€ํ•ด ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ „๋‹ฌ๋œ ์ธ์ž ๊ฐ’์€ vuex์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

mutations: {
  addCounter(state, payload) {
    state.counter = payload.value;
  }
}

์—ฌ๊ธฐ์„œ ๋ฐ์ดํ„ฐ ์ธ์ž๋ช…์€ ๋ณดํ†ต payload๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

mapMutations

Vuex์˜ ๋‚ด์žฅ๋œ helper๋กœ mapMutations๋ฅผ ์ด์šฉํ•ด ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค.

// App.vue
import { mapMutations } from 'vuex'

methods: {
  ...mapMutations([
    'addCounter'
  ]),
  ...mapMutations({
    addCounter: 'addCounter'
  })
}

Vuex ์˜ Mutations ๋ฉ”์„œ๋“œ ๋ช…๊ณผ App.vue ๋ฉ”์„œ๋“œ ๋ช…์ด ๋™์ผํ•  ๋•Œ [] ์‚ฌ์šฉํ•˜๋ฉฐ, ๋ช…์นญ์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ์—๋Š” {}๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

Actions

Mutations๋Š” state๊ด€๋ฆฌ๋ฅผ ์ฃผ๋กœ ํ•˜๋ฉฐ, ์ƒํƒœ ๊ด€๋ฆฌ๋Š” ํ•œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ด€์—ฌํ•˜๋Š” ๊ฒƒ์„ ํšจ์œจ์ ์œผ๋กœ ํ•˜๊ธฐ ์œ„ํ•จ์ด๋ฏ€๋กœ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค. Actions์—์„œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ๊ด€๋ฆฌํ•œ๋‹ค.

setTimeout() ์ด๋‚˜ ์„œ๋ฒ„์™€์˜ http ํ†ต์‹ ๊ณผ ๊ฐ™์ด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ์‹œ๊ฐ„์ด ์˜ˆ์ธก๋˜์ง€ ์•Š๋Š” ๋กœ์ง์€ actions์— ์„ ์–ธํ•œ๋‹ค.

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
    ...
   actions: {
    addCounter(context) {
      return context.commit('addCounter');
    }
  }
});
// App.vue
methods: {
  // Actions ๋ฅผ ์ด์šฉํ•  ๋•Œ
  addCounter() {
    this.$store.dispatch('addCounter');
  }
},

Actions๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋Š” .dispatch() ๋ฅผ ์ด์šฉํ•œ๋‹ค.

Actions์—์„œ ์ธ์ž๋ฅผ ๋„˜๊ธฐ๋Š” ๋ฐฉ๋ฒ•์€ Mutations์™€ ์œ ์‚ฌํ•˜๋‹ค.

mapActions

mapActions๋„ ์œ„์˜ map๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

import {mapActions} from 'vuex';

export default {
  methods: {
    ...mapActions([
      'asyncIncrement',
      'asyncDecrement'
    ])
  },
}

์ฐธ์กฐ

Last updated

Was this helpful?