본문 바로가기

Vue.js

Computed와 Watch


computed 속성



# 기본 예제

<div id="example">
<p>원본 메시지: "{{ message }}"</p>
<p>역순으로 표시한 메시지: "{{ reversedMessage }}"</p>
</div>
<script>
var vm = new Vue({
el: "#example",
data: {
message: "안녕하세요 computed 공부!"
},
computed: {
// 계산된 getter
reversedMessage: function() {
// `this` 는 vm 인스턴스를 가리킵니다.
return this.message
.split("")
.reverse()
.join("");
}
</script>

결과 :


computed 옵션은 함수처럼 생겼다. 하지만 취급은 속성처럼 취급한다. computed 뜻 그대로 이미 계산된 것이다. 그렇기 때문에 접근시에도 example.reversedMessage 로 메세지 역순이 나온다

일반 속성처럼 computed 속성에도 템플릿에서 데이터 바인딩 할 수 있다.

그리고 가장 중요한 것은 우리가 선언적으로(선언형 프로그래밍 방식에 따라서(아래 computed와 watch 비교에 추가 설명)) 의존 관계를 만들었다는 것이다.



# computed의 속성의 캐싱 vs 메소드


표현식에서 메소드를 호출하여 같은 결과를 얻을 수도 있다.

// ...
<p>뒤집힌 메시지: "{{ reversedMessageMethod() }}"</p>

methods: {
reversedMessageMethod: function() {
return this.message
.split("")
.reverse()
.join("");
}
}
// ...


computed 속성 대신 메소드와 같은 함수를 정의할 수도 있다. 최종 결과에 대해 두 가지 접근 방식은 서로 동일하다. 

차이점은 computed 속성은 종속 대상을 따라 저장(캐싱)된다는 것 이다. 즉, methods 는 매번 함수를 계산을 하고computed 옵션은 캐싱된 결과를 바로 리턴하는 것이다.



# computed 속성의 setter 함수


computed 속성은 기본적으로 getter 함수만 가지고 있지만, 필요한 경우 setter 함수를 만들어 쓸 수 있다.

// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...




watch 속성



대부분의 경우 computed 속성이 더 적합하지만 사용자가 만든 감시자가 필요한 경우가 있다. 

watch는 데이터 변경에 대한 응답으로 비동기시 또는 시간이 많이 소요되는 조작을 수행하려는 경우에 유용하게 사용된다.

시간이 오래걸리거나 비동기로 처리하게되면 값이 나중에 바뀌는데, 그 시간동안 사용자가 뭘할지는 알 수가 없다. 

하지만, watch를 사용하면 값이 변경되는 순간 변경된 값을 체크해내서 값을 읽어들일 수 있다.



# 예제

<div id="watch-example">
<p>yes/no 질문을 물어보세요:
<input v-model="question" /></p>
<p>{{ answer }}</p>
</div>
// ...
<!-- 이미 Ajax 라이브러리의 풍부한 생태계와 범용 유틸리티 메소드 컬렉션이 있기 때문에, -->
<!-- Vue 코어는 다시 만들지 않아 작게 유지됩니다. -->
<!-- 이것은 이미 익숙한 것을 선택할 수 있는 자유를 줍니다. -->
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: "#watch-example",
data: {
question: "",
answer: "질문을 하기 전까지는 대답할 수 없습니다."
},
watch: {
// 질문이 변경될 때 마다 이 기능이 실행됩니다.
question: function(newQuestion) {
this.answer = "입력을 기다리는 중...";
this.getAnswer();
}
},
methods: {
// _.debounce는 lodash가 제공하는 기능으로
// 특히 시간이 많이 소요되는 작업을 실행할 수 있는 빈도를 제한합니다.
// 이 경우, 우리는 yesno.wtf/api 에 액세스 하는 빈도를 제한하고,
// 사용자가 ajax요청을 하기 전에 타이핑을 완전히 마칠 때까지 기다리길 바랍니다.
// _.debounce 함수(또는 이와 유사한 _.throttle)에 대한
// 자세한 내용을 보려면 https://lodash.com/docs#debounce 를 방문하세요.
getAnswer: _.debounce(
function() {
if (this.question.indexOf("?") === -1) {
this.answer = "질문에는 일반적으로 물음표가 포함 됩니다. ;-)";
return;
}
this.answer = "생각중...";
var vm = this;
axios
.get("https://yesno.wtf/api")
.then(function(response) {
vm.answer = _.capitalize(response.data.answer);
})
.catch(function(error) {
vm.answer = "에러! API 요청에 오류가 있습니다. " + error;
});
},
// 사용자가 입력을 기다리는 시간(밀리세컨드) 입니다.
500
)
}
});
// ...


결과 :




# computed 속성 vs watch 속성


특히 AngularJS를 사용하던 경우 watch남용하는 경우가 있다. 하지만 명령적인 watch 콜백보다 계산된 속성을 사용하는 것이 더 좋다.

watch 속성은 감시할 데이터를 지정하고 그 데이터가 바뀌면 이런 함수를 실행하라는 방식으로 소프트웨어 공학에서 이야기하는 ‘명령형 프로그래밍’ 방식. 

computed 속성은 계산해야 하는 목표 데이터를 정의하는 방식으로 소프트웨어 공학에서 이야기하는 ‘선언형 프로그래밍’ 방식.


*watch 속성 남용의 예

<div id="app">
<h1>{{ count }}</h1>
<br />
<h2>computed: {{ calculated }}</h2>
<h2>watch: {{ watchedCount }}</h2>
<button @click="count --">카운트 감소</button>
</div>
// ...
new Vue({
el: "#app",
data: {
count: 3,
watchedCount: 9
},
computed: {
calculated: function() {
return this.count * 3;
}
},
watch: {
count: function(newVal) {
this.watchedCount = newVal * 3;
}
}
});
// ...

결과는 watchedCount calculated가 완벽히 똑같은 값을 출력한다.


*명령형 프로그래밍 방식

<div id="demo">{{ fullName }}</div>
// ...
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
// ...

명령형이고 또 코드를 반복하는 것을 볼 수 있다.



*선언형 프로그래밍 방식

<div id="demo">{{ fullName }}</div>
// ...
var vm = new Vue({
el: "#demo",
data: {
firstName: "Foo",
lastName: "Bar"
},
computed: {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
});
// ...

훨씬 깔끔하다. 일반적으로 선언형 프로그래밍이 명령형 프로그래밍보다 코드 반복이 적은 등 우수하다고 평가하는 경향이 있다.



# computed와 구별되는 watch 사례


 computed 가 계산된 값을 출력할 때 쓰인다면, watch 는 어떤 조건이 되었을 때 함수를 실행시키기 위한 트리거로서 사용할 수 있다.    

<div id="app3">
<h1>{{ count }}</h1>
<!--computed의 calculated안의 함수를 실행시키기 위해서는 calculated를 실제로 출력해야 한다.-->
<!-- {{ calculated }} -->
<br />
<button @click="count --">카운트 감소</button>
</div>
// ...
new Vue({
el: "#app3",
data: {
count: 3
},
computed: {
calculated: function() {
if (this.count === 2) {
alert("값이 2가 되었습니다.");
}
}
},
watch: {
count: function(newVal) {
if (newVal == 0) {
alert("값이 0이 되었습니다.");
this.count = 3;
}
}
}
});
// ...


결과는 버튼을 눌러서 count가 0이 되는 순간 alert창이 뜨고 count가 3으로 리셋이 된다. 하지만 calculated를 출력하지 않았기 때문에 computed 속성의 alert창은 나타나지 않는다.

만약 위의 calculated안에 alert창을 띄우고 싶다면 HTML 코드 안에 {{ calculated }}를 넣어서 실제로 출력을 해야calculated 안에 정의된 함수가 실행된다.



'Vue.js' 카테고리의 다른 글

클래스와 스타일 바인딩  (0) 2019.01.14
MVVM, MVC 패턴  (0) 2019.01.08
SPA(Single Page Application)  (0) 2019.01.06
템플릿 문법  (0) 2019.01.06
Vue 인스턴스  (0) 2019.01.06