네임스페이스 모듈
모듈 사용 시, 주의할 점
모듈을 사용해 스토어를 관리하게 될 때 각각의 모듈이 가진 액션이 동일한 이름일 경우 문제가 발생합니다.
예를 들어 books
모듈과 cart
모듈에 동일한 이름의 액션이 있을 경우 어떤 문제가 발생하는지 확인해봅시다.
books
모듈의 fetchBooks
액션 코드에 Console에서 확인 가능한 로그를 남깁니다.
// store/modules/books.js
actions: {
fetchBooks() {
console.log('books에서 실행');
return new Promise((resolve, reject) => { ... });
}
}
cart
모듈에 동일한 이름의 액션을 추가하고 Console에서 확인 가능한 로그를 남깁니다.
// store/modules/cart.js
actions: {
fetchBooks() {
console.log('cart에서 실행');
}
}
브라우저 Console을 확인하면 각 모듈의 동일한 이름의 액션이 모두 실행된 것을 확인할 수 있습니다. 이처럼 의도와 달리 모듈 마다 동일한 이름의 액션 또는 뮤테이션을 가질 경우 모두 실행되는 문제가 발생될 수 있습니다.
주의!
게터의 경우, 모듈 마다 동일한 이름을 가질 경우 브라우저 Console에 중복 오류를 출력합니다.
네임스페이스로 문제 해결
이러한 문제를 해결하기 위해 Vuex는 모듈의 네임스페이스를 사용할 수 있도록 제공합니다.
각 모듈에 네임스페이스를 설정하는 방법은 다음과 같습니다. 모듈에 namespaced
속성을 추가하고 값을 true
로 설정합니다.
// store/modules/books.js
export default {
namespaced: true,
// ...
}
// store/modules/cart.js
export default {
namespaced: true,
// ...
}
하지만 namespaced
속성을 모듈에 설정하면 브라우저 Console에 다음의 오류가 출력됩니다.
오류 발생!
unknown action type: fetchBooks unknown action type: cartBooks
오류가 발생한 이유는 모듈의 네임스페이스를 사용하기로 설정함에 따라 모듈의 네임스페이스를 통하지 않은 기존의 액션, 뮤테이션, 게터 코드는 정상적으로 모듈에 접근할 수 없기 때문입니다. Vuex 탭을 보면 네임스페이스가 설정된 모듈의 게터를 확인할 수 있습니다.
코드 리팩토링
cart
모듈에서 코드 수정이 필요한 부분은 checkOutOfStock
입니다. 모듈 내부에서 사용된 getters
는 로컬 게터로
books
모듈 네임스페이스의 checkOutOfStock
에 접근하려면 rootGetters
를 context
에서 불러와 사용해야 합니다.
// store/modules/cart.js
export default {
namespaced: true,
// ...
actions: {
addBookToCart({state, getters, commit, rootGetters}, book) {
if (!rootGetters['books/checkOutOfStock'](book)) {
// if (!getters.checkOutOfStock(book)) {
const cartItem = state.items.find(item => item.id === book.id);
if (!cartItem) {
commit('pushBookToCart', book.id);
} else {
commit('increamentBookQuantity', cartItem);
}
commit('decreamentBookInventory', book);
}
},
// ...
},
// ...
}
BookList
컴포넌트에서 수정이 필요한 부분은 게터와 액션 부분입니다. 적합한 모듈의 네임스페이스를 설정합니다.
// components/BookList.vue
export default {
name: "BookList",
// ...
computed: {
...mapState({
books: state => state.books.items
}),
...mapGetters({ checkOutOfStock: "books/checkOutOfStock" })
// ...mapGetters({ checkOutOfStock: "checkOutOfStock" })
},
// ...
methods: {
...mapActions({
fetchBooks: "books/fetchBooks",
// fetchBooks: "fetchBooks",
addBookToCart: "cart/addBookToCart"
// addBookToCart: "addBookToCart"
})
}
}
ShoppingCart
컴포넌트 역시 마찬가지로 게터와 액션 부분에 적절한 네임스페이스를 설정합니다.
// components/ShoppingCart.vue
export default {
name: "ShoppingCart",
computed: {
...mapState({
purchageStatus: state => state.cart.purchageStatus
}),
...mapGetters({
books: "cart/cartBooks",
// books: "cartBooks",
total: "cart/cartTotal"
// total: "cartTotal"
})
},
methods: {
...mapActions({
purchageBook: "cart/purchage"
// purchageBook: "purchage"
})
}
}
NOTE
동일한 모듈에서 다수의 속성을 빼내야 할 경우 Map 헬퍼의 첫번째 인자로 네임스페이스를 설정할 수 있습니다.
...mapGetters('cart', {
books: "cartBooks",
total: "cartTotal"
})
BookList
, ShoppingCart
컴포넌트를 수정 완료하면 브라우저 화면에 애플리케이션이 문제 없이 그려집니다.
하지만 '카트에 추가' 버튼을 누르면 오류가 발생합니다.
오류 발생!
unknown local mutation type: decreamentBookInventory, global type: cart/decreamentBookInventory
문제가 발생한 이유는 cart
모듈의 addBookToCart
액션에서 사용된
decreamentBookInventory
뮤테이션이 books
모듈의 것이기 때문입니다.
문제를 해결하기 위해 books
네임스페이스를 적용한 코드로 수정합니다.
// store/modules/cart.js
actions: {
addBookToCart({state, getters, commit, rootGetters}, book) {
if (!rootGetters['books/checkOutOfStock'](book)) {
const cartItem = state.items.find(item => item.id === book.id);
if (!cartItem) {
commit('pushBookToCart', book.id);
} else {
commit('increamentBookQuantity', cartItem);
}
commit('books/decreamentBookInventory', book);
// commit('decreamentBookInventory', book);
}
},
}
하지만 문제는 해결되지 않습니다. 오류는 여전히 발생합니다. 문제가 여전히 발생하는 이유는 오류 메시지가 알려줍니다.
오류 발생!
unknown local mutation type: books/decreamentBookInventory, global type: cart/books/decreamentBookInventory
원인은 cart/books/decreamentBookInventory
에 있습니다. cart/books/
로 보아 cart
모듈 내부에서 books/decreamentBookInventory
하위 모듈을 찾으려 하기 때문입니다. 이 문제를 해결하려면 cart
모듈이 아니라 루트에서 커밋을 실행하도록 설정해야 합니다.
// store/modules/cart.js
actions: {
addBookToCart({state, getters, commit, rootGetters}, book) {
if (!rootGetters['books/checkOutOfStock'](book)) {
const cartItem = state.items.find(item => item.id === book.id);
if (!cartItem) {
commit('pushBookToCart', book.id);
} else {
commit('increamentBookQuantity', cartItem);
}
commit('books/decreamentBookInventory', book, {root: true});
// commit('books/decreamentBookInventory', book);
}
},
}
NOTE
모듈에서 커밋을 루트에서 실행한 것과 같이 설정할 경우 다음과 같이 3번째 인자로 옵션을 설정합니다.
commit('모듈네임스페이스/뮤테이션', 페이로드, {root: true})
이제 모든 문제가 해결되었습니다. 쇼핑 카트 애플리케이션은 문제 없이 정상 작동합니다. 도서를 카트에 추가하거나, 결재 버튼을 누르면 Vue Devtools 개발 도구 Vuex 탭에 이벤트가 기록됩니다. 기록된 이벤트를 확인하면 어떤 모듈의 뮤테이션이 실행되었는지 검토할 수 있습니다.