ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Vue.js] Template 문법
    Front-End(Web)/Vue 2021. 6. 3. 01:37
    반응형

    Vue에서도 React 처럼 state, props 등의 데이터를 HTML 요소에 바인딩할 수 있다. (이러한 연관성을 Reactivity 라고 칭한다.)

    이러한 방법을 data를 활용한 선언적 바인딩이라고 한다. 이렇게 화면을 조작하기 위한 Vue의 문법들에 대해 학습해보았다.

     

    * 이 글은 캡틴판교님의 Vue.js 강의 및 블로그를 기반으로 학습한 내용을 정리하고 있습니다.


    💚 Vue Template

    위에서 언급한 선언적 바인딩을 포함해서 Vue로 화면을 조작하는 문법들을 "Vue Template" 이라고 한다.

    Template 문법은, 1) Vue 인스턴스에서 관리하는 데이터를 화면에 연결하는 데이터 바인딩, 2) 화면조작을 편하게 하게 해주는 Vue 디렉티브 2가지가 있다.

     

    1. 데이터 바인딩

    // Template
    <div id="app">
      <p>{{ num }}</p>
      <p>{{ doubleNum }}</p>
    </div>
    
    // script
    new Vue({
      el: '#app,
      data: {
        num: 10,
      },
      compouted: {
        doubleNum: function() {
          return this.num * 2;
        };
      };
    });

    우선 <template> 양식 내에서, mustach(콧수염, {{ }}) 문법을 통해 data 값을 바인딩할 수 있다. 이렇게 바인딩된 데이터(여기선 num)가 변경될 때마다, 표현되는 값이 갱신된다. 

    React의 JSX 템플릿 리터럴과 유사하다. 그렇기에, JS문법(연산, 삼항연산자, 함수/메서드 등)도 사용이 가능하다.

     

    * computed 속성

    함수를 필드값으로 받으며, data 값을 기반으로 연산되는 값의 관리를 위한 필드이다. 아래에서, watch 필드와의 비교를 다룰 것이다.

     

    - v-once 디렉티브

    <template> 내에서 v-once 디렉티브 조건을 줄 수 있다. 해당 태그는, 데이터 바인딩 값이 변경되어도 갱신되지 않는다.

    <span v-once>다시는 변경하지 않습니다: {{ msg }}</span>

     

     

    2. Directive(디렉티브)

    <template> 내에서, v- 접두사가 붙은 특별한 속성을 통해 화면의 DOM을 쉽게 조작할 수 잇는 문법들을 제공한다.

    <!-- seen의 진위 값에 따라 p 태그가 화면에 표시 또는 미표시 -->
    <p v-if="seen">Now you see me</p>
    <!-- 화면에 a 태그를 표시하는 시점에 뷰 인스턴스의 url 값을 href에 대입 -->
    <a v-bind:href="url"></a>
    <!-- 버튼에 클릭 이벤트가 발생했을 때 doSomething이라는 메서드를 실행 -->
    <button v-on:click="doSomething"></button>

     

    1) v-bind

    특정 전달인자(data, props, computed) 등을 태그에 연결해줄 때 사용하는 디렉티브다. 문법은 아래와 같다.

    // template
    <div id="app">
      <p v-bind:class="name">bind</p>
    </div>
    
    // script
    new Vue({
      el: "app",
      data: {
        name: 'text-blue',
      }
    });
    
    //style(sass style)
    #app {
      p.text-blue{
        // css...
      }
    }

     

    v-bind는 props 하달 외에도, 위처럼 클래스나 인라인 스타일링에도 적용될 수 있다.

    // 1) 기본적인 조건부 클래스
    <div
      class="static"
      v-bind:class="{ active: isActive, 'text-danger': hasError }"
    ></div>
    
    data: {
      isActive: true,
      hasError: false
    }
    // 2) 객체를 통한 바인딩
    <div v-bind:class="classObject"></div>
    
    data: {
      isActive: true,
      error: null
    },
    computed: {
      classObject: function () {
        return {
          active: this.isActive && !this.error,
          'text-danger': this.error && this.error.type === 'fatal'
        }
      }
    }
    // 3) 배열을 통한 바인딩
    <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
    
    data: {
      activeClass: 'active',
      errorClass: 'text-danger'
    }

     

    // 인라인 스타일 바인딩
    <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
    
    data: {
      activeColor: 'red',
      fontSize: 30
    }

    인라인 스타일 역시 객체, 배열로도 바인딩이 가능하다.

     

    * 동적 전달인자

    아래와 같이, v-bind 값이 아닌 속성명도 동적으로 하달할 수 있게 되었다. (Vue 2.6.0 버전부터 지원)

    <a v-bind:[attributeName]="url">link</a>
    
    data {
      attributeName: 'href',
      link: 'http://www.naver.com/',
    }

     

    * 약어

    <!-- 전체 문법 -->
    <a v-bind:href="url"> ... </a>
    
    <!-- 약어 -->
    <a :href="url"> ... </a>
    
    <!-- shorthand with dynamic argument (2.6.0+) -->
    <a :[key]="url"> ... </a>

     

    2) v-on 

    마찬가지로 전달인자를 활용하는 디렉티브다. 통상, DOM 이벤트 수신을 위해 사용되므로 전달인자 역시 click과 같은 이벤트명이다.

    <a v-on:click="doSomething"> ... </a>

    당연히 다른 이벤트들도 설정이 가능하다. (keypress 등) 특히, 키, 마우스 관련 이벤트는 키 수식어를 통해 종류까지 설정할 수 있다.

    <input type="text" v-on:keyup.enter="logText" />

     

    * 이벤트 수식어

    React에서 submit 이벤트 등은 보통 핸들러 내부에서 e.preventDefault(), e.stopPropagation() 등의 형태로 기본동작을 막아줬다.

    Vue는 v-on 이벤트에 "이벤트 수식어"를 제공함으로서, 디렉티브에 DOM 제어를 첨가하면서 메서드는 로직에만 집중되는 장점이 있다.

    <!-- 클릭 이벤트 전파가 중단됩니다 -->
    <a v-on:click.stop="doThis"></a>
    
    <!-- 제출 이벤트가 페이지를 다시 로드 하지 않습니다 -->
    <form v-on:submit.prevent="onSubmit"></form>
    
    <!-- 수식어는 체이닝 가능합니다 -->
    <a v-on:click.stop.prevent="doThat"></a>
    
    <!-- 단순히 수식어만 사용할 수 있습니다 -->
    <form v-on:submit.prevent></form>
    
    <!-- 이벤트 리스너를 추가할 때 캡처모드를 사용합니다 -->
    <!-- 즉, 내부 엘리먼트를 대상으로 하는 이벤트가 해당 엘리먼트에서 처리되기 전에 여기서 처리합니다. -->
    <div v-on:click.capture="doThis">...</div>
    
    
    <!-- event.target이 엘리먼트 자체인 경우에만 트리거를 처리합니다 -->
    <!-- 자식 엘리먼트에서는 안됩니다 -->
    <div v-on:click.self="doThat">...</div>

     

    * 약어 : 마찬가지로 동적 전달인자 활용이 가능하며, 약어는 v-bind와 구분을 위해 @를 활용한다.

    <!-- 전체 문법 -->
    <a v-on:click="doSomething"> ... </a>
    
    <!-- 약어 -->
    <a @click="doSomething"> ... </a>
    
    <!-- shorthand with dynamic argument (2.6.0+) -->
    <a @[event]="doSomething"> ... </a>

     

    3) v-if, v-else

    React JSX에서 삼항연산자를 쓰듯이, boolean(true/false)에 따른 조건부 렌더링을 위한 디렉티브이다.

    // template
    <div id="app">
      <div v-if="loading">Loading...</div>
      <div v-else>Done!</div>
    </div>
    
    // script
    new Vue({
      el: "app",
      data: {
        loading: false,
      }
    });

     

    * v-show

    비슷한 문법으로 v-show 디렉티브가 있다.

    v-if, v-show 모두 조건부 렌더링을 지원하되, false인 경우 v-if는 DOM 요소 자체를 지우지만 v-show는 display: none 처리한다.

    // template
    <div id="app">
      <div v-if="loading">Loading...</div>
      <div v-show="loading">Loading2...</div>
    </div>
    
    // script
    new Vue({
      el: "app",
      data: {
        loading: false,
      }
    });

     

    4) v-for

    배열을 기반으로 리스트를 렌더링하는데 사용되는 디렉티브이다. 특히, "item in items" 라는 특별한 문법으로 적용된다.

    <ul id="example-1">
      <li v-for="(item, index) in items">
        {{ item.message }}
      </li>
    </ul>

    v-for 문법 역시, 두 번째 인자로 인덱스(index)도 전달할 수 있다.

    또한, items는 배열뿐만 아니라 객체를 통해서도 리스트를 만들 수 있다. (이 때, item은 각 프로퍼티의 value)

     

    또한, items 배열에 아래와 같은 메서드가 적용될 경우, Vue 갱신을 트리거한다.

    - push(), pop(), shift(), unshift(), splice(), sort(), reverse()

     

    5) v-model

    <Input> 등 폼 바인딩에 사용되는 디렉티브이다. 이벤트 핸들러의 역할로서 특정 data 필드와 해당 태그의 양방향 바인딩을 지원해준다.

    * v-model은 통상 사용자 입력 이벤트 데이터를 업데이트하는 Syntax Sugar 류에 해당된다.

    // template
    <div id="app">
      <input v-model="msg" placeHolder="메세지를 입력하세요." />
      <p>{{ msg }}</p>
    </div>
    
    // script
    new Vue({
      el: "app",
      data: {
        msg: '',
      }
    });

     

    다만, 주의해야 할 점은 <form> 내 엘리먼트들의 초기값(value, checked, selected)를 참조하지 않는다.

    Vue 인스턴스 data() 필드를 참조하기 때문에 여기에 초기값을 설정해야 한다.

     

    * 수식어

    v-model 디렉티브 역시 on 등과 같이 .[수식어] 로 추가적인 옵션 부여가 가능하다.

    • lazy : 각 입력 이벤트가 끝난 뒤 입력과 데이터를 동기화한다. (키워드 입력 후 서버요청 등에 유용할 것 같다!)
    • number : 입력값이 숫자로 변환시키려고 할 때 적용한다. (parseFloat() 과 유사)
    • trim : 입력값의 공백을 지워주는 자동 트리밍 역할을 한다.

    💚 Computed vs Watch

    위 둘 모두 Vue 인스턴스의 프로퍼티이다. 심플하게는 프로퍼티 설정보다는, 템플릿 문법 내에 표현식을 넣으면 편리하다.

    복잡한 연산은 템플릿 안에서 코드가 비대해지고 유지보수가 어려워지는 단점이 생긴다. 그렇기에, 위 프로퍼티들을 적절히 사용하는 것이 중요하다!

    // Template
    <div id="app">
      <p>{{ num }}</p>
      <button v-on:click="addNum">increase</button>
    </div>
    
    // script
    new Vue({
      el: '#app,
      data: {
        num: 10,
      },
      
      // 1) computed : data 변경에 따라 계산되는 값에 사용
      computed: {
        doubleNum: function() {
          return this.num * 2;
        }
      },
      // 2) watch : data 변경에 따라 실행되는 로직에 사용
      watch: {
        num: function() 
          this.logText();
        }
      },
      methods: {
        addNum: function() {
          this.num = this.num + 1;
        };
        logText: function() {
          console.log('changed');
        };
      },
    });

     

    • watch : 감시할 데이터를 지정하고, 갱신되면 메서드를 실행하는 명령형 프로그래밍. 데이터 갱신에 연계된 로직이 있을 때 유용.
    • computed: 계산해야하는 목표 데이터를 정의하는 선언형 프로그래밍. 데이터 값과 연계된 계산이 수행되는 경우 유용.

    watch는 상대적으로 무거운 로직들을 다룬다. 또한, 그러한 로직들이 methods 필드의 메서드 함수들인 경우가 많다.

    computed는 종속 대상을 따라 저장(캐싱)이 된다. 그렇기에 num 값이 변경되지 않는 한, computed 속성 역시 최신값을 계속해서 반환하게 될 것이다. (공식문서 역시 computed로 처리할 수 있는 로직들은 무조건 적용하도록 권장!)

     

    - computed를 활용한 class binding

    // Template
    <div id="app">
      <p v-bind:class="{ warning: isError }">Warning Text!</p>
      <button v-on:click="">increase</button>
    </div>
    
    // Script
    new Vue({
      el: '#app,
      data: {
        isError: false,
      },
    });
    
    // Styles
    p {
      color: black;
      &.warning { color: red; };
    };

    위 예시는, isError data의 true/false 에 따라, <p>태그에 warning 클래스명을 토글하면서 색깔이 바뀌는 경우이다.

    class에 바인딩하는 부분을, computed를 통해 로직을 분리할 수 있다.

    // Template
    <div id="app">
      <p v-bind:class="errorTextColor">Warning Text!</p>	// computed 클래스 바인딩
      <button v-on:click="">increase</button>
    </div>
    
    // Script
    new Vue({
      el: '#app,
      data: {
        isError: false,
      },
      computed: {
        errorTextColor: function() {		// errorTextColor 함수 추가
          return this.isError ? "warning" : "";
        };
      },
    });
    
    // Styles
    p {
      color: black;
      &.warning { color: red; };
    };

    Vue에서 state처럼 사용되는 data를 바인딩하여 효율적으로 렌더링하는 다양한 문법들을 알 수 있었다.

    특히, Vue의 강점이자 진입장벽인 디렉티브들의 종류와 적절한 사용이 중요할 것 같다고 느껴졌다.

    또한, data 필드값들을 연계하는 대표적인 2가지 기법인 computed와 watch의 구분도 짚고 넘어간 유익한 계기였다.

     

    [출처]

    - Vue 공식문서 : https://kr.vuejs.org/v2/guide/syntax.html

    - 캡틴판교 님의 블로그 : https://joshua1988.github.io/web-development/vuejs/vuejs-tutorial-for-beginner/  

    반응형
Designed by Tistory.