ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Vue 데이터 바인딩
    개발/Front-end 2021. 8. 25. 15:32
    728x90

    이번 시간에는 HTML 입력 폼 객체에 따른 데이터 바인딩 방법을 알아보겠습니다. 

     

    1. 데이터 바인딩이란?

    Vue는 Angular와 마찬가지로 양방향 데이터 바인딩을 지원합니다. 아래 MVVM 패턴 그림을 봅니다. 뷰 모델에 있는 데이터 바인딩은 양방향 데이터 바인딩이 이뤄집니다. 모델과 뷰 중 어느 한쪽에 변경이 일어나면 다른 한쪽에 반영됩니다.

    • MVVM 패턴: Model-View-ViewModel 약자로 프로그래밍 로직과 화면에 해당하는 View를 분리해서 개발하기 위해 설계된 패턴입니다.
      • 모델: 데이터를 담은 용기. 서버에서 가져온 데이터를 js 객체로 저장
      • 뷰: 사용자 화면
      • 뷰 모델(Vue): 뷰와 모델의 중간 영역으로 돔 리스너와 데이터 바인딩을 제공
        • 돔 리스너: 돔의 변경 내용을 즉각적으로 반응하여 특정 로직을 수행
        • 데이터 바인딩: 뷰에 표시되는 내용과 모델의 데이터를 동기화 (양방향)

     


    2. 뷰의 템플릿 문법

    뷰의 템플릿 문법이란 뷰로 화면을 조작하는 방법을 의미합니다. 템플릿 문법은 크게 데이터 바인딩과 디렉티브로 나뉩니다.

    2-1. 데이터 바인딩

    데이터 바인딩은 뷰 인스턴스에서 정의한 속성들을 화면에 표시하는 방법입니다. 가장 기본적인 데이터 바인딩 방식은 콧수염 괄호(Mustache Tag)입니다.

    <div>{{ message }}</div>

     

    2-2. 디렉티브

     디렉티브는 v- 접두사가 있는 특수 속성입니다. 디렉티브는 뷰로 화면의 요소를 더 쉽게 조작하기 위한 문법입니다. 화면 조작에서 자주 사용되는 방식들을 모아 디렉티브 형태로 제공하고 있습니다.

    아래 디렉티브는 가장 자주 사용되는 디렉티브입니다.

    • v-bind
    • v-on
    • v-model

    3. 데이터 바인딩

    3-0. 데이터 바인딩을 위한 기본 코드 작성

    데이터 바인딩을 학습하기 위해 새로운 페이지(vue)를 추가해줍니다.

    • src/views/DataBinding.vue 파일 생성
      • script 태그에 있는 data 프로퍼티에 정의된 title이 template 태그 안에 {{ title }}에 바인딩되었습니다. {{}} 이중 괄호를 이용해서 html 바인딩할 수 있습니다.
    <template>
      <h1>Hello, {{ title }}</h1>
    </template>
    
    <script>
    export default {
      data() {
        return {
          title: 'World'
        }
      }
    }
    </script>
    <style>
    </style>
    • src/router/index.js
      • DataBinding 라우터를 추가합니다.
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Home from '../views/Home.vue'
    import DataBinding from '../views/DataBinding.vue'
    
    Vue.use(VueRouter)
    
    const routes = [
    ... 생략 
      {
        path: '/databinding',
        name: 'DataBinding',
        component: DataBinding
      },
    ]
    
    • src/App.vue
      • 화면에 DataBinding 메뉴를 보여주기 위해 아래와 같이 DataBinding 메뉴를 추가합니다.
    <template>
      <div id="app">
        <div id="nav">
          <router-link to="/">Home</router-link> |
          <router-link to="/about">About</router-link>  |
          <router-link to="/databinding">DataBinding</router-link>
        </div>
        <router-view/>
      </div>
    </template>
    

    문자열 바인딩 적용 화면

     

    3-1. 문자열 바인딩

    앞에서 실행한 것과 같이 이중 괄호를 이용해서 문자열 바인딩을 할 수 있습니다.

      <h1>Hello, {{ title }}</h1>

     

    3-2. raw(원시) HTML 데이터 바인딩

    HTML 태그를 바인딩할 때는 문자열 바인딩할 때 사용한 이중 중괄호를 이용하면 안 됩니다.

    이중 중괄호(mustaches)는 HTML이 아닌 일반 텍스트로 데이터를 해석합니다. 실제 HTML을 출력하려면 v-html 디렉티브를 사용해야 합니다.

    • src/views/DataBinding.vue
    <template>
      <div>
        <div>{{htmlString}}</div>
        <div v-html="htmlString"></div>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          htmlString: '<p style="color:red">This is a red string</p>'
        };
      }
    }
    </script>
    <style>
    </style>

    이중괄호, v-html 디렉티브 사용 결과

     

    3-3. Form 입력 데이터 바인딩

    웹 페이지에서 사용자로부터 데이터를 입력받을 수 있는 필드를 fom element라고 합니다. v-model 디렉티브를 사용하여 양방향 데이터 바인딩을 생성할 수 있습니다.

    3-3-1. Input type=text

    사용자로부터 텍스트를 입력받을 수 있는 Input type=text 경우, 입력받은 텍스트가 value에 저장됩니다. 아래에서 v-model은 내부적으로 Input type=text의 value 속성을 사용하게 됩니다. 따라서 value 속성이 서로 양방양으로 데이터 바인딩 설정됩니다.

    <template>
      <div>
       <input type="text" v-model="valueModel">
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          valueModel: 'South Korea'
        };
      }
    }
    </script>
    <style>
    </style>

    Input type=text 객체의 value에 valueModel 값인 'South Korea'에 바인딩되어서 화면에 나타납니다.

     

    3-3-2. Input type=number

    아래는 v-model.number="numberModel"로 데이터 바인딩이 되어 기본 숫자가 3으로 설정됩니다.

    <template>
      <div>
       <input type="number" v-model="numberModel">
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          numberModel: 3
        };
      }
    }
    </script>
    

    3-3-3. textarea

    <template>
      <div>
       <textarea v-model="message"></textarea>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          message: "여러 줄을 입력할 수 있는 textarea 입니다."
        };
      }
    }
    </script>

    3-3-4. select

    v-model은 내부적으로 select의 value 속성을 사용해서 양방향 데이터 바인딩을 합니다.

    <template>
      <div>
       <select v-model="city">
         <option value="02">서울</option>
         <option value="21">부산</option>
         <option value="064">제주</option>
       </select>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          city: "064"
        };
      }
    }
    </script>

    기본값으로 064를 설정하여 초기에 제주가 설정되어 있습니다.

    3-3-5. 체크박스(input type=checkbox)

    체크박스는 input type=text, select와 다르게 v-model 내부적으로 value 속성이 아닌 checked 속성을 사용합니다. 따라서 vaule 속성을 사용하기 위해서는 v-bind:value를 사용해야 합니다.

    <template>
      <div>
        <label>
          <input type="checkbox" v-model="checked"> {{ checked }}
        </label>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          checked: true
        };
      }
    }
    </script>

    아래 true-value="yes"를 통해 체크되었을 때 기본값은 yes. false-value="no"를 통해 체크 해제되었을 때 기본 값을 no로 설정하였습니다.

    <template>
      <div>
        <label>
          <input type="checkbox" v-model="checked" true-value="yes" false-value="no"> {{ checked }}
        </label>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          checked: true
        };
      }
    }
    </script>

    yes, no로 기본값 설정 

     

     

    여러 개의 체크박스를 사용할 때는 배열을 이용해서 데이터 바인딩을 한 번에 처리할 수 있습니다.

    <template>
      <div>
        <label><input type="checkbox" value="서울" v-model="checked">서울</label>
        <label><input type="checkbox" value="부산" v-model="checked">부산</label>
        <label><input type="checkbox" value="제주" v-model="checked">제주</label>
        <br>
        <span> 체크한 지역: {{ checked }}</span>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          checked: []
        };
      }
    }
    </script>

    배열을 이용해서 데이터 바인딩

     

     

     

    3-3-6. 라디오(input type=radio)

    라디오 역시 체크박스와 동일하게 v-model은 내부적으로 checked 속성과 바인딩이 이뤄집니다. value 바인딩을 하고 싶다면 v-model이 아닌 v-bind:value을 사용해야 합니다.

    <template>
      <div>
        <label><input type="radio" v-bind:value="radioValue1" v-model="picked">서울</label>
        <label><input type="radio" v-bind:value="radioValue2" v-model="picked">부산</label>
        <label><input type="radio" v-bind:value="radioValue2" v-model="picked">제주</label>
        <br>
        <span> 체크한 지역: {{ checked }}</span>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          picked: '',
          radioValue1: '서울',
          radioValue2: '부산',
          radioValue3: '제주',
        };
      }
    }
    </script>

     

     

    라디오 바인딩

     

    3-4. 속성(Attribute)

    3-4-1. img 객체의 src

    이미지 주소를 img 객체의 src에 바인딩하는 경우 v-bind:src=""를 사용합니다.

    <template>
      <div>
       <img v-bind:src="imgSrc" />
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          imgSrc: "https://kr.vuejs.org/images/logo.png"
        };
      }
    }
    </script>

    3-4-2. button 객체의 disabled

    버튼 객체를 disabled 속성을 넣어서 비활성화시키려고 합니다. v-bind:disabled 속성이 true 되어 있으면 버튼이 비활성화됩니다.

    <template>
      <div>
       <input type="text" v-model="textValue" />
        <button type="button" v-bind:disabled="textValue==''">Click</button>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          textValue: ""
        };
      }
    }
    </script>

    예제 코드는 input에 값이 입력되면 버튼이 활성화되고 비어있는 경우는 비활성화됩니다.

    disabled 속성에서 disabled 제거되는 화면 

     

     

    3-5. 클래스 바인딩

    조건에 따라 바인딩할 클래스를 설정해야 하는 경우 v-bind:class를 이용해서 추가적으로 정의해서 사용할 수 있습니다.

    <template>
      <div class="container" v-bind:class="{
        'active': isActive, 'text-red': hasError
      }">Class Binding</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          isActive: true,
          hasErrors: false
        };
      }
    }
    </script>
    <style scoped>
    container {
      width: 100%;
      height: 200px;
    }
    .active {
      background-color: yellow;
      font-weight: bold;
    }
    
    .text-red {
      color: red;
    }
    </style>

    위 코드 결과는 class="container active"가 적용됩니다.

    아래와 같이 배열을 이용해서 클래스를 바인딩할 수도 있습니다.

    <template>
      <div class="container" v-bind:class="[activeClass, errorClass]">Class Binding</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          activeClass: 'active',
          errorClass: 'text-red'
        };
      }
    }
    </script>
    <style scoped>
    container {
      width: 100%;
      height: 200px;
    }
    .active {
      background-color: yellow;
      font-weight: bold;
    }
    
    .text-red {
      color: red;
    }
    </style>

    위 코드 결과는 class="container active text-red"가 적용됩니다. 그러나 배열을 사용하면 조건에 따른 true/false로 할 수 없습니다.

     

    3-6. 인라인 스타일 바인딩

    <template>
     <div v-bind:style="styleObject">인라인 스타일 바인딩</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          styleObject: {
            color: 'red',
            fontSize: '13px'
          }
        };
      }
    }
    </script>

    인라인 스타일 바인딩 역시 배열을 이용해서 바인딩할 수 있습니다.

    <template>
     <div v-bind:style="[baseStyle, addStyle]">인라인 스타일 바인딩</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          baseStyle: 'background-color: yellow; width:100%; height:200px',
          addStyle: 'color:red;font-weight:bold'
        };
      }
    }
    </script>

    4. 리스트 랜더링 (v-for)

    다중 데이터를 처리해야 할 일이 있는 경우 반복되는 객체를 처리할 때가 존재합니다. select의 option, table의 tr 데이터는 반복되는 객체를 많이 처리해야 합니다.

    v-for 디렉티브를 이용해서 배열 데이터를 바인딩할 수 있습니다.

    사용 방법은 아래와 같은 형식으로 사용합니다.

    v-for="(item, index) in items"

     

    아래와 같은 테이블 예제를 입력해봅니다.

    <tr :key="i" v-for="(product,i) in productList"> 에서 for문으로 상품 리스트를 반복해서 돌립니다. key는 가상 돔 알고리즘이 노드의 ID를 식별하기 위한 힌트로 사용됩니다. vue 사이트에서 자세히 확인해 볼 수 있습니다.

    <template>
      <div>
        <table>
          <thead>
            <tr>
              <th>제품명</th>
              <th>가격</th>
              <th>카테고리</th>
              <th>배송료</th>
            </tr>
          </thead>
          <tbody>
          <tr :key="i" v-for="(product,i) in productList">
            <td>{{product.product_name}}</td>
            <td>{{product.price}}</td>
            <td>{{product.category}}</td>
            <td>{{product.delivery_price}}</td>
          </tr>
          </tbody>
        </table>
    
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          productList: [
            { "product_name": "기계식키보드", "price": 25000, "category": "노트북/테블릿", "delivery_price": 5000},
            { "product_name": "무선마우스", "price": 12000, "category": "노트북/테블릿", "delivery_price": 5000},
            { "product_name": "아이패드", "price": 725000, "category": "노트북/테블릿", "delivery_price": 5000},
            { "product_name": "테블릿거치대", "price": 32000, "category": "노트북/테블릿", "delivery_price": 5000},
            { "product_name": "무선충전기", "price": 42000, "category": "노트북/테블릿", "delivery_price": 5000},
    
          ]
    
        };
      }
    }
    </script>
    <style scoped>
    table {
      font-family: arial, sans-serif;
      border-collapse: collapse;
      width: 100%
    }
    td, th {
      border: 1px solid #dddddd;
      text-align: left;
      padding: 8px;
    }
    </style>

    리스트 랜더링 적용 화면


    5. 랜더링 문법(v-if, v-show)

    vue 컴포넌트에서 조건에 따라 랜더링을 하는 방법은 v-if 디렉티브와 v-show 디렉티브를 사용하는 방법이 있습니다.

    5-1. v-if

    아래와 같은 v-if, v-else-if, v-else를 넣어서 조건문에 만족하는 경우 화면에 보이게 할 수 있습니다.

    <template>
      <div>
        <h1 v-if="type=='A'">A</h1>
        <h1 v-else-if="type=='B'">B</h1>
        <h1 v-else>C</h1>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
         type: 'A'
        };
      }
    }
    </script>

    5-2. v-show

    v-show 디렉티브를 통해 화면에 숨기거나 보이게 할 수 있습니다. 여기서는 false로 설정되어서 보이지 않습니다.

    <template>
      <div>
        <h1 v-show="bShow">bShow가 true이면, 현재 블록이 화면에 보이게 됩니다.</h1>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          bShow: false
        };
      }
    }
    </script>

    style="display" 를 확인 가능

     

    5-3. v-if와 v-show의 차이점

    둘은 비슷해 보이지만, 내부에서 랜더링 되는 방식은 큰 차이가 있습니다.

    • v-if: 조건을 만족하는 경우 그 순간 html 블록 생성/제거
      • 토글이 일어날 때 블록 전체를 생성하다가 삭제하기 때문에 v-show보다 많은 자원 사용하게 됨 ⇒ 토글 빈도가 적은 경우 사용
    • v-show: css display를 이용해서 화면에 숨기거나 보이게 됨
      • 처음에 조건이 만족하지 않아도 html 블록을 무조건 생성하여 자원을 사용 ⇒ 토글이 자주 일어나는 경우 사용

    6. 이벤트 처리(v-on)

    vue 컴포넌트에서 이벤트를 처리할 때는 v-on 디렉티브를 사용합니다. v-on 디렉티브는 심볼 @로 사용 가능합니다.

    6-1. 클릭 이벤트(v-on:click)

    v-on:click="메소드명" 혹은 @click="메소드명"을 사용해서 추가할 수 있습니다.

    <template>
      <div>
        <button type="button" @click="increaseCounter">Add 1</button>
        <p>the counter is: {{counter}}</p>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          counter: 0
        };
      },
      methods: {
        increaseCounter(){
          this.counter = this.counter + 1
        }
      }
    }
    </script>

    counter가 증가 

     

    • 클릭 이벤트를 통해 지정된 함수로 파라미터를 전달하고 싶다면 아래와 같이 전달 가능합니다. 
    <template>
      <div>
        <button type="button" @click="setCount(7)">set 7</button>
        <p>the counter is: {{counter}}</p>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          counter: 0
        };
      },
      methods: {
        setCount(counter){
          this.counter = counter;
        }
      }
    }
    </script>
    • 클릭이벤트 발생 시 여러 개의 함수를 호출하고 싶다면 아래와 같이 설정하면 가능합니다. 
    <template>
      <div>
        <button type="button" @click="one(), two()">Click</button>
      </div>
    </template>
    
    <script>
    export default {
      methods: {
       one(){
         alert("One");
       },
        two(){
          alert("Two");
        }
      }
    }
    </script>

    여러 함수 호출 

     

    6-2. change 이벤트

    change 이벤트가 가장 많이 사용되는 html 태그는 select 입니다. 사용자가 select에서 옵션을 바꿀 때마다 change 이벤트가 발생합니다.

    @change="메소드명"으로 사용합니다.

    <template>
      <div>
       <select v-model="selectedValue" @change="changeSelect">
         <option value="서울">서울</option>
         <option value="부산">부산</option>
         <option value="제주">제주</option>
       </select>
      </div>
    </template>
    
    <script>
    export default {
      data(){
        return {
          selectedValue: ''
        };
      },
      methods: {
        changeSelect(){
          alert(this.selectedValue);
        }
      }
    }
    </script>

     

    변경될때 마다 alter 호출됨 

     

    6-3. key 이벤트

     key 이벤트는 사용자가 키보드 자판을 입력할 때 발생하는 이벤트입니다. 예를 들어 구글 검색을 사용할 때 검색 시, 버튼을 클릭할 수도 있지만 키보드 자판에서 엔터키를 입력합니다. 이때 검색 버튼을 클릭했을 때 호출하는 함수와 동일하게 호출하도록 구현됩니다. 이때 사용되는 것이 key 이벤트입니다.

     

    아래 @keyup.enter=를 통해 엔터를 감지할 수 있습니다.

    <input @keyup.enter="submit" />

    엔터 이외에도 vue에서는 자주 사용되는 key 이벤트를 제공합니다. vue.js 문서 가이드에서 확인 가능합니다.


    7. computed와 watch

    computed와 watch 둘 다 Vue 인스턴스 내 정의된 데이터 값에 변경이 일어나는지 감시하고 변경될 때마다 정의된 함수가 실행됩니다. 데이터의 값이 변경되었는지를 계속 감시한다는 측면에서 computed와 watch는 매우 비슷해 보이지만, 사용되는 용도에는 분명 차이가 있습니다.

     

    7-1. computed

    아래는 fullName을 method, computed에 정의해서 해당 문자열을 합쳐 출력합니다. computed는 method에 정의한 것과 다르게 화면 내 여러 곳에서 호출하더라도 한 번만 호출됩니다. 그리고 method와 다르게 변경된 경우 감지(확인 필요)를 하게 됩니다.

    7-1-1. method 정의

    우선 method와 computed 이용해서 정의하는 방식을 비교하기 위해 method로 fullName을 정의합니다.

    <template>
      <div>
        <h1>Full Name: {{fullName()}}</h1>
        <h1>Full Name: {{fullName()}}</h1>
      </div>
    </template>
    
    <script>
    export default {
      data(){
        return {
          firstName : "JungEe",
          lastName : "Yoo",
        };
      },
      methods: {
        fullName() {
          console.log("methods");
          return this.firstName + ' ' + this.lastName;
        }
      }
    
    }
    </script>

    결과를 보면 2번 호출하여 log가 2번 호출됩니다. 값이 변경되어도 똑같이 2번 호출됩니다.

     

    7-1-2. computed 정의

    이번에는 computed로 정의해보겠습니다.

    <template>
      <div>
        <h1>Full Name: {{fullName}}</h1>
        <h1>Full Name: {{fullName}}</h1>
      </div>
    </template>
    
    <script>
    export default {
      data(){
        return {
          firstName : "JungEe",
          lastName : "Yoo",
        };
      },
      computed: {
        fullName() {
          console.log("computed");
          return this.firstName + ' ' + this.lastName;
        }
      }
    
    }
    </script>

    computed로 정의한 경우 method와 다르게 log가 한번 호출됩니다.

     

    7-2. watch

    computed 경우는 기존에 정의된 데이터 값을 기반으로 새로운 데이터 값을 활용하기 위해 사용됩니다. 반면 watch는 정의된 데이터 값 하나만 감시하기 위한 용도로 사용됩니다.

    watch는 초기에는 실행하지 않습니다.

    <template>
      <div>
        <h1>Full Name: {{fullName}}</h1>
      </div>
    </template>
    
    <script>
    export default {
      data(){
        return {
          firstName : "JungEe",
          lastName : "Yoo",
          fullName: ''
        };
      },
      watch: {
        firstName() {
          console.log("watch- firstName");
          this.fullName = this.firstName + ' ' + this.lastName;
        },
        lastName() {
          console.log("watch - lastName");
          this.fullName = this.firstName + ' ' + this.lastName;
        }
      }
    }
    </script>

    아래와 같이 값을 변경해야 watch에 존재하는 로그가 출력되는 것을 확인할 수 있습니다.

     

    7-3. computed vs watch

    둘의 차이점을 확인해보면 아래와 같습니다.

    • method: 같은 함수 호출 시, 여러 번 호출
    • computed: 기존 데이터의 변경을 감지합니다. + 최초에 computed에 정의된 데이터 함수를 실행합니다. ⇒ 간단한 계산, 대부분 computed 사용
    • watch: 초기에 할당된 값에서 변경이 일어나야 watch에 정의된 함수 실행합니다. ⇒ 무거운 로직에 사용

    아래 사이트에 언제 computed를 사용해야 하고 watch를 사용해야 하는지 확인할 수 있습니다.

    Computed | Cracking Vue.js

     

    Computed | Cracking Vue.js

    computed 속성 컴퓨티드(computed) 속성은 템플릿의 데이터 표현을 더 직관적이고 간결하게 도와주는 속성입니다. 바로 코드를 보겠습니다. computed 속성 예시 위 코드는 message라는 데이터 속성의 문자

    joshua1988.github.io

     

     

     

    이번 시간에는 vue 데이터 바인딩에 대한 방법에 대해서 알아보았습니다. 데이터 바인딩 관련 모르는 문법이 나오는 경우 https://v3.vuejs.org/ 사이트에 접속해서 아래와 같이 검색을 진행하면 문서가 잘 나와있습니다. 설명과 예시를 통해 직접 문서에 들어가서 확인하는 방법을 추천합니다.

    아래는 맨 오른쪽 검색 버튼을 클릭 후, input 관련 데이터 바인딩 예시를 살펴본 것입니다.

     

    댓글

Designed by Tistory.