FrontEnd

vue js 내용정리 (1. 설치 ~ 2.3.8. 컴포넌트 - 이벤트 발생시 사용자 정의함수 호출)

Dean83 2022. 3. 25. 09:22

참조 사이트 : https://kr.vuejs.org/v2/guide/installation.html
                   https://joshua1988.github.io/web-development/vuejs/vuejs-tutorial-for-beginner/

1. 설치
    1.1. 호환성 정보
           - IE8 이하버전 지원불가
           - ECMAScript 5 지원 브라우저 사용가능
             - https://caniuse.com/#feat=es5

    1.2. 설치 - NPM 부터는 무슨말인지 이해가....
           - 브라우저에 Vue Devtools 설치
           - 참조사이트에서 다운로드 받은 vue.js를 <script> 태그를 이용하여 추가
             - 개발용, 배포용이 있음
             - vue는 전역 변수로 등록됨
           - 스크립트 태그에 https://unpkg.com/vue 이나 https://unpkg.com/vue/를 입력하여 자동으로 다운로드 및 적용
              - 단, 최신버전이 아닐 수 있음

           * 스크립트 태그는 body의 최 하단부에 선언해야 상단부 html태그에서 사용 가능

           - 예 : 

<div id="app">
      {{ message }}
    </div>

<script src="https://unpkg.com/vue@2.3.3&quot;&gt;&lt;/script>
    <script>
      new Vue({
        el: '#app',
        data: {
          message: 'Hello Vue.js!'
        }
      })
    </script>

// html에 정의하여 vue 인스턴스를 하나 생성하여 만든 간단한 vue app

    1.3. Vue Devtools(크롬) 사용법
           - 마우스 우클릭 -> 확장프로그램 관리 -> 하단부 파일 url에 대한 엑세스 허용 켬
           - vue가 포함한 html 페이지 -> f12 -> 상단 element, console .... 에서 >> 클릭 -> vue 선택
           - 클릭을 통한 값 변경 가능
           - 하단부 콘솔을 통해서도 접근 및 값 변경 가능
             - $vm0.$.........

2. Vue.js
     - MVVM 패턴 중  ViewModel 에 해당
     - 데이터 바인딩 제공
     - html이 동적으로 동작함
        - 기존에는 html 태그 내용이 로딩된 후에는 변동되지 않음. 새로고침 해야함
        - 바인딩을 통해 html을 작성할 경우, 변수명을 호출하므로, 변수값이 동적으로 변한다면, 페이지 로딩을 다시하지 않아도 동적으로 동작함
        - 예 : <p>hello</p>  => hello만 보여짐
                 <p>{{message}}</p> => message 변수값이 변경될 때마다 동적으로 보여짐

     2.1. Vue Instance 생성
            - var vm = new Vue( { 옵션 } )
            - 생성 후 html 태그와 연결하면, 맴버변수를 {{}} 를 이용하여 바로 쓸 수 있다. (vm.변수명 이 아님)
            - 하나의 인스턴스는 하나의 태그와 연결되야 한다.

            2.1.1. 옵션
                      - $를 사용하여 옵션에 바로 접근 가능
                        - vm.$data.xxx 이런식으로 접근 가능
                          - vm.xxx 와도 같음
                      - 참조 : https://kadamon.tistory.com/13

                      2.1.1.1. data
                                  - 맴버변수 선언 및 초기값 할당
                                  - data {} 안에 선언한 맴버변수들을 html에서도 사용가능
                                     - {{ 맴버변수명 }} 으로 사용가능
                                  - (중요) Vue 생성시 data에서 맴버변수:값을 바로 설정해도 되나 이렇게 하지 않는것이 좋다
                                     - 인스턴스는 html 태그와 1:1 매칭이기 때문에, 공통으로 사용하는 맴버변수값을 2군데 이상 html 태그에서 사용시
                                        값의 연동이 되지 않는다. 
                                  - 올바른 사용법                                       

<script>
//mvvm의 모델에 해당 즉, class 선언
  var model = {

    number : []

  };

// mvvm의 vm에 해당. 즉, 모델에서 선언한 클래스를 활용하여 로직 작성
  var test1 = new Vue({

    el: '#app',

    data: model

  })

  var test2 = new Vue({

    el: '#app2',

    data: model

  })

</script>//이렇게 해야 각기 다른 html태그에서 number를 참조시 동일한 객체를 활용할 수 있다.
// A 태그에서 test1을 연결, number 값 출력, B 태그에서 test2를 연결, number 값 변경시 A 태그에도 B에서 변경한 값이 바로 반영된다.
// 이렇게 하지 않고 각 test1, test2 의 data에 맴버변수 선언 및 값 설정시, A 태그에는 B태그에서 변경한 내용이 반영되지 않는다. 

                      2.1.1.2. el
                                  - element의 약자
                                  - html과 연결하는 연결고리로, 한 태그에 하나만 지정
                                  - '#배정할문자열값' 으로 설정
                                  - html 태그에서는 id 어트리뷰트 = "#값" 으로 연결 (class 연결과 비슷)
                                     
                      2.1.1.3. computed
                                  - 읽기전용. 이미 계산된 속성
                                  - 캐싱된 결과값을 바로 리턴

computed : {

    multi : function() {

      var a =  Number(this.num);

      if ( Number.isNaN(n)|| n<1) return 0;

      return num*10;

    }

  }

                                  - 호출 : vue인스턴스 변수명.multi 로 호출시 결과값이 나옴

                      2.1.1.4. methods
                                  - computed와 비슷하나, 매번 계산한다는 차이가 있음

methods : {

    multi : function() {

      var a =  Number(this.num);

      if ( Number.isNaN(n)|| n<1) return 0;

      return num*10;

    }

  }

                                  - 호출 : vue인스턴스 변수명.multi 로 호출시 결과값이 나옴

                      2.1.1.5. watch
                                  - 비동기처리 혹은 시간이 많이 소요되는 작업에 사용
                                  - 추후 내용 추가

     2.2. 바인딩 및 디렉티브
            - html이 동적으로 동작한다. 맴버변수 값을 변경시, 바로 적용되는 반응형 웹이 된다.
            - 디렉티브는 v- 로 시작

            2.2.1. 데이터 바인딩
                      - {{ 맴버변수명 }} 을 통해 데이터를 바인딩 한다.

            2.2.2. 디렉티브
                      - 일반적으로 읽기만 가능한 단방향임. v-model은 양방향
                      - html 태그의 어트리뷰트에 사용한다.
                      - 참조 : https://kadamon.tistory.com/12?category=801517

                      2.2.2.1. v-bind
                                  - 엘리먼트 속성에 사용할 항목을 바인딩하여, 동적으로 동작한다.
                                     - 예 : <img v-bind:src="맴버변수명" />  == <img :src="맴버변수명"/> (v-bind 생략가능)

                      2.2.2.2. v-html
                                  - 맴버변수 값에 있는 태그를 태그로 인식하여 html 페이지에 반영한다.
                                  - tempItem ="<h1>hello</h1>" 이고,  {{ tempItem }}으로 호출하게되면, h1 태그가 동작한다.
                                  - 보안문제로 사용치 않는것을 권장

                      2.2.2.3. v-text
                                  - 맴버변수 값을 텍스트로 표시. 위의 2.2.2.2. 예제의 tempItem의 태그를 텍스트로 취급

                      2.2.2.4. v-model (양방향)
                                  - 양방향으로 Vue 인스턴스 맴버변수 값을 활용. 읽기, 쓰기 모두 가능.
                                  - 쓰기의 경우, 이벤트를 통해 쓰기 명령을 내림

<h1>{{message}}</h1>

<input v-model="message">

                      - 실제 렌더링 되는 html은 다음과 같음 

<input v-model="something"> 의 경우

<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">

// v-bind를 통해 value에 맴버변수를 연결
// v-on 을 통해 입력된 새로운 값($event.target.value)이 맴버변수에 배정
// 즉, 실제로는 이벤트를 통하여 값을 변경하는 것임.

                      2.2.2.5. v-show, v-if
                                  - 조건문. v-show는 일단 렌더링을 하고, 조건에 부합하지 않을경우, 보여주지 않고, v-if는 아예 렌더링을 안함. 
                                    즉, v-if를 사용할것.
                                  - v-else-if , v-else

<img v-if="number < 1000" v-bind:src="imagePath" >
// number 맴버변수 값이 1000 미만일때 이미지 출력

                      2.2.2.6. v-for
                                  - 반복문
                                  - 형태는 다양하나 객체항목을 반복하는것이 좋을듯. 
                                  - (반복변수명, index) in 딕셔너리맴버변수명 처럼 () 안에 다음의 값을 받을 수 있음
                                     - key 값
                                     - value 값
                                     - index 값
                                     - 오브젝트 그 자체
                                  - v-for="반복변수명 in 딕셔너리맴버변수명"
                                  - 맴버변수는 key:value 형태의 배열을 사용하는것이 편함

<div id="app"> //app 은 vue 인스턴스의 el 값

    <div v-for="team in teams">

      <div>랭크 : {{team.rank}}</div>

      <div>팀명 : {{team.name}}</div>

      <div>홈구장 : {{team.homeground}}</div>

      <br>

    </div>

  </div>
var model = {

    teams: [

      { rank: 1, name: "리버풀", homeground: "안필드"},

      { rank: 2, name: "맨유", homeground: "올드트래포트"},

      { rank: 3, name: "바르샤", homeground: "?"}

    ]

  };

  new Vue({

    el: '#app',

    data: model

  })

//vue 의 data는 model을 배정하여 다른 html 태그에서도 동일한 오브젝트를 참조하도록 한다.

                                  2.2.2.6.1. range v-for
                                                 - v-for="n in 10" 으로 사용

                                  2.2.2.6.2. v-for template
                                                 - 2.2.5. 템플릿 참조
                                                 - template를 이용하여 여러 태그를 template 태그로 묶어 한번에 렌더링 가능

                                  2.2.2.6.3. v-for 과 컴포넌트
                                                 - 

                                  2.2.2.6.4. v-for 의 key (중요)
                                                 - 각각의 노드를 구별하기 위한 id로, 렌더링 되지 않음
                                                 - 전역이 아닌 형제 노드끼리만 고유하면 됨
                                                 - 렌더링 최적화, 효율성 증대를 위해 v-for를 사용할때에 사용
                                                 - 반복문에 사용되는 클래스의 맴버변수로 설정하며, 해당 맴버변수는 고유값을 가져야 함
                                                 - 사용 전, 후 예
                                                    - 1, 2, 3, 4 의 text를 가진 배열을 렌더링 하고 있고, 1번항목을 지우고 2,3,4 만 렌더링 하려고 할경우.
                                                    - 사용 전 : 1 -> 2로 수정, 2->3 으로 수정, 3->4 로 수정, 4 -> 5로 수정, 5 삭제
                                                    - 사용 후 : 1 항목 삭제
                                                 - v-for="item in items" :key="고유한값을가진 맴버변수"

<ul>

  <li v-for="item in myItems" :key="number">

    {{ item.number }}

    {{ item.name }}

  </li>

</ul>

                      2.2.2.7. 배열 컨트롤 함수
                                   2.2.2.7.1. 원본 항목 변경
                                                   - push() : 끝에 삽입.
                                                     - items.push({message:'aaa'})
                                                   - pop() : 마지막 항목을 꺼내고 원본에서 삭제
                                                   - shift() : 처음 항목을 꺼내고 원본에서 삭제
                                                   - unshift() : 배열 앞에 항목 추가
                                                   - splice(pos, length) : pos 위치부터 length 만큼 추출 
                                                   - sort() : 정렬
                                                   - reverse() : 역순 정렬

                                   2.2.2.7.2. 원본 항목을 변경치 않는 함수
                                                   - 항상 새 배열을 리턴
                                                   - filter()
                                                   - concat()
                                                   - slice()
                                   2.2.2.7.3. filter
                                                   - 항상 새 배열을 리턴                                                    

<li v-for="n in evenNumbers">{{ n }}</li>

data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

                                   2.2.2.7.4. 주의 사항
                                                   - 참조 : https://kr.vuejs.org/v2/guide/list.html
                                                   - items[index] = newvalue 같은 경우, 인지 하지 못함. 
                                                     =>Vue.set(items,index,newvalue) 를 사용 하여야 함
                                                   - items.length 또한 인지하지 못함
                                                     => items.splice() 를 통해 제어
                                                   - 이미 선언한 클래스에 맴버변수를 추가 하는 법
                                                     - Vue.set()을 사용하여 추가                                                     

var vm = new Vue({
  data: {
    userProfile: {
      name: 'Anika'
    }
  }
})

Vue.set(vm.userProfile, 'age', 27)

                      2.2.2.8. v-pre
                                  - {{message}} 를 할경우, message 변수에 담긴 내용이 보이는데, {{ }}까지 보여주고 싶을때 사용

                      2.2.2.9. v-once
                                  - 한번만 렌더링 할 경우에 사용
                                  - <h3 v-once>{{message}}</h3> 로 작성 후 message 값을 변경하여도 변경되지 않음. 

                      2.2.2.10. v-cloak
                                  - Vue 인스턴스가 컴파일에 시간이 너무 오래 걸려, html 태그에 연결한 맴버변수가, 맴버변수의 값이 아닌 맴버변수명이 보이는것을 방지
                                  - <h3 v-cloak>{{message}}</h3> 일 경우, 컴파일이 너무 오래걸려 message의 값이 보이지 않고, message 라고 표기되는것을 방지

                      2.2.2.11. v-on
                                  - Vue 인스턴스 메서드 호출. 사용자 상호작용을 위해 사용함

<button id="app-5" v-on:click="reverseMessage">메시지 뒤집기</button>
// Vue의 reverseMessage 함수 호출 

var app5 = new Vue({
  el: '#app-5',
  data: {
    message: '안녕하세요! Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

            2.2.3. 인라인 스타일 바인딩
                      - style 속성에 대하여 바인딩 함으로서, 동적으로 변경 가능

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

data: {
  activeColor: 'red',
  fontSize: 30
}

또는 

<div v-bind:style="styleObject"></div>

data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

                      2.2.3.1. 배열 바인딩
                                  - 여러개의 스타일을 바인딩 할 수 있다. 

<div v-bind:style="[baseStyles, overridingStyles]"></div>

            2.2.4. html 클래스 바인딩 (음.......이해가..)
                      - style 속성에 대하여 바인딩 함으로서, 동적으로 변경 가능
                      - v-bind:class="{....}" 으로 사용

                      2.2.4.1. bool 값
                                  - 해당 클래스 바인딩을 적용할지 말지 bool로 값을 설정

<div v-bind:class="{ active: isActive }"></div>
//active 라는 클래스의 존재여부를 설정함으로서, 스타일 적용 유무가 변경

                      2.2.4.2. 기존 class와 혼용
                                  - 기존 class="클래스명" 과 v-bind:class 를 혼용하여 사용가능

 

                      2.2.4.3. 객체 바인딩
                                  - 미리 정의된 객체를 바인딩 하여 일괄 적용

<div v-bind:class="classObject"></div>

data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

                      2.2.4.4. computed를 이용한 속성 바인딩 (일반적으로 많이 쓰임)
                                  - 특정 계산식을 통해 결과값을 바인딩

<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'
    }
  }
}

                      2.2.4.5. 배열을 통한 다중 속성 바인딩
                                   - 배열을 통해 여러개의 속성을 바인딩 할 수 있음

<div v-bind:class="[activeClass, errorClass]"></div>

data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

                      2.2.4.6. 삼항 연산자 사용 바인딩
                                   - a ? b : c 의 삼항 연산자를 통해 바인딩 가능

 

            2.2.5. 템플릿
                      - <template> 태그를 이용하여 여러개의 태그를 묶어 한번에 렌더링
                      - <template> 태그의 경우, 사용하지 않는한 렌더링 되지 않는다. 
                      - 

     2.3. 컴포넌트
            - 작은 기능을 하는 컴포넌트 생성 -> 재사용
            - 기본 html 태그를 확장하여 재사용
            - 경우에 따라 is 를 사용하여 html 태그 확장
            - Vue 컴포넌트는 Vue 인스턴스 이기도 하므로, 모든 옵션을 사용할 수 있음
            - 컴포넌트 등록은 new Vue를 통한 루트 Vue 인스턴스 생성전에 해야함

            2.3.1. 컴포넌트 등록 (전역)
                      - Vue.component('태그명', 옵션) 사용
                      - 옵션은 2.1.1. 참조

// todo-item 이름을 가진 컴포넌트를 정의합니다     

Vue.component('todo-item', {
  template: '<li>할일 항목 하나입니다.</li>'
})

            2.3.2. 컴포넌트 등록 (지역)
                      - components 인스턴스 옵션 내부에 선언
                      - 다른 인스턴스 혹은 컴포넌트 범위에서만 사용가능

var Child = {
  template: '<div>사용자 정의 컴포넌트 입니다!</div>'
}

new Vue({
  // ...
  components: {
    // <my-component> 는 상위 템플릿에서만 사용할 수 있습니다.
    'my-component': Child
  }
})

            2.3.3. 컴포넌트 사용
                      - 등록된 컴포넌트 이름을 사용

<ol>
  <!-- todo-item 컴포넌트의 인스턴스 만들기 -->
  <todo-item></todo-item>
</ol>

// 이경우 똑같은 항목을 렌더링 하게 됨.

            2.3.4. 제약사항
                      - <ul>, <ol>, <table>, <select> 의 경우, 다음에 올 하위 태그가 정해져 있으므로, 컴포넌트 사용시 오류가 발생함. 
                      - 해결하기 위해서 is 속성을 사용                         

<table>
  <tr is="my-row"></tr>
</table>

                      - data는 반드시 함수 여야 함
                         - data:{ } 형태가 아닌, data: function() { } 형태로 정의해야 함

<div id="example-2">
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
</div>

Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  // 데이터는 기술적으로 함수이므로 Vue는 따지지 않지만
  // 각 컴포넌트 인스턴스에 대해 같은 객체 참조를 반환합니다.
  data: function () {
    return { counter:0 }
  }
})

new Vue({
  el: '#example-2'
})

//data는 맴버변수 선언 및 초기화를 담당하며, html에서 맴버변수 접근하여 사용가능하다.

            2.3.5. 컴포넌트 부모 관계
                      - 컴포넌트는 부모 자식관계에서 가장 일반적으로 사용함. 
                        - parent 가 child에게 데이터를 전달할때 : props 사용
                        - child가 parent에게 메세지를 보냄 : event 사용

                      2.3.5.1. Props 옵션
                                   - 참조 : https://beomy.tistory.com/56
                                   - 오브젝트를 하위 자식에게 전달하기 위해 사용하는 옵션
                                   - data 에 변수 선언하는것처럼, props 옵션을 사용하면, 변수를 선언하는것임. 즉, data 에서 변수선언 하면 안됨 (중복오류)
                                    - v-model을 이용하여 양방향 바인딩은 html태그의 id와 연결된 Vue instance에서 data: 로 선언한 변수와 연동하는것임.
                                    - 컴포넌트 변수와 양방향 연동을 위해서는 props를 사용해야함  
                                   

Vue.component('child', {
  // props 정의
  props: ['message'],
  // 데이터와 마찬가지로 prop은 템플릿 내부에서 사용할 수 있으며
  // vm의 this.message로 사용할 수 있습니다.
  template: '<span>{{ message }}</span>'
})

<child message="안녕하세요!"></child>

//컴포넌트 명이 child 이며, html의 child 태그에서 props로 정의한 message 변수 값을 변동함

                                   2.3.5.1.1. 선언
                                                   - props:['변수명', '변수명'...],
                                                      - 변수들은 string이 된다.
                                                   - props: { 변수명:String, 변수명:Number, 변수명:Boolean, 변수명:Array, 변수명:Object...},
                                                      - 특정 자료형을 갖는 변수들 생성
                                                      - props 변수값이 변경될 때 검증이 가능하며, 자료형과 맞지않으면 경고 발생 
                                                         - 예 : bool 변수에 문자열을 넣는 등
                                                   - 초기값을 설정하기 위해 default: 를 선언부에 사용한다.
                                                   - 필수입력을 위해 required:를 선언부에 사용한다. 

Vue.component('example', {
  props: {
    // 기본 타입 확인 (`null` 은 어떤 타입이든 가능하다는 뜻입니다)
    propA: Number,
    // 여러개의 가능한 타입
    propB: [String, Number],
    // 문자열이며 꼭 필요합니다
    propC: {
      type: String,
      required: true
    },
    // 숫자이며 기본 값을 가집니다
    propD: {
      type: Number,
      default: 100
    },
    // 객체/배열의 기본값은 팩토리 함수에서 반환 되어야 합니다.
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 사용자 정의 유효성 검사 가능
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})

                                   2.3.5.1.2. Props case
                                                   - javascript 에서 카멜케이스 (두번째 문자열이 대문자) 사용 하여 변수 선언시, html에서 케밥케이스인 -을 사용해야함

Vue.component('child', {
  // JavaScript는 camelCase
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})

<!-- HTML는 kebab-case -->
<child my-message="안녕하세요!"></child>

                                  2.3.5.1.3. 동적 Props
                                                  - Vue instance 의 data 에 선언한 변수를 props 변수에 bind 하여 동적으로 값이 바뀌도록 할 수 있다. 
                                                  - v-bind:props선언변수="Vue Instance의 data에 선언한 변수"

<div id="test">
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>

Vue.component(
{
     props:['myMessage'],
     .....
}
)

new Vue({
      el:"#test",
      data: { parentMsg:'' }
})

//v-bind

                                  2.3.5.1.4. 동적 Props - 오브젝트 할당
                                                  - String, Number 등 값을 할당하는것이 아닌, Vue Instance의 data: 에서 클래스 형식으로 선언한 변수를 배정
                                                  - props 변수명.맴버변수 로 데이터 접근이 가능

<div id="app-7">
  <ol>
    <!-- 
      이제 각 todo-item 에 todo 객체를 제공합니다.
      화면에 나오므로, 각 항목의 컨텐츠는 동적으로 바뀔 수 있습니다. 
      또한 각 구성 요소에 "키"를 제공해야합니다 (나중에 설명 됨).
     -->
    <todo-item
      v-for="item in groceryList"
      v-bind:todo="item"
      v-bind:key="item.id">
    </todo-item>
  </ol>
</div>

Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { id: 0, text: 'Vegetables' },
      { id: 1, text: 'Cheese' },
      { id: 2, text: 'Whatever else humans are supposed to eat' }
    ]
  }
})

                                  2.3.5.1.5. html 속성 병합 및 대체
                                                  - 컴포넌트 내부 template 에 커스텀 속성 및 클래스가 있을경우, 기본 속성과 병합됨
                                                     - class 속성과 style 속성은 병합되나, 그외 속성들은 컴포넌트에 정의된 속성으로 대체됨.  

<input type="date" class="form-control"> 

//컴포넌트 template에 아래와 같이 정의되어 있을경우
<bootstrap-date-input
  data-date-picker="activated"
  class="date-picker-theme-dark"
  type="text"
></bootstrap-date-input>

//class는 form-control date-picker-theme-dark 로 병합됨
// 속성인 data-date-picker 속성은 추가되어 type 처럼 사용됨 
// type는 date가 아닌 text로 대체됨

                                  2.3.5.1.6. html 속성 병합 하지 않기
                                                  - 컴포넌트에 inheritAttrs:false 선언을 해주면, html의 속성을 병합하지 않고 무시한다.

<base-input type="text" ...></base-input>

Vue.component('base-input', {
  inheritAttrs: false,
  template: '<input />'
})

//이 경우, 최종 생성되는 태그는 <input /> 이다   

            2.3.6. 사용자 이벤트 
                      - 2.5. 사용자 정의 이벤트 참조

// 컴포넌트 템플릿에 정의한 v-on:click 에 의해 클릭시 incrementCounter 함수 (컴포넌트에 정의한 함수) 실행
// -> emit을 통해 increment 이벤트 실행 -> 버튼의 v-on:increment 에서 이벤트 수신 -> incrementTotal 함수 (Vue 인스턴스에서 정의) 호출

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>

Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})

new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})

            2.3.7. 사용자 이벤트를 통한 컴포넌트 간 통신
                      - 2.5. 사용자 정의 이벤트 참조
                      - 부모, 자식 관계가 아닌 컴포넌트 간 통신
                      - 비어있는 Vue instance를 이용
                         - 각 컴포넌트에서 공통으로 접근 가능
                         - emit, on 을 통해 이벤트 함수 사용을 위해 생성

var bus = new Vue()

// 컴포넌트 A의 메소드
bus.$emit('id-selected', 1)

// 컴포넌트 B의 created 훅
bus.$on('id-selected', function (id) {
  // ...
})

            2.3.8. 이벤트 발생시 사용자 정의 함수 호출 
                      - 2.5. 사용자 정의 이벤트 참조
                      - v-bind:value="preps변수", v-on:input="사용자 정의 함수($event.target.value)" 를 통해 사용자 정의 함수 접근
                      - 사용자 정의 함수에서 계산 후 preps변수에 값을 할당
                      - 기존 input 이벤트로 다시 변경된 값을 전달하기 위해 이벤트 발생 

html : <currency-input v-model="price"></currency-input>

Vue.component('currency-input', {
  template: '\
    <span>\
      $\
      <input\
        ref="inputTag"\  //다른곳에서 이 태그를 참조하기 위해 ref 속성을 이용해 값을 넣어줌
        v-bind:value="propsValue"\ //input 태그의 value 에 props변수를 연결
        v-on:input="updateValue($event.target.value)">\ //input 이벤트 발생시 사용자 정의 함수 호출. 원본 이벤트의 값을 인자값으로 넘겨줌
    </span>\
  ',
  props: ['propsValue'],
  methods: {
    // 값을 직접 업데이트하는 대신 이 메소드를 사용하여
    // 입력 값에 대한 서식을 지정하고 배치 할 수 있습니다
    updateValue: function (value) {
      var formattedValue = value
        // 공백을 제거합니다.
        .trim()
        // 소수 자릿수 2자리로 줄입니다
        .slice(
          0,
          value.indexOf('.') === -1
            ? value.length
            : value.indexOf('.') + 3
        )
      // 값이 아직 정규화 되지 않은 경우
      // 이를 수동으로 재정의하여 조건을 충족시킵니다.
      if (formattedValue !== value) {
        this.$refs.inputTag.value = formattedValue
      }
      // 입력 이벤트를 통해 숫자 값을 내보냅니다.
      this.$emit('input', Number(formattedValue))
    }
  }
})

new Vue({
  el: '#app',
  data: {
    price: 0
  },