글 작성자: 개발섭

뭐야 이게...

uetify에서 Dialog를 구성하던 도중 한 페이지에 너무 많은 Dialog가 존재했다. 그로인해 코드가 너무 길어졌는데, 차라리 Component를 분리해서 관리하면 좋겠다고 생각했다.

dialog는 결국 버튼을 누르면 브라우저 내부적인 새창이 튀어오른다

그래서 일단 분리를 해서 버튼을 누르자 다음 아래와 같은 오류가 발생했다. 그리고 정상적으로 작동을 하지 않았다 딱 한번만 재대로 작동하고 다음번에 버튼을 누르면 작동을 하지 않았다. 

🚨 Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "dialog"

 

아니 내가 자식 컴포넌트에서 Props로 받은 값을 임의로 막 변경하면 오류가 난다고...?

구성 형식을 구체적으로 한번 더 자세히보자.

Vuetify 공식 홈페이지에서는 dialog를 구성할때 v-slot:activator를 통해서 구성하라고 버튼을 구성했는데, 실제로 dialog는 v-model을 통해서 받아진 값만 true or false 이기만 해도.. dialog가 켜지고 닫혀진다. 굳이 v-slot:activator를 구성해서 dialog안에 굳이 v-button을 만들필요가 없다.

즉, 특정 boolean일 만한 data 변수 하나만 가지고 있으면 특정창을 키고 끌 수 가 있는것이다.

<!--상위 컴포넌트 -->
<v-btn
       color="blue lighten-2"
       dark
       @click="window=true"
       >
  눌러!
</v-btn>

<Semidialog  :window="window" @close="window=false"/>


...
window: false

그러면 대충 이런 형식으로 dialog를 키고 끌 수 있게되는데

위의 예제에서 이 dialog를 키게하는 주체는 window라는 Boolean 변수를 통해서 전달되어지는데, 상위 컴포넌트에서 하위 컴포넌트에 window를 전달해야 한다.

그래서 하위쪽에서는 Props를 통해서 전달 받아야한다.

<!--자식 컴포넌트 -->
<v-dialog
      v-model="window"
      width="500"
    >
  ....
<v-btn color="primary" @click="window=false"> //props의 값인 window를 false로 임의로 변경
  확인
</v-btn>

위의 예제처럼 내가 Props를 통해 전달 받은 값은 -> false로 자식 컴포넌트 내부에서 임의로 바꾸게되면 위의 에러가 발생하게 된다.

왜 이러는걸까?

내 추측은 부모의 값을 자식에서 임의로 쓰슥 값을 막 만지면, 값이 내부적으로 꼬일 가능성이 커진다. 한 컴포넌트내에서 자신의 값만 건들여야하는데, 임의로 다른 컴포넌트가 이 값을 건들기 시작하면 내부적으로 값이 수정되고 바꿔치기 당할 성이 높지 않을까? 아마 그래서 임의로 Vue 내부에서 커트한게 아닌가 싶다.

그러면 위처럼 저런 문제를 해결하기 위해서는 어떤 해결책이 존재할까?

  • Props를 아예 건들지 않기
<v-btn
  color="primary"
  @click="closeDialog(window)"
>
  확인
</v-btn>

closeDialog(dialog){
  this.$emit('close', dialog) 
}

<!-- 상위 컴포넌트 -->
<Semidialog  :window="window" @close="window=false"/> => emit으로 값을 전달해서 그걸 바로 처리한다.

일단 Props를 통해서 받아온 값을 상위에서 값을 변경할 수 있도록 아예 값을 전이해버리는 방식이 존재한다.

$emit 을 통해서 자식에서 Props를 통해서 받은 값을 그대로 통로처럼 전달해서 자식 컴포넌트에서 직접 그 값을 만지는 일은 없되, 그 값이 자식 컴포넌트에 영향을 줄수 있도록 구현하는 방식을 채용하는 법이 있다.

 

아래의 다른 방식으로도 구현하려 했으나 Dialog를 켜고 끄는데는 개발 노력이 더 들어가서 굳이 이렇게 안해도 될것 같다는 생각을 했었다. 아래는 여러가지 방식에 대한 여러가지 의견을 담아보았으니 가볍게 읽고 넘어가도 좋을 것 같다. 

 

이런 방법도 존재하지만, 실제로 Dialog에서는 쓸모 없어보인다.

  • 자식 컴포넌트에서 Props의 값을 로컬 변수로 전달받아 사용하기

자식 컴포넌트에서 값을 바로 받아서 사용하는 방식도 존재하는데 아래와 같은 방식은 작동이 불가능하다.

<!--자식 컴포넌트 -->
<v-dialog
      v-model="local"
      width="500"
    >

data
  local: this.window 

왜냐면, 자식 컴포넌트를 통해서 props로 받아서 사용하면 초기화가 안되서 정상작동이 안된다. 이렇게 사용할 수 없으므로, 다른 방식을 사용해야하는데, 바로 이럴때 사용하는게 computed기능이다.

<v-dialog
      v-model="computedWindow"
      width="500"
    >

  computed:{
      computedWindow(){
        return this.window
      }
    },

이런 기능을 사용하기 위해서는 computed로 값을 초기화를 먼저 시켜준 뒤 -> 그값을 아까와 같은 방식으로 $emit으로 전달하는것인데, 이러면 local 변수로 받아서 사용하는 의미가 사라진다.

 

그리고 mounted, created와 같은 방식을 통해서 값을 넣어서 값을 초기화하는 방식도 존재하나.. 내가 보기에는 값이 업데이트 되는 방식에서 vue의 life cycle을 정확하게 알아야지  재랜더링을 통해서 업데이트시키므로 이 값을 잘 처리하려면 여러가지 따질 조건이 많아져서 어려워진다.

 

또한 computed의 setter나 Watch를 통한 값의 변경을 인식한다던가의 여러가지 방법은 존재하겠지만.. $emit보다 너무 많은 일처리를 하는 것으로 보이고, dialog을 켜고 끄는 방식에서만큼은 $emit 으로 하는게 좋다고 생각한다.

 

물론 로컬변수로 값을 불러와서 그 값을 변경시켜야하는 경우에는 여러방식을 고려해보는것도 좋다고 생각하지만, 단지 boolean만의 값을 처리하는데 너무 복잡하게 안해도 될것 같다.

결국 복잡하게 돌아가는게 이득이 될것이 없어서 위의 방법을 쓰자.

 

추가!

글또의 정종윤님이 답변을 남겨주셨다. 이해에 더 도움을 드릴 수 있을 것 같아서 요약본을 추가를 해보았다.

 

Props를 자식 컴포넌트에서 임의로 바꾸지 못하게 하는 이유는 "단방향 바인딩 규칙을 유지함으로써 데이터 변경 주체를 명확하게 관리할 수 있음"이라고 정리할 수 있을 것 같아요.

아래는 원문입니다! 

 

'FrontEnd > Vue' 카테고리의 다른 글

Vuetify 방식의 실시간 검증 방식  (0) 2021.08.19