Vue 기본 문법 3. computed, method, watch 정리 (1편)

손근영·2025년 5월 13일

Vue

목록 보기
4/7

지난 글에서는 Vue의 주요 디렉티브 중 하나인 v-model을 다뤘습니다.
특히 v-model을 통해 JavaScript와 HTML 간 양방향 바인딩이 얼마나 자연스럽게 이루어지는지를 확인할 수 있었습니다.

예를 들어, 사용자가 입력창에 특정 값을 입력하면 JS 변수도 자동으로 바뀌고, 반대로 변수 값이 바뀌면 화면에도 곧바로 반영되는 것을 볼 수 있었습니다.
Vue의 반응형 시스템 덕분에 정말 간단하게 "데이터 ↔ 화면"의 흐름을 제어할 수 있습니다.


그런데 다음과 같은 상황이 분명히 존재합니다.

사용자 입력만 반영하는 게 아니라,

입력된 데이터를 가공하거나, 조건에 따라 계산해서 보여줘야 할 때는 어떻게 해야 할까요?

예를 들어 사용자가 장바구니에 상품을 담는 쇼핑몰을 만든다고 해봅시다.

각 상품에는 가격수량이 있고

우리는 화면 어딘가에 "총합계"를 실시간으로 보여줘야 합니다

이 총합은 상품이 추가되거나 수량이 변경될 때 자동으로 다시 계산되어야 합니다.


이렇게 단순 데이터 바인딩만으로는 부족할 때,
Vue에서는 계산된 데이터를 다루기 위한 문법을 제공합니다.
대표적인 것이 바로method, computed, watch입니다.

method

앞에서 살펴본 장바구니 예제를 계속 이어가 봅시다.

const cartItems = ref([
  { name: '사과', price: 1000, quantity: 2 },
  { name: '바나나', price: 1500, quantity: 1 }
])

지금 cartItems라는 반응형 변수에는 사과와 바나나, 두 개의 상품이 담겨 있습니다.
그런데 사용자가 장바구니에 새로운 상품을 추가하거나 수량을 변경하면,
우리는 총합계 가격도 실시간으로 다시 계산해서 화면에 보여줘야 합니다.


어떻게 구현할 수 있을까요?

가장 먼저 떠오르는 방법은, 합계를 계산하는 함수를 따로 정의하고
필요할 때마다 호출하는 방식일 것입니다.

const getTotalPrice = function() {
  return cartItems.value.reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  )
}

cost totalPrice = getTotalPrice()

그리고 이렇게 얻은 totalPrice 값을 화면에 출력하겠죠.

<p>총합: {{ totalPrice }}</p>

하지만 이 방식에는 치명적인 문제가 있습니다.
cartItems가 바뀌어도 getTotalPrice()는 자동으로 다시 실행되지 않기 때문에,
우리가 수동으로 계속 함수 호출 → 값 다시 할당 → 화면 업데이트를 반복해야 합니다.


Vue에서는 더 간단한 방법이 있습니다.

Vue에서는 이런 계산을 method로 정의하고,
템플릿에서 직접 함수처럼 호출할 수 있습니다.

const getTotalPrice = () => {
  return cartItems.value.reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  )
}

<p>총합: {{ getTotalPrice() }}</p>

이처럼 Vue 템플릿 안에서는 method를 호출할 수 있기 때문에,
마치 변수를 사용하는 것처럼 간단하게 값을 출력할 수 있습니다.


어떻게 이게 가능할까요?

Vue는 내부적으로 반응형 상태가 바뀌면 템플릿을 다시 렌더링합니다.
즉, cartItems가 변경되면 컴포넌트는 다시 렌더링되고,
그때 getTotalPrice()도 다시 호출되어 최신 합계가 반영되는 것이죠.

이와 비슷한 경험을 예전에 했을 수도 있습니다.
예를 들어 v-model.lazy를 사용하지 않고 input에 값을 입력하면,
입력할 때마다 화면이 즉시 반응하는 것을 볼 수 있었죠.
Vue는 그렇게 데이터의 변화에 따라 화면을 자동으로 갱신해주는 프레임워크입니다.


method는 언제 호출될까?

지금 코드에서는 getTotalPrice라는 method를 만들어 총합을 계산하고 있습니다.
그렇다면 이 함수는 언제 호출되는 것이 가장 자연스러울까요?

당연히 cartItems에 어떤 값이 추가되거나, 수량이 바뀔 때
즉, 실제 데이터가 변경될 때마다 한 번씩 호출되어 합계를 다시 계산하는 것이 가장 이상적입니다.


그런데 과연 실제로 그렇게 동작할까요?

다음은 Vue의 템플릿 안에서 method를 사용하고 있을 때,
console.log()를 이용해 얼마나 자주 호출되는지를 살펴본 예시입니다.

<script setup>
import { ref } from 'vue'

const cartItems = ref([
  { name: '사과', price: 1000, quantity: 2 },
  { name: '바나나', price: 1500, quantity: 1 }
])

const inputValue = ref('') // 단순 렌더링 유도용

// 장바구니에 상품 추가
const addApple = () => {
  cartItems.value.find(item=>item.name=='사과').quantity++;
}
const addBanana = () => {
  cartItems.value.find(item=>item.name=='바나나').quantity++;
}

// method 함수
const getTotalPrice = () => {
  console.log('getTotalPrice() 실행됨')
  return cartItems.value.reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  )
}
</script>

<template>
  <header>
    <h1>🛒 장바구니 예제</h1>
  </header>

  <main>
    <div>
      <button @click="addApple">사과 추가</button>
      <button @click="addBanana">바나나 추가</button>
    </div>

    <input v-model="inputValue" placeholder="아무거나 입력해보세요." />

    <ul>
      <li v-for="(item, index) in cartItems" :key="index">
        {{ item.name }} - {{ item.price }}원 × {{ item.quantity }}
      </li>
    </ul>

    <p>총합: {{ getTotalPrice() }}</p>
  </main>
</template>

과연 어떻게 동작할까요?

예상하신 분도 있겠지만,
버튼을 클릭할 때뿐만 아니라, input에 값을 입력하거나 다른 어떤 동작만 해도
getTotalPrice() 함수는 계속해서 호출되는 것을 확인할 수 있습니다.

이는 Vue가 반응형 상태의 변화 → 렌더링 발생 → 템플릿 재계산이라는 과정을 자동으로 처리하기 때문에 생기는 자연스러운 결과입니다.
하지만 우리가 정말 원하는 동작은 아닙니다.

단순히 화면의 다른 부분이 바뀌었다고 해서 매번 합계를 다시 계산하는 건 불필요한 낭비입니다.


이런 상황에서는 computed가 더 적합합니다.

실제로는 값이 바뀌었을 때만 계산되길 원하잖아요?

그럴 때 사용하는 것이 바로 computed 입니다.
내부에서 사용하는 반응형 값이 바뀔 때만 다시 계산하고,
그 외에는 캐싱된 결과를 재사용합니다.


다음 편에서는 computed를 사용하면 이 문제를 어떻게 해결할 수 있는지 살펴보겠습니다.

0개의 댓글