10 템플릿 문법 - 실전

source: categories/study/vue-beginner-lv1/vue-beginner9-01.md

10.1 watch 속성


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>watch</title>
</head>
<body>
<div id="app">
    <button type="button" v-on:click="addNum">increase</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
    el: '#app',
    data: {
        num: 10,
    },
    methods: {
        addNum: function () {
            this.num++;
        }
    }
})
</script>
</body>
</html>

increase 버튼을 눌렀을 때 바로바로 num의 값이 표시되는 화면에선 올라가지않는다.(실제론 올라가지만.. Root 컴포넌트를 다시 찍으면 올라가있다)

Root 컴포넌트를 다시 누르면 값이 올라가있는 것을 볼 수 있다.

다시 위와 같이 화면에 num 값이 나오도록 data binding을 해준 다음에 확인해보자.

위와 같이 increase 버튼을 누를 때마다 실시간으로 바로바로 num 값이 증가되는 것을 볼 수 있습니다.

여기서 어떤걸 해볼거냐면, num이 증가될 때마다 console로 찍어보려고합니다.

위와 같이 logText 메소드를 만들고 콘솔에 changed 텍스트를 찍으려고합니다.

addNum 메소드가 실행되어 num 값이 1 증가할 때마다 logText 메소드가 실행돼서 changed 텍스트가 콘솔에 찍히게 해보도록 합시다.

그랬을 때 사용할 수 있는 속성이 바로 watch입니다.

watch라는 속성은 기본적으로 data를 대상으로 넣을 수 있고, data의 변화에 따라서 특정 로직을 실행할 수 있는 Vue의 속성이라고 보시면됩니다.

watch에 위와 같이 num이라는 data를 집어넣고

data 속성의 num 값이 바뀌게되면 watch 속성 안의 num이 실행이됩니다.

그랬을 때 logText가 실행되도록 위와 같이 정의할 수 있겠죠.

그럼 datanum이 바뀔 때마다 watchnum이 계속 실행이되면서 logText를 호출합니다.


<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>watch</title>
</head>
<body>
<div id="app">
   <!-- 화면에 num 값이 나오도록 data binding을 해줍니다. -->
   {{ num }}
   <!-- 아래 increase 버튼을 눌렀을 때 개발자창에서 num 값이 바로바로 올라가지 않는다. (실제론 올라가지만, Root 컴포넌트를 다시 찍으면 올라가있다) -->
   <button type="button" v-on:click="addNum">increase</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
   new Vue({
      el: '#app',
      data: {
         num: 10,
      },
      // addNum 메소드가 실행되어 num 값이 1 증가할 때마다 logText 메소드가 실행돼서 changed 텍스트가 콘솔에 찍히도록 해봅시다.
      // 그랬을 때 사용할 수 있는 속성이 바로 watch입니다.
      // 이 watch라는 속성은 기본적으로 data를 대상으로 넣을 수 있고, data의 변화에 따라서 특정 로직을 실행할 수 있는 vue의 속성이라고 보시면됩니다.
      watch: {
         // 이렇게 data에 있는 num이란 속성을 집어넣고 data의 num이 바뀌게되면 아래 num에 있는 함수가 실행되게됩니다.
         num: function () {
            this.logText();
         },
      },
      methods: {
         addNum: function () {
            this.num++;
         },
         // addNum 메소드가 실행되어 num 값이 1 증가할 때마다 logText 메소드가 실행돼서 changed 텍스트가 콘솔에 찍히도록 해봅시다.
         logText: function () {
            console.log('changed');
         },
      }
   })
</script>
</body>
</html>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>watch</title>
</head>
<body>
<div id="app">
    {{ num }}
    <button type="button" v-on:click="addNum">increase</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
    el: '#app',
    data: {
        num: 10,
    },
    watch: {
        num: function () {
            this.logText();
        },
    },
    methods: {
        addNum: function () {
            this.num++;
        },
        logText: function () {
            console.log('changed');
        },
    }
})
</script>
</body>
</html>

datanum 값이 바뀔 때마다 watchnum 함수가 실행되고, watchnum 함수가 logText 메소드를 호출하면서 위와 같이 콘솔창에 changed 텍스트가 찍힙니다.

이것이 바로 watch 속성입니다.


<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>watch</title>
</head>
<body>
<div id="app">
   <!-- 화면에 num 값이 나오도록 data binding을 해줍니다. -->
   {{ num }}
   <!-- 아래 increase 버튼을 눌렀을 때 개발자창에서 num 값이 바로바로 올라가지 않는다. (실제론 올라가지만, Root 컴포넌트를 다시 찍으면 올라가있다) -->
   <button type="button" v-on:click="addNum">increase</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
   new Vue({
      el: '#app',
      data: {
         num: 10,
      },
      // addNum 메소드가 실행되어 num 값이 1 증가할 때마다 logText 메소드가 실행돼서 changed 텍스트가 콘솔에 찍히도록 해봅시다.
      // 그랬을 때 사용할 수 있는 속성이 바로 watch입니다.
      // 이 watch라는 속성은 기본적으로 data를 대상으로 넣을 수 있고, data의 변화에 따라서 특정 로직을 실행할 수 있는 vue의 속성이라고 보시면됩니다.
      // --- 그런데 아마 이전에 배운 computed 속성과 뭐가 다른거지? 라고 생각하시는 분들이 있을 수 있습니다.
      // --- computed도 마찬가지로 data의 어떤 속성이 바뀌면 그 속성과 관련있는 부분이 자동 실행되면서 바뀌었습니다.
      watch: {
         // 이렇게 data에 있는 num이란 속성을 집어넣고 data의 num이 바뀌게되면 아래 num에 있는 함수가 실행되게됩니다.
         num: function () {
            this.logText();
         },
      },
      methods: {
         addNum: function () {
            this.num++;
         },
         // addNum 메소드가 실행되어 num 값이 1 증가할 때마다 logText 메소드가 실행돼서 changed 텍스트가 콘솔에 찍히도록 해봅시다.
         logText: function () {
            console.log('changed');
         },
      }
   })
</script>
</body>
</html>

아마 이전에 배운 computed 속성과함께 보통 엮어서 생각하시거나, computed와 뭐가 다른거지? 라고 생각하시는 분들이 있을 수도 있습니다.

이 다음 시간에 watch와 computed의 차이점에 대해 살펴보도록 하겠습니다.

10.2 watch 속성 vs computed 속성

  1. 첫번째 보실거는 datanum10이라는 값이 할당되어있다는 것입니다.
  2. 두번째로 보실 내용은 computed입니다.

    computeddobleNum이라는 함수가 정의되어있습니다.

    이 함수는 datanum이라는 속성에 2를 곱한 값return 합니다.

    dobleNum콧수염 괄호(Mustache Tag)로 화면에 표시하게됐을 때, num에서 2배된 값을 화면에 표시하게됩니다.

    이렇게 자연스럽게 vue 내부적으로 계속 계산을 하는 속성이라고 보시면됩니다.

    기본적으로 data의 의존성, 의존성이라고하는 것은 위 dobleNum 로직이 실행될 때, 기준이 되는 값은 datanum 값입니다. 이 datanum 값이 변할 때마다 computeddobleNum 함수가 실행되고 굉장히 빠르게 실행된다는 장점이 있습니다. 캐싱이라던지 다른 장점도 있지만.

  3. 세번째로 보실건 watch입니다.

    watchcomputed와 굉장히 비슷한 느낌입니다.

    watch 같은 경우도 datanum이 바뀌었을 때 특정 로직을 실행하기 때문에 computed와 별반 차이가 없어보입니다.


<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>watch</title>
</head>
<body>
<div id="app">
   <!-- 화면에 num 값이 나오도록 data binding을 해줍니다. -->
   {{ num }}
   {{ doubleNum }}
   <!-- 아래 increase 버튼을 눌렀을 때 개발자창에서 num 값이 바로바로 올라가지 않는다. (실제론 올라가지만, Root 컴포넌트를 다시 찍으면 올라가있다) -->
   <button type="button" v-on:click="addNum">increase</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
   new Vue({
      el: '#app',
      data: {
         num: 10,
      },
      // addNum 메소드가 실행되어 num 값이 1 증가할 때마다 logText 메소드가 실행돼서 changed 텍스트가 콘솔에 찍히도록 해봅시다.
      // 그랬을 때 사용할 수 있는 속성이 바로 watch입니다.
      // 이 watch라는 속성은 기본적으로 data를 대상으로 넣을 수 있고, data의 변화에 따라서 특정 로직을 실행할 수 있는 vue의 속성이라고 보시면됩니다.
      // --- 그런데 아마 이전에 배운 computed 속성과 뭐가 다른거지? 라고 생각하시는 분들이 있을 수 있습니다.
      // --- computed도 마찬가지로 data의 어떤 속성이 바뀌면 그 속성과 관련있는 부분이 자동 실행되면서 바뀌었습니다.
      watch: {
         // 이렇게 data에 있는 num이란 속성을 집어넣고 data의 num이 바뀌게되면 아래 num에 있는 함수가 실행되게됩니다.
         num: function () {
            this.logText();
         },
         // watch 같은 경우에도 data의 num이 바뀌었을 때, 특정 로직을 실행하기 때문에 computed와 별반 차이가 없어보입니다.
      },
      computed: {
         doubleNum: function () {
            // data의 의존성, 의존성이라고 하는 것은 이 doubleNum 로직이 실행될 때, 기준으로 되는 값은 data의 num 값입니다.
            // 이 data의 num 값이 변할때마다 computed의 doubleNum 함수가 실행됩니다. - 굉장히 빠르게 실행된다는 장점이 있습니다. - 캐싱이라던지 다른 장점도 있습니다.
            return this.num * 2;
         }
      },
      methods: {
         addNum: function () {
            this.num++;
         },
         // addNum 메소드가 실행되어 num 값이 1 증가할 때마다 logText 메소드가 실행돼서 changed 텍스트가 콘솔에 찍히도록 해봅시다.
         logText: function () {
            console.log('changed');
         },
      }
   })
</script>
</body>
</html>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>watch vs computed</title>
</head>
<body>
<div id="app">
    {{ num }}
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
    el: '#app',
    data: {
        num: 10,
    },
    computed: {
        doubleNum: function () {
            return this.num * 2;
        }
    },
    watch: {
        num: function (newValue) {
            this.fetchUserByNumber(newValue);
        }
    },
    methods: {
        fetchUserByNumber: function (num) {
            console.log(num);
        }
    }
})
</script>
</body>
</html>

computed

위 코드는 computedwatch의 가장 기본적인 차이점을 알기위해 적어놓은 코드입니다.

computed같은 경우는 단순한 값에 대한 계산, 특히 b-validate 라는 validation vue 라이브러리가 있는데, 그 내부적으로 구현되어있는 것들의 대부분이 computed 속성으로 구현되어있습니다.

단순한 텍스트 입력을 받아서 거기에 대한 validation 값을 계산하는 것은 computed를 많이 사용합니다.

watch

watch 같은 경우는 실제로 무거운 로직들, 매번 실행되기엔 부담스러운 그런 로직이라고 보시면됩니다.

예를 들어서 위와 같이 datanum 값이 수정되었을 때, this.fetchUserByNumber() 코드가 실행되도록 작성했죠?

위 코드를 실행하면 위와 같이 Root 컴포넌트num값과 doubleNum 값이 있는걸 볼 수 있습니다.

위와 같이 datanum값을 증가시키면 콘솔창에 11이 찍히죠?

이 부분이 watch에 설정한 함수입니다.

console.log(num); 코드 부분이 실행이 된겁니다.

위 코드를 watchnum 함수에서 실행했죠?

watchnum에서 fetchUserByNumber 함수를 실행했습니다.

watch의 함수들은 위와 같이 기본적으로 2가지 인자를 받습니다.

  • 첫번째 인자: newValue
  • 두번째 인자: oldValue

두번째 인자는 이전값이고 첫번째 인자는 갱신된 값입니다.

watch라는게 계속 값의 변화를 추적하고 있기 때문에 이전값과 현재 갱신된 값을 모두 인자로 받을 수 있습니다.

그래서 새로 갱신된 값(첫번째 인자)을 fetchUserByNumber의 인자값으로 넘기고 이 함수에서 인자값으로 받습니다.

그리고 그걸 console.log로 찍은건데,

만약 받은 인자 값을 가지고 axios.get() 데이터 요청을 했다고하면, 이게 watch의 의미에 가장 잘 맞는 사용법이라고 생각합니다.


<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>watch vs computed</title>
</head>
<body>
<div id="app">
   {{ num }}
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
   new Vue({
      el: '#app',
      data: {
         num: 10,
      },
      // 아래 코드는 computed와 watch의 가장 기본적인 차이점을 알기위해 적어놓은 코드입니다.
      // computed 같은 경우는 단순한 값에 대한 계산, 특히 b-validate 라는 validation vue 라이브러리가 있는데, 그 내부적으로 구현되어 있는 것들의 대부분이 computed 속성으로 구현되어있습니다.
      // 단순한 텍스트 입력을 받아서 거기에 대한 validation 값을 계산하는 것은 computed를 많이 사용합니다.
      computed: {
         doubleNum: function () {
            return this.num * 2;
         }
      },
      // watch 같은 경우는 실제로 무거운 로직들, 매번 실행되기엔 부담스러운 그런 로직이라고 보시면됩니다.
      // 예를 들어서 아래와 같이 data의 num 값이 수정되었을 때, this.fetchUserByNumber() 코드가 실행되도록 작성했죠?
      // watch의 함수들은 아래와 같이 기본적으로 2가지 인자를 받습니다.
      // - 첫번째 인자: newValue
      // - 두번째 인자: oldValue
      // 두번째 인자는 이전값이고 첫번째 인자는 갱신된 값입니다.
      // watch라는게 계속 값의 변화를 추적하고 있기 때문에 이전 값과 현재 갱신된 값을 모두 인자로 받을 수 있습니다.
      watch: {
         num: function (newValue, oldValue) {
            this.fetchUserByNumber(newValue, oldValue);
         }
      },
      methods: {
         fetchUserByNumber: function (num, old) {
            console.log(num, old);
         }
      }
   })
   
   // 웬만하면 computed를 사용하는 것이 좋고, watch보다는 computed가 대부분의 케이스에 적합하다라고 되어있습니다.
   // 실제로 서비스를 구현해보더라도 computed로도 충분히 해결 가능한데 굳이 watch를 쓰면 코드가 좀 지저분해진다는 문제점이 생기고,
   // computed가 이미 캐싱이라던지 이런 내부적인 튜닝이 더 많이 되어있기 때문에 watch보다는 computed를 통해서 간단한 값들을 계산하는 것을 추천드립니다.
</script>
</body>
</html>

  • computed: validation이라던지 간단한 텍스트 연산에 사용
  • watch: 무거운 동작들, 특히 데이터 요청에 적합. 위와 같이 methods에 있는 내용들과 엮어주시는게 적합할듯.

위와 같이 결론을 낸 근거자료

공식문서에서 말하는 내용은 이겁니다.

웬만하면 computed를 사용하는 것이 좋고, watch보다는 computed가 대부분의 케이스에 적합하다 라고 되어있습니다.

실제로 서비스를 구현해보더라도 computed로도 충분히 해결 가능한데 굳이 watch를 쓰면 코드가 좀 지저분해진다는 문제점이 생기고, computed가 이미 캐싱이라던지 이런 내부적인 튜닝이 더 많이 되어있기 때문에 watch보다는 computed를 통해서 간단한 값들을 계산하는 것을 추천드립니다.

10.3 computed 속성을 이용한 클래스 코드 작성 방법

이번 시간엔 computed를 이용한 직관적인 코드 작성에 대해 알아보도록 하겠습니다.

예시 코드로 class binding을 해보도록하겠습니다.

computed를 활용해서 class binding을 어떻게 할 수 있는지 알아보도록 하겠습니다.

그냥 class binding


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>watch vs computed</title>
    <style>
        .warning {
            color: red;
        }
    </style>
</head>
<body>
<div id="app">
    <p v-bind:class="{ warning: isError }">Hello</p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
    el: '#app',
    data: {
        isError: false,
    }
})
</script>
</body>
</html>


computed 활용 class binding


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>watch vs computed</title>
    <style>
        .warning {
            color: red;
        }
    </style>
</head>
<body>
<div id="app">
    <p v-bind:class="errorTextColor">Hello</p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
    el: '#app',
    data: {
        isError: false,
    },
    computed: {
        errorTextColor: function () {
            return this.isError ? 'warning' : null;
        }
    }
})
</script>
</body>
</html>

위와 같이 코드를 정리하면 템플릿 상에서 { warning: isError }를 넣을 필요 없이 아래 인스턴스에서 더 깔끔하게 코드를 정리할 수 있다.

computed는 이처럼 아주 유용한 속성이기 때문에 깊게 연구하셔서 사용하시면 좋을 것 같습니다.


<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>단순 class binding</title>
   <style>
      .warning {
         color: red;
      }
   </style>
</head>
<body>
<div id="app">
   <p v-bind:class="errorTextColor">Hello</p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
   new Vue({
      el: '#app',
      data: {
         isError: false,
      },
      computed: {
         errorTextColor: function () {
            return this.isError ? 'warning' : null;
         }
      }
   })
</script>
</body>
</html>