Vue에서 중첩 데이터를 감시하는 법


원문 : http://michaelnthiessen.com/how-to-watch-nested-data-vue/

앱은 배열이나 객체의 데이터 속성을 가질 수 있다. 그리고 당신은 데이터가 변경될 때마다 앱이 무엇가를 수행하길 원한다.

image

그래서 watch속성을 만들 것이다, 하지만 Vue는 중첩된 데이터의 변경에 대해서는 watch하지 못한다.

다음은 이를 해결하는 방법이다.

객체나 배열을 watch할 때 deep 속성을 설정하여 Vue에게 중첩된 데이터를 watch해야 한다고 알릴 필요가 있다.

이 글에서 중첩 데이터와 deep 속성이 어떻게 생겼는지, 그리고 watch속성을 사용할 때 유용하게 써먹을 수 있는 것들을 더 자세히 설명하겠다.

또한 Vue 문서watch 사용법을 확인하면 도움이 될 것이다.

이 글에서 다룰 내용

우선 watch가 무엇인지 빠르게 복습해 볼 것이다.

둘째, 주제에서 약간 우회하여 computedwatch의 차이점을 명확히 알아볼 것이다.

셋째, 배열과 객체에서 중첩된 데이터를 watch할 수 있는 방법에 대해 살펴볼 것이다. 필요한 경우 부담없이 앞부분을 건너띄고 여기부터 읽어도 좋다(앞부분이 궁금하면 언제든지 앞부분으로 돌아와 확인하면 된다).

또한 우리는 watchimmediatehandler속성으로 무엇을 할 수 있는지 살펴볼것이다. 이 과정은 당신의 watch 사용 스킬을 한 차원 높여 줄 것이다.

그러나 그 전에 꼭 알려주고 싶은 좋은 소식이 있다.

13,000명 이상의 월간 독자에게 바친다

매일 많은 사람이 내가 쓴 글을 읽고 있다는 사실에 놀랐다. 진정 겸손하게, 내가 계속해서 글을 쓰는데 동기부여가 된다.

당신에게 감사드리고 싶었다.

나는 당신이 Vue 앱을 만들 때 겪게 될 가장 일반적인(좌절감을 주는) 문제를 해결하는 144페이지 분량의 책을 썼다.

이 책은 반응성 문제를 다루는 것 부터 혼란스러운 오류를 이해하는 것까지의 모든 것을 다룬다.

또한, 일반적인 동작들(클래스를 동적으로 변경하는 것과 같은)을 올바른 방법으로 구현하는 방법을 살펴볼 수 있을 것이다.

제일 좋은 부분은? 이 책을 돈을 받고 판매할 수 있음에도, 무료로 제공하고 있다는 것이다.

watch 메서드란 무엇인가?

Vue에서 속성이 변경될 때 우리는 변화를 지켜보고, 변화에 대응하는 무언가를 할 수 있다.

예를 들면, colour 속성(prop)이 변할때 콘솔에 로그를 기록하게 할 수 있다.

export default {
  name: 'ColourChange',
  props: ['colour'],
  watch: {
    colour()
      console.log('The colour has changed!');
    }
  }
}

watch는 많은 경우에 유용하게 사용할 수 있다. 하지만 computed 속성만으로도 충분한 문제를 watch를 사용해서 해결하는 경우가 많다.

watch를 사용할 것인가 computed를 사용할 것인가?

watch속성은 computed 속성과 비슷한 방식으로 작동하기 때문에 종종 혼동된다. 언제 어느 것을 사용할 지 아는 것은 더 까다롭다.

그러나 나는 경험을 통해 이것을 구분하는 쉬운 방법을 생각해냈다.

watch는 "부수 효과" 처리를 위한 것이다. 만약 상태를 변경하고 싶다면 computed를 사용하는 것이 좋다.

"부수 효과"는 컴포넌트 외부의 동작이나 비동기 처리를 말한다.

일반적인 예는 다음과 같다.

  • 데이터를 가져오는 것 (Fetching data)
  • DOM 조작하기
  • 로컬 저장소 또는 오디오 재생과 같은 브라우저 API 사용

이 중 어떤 것도 직접 컴포넌트에 영향을 주지 않으므로 "부수 효과"로 간주한다.

만약 이런 "부수 효과"에 대한 일이 아니면, 아마 computed을 사용하는 것이 맞을 것이다. computed다른 변화에 대한 응답으로 계산을 업데이트해야 할 때 정말 좋다.

하지만 data를 업데이트 하기 위해 watch를 사용해야 하는 경우도 있다.

때로는 computed를 사용하는 것이 의미가 없는 경우도 있다. <template>이나 메서드에서 업데이트해야 한다면, 당신의 data 안으로 들어가야 한다. 그러나 그 후의 속성 변경에 대한 응답으로 업데이트 해야 하는 경우 watch를 사용해야 한다.

image

주의: watch를 사용하여 상태를 업데이트하려는 경우 주의해야 한다. 컴포넌트와 상위 컴포넌트가 같은 상태로 (직접 또는 간접적으로) 업데이트되고 있음을 의미한다. 매우 빠르게 이상해질 수 있다.

중첩된 데이터 watch하기 - 배열 또는 객체

그럼, 실제로 watch가 필요한 경우라고 가정해보자.

그러나 배열이나 객체를 watch할 때, 예상했던 대로 동작하지 않는다.

무슨 일이 벌어지고 있는 거지?

몇가지 값이 포함된 배열을 만든다고 가정해 보자.

const array = [1, 2, 3, 4];
// array = [1, 2, 3, 4]

이제 더 많은 값을 넣어 배열을 업데이트하자.

array.push(5);
array.push(6);
array.push(7);
// array = [1, 2, 3, 4, 5, 6, 7]

질문이 있다. array가 변경되었는가?

글쎄, 그렇게 간단하지 않다.

배열의 내용은 변경되었지만, 변수는 여전히 같은 array 객체를 가리킨다. 배열 컨테이너는 변경되지 않았지만 배열 내부는 변경되었다.

따라서 Vue는 배열이나 객체를 watch할 때, 속성 내부가 변경되었다고 생각하지 않는다. Vue에게 변경을 watch할 때 속성 내부를 검사하길 원한다고 알려줘야 한다.

다음과 같이 deeptrue로 설정하고 handler 메서드를 재배치 함으로써 Vue에게 원하는 바를 알려줄 수 있다.

export default {
  name: 'ColourChange',
  props: {
    colours: {
      type: Array,
      required: true,
    },
  },
  watch: {
    colours: {
      // This will let Vue know to look inside the array
      deep: true,

      // We have to move our method to a handler field
      handler()
        console.log('The list of colours has changed!');
      }
    }
  }
}

이제 Vue는 변경 사항을 watch할 때 속성 내부의 내용을 추적해야 함을 알 것이다.

그런데 handler 함수는 무엇일까?

조금 후에 handler에 대해 알아볼 것이다. 그 보다 우선 더 중요한 watch의 immediate에 대해 알아보자.

Immediate

watch는 속성의 값이 변할 때 실행되지만, 종종 변경과 관계없이 처음 한 번 실행해야 하는 경우가 있다.

다음은 movie속성의 설정에 따라 서버에서 데이터를 가져오는 MovieData 컴포넌트 예제이다.

export default {
  name: 'MovieData',
  props: {
    movie: {
      type: String,
      required: true,
    }
  },
  data() {
    return {
      movieData: {},
    }
  },

  watch: {
    // Whenever the movie prop changes, fetch new data
    movie(movie) {
      // Fetch data about the movie
      fetch(`/${movie}`).then((data) => {
        this.movieData = data;
      });
    }
  }
}

이 컴포넌트는 훌륭하게 작동한다. movie 속성이 변경할 때마다 watch가 실행되고 새로운 데이터를 가져온다.

여기에 작은 문제가 있다는 것만 빼고는 잘 동작한다.

문제는 페이지가 로드 될 때 movie가 기본값으로 설정된다는 것이다. 그러나 속성이 아직 변경되지 않았으므로 watch는 실행되지 않는다. 다른 동영상을 선택할 때까지 데이터가 로드되지 않음을 의미한다.

그렇다면 페이지 로드 시 즉시 watch가 실행되도록 할 수 있을까?

immediatetrue로 설정하고 핸들러 함수를 아래처럼 옮긴다.

watch: {
  // Whenever the movie prop changes, fetch new data
  movie: {
    // Will fire as soon as the component is created
    immediate: true,
    handler(movie) {
      // Fetch data about the movie
      fetch(`/${movie}`).then((data) => {
        this.movieData = data;
      });
    }
  }
}

자, 여러분은 handler 함수를 두 번이나 보았다. 이제 그게 무엇인지 다룰 때가 되었다.

Handler

Vue의 watch에게 세 가지 속성을 지정할 수 있다.

  • immediate
  • deep
  • handler

처음 두 가지는 이미 보았고, 세 번째도 그리 어렵지 않다. 아마 당신도 모르게 벌써 사용하고 있었을 것이다.

handlerwatch된 속성이 변경될 때 호출될 함수를 지정한다.

우리가 흔히 보았던 코드는 deep, immediate를 지정할 필요가 없는 경우 사용하는 단축표현이다. 단축표현 대신 아래처럼 사용할 수 있다.

watch: {
  movie: {
    handler(movie) {
      // Fetch data about the movie
      fetch(`/${movie}`).then((data) => {
        this.movieData = data;
      });
    }
  }
}

단축표현을 사용하여 아래처럼 함수를 직접 지정할 수 있다.

watch: {
  movie(movie) {
    // Fetch data about the movie
    fetch(`/${movie}`).then((data) => {
      this.movieData = data;
    });
  }
}

Vue의 멋진 점은 함수를 처리하는 메서드 이름을 문자열로 지정할 수 있는 것이다. 두 개 이상의 속성이 변경될 때의 처리에 유용하다.

기존 예제에서 actor속성과 함께 movie속성도 사용하여 데이터를 가져와 보자. 메서드와 watch는 아래와 같은 모습일 것이다.

watch: {
  // Whenever the movie prop changes, fetch new data
  movie {
    handler: 'fetchData'
  },
  // Whenever the actor changes, we'll call the same method
  actor: {
    handler: 'fetchData',
  }
},

methods: {
  // Fetch data about the movie
  fetchData() {
    fetch(`/${this.movie}/${this.actor}`).then((data) => {
      this.movieData = data;
    });
  }
}

이와 같이 사용하면 여러 속성이 같은 "부수 효과"를 일으킬 때, 코드를 좀 더 깔끔하게 해준다.


김진우, FE Development Lab2019.03.07Back to list