# 변이

<div class="scrimba"><a href="https://scrimba.com/p/pnyzgAP/ckMZp4HN" target="_blank" rel="noopener noreferrer">Scrimba에서이 수업을 해보십시오.</a></div>

Vuex 저장소에서 실제로 상태를 변경하는 유일한 방법은 변이하는 것입니다. Vuex 변이는 이벤트와 매우 유사합니다. 각 변이에는 **타입** 문자열 **핸들러** 가 있습니다. 핸들러 함수는 실제 상태 수정을 하는 곳이며, 첫 번째 전달인자로 상태를받습니다.

``` js
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 상태 변이
      state.count++
    }
  }
})
```

변이 핸들러를 직접 호출 할 수는 없습니다. 이 옵션은 이벤트 등록과 비슷합니다. "타입이 `increment`인 변이가 발생하면이 핸들러를 호출합니다." 변이 핸들러를 호출하려면 해당 타입과 함께 **store.commit** 을 호출해야합니다.

``` js
store.commit('increment')
```

### 페이로드를 가진 커밋

변이에 대해 **payload** 라고하는 `store.commit`에 추가 전달인자를 사용 할 수 있습니다.

``` js
// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
```
``` js
store.commit('increment', 10)
```

대부분의 경우 페이로드는 여러 필드를 포함할 수 있는 객체여야하며 기록 된 변이는 더 이해하기 쉽습니다.

``` js
// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
```
``` js
store.commit('increment', {
  amount: 10
})
```

### 객체 스타일 커밋

변이를 커밋하는 또 다른 방법은 `type` 속성을 가진 객체를 직접 사용하는 것입니다.

``` js
store.commit({
  type: 'increment',
  amount: 10
})
```

객체 스타일 커밋을 사용할 때 전체 객체는 변이 핸들러에 페이로드로 전달되므로 핸들러는 동일하게 유지됩니다.

``` js
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
```

### Vue의 반응성 규칙을 따르는 변이

Vuex 저장소의 상태는 Vue에 의해 반응하므로, 상태를 변경하면 상태를 관찰하는 Vue 컴포넌트가 자동으로 업데이트됩니다. 이것은 또한 Vuex 변이가 일반 Vue로 작업 할 때 동일한 반응성에 대한 경고를 받을 수 있음을 의미합니다.

1. 원하는 모든 필드에 앞서 저장소를 초기화하는 것이 좋습니다.

2. 객체에 새 속성을 추가할 때 다음 중 하나를 수행해야합니다.

  - `Vue.set(obj, 'newProp', 123)`을 사용하거나,

  - 객체를 새로운 것으로 교체하십시오. 예를 들어, 3 단계 [객체 확산 문법](https://github.com/sebmarkbage/ecmascript-rest-spread)을 사용하면 다음과 같이 작성할 수 있습니다.

    ``` js
    state.obj = { ...state.obj, newProp: 123 }
    ```

### 변이 타입에 상수 사용

다양한 Flux 구현에서 변이 유형에 상수를 사용하는 것은 일반인 패턴입니다. 이를 통해 코드는 linter와 같은 툴링을 활용할 수 있으며 모든 상수를 단일 파일에 저장하면 공동 작업자가 전체 애플리케이션에서 어떤 변이가 가능한지 한눈에 파악할 수 있습니다.

``` js
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
```

``` js
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // ES2015에서 계산 된 프로퍼티 이름 기능을 사용하여
    // 상수를 함수 이름으로 사용할 수 있습니다
    [SOME_MUTATION] (state) {
      // 변이 상태
    }
  }
})
```

상수를 사용할지 여부는 대부분 환경 설정입니다. 개발자가 많은 대규모 프로젝트에서 유용할 수 있지만, 이는 완전히 선택 사항입니다.

### 변이는 무조건 동기적이어야 합니다.

기억 해야할 한 가지 중요한 규칙은 **변이 핸들러 함수는 동기적** 이어야 한다는 것입니다. 왜 그럴까요? 다음 예제를 확인해보십시오.

``` js
mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}
```

이제 우리가 앱을 디버깅하고 devtool의 돌연변이 로그를 보고 있다고 상상해보십시오. 기록 된 모든 변이에 대해 devtool은 상태의 "이전" 및 "이후" 스냅 샷을 캡처 해야 합니다. 그러나 위의 예제 변이 내의 비동기 콜백은 불가능합니다. 변이가 커밋 되었을 때 콜백은 아직 호출되지 않으며, 콜백이 실제로 호출 될 시기를 devtool이 알 수 있는 방법이 없습니다. 콜백에서 수행 된 모든 상태 변이는 본질적으로 추적 할 수 없습니다!

### 컴포넌트 안에서 변이 커밋하기

`this.$store.commit('xxx')`를 사용하여 컴포넌트에서 변이를 수행하거나 컴포넌트 메소드를 `store.commit` 호출에 매핑하는 `mapMutations` 헬퍼를 사용할 수 있습니다 (루트 `store` 주입 필요)

``` js
import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment' // this.increment()를 this.$store.commit('increment')에 매핑합니다.
    ]),
    ...mapMutations({
      add: 'increment' // this.add()를 this.$store.commit('increment')에 매핑합니다.
    })
  }
}
```

### 액션에서 사용

비동기성이 상태의 변이와 결합하면 프로그램을 파악하기가 매우 어려워 질 수 있습니다. 예를 들어 상태를 변경하는 두 가지 비동기 콜백 메소드를 호출할 때 호출되는 시점과 먼저 호출 된 콜백을 어떻게 알 수 있습니까? 이것이 우리가 두 개념을 분리하려는 이유입니다. Vuex에서 **변이는 동기적으로 트랜잭션합니다.**

``` js
store.commit('increment')
// "increment" 변이가 일으킬 수 있는 모든 상태 변화는 이 순간에 이루어져야합니다.
```

비동기 작업을 처리하기 위한 [액션](actions.md)를 소개합시다.
