-
[Vue.js] props로 받은 데이터를 data로 사용하기programing/Web 2019. 10. 10. 21:58
안녕하세요, Einere입니다.
(ADblock을 꺼주시면 감사하겠습니다.)
오늘은 제가 몇시간동안 삽질한 제 경험담을 공유하고자 합니다.
목적
현재 저는 Todo Web App을 만들고 있습니다. 해당 앱에는 TodoRouter 컴포넌트와 TodoBoard 컴포넌트가 있습니다.
TodoRouter 컴포넌트는 사용자가 투두 화면에 접근할 때 라우팅 뷰에 뿌려주기 위한 라우터 컴포넌트이며, 제가 구현한 서버로부터 데이터를 받아오는 역할을 수행합니다.
TodoBoard 컴포넌트는 TodoRouter에서 받아온 board, categories 데이터를 props로 받아서 category를 동적으로 리스트렌더링을 하는 컴포넌트입니다.
여기서 저는 props로 받는 board와 categories데이터를 초깃값으로 받은 후, 사용자 입력에 따라 로컬 데이터로써 활용하기 위해 props의 데이터와 data의 데이터를 분리하고자 합니다.
문제
Vue.js 공식 홈페이지 가이드의 단방향 데이터 흐름에서 다음과 같은 내용이 있습니다.
일반적으로 prop을 변경시키고 싶은 유혹을 불러 일으킬 수있는 두 가지 경우가 있습니다.
1. 이 prop는 초기 값을 전달 하는데만 사용되며 하위 컴포넌트는 이후에 이를 로컬 데이터 속성으로 사용하기만 합니다.
2. prop는 변경되어야 할 원시 값으로 전달됩니다.저는 부모 컴포넌트인 TodoRouter의 값을 자식 컴포넌트인 TodoBoard에서 조작하고 싶지 않았기 때문에, 책임 분리를 위해 1번에 해당하는 예제를 보고 따라했습니다.
// TodoRouter.vue <template> <todo-board :initBoard="board" :initCategories="categories"></todo-board> </template> <script> data() { return { board: {}, categories: [], }; }, mounted() { // 여기서 board와 categories데이터를 서버로부터 받아옵니다. } </script>
// TodoBoard.vue <template> <section class="category-container"> <todo-category v-for="category in categories" :key="category.id" @deleteNote="deleteNote" :category="category" ref="cate" @moveNote="moveNote" @deleteCategory="deleteCategory"></todo-category> <new-todo-category :boardId="board.id" @addNewCategory="addNewCategory"></new-todo-category> </section> </template> <script> props: { initBoard: Object, initCategories: Array, }, data() { return { board: this.initBoard, categories: this.initCategories, boardTitleEdit: false, newCategory: undefined }; }, </script>
그런데 실제로 테스트를 해보니 다음과 같은 결과를 얻었습니다.
분명 board와 categoreis가 비면 안될 터인데, 비어있습니다..
(예재대로 했는데 어째서..)
혹시 카멜케이스 케밥케이스 이슈인가 싶어서 바꿔봐도 결과는 동일했습니다.
해결
결국 페이스북에 자문을 구했는데, watch나 computed를 이용해보라고 하시더군요.
반신반의하면서 watch를 써보니, 역시나 결과는 동일했습니다.
하지못해 computed를 썻는데, 와! 정상작동합니다!
그런데 자세히 생각해보니, computed를 쓰는 것은 위에서 언급했던 단방향 데이터흐름의 "2. prop는 변경되어야 할 원시 값으로 전달됩니다."에 해당하는 사항이었습니다.
저는 변경되어야 할 데이터도 아니며, 심지어 원시(primitive) 값 또한 아니었습니다.
뭔가 우아한 방법은 아닌 듯 하지만 그래도 어쩌겠습니까.. 어찌됬건 돌아가는게 급하니까 그냥 computed를 쓰고자 합니다.
추가
나중에 구글링을 하다가 저와 비슷한 사례의 스택오버플로우 글을 발견했습니다.
위 사례에서 답변 중 하나는 다음과 같이 얘기를 하고 있습니다.
Is department populated asynchronously? What you are doing is correct. But it if is async, then the initialized value will be null and will not be updated. In which case, the computed is the correct approach.
- 부모 컴포넌트인 TodoRouter에서 data를 받을 때, 서버로부터 받기 때문에 asynchronou한 데이터입니다.
- 인스턴스 생성 순서는 부모 -> 자식이며, 마운팅 순서는 자식->부모 순입니다.
위 두 사실을 고려한다면, 제 예상 시나리오는 다음과 같습니다.
- TodoRouter인스턴스가 생성되며 data는 우선 empty한 값으로 초기화됩니다.
- TodoBoard인스턴스가 생성되며 부모로부터 받은 props이 type 조건에 의해 empty한 객체가 됩니다.
- TodoBoard의 자식 컴포넌트들이 다 마운팅 되면, TodoBoard 컴포넌트가 TodoRouter의 템플릿에 마운팅됩니다.
- TodoRouter가 서버로부터 데이터를 받아 data에 설정합니다.
- 그런데 TodoBoard의 data는 빈 객체를 레퍼런스 하고 있고, TodoRouter의 data에 새로운 객체가 대입되므로, TodoBoard의 data는 반응적으로 업데이트되지 않습니다.
아마도 props가 객체가 아닌 원시 값이라면 위 같은 상황에서도 반응형으로 잘 업데이트 하지 않을까 싶습니다. (팩트 체크 필요..)
어찌됬건, 이런 비동기적인 props를 전달받는 상황이라면 computed속성을 사용하는 것이 맞다고 하네요.
혹은 다음과 같이 v-if조건을 이용해 props로 넘겨줄 data가 트루시한 값이라면 렌더링을 해주는 방법도 있습니다.
// in the parent component <template> <child :foo="bar" v-if="bar" /> </template>
참고
https://stackoverflow.com/questions/45943682/how-to-initialize-data-properties-with-prop-values
'programing > Web' 카테고리의 다른 글
[passport] DNS를 이용하여 배포환경에서 OAuth 사용하기 (0) 2019.11.19 [sequelize, mysql2] sequelize 사용시 오류 해결 방법 (0) 2019.10.15 [NCloud] Naver NCloud로 Express서버 배포하기 - 2 (0) 2019.10.05 [NCloud] Naver NCloud로 Express서버 배포하기 - 1 (0) 2019.10.05 [Passport] isAuthenticate가 false를 반환할 때 (2) 2019.10.03 댓글