Vue Composition API를 사용하면서 store 데이터를 watch 하는 방법이 손에 익지 않아 기록으로 남겨본다..
우선..
내가 구현하고 싶던 기능은 사용자가 웹사이트의 메뉴를 변경할 때 메뉴목록의 하이라이트 지점도 바뀌게 하는 것이었다.
이를 위해선 사용자와 인터랙션하는 Component와
메뉴목록을 관리하는 Component 간의 데이터 전달이 필요했다.
Vue에서 상태 관리 등 전역으로 다루어야 할 데이터는 vuex의 store 기능을 사용하기에
store의 데이터를 조작하여 Component들 간의 데이터 전달을 하고자 하였다.
작업은 다음의 3 단계로 진행하였다.
1. Store에 변수 생성
// @/module/common.js
import { createNamespacedHelpers } from "vuex";
export const { mapState, mapMutations, mapActions } =
createNamespacedHelpers("common");
export default {
namespaced: true,
state: () => ({
CUR_MENU: "first-menu",
}),
getters: {
curMenu(state) {
return state.CUR_MENU;
},
},
mutations: {
CHANGE_MENU(state, nextMenu) {
state.CUR_MENU = nextMenu;
},
},
};
진행중인 프로젝트는 store 변수들을 용도에 따라 여러 파일로 분리하여 관리하고 있다.
메뉴 관련 데이터는 모든 Component에서 공용으로 사용되기 때문에 "@/module/common.js" 파일에 위치한다.
여기서 namespace 개념을 사용하는 데, 이것의 중요성을 알지 못해서 초반에 삽질을 좀 하게 되었다...
자세한 건 2, 3번 항목에서 더 다루도록 하자.
무튼,
현재 선택된 메뉴를 표시하는 CUR_MENU 변수를 store의 state로 선언하였다.
메뉴를 변경하고자 할 경우 mutations의 CHANGE_MENU()를 호출한다.
메뉴가 변경되었음을 감지하고자 할 경우 getters의 curMenu()를 참조한다.
2. Store 변수 변경
사용자가 특정 url로 바로 접속하였을 때 그 위치는 첫번째 메뉴가 아닐 수 있다.
이럴 경우 접속한 메뉴에 맞춰 메뉴목록을 업데이트 시켜주어야 했다.
사용자는 두번째 메뉴 화면을 보고 있는데 메뉴목록은 첫번째 메뉴가 선택(하이라이트)되어 있으면 이상하니까.
그래서 사용자가 특정 메뉴에 바로 접속하면 onMounted() 함수에서 store의 CUR_MENU 상태를 변경해주어야 한다.
state의 상태 변경은 mutations을 통해 할 수 있으므로 CUR_MENU state를 변경하기 위해 CHANGE_MENU() mutations를 onMounted() 훅에서 사용하였다.
// 두번째 메뉴 호출 시 onMounted()를 통해 선택된 메뉴가 변경됨을 store에 알림
<script setup>
import { useStore } from "vuex"
const store = useStore()
onMounted(() => {
// commit할 때 namespace (여기선 common) 꼭 붙여줘야 한다 !! 안 그러면 나처럼 몇 시간동안 삽질함..
store.commit('common/CHANGE_MENU', 'second-menu')
})
</script>
여기서 중요한건 store.commit()을 호출하여 mutaitons을 사용할 때 namespace를 꼭 붙여서 호출해야 한다는 사실이다.
namespace를 붙이지 않으면 Vue가 바보가 된다. 아무리 commit() 해봐도 우리가 원하는 CHANGE_MENU()는 호출되지 않는다..
3. 변경된 Store 변수를 참조하여 Vue Component 상태 업데이트
자, store의 메뉴 상태가 잘 변경되었다면 이를 감지하여 메뉴목록의 상태도 잘 변경되어야 한다.
상태 변화를 감지하고자 Vue의 computed() 기능을 사용하였다. 바로 살펴보자.
<ul>
<li>
<a>
<v-icon class="menu-icon-btn"
:class="curMenu == 'first-menu' ? 'highlighted' : ''" />
<v-icon class="menu-icon-btn"
:class="curMenu == 'second-menu' ? 'highlighted' : ''" />
<v-icon class="menu-icon-btn"
:class="curMenu == 'third-menu' ? 'highlighted' : ''" />
</a>
</li>
</ul>
<script setup>
import { computed } from 'vue'
import { useStore } from "vuex"
const store = useStore()
// store의 getters를 호출할 때에도 namespace를 붙여주자!!
var curMenu = computed(() => store.getters["common/curMenu"])
</script>
<style lang="scss" scoped>
.menu-icon-btn {
background: black;
color: white;
}
.highlighted {
background: white;
color: black;
}
</style>
store의 상태 변화를 얻기 위해선 getters를 사용해야 하므로,
curMenu getters를 호출하여 CUR_MENU state 값을 불러왔다.
그리고 CUR_MENU state가 변경될 때마다 해당 값에 맞게
하이라이트되는 메뉴가 바뀌어야 하므로 Vue의 computed() 기능을 사용하였다.
그리고 2번에서와 같이 store의 commit()을 할 때에는 namespace를 꼭!꼭! 붙여주자!!
'Development Experience > Web' 카테고리의 다른 글
콘솔에 객체 내부 데이터 출력하기 (feat. Vue, React, Javascript) (0) | 2022.09.26 |
---|---|
Vue Mutations vs Actions (feat. Composition API) (0) | 2022.09.26 |
여러 HTML 태그를 겹쳐서(overlay) 사용하고 싶을 때 (0) | 2022.09.16 |
Vue에서 글자 옆에 사각형을 그릴 때.. (0) | 2022.08.22 |
JAVA Optional of() vs ofNullable() (0) | 2022.04.01 |