-
Vue 데이터 바인딩개발/Front-end 2021. 8. 25. 15:32728x90
이번 시간에는 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>
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>
여러 개의 체크박스를 사용할 때는 배열을 이용해서 데이터 바인딩을 한 번에 처리할 수 있습니다.
<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에 값이 입력되면 버튼이 활성화되고 비어있는 경우는 비활성화됩니다.
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>
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>
- 클릭 이벤트를 통해 지정된 함수로 파라미터를 전달하고 싶다면 아래와 같이 전달 가능합니다.
<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>
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를 사용해야 하는지 확인할 수 있습니다.
이번 시간에는 vue 데이터 바인딩에 대한 방법에 대해서 알아보았습니다. 데이터 바인딩 관련 모르는 문법이 나오는 경우 https://v3.vuejs.org/ 사이트에 접속해서 아래와 같이 검색을 진행하면 문서가 잘 나와있습니다. 설명과 예시를 통해 직접 문서에 들어가서 확인하는 방법을 추천합니다.
아래는 맨 오른쪽 검색 버튼을 클릭 후, input 관련 데이터 바인딩 예시를 살펴본 것입니다.
'개발 > Front-end' 카테고리의 다른 글
Vue 데이터 바인딩 실습 - TODO 앱 구현하기 (2) 2021.09.15 Vue HTTP 통신 라이브러리 - axios (0) 2021.09.03 Vue 컴포넌트 Basic (0) 2021.08.24 Vue Lazy Load(비동기 컴포넌트)란? (0) 2021.08.24 Vue Lazy load 적용하기 (0) 2021.08.24 - MVVM 패턴: Model-View-ViewModel 약자로 프로그래밍 로직과 화면에 해당하는 View를 분리해서 개발하기 위해 설계된 패턴입니다.