Vue Vuex Advanced

YEZI🎐·2022년 12월 14일
1

Vue

목록 보기
45/45

구조

실제로 Vuex는 코드 구조를 제한하지는 않는다. 하지만,

  1. 애플리케이션 레벨의 상태(state)는 중앙 집중된 저장소임
  2. 상태를 변경시키는 유일한 방법은 동기 트랜잭션인 mutation을 commit하는 것
  3. 비동기식 로직은 캡슐화되어야 하며 action으로 구성

위 규칙은 따르는 것이 좋다. 위 규칙대로 프로젝트를 구조화한다면,
저장소 파일이 너무 커졌을 때 action, mutation, getter를 개별 파일로 분할하기 수월하다.

├── index.html
├── main.js
├── api
│   └── ... # API 요청을 위한 추상화 포함
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 모듈을 조합하고 저장소를 내보내는 곳
    ├── actions.js        # 루트 액션
    ├── mutations.js      # 루트 변이
    └── modules
        ├── cart.js       # cart 모듈
        └── products.js   # products 모듈

플러그인

Vuex 저장소(Store)는 각 mutation에 대한 훅을 노출하는 plugins 옵션을 허용한다.
Vuex 플러그인은 저장소(Store)를 유일한 전달인자로 받는 함수이다.

const myPlugin = store => {
  // 저장소 초기화 될 때 사용
  store.subscribe((mutation, state) => {
    // mutation할 때마다 호출
    // mutation은 { type, payload } 포맷으로 제공
  })
}

그리고 다음과 같이 사용할 수 있다.

const store = new Vuex.Store({
  // ...
  plugins: [ myPlugin ]
})

플러그인 내부 mutation commit

플러그인은 상태를 직접 mutation할 수 없다.
컴포넌트와 마찬가지로 mutation을 commit하여 변경을 트리거한다.

mutation를 커밋함으로써 플러그인을 사용하여 데이터 소스를 저장소(Store)에 동기화할 수 있다.

export default function createWebSocketPlugin (socket) {
  return store => {
    socket.on('data', data => {
      store.commit('receiveData', data)
    })
    store.subscribe(mutation => {
      if (mutation.type === 'UPDATE_DATA') {
        socket.emit('update', mutation.payload)
      }
    })
  }
}
const plugin = createWebSocketPlugin(socket)

const store = new Vuex.Store({
  state,
  mutations,
  plugins: [plugin]
})

상태 스냅샷 가져오기

때로는 플러그인이 상태의 스냅샷(어떠한 시점의 데이터)이나
mutations 이후 상태와 mutations 이전 상태의 비교가 필요하다.
이를 위해서는 상태 객체에 대한 깊은 복사를 수행해야 한다.

const myPluginWithSnapshot = store => {
  let prevState = _.cloneDeep(store.state)
  store.subscribe((mutation, state) => {
    let nextState = _.cloneDeep(state)

    // prevState와 nextState 비교

    // 다음 변이를 위한 상태 저장
    prevState = nextState
  })
}

상태 스냅 샷을 사용하는 플러그인은 개발 중에만 사용해야 한다.
빌드 도구(ex. Browserify)process.env.NODE_ENV !== 'production'의 값을 최종 빌드를 위해 false로 변환하여 이을 처리하도록 할 수 있다.

const store = new Vuex.Store({
  // ...
  plugins: process.env.NODE_ENV !== 'production'
    ? [myPluginWithSnapshot]
    : []
})

Strict 모드

strict 모드를 사용하려면 Vuex 저장소에 strict: true를 추가하면 된다.

const store = new Vuex.Store({
  // ...
  strict: true
})

Strict 모드에서는 Vuex 상태가 mutation 핸들러 외부에서 mutation을 하는 부적절한 mutation이 발생할 때 마다 오류가 발생시킨다.
이렇게하면 디버깅 도구로 모든 상태 mutation을 명시적으로 추적할 수 있다.

개발 vs 배포

Strict 모드는 부적절한 mutation를 감지하기 위해 상태 트리를 자세히 관찰한다.
성능 이슈를 피하기 위해 배포 환경에서는 strict 모드를 켜지않아야 한다.

플러그인과 마찬가지로 빌드 도구(ex. Browserify)process.env.NODE_ENV !== 'production'의 값을 최종 빌드를 위해 false로 변환하여 이을 처리하도록 할 수 있다.

const store = new Vuex.Store({
  // ...
  strict: process.env.NODE_ENV !== 'production'
})

폼 핸들링

Strict 모드로 Vuex를 사용하는 경우,
Vuex에 포함된 부분에 v-model을 사용하는 것은 약간 까다로울 수 있다.

<input v-model="obj.message">

위 소스에서 obj가 저장소(Store)에서 객체를 반환하는 computed 속성이라면,
v-model은 사용자가 입력 할 때 obj.message를 직접 변경하려고 한다.
하지만, Strict 모드에서는 mutation 핸들러 외부에서 mutation을 하는 부적절한 mutation이 발생하면 이를 수행되지 않고 오류가 발생시킨다.

이를 해결하는 Vuex 방식<input>의 값을 바인딩하고
input 또는 change 이벤트에 대한 action을 호출
하는 것이다.

<input :value="message" @input="updateMessage">
// ...
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},
methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}
// ...
mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}

양방향 계산된 속성

앞서 다룬 내용으로 작업을 하게 되면 v-model + 지역 상태보다 더 장황해지고
v-model의 유용한 기능 중 일부를 잃어 버리게 된다.
v-model을 사용하여 폼 핸들링을 하는 또 다른 방법은 setter를 이용하여 양방향 computed 속성을 사용하는 것이다.

<input v-model="message">
// ...
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}
profile
까먹지마도토도토잠보🐘

0개의 댓글