vue

[vue 3] computed와 v-model을 통한 데이터 바인딩.

weaklion 2023. 2. 9. 19:23

제목은 거창하게 써놨지만 별 건 아닙니다.

부모에 v-model로 연결된 값을 자식이 바꾸고자 할때 사용할 수 있는 방법입니다.

이야기만 들으면 간단하죠. 스토어를 사용하면 됩니다.

pinia를 통해 store를 만들고 store에 값을 넣으면 되죠.

 

저 역시도 그렇게 생각했었고, 실무에서 그렇게 사용할려고 했습니다.

하지만. 만약 배열 안에 있는 객체의 값만을 바꿀려면 어떻게 해야 할까요?

  items: [
    {
      uuid: '1',
      keyword: '탭 1',
      title: '',
      content: '',
      thumbnail: '',

    },
    {
      uuid: '2',
      keyword: '탭 2',
      title: '',
      content: '',
      thumbnail: '',

    },
    {
      uuid: '3',
      keyword: '탭 3',
      title: '',
      content: '',
      thumbnail: '',

    },
  ],

저 같은 경우, 코드를 작성할때에 아토믹 디자인 형식으로 코드를 작성합니다. 

 

아토믹 디자인을 활용한 디자인 시스템 도입기

카카오엔터테인먼트 FE 기술블로그

fe-developers.kakaoent.com

 

위는 카카오에서 아토믹 디자인을 이용한 디자인 시스템 도입기로, 잘 모르실 때에 참조하면 좋습니다.

아무튼, 그래서 폴더 구조를 크게 분류하면,

src

- pages

   -기능별 페이지

- components

  - atoms

  - commons

  - resources

- ...

이런 식으로 구성이 되어 있죠.

atoms에 페이지를 구성하는. 그리고 로직이 없는 컴포넌트들을 담고, atoms를 합친 걸 commons에 담습니다.

그리고 commons를 합친 것에 로직을 담아 resources에 담고 이후 resources의 컴포넌트들을 pages에 담습니다.

이후 pages에 라우터를 연결하는 형식으로 pages가 보여지는 거죠.

그래서 props를 통해 값을 전달하게 되면 여러번 전달하는 형식이 많아집니다.

 

잡설이 길었는데, 예제로 넘어가보죠.

//parent.vue

<template>
  <template v-for="(item, index) in items">
    <ContentsCard v-model="items[index]" />
  </template>
</template>


<script setup lang="ts">
const { ref } from 'vue';

const items = ref({
  items : [
    {
      uuid: '1',
      keyword: '탭 1',
      title: '',
      content: '',
      thumbnail: '',

    },
    {
      uuid: '2',
      keyword: '탭 2',
      title: '',
      content: '',
      thumbnail: '',

    },
    {
      uuid: '3',
      keyword: '탭 3',
      title: '',
      content: '',
      thumbnail: '',

    },
    ]
})

</script>

items 배열을 v-for를 이용해서 렌더링 한뒤에 v-model을 통해 데이터 바인딩을 시도했습니다.

여기서도 사실 v-model을 5개 둬도 되지만, 객체 내부의 값이 많아지게 된다는 수를 가정해서 object를 통채로 v-model을 넣었습니다. 

이제 ContentsCard.vue를 보죠.

 

//contentCard.vue

<template>
  <div>
      <div class="field">
        <label for="title"> <span > * </span> 제목 </label>

        <Input id="title" v-model="localModelValue.title" type="text />
      </div>
      
      <div class="field">
        <label for="title"> <span > * </span> 내용 </label>

        <Textarea id="title" v-model="localModelValue.content" type="text />
      </div>

  </div>
</template>

<script setup lang="ts">

const $props = defineProps<{
  modelValue: any;
}>();
const $emit = defineEmits(['update:modelValue']);

const localModelValue = computed({
  get: () => $props.modelValue,
  set: (value) => $emit('update:modelValue', value),
});

</script>

vue 3에서는 model로 오는 값을 prop로 처리할 수 있습니다.

만약 modelValue에 있는 값을 그대로 input에 v-model로 처리하려 한다면 지속적으로 warning이 뜨겠죠.

이유는 간단합니다. props를 자식이 그대로 변경시키는 건 vue에서 권장하지 않기 때문입니다.

그래서 보통 자식 컴포넌트 내부에 새로운 변수를 생성하고 거기에 props를 넣은 후에 v-model을 연동합니다.

 

허나 여기선 modelValue를 computed로 전환한 후에 get과 set 메소드를 선언하고 그 값을 그대로 사용했습니다.

vue3는 vue2와는 다르게 proxy객체로 이루어져 있습니다.

 

여기선 localModelValue를 computed로 선언하고 get과 set을 새로 정의해서 사용했습니다.

그리고 각각의 input에서 localModelValue를 v-model로 바인딩했습니다.

이렇게 되면 input에서 값이 바뀔때마다 emit을 통해 값이 업데이트 되고, props에 있는 items내부의 값이 바뀌게 됩니다.

 

물론 더 좋은 방법이 있겠습니다만, 저는 이게 제일 깔끔할 것 같아서 사용했습니다.

 

'vue' 카테고리의 다른 글

v-if와 v-show의 차이점.  (0) 2023.04.07