본문 바로가기
Study

[Vue.js] Vue의 Instance와 속성

by 안자두 2023. 5. 24.

Instance

인스턴스는 뷰로 개발할 때 필수로 생성해야 하는 코드이다.

Vue를 설치한 후, new 키워드로 Vue 생성자 함수를 호출해 새로운 인스턴스를 만들 수 있다.

new Vue( ... );

Vue의 정보를 객체 상태로 넘겨주기만 하면 앱을 생성할 수 있다!

 

인스턴스 내부에는 여러 속성이 올 수도 있는데 자주 사용하는 속성들에 대해서 나열하면 다음과 같다.

new Vue({
  el: 인스턴스가 그려지는 화면의 시작점 (특정 html 태그)
  components: 화면에 표시할 요소
  data: 뷰의 반응성이 반영된 데이터 속성
  computed: 계산된 데이터 속성
  methods: 화면의 동작과 이벤트 로직을 제어하는 메소드
  created: 뷰의 라이프 사이클과 관련된 속성
  watch: data에서 정의한 속성이 변화했을 때, 추가 동작을 수행할 수 있게 정의하는 속성
  router: single page application에서 페이지를 이동할 때 사용하는 라우터 정보가 담긴 속성
})

각 속성은 여러 개가 올 수 있기 때문에 복수형의 이름으로 작성해야 함을 기억해야 한다.

간단한 예제를 만들어 보았다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <h1>{{ title }}</h1>
  </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        title: 'hi~'
      }
    })
  </script>
</html>

live server로 실행

간단하게 CDN 방식으로 Vue를 사용하였다.

생성자 함수에 넘겨주는 객체에는 el이라는 element 속성이 존재한다.
el은 해당 Vue가 그려지는 화면의 시작점이다. 즉, 해당 요소(와 그 하위 요소)만이 인스턴스에 종속된다.
예제의 경우에는 id가 app인 요소를 찾아 할당시켜 주었다.

아래에서 각 속성들에 대해 하나씩 설명하였다.

 


 

data

data 속성은 Vue의 반응성이 반영된 데이터 속성으로, 앱 내에서 사용할 값이다. React로 치면 state와 비슷하다고 생각한다.

Vue 인스턴스가 생성될 때, data 객체에 있는 모든 속성이 Vue의 반응형 시스템에 추가된다. 각 속성값이 변경될 때, Vue가 반응하여 새로운 값과 일치하도록 업데이트된다.

데이터 바인딩의 가장 기본 형태는 “Mustache” 구문(이중 중괄호: {{ }})을 사용한 텍스트 보간이다.
위의 예제를 함께 보면, title이라는 문자열 변수를 생성한 후,  {{ }}로 사용해 주었다.
태그 사이에는 {{ }}로 변수와 그냥 문자를 구분하지만, 요소 속성의 속성값 등 동적으로 사용하려면 v-bind라는 디렉티브 문법을 사용하여 구분해 줄 수 있다.
디렉티브 문법 2023.05.24 - [Study] - [Vue.js] Vue Template (v-bind, v-on, v-if, v-show)

인스턴스의 데이터가 변경되면 화면은 이에 반응해 다시 렌더링 해준다.
주의할 점은, 인스턴스가 생성될 때 존재했던 data에 대해서만 반응한다는 것이다. 만약 인스턴스 생성 이후, 새 속성을 추가한다면 해당 속성에 대해서는 갱신이 이뤄지지 않는다.

const vm = new Vue({
  data: {
  	a: 1
  }
});

vm.b = 'hi';

vm.a; // 1
vm.b; // hi

vm.a = 3; // 화면에 바로 바뀌는 것을 볼 수 있다

만약 처음에는 b를 사용하지 않아도, 이후에 사용하려면 다음과 같이 초기값을 지정해 처음부터 인스턴스에 존재할 수 있도록 해주어야 한다.

const vm = new Vue({
  data: {
  	a: 1,
    b: ''
  }
});

 


 

Component

컴포넌트는 화면의 영역을 구분하여 개발할 수 있는 뷰의 기능이다.
컴포넌트를 사용함으로써 코드의 재사용성이 높아지고 화면 제작 기간을 단축시킬 수 있다.

하나의 컴포넌트는 컴포넌트 이름과 내용을 key, value로 갖는 객체 형식으로 작성한다.
컴포넌트는 크게 전역 컴포넌트와 지역 컴포넌트로 나눌 수 있다.

우선 전역 컴포넌트의 경우, 한 번 선언한 컴포넌트는 특정 Vue 인스턴스에 상관없이 어느 Vue에서든 사용할 수 있다.

// Vue.component('컴포넌트 이름', 컴포넌트 내용);
Vue.component('app-header', {
  template: '<h1>Header</h1>'
});

 

지역 컴포넌트의 경우에는 특정 Vue 인스턴스 내부에 작성한다.

new Vue({
  el: '#app',
  // 지역 컴포넌트 등록 방식
  components: {
    // '컴포넌트 이름': 컴포넌트 내용,
    'app-footer': {
      template: '<footer>footer</footer>'
    }
  },
});

 

이 두 가지를 한번에 작성한 예시이다.

<body>
  <app-content></app-content>

  <div id="app">
    <app-header></app-header>
    <app-content></app-content>
    <app-footer></app-footer>
  </div>

  <div id="app2">
    <app-header></app-header>
    <app-footer></app-footer>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('app-header', {
      template: '<h1>Common Header</h1>'
    });  

    Vue.component('app-content', {
      template: '<p>vue content</p>'
    })

    new Vue({
      el: '#app',
      components: {
        'app-footer': {
          template: '<footer>footer11</footer>'
        }
      },
    });

    new Vue({
      el: '#app2',
      components: {
        'app-footer': {
          template: '<footer>footer22</footer>'
        }
      }
    })
  </script>
</body>

전역으로 작성한 'app-header'와 'app-content'는 어떤 Vue 내부에서도 사용할 수 있다.

하지만 지역으로 작성한 'app-footer'는 각 Vue 내부로 스코프가 한정되어 있기 때문에 각 Vue에서만 사용할 수 있으며, 여러 Vue에서 이름이 같아도 상관없다.

결과는 다음과 같다.

주의해야 할 점은, 전역 컴포넌트라는 말이 특정 Vue에 종속되어 있지 않다는 말이지, Vue 외부에서 사용할 수 있다는 것이 아니다.

body의 가장 상단에서 'app-content'를 호출하여도 이 위치는 Vue 내부가 아니기 때문에 렌더링 되지 않는다.

 

보통의 경우, 전역 컴포넌트는 플러그인이나 라이브러리로만 사용하고 그 외에는 지역 컴포넌트로 선언한다.

 


 

computed

템플릿 내에 표현식을 넣기 위해 로직을 computed에 저장해둘 수 있다.

<style>
  .warning {
    color: red;
  }
</style>

warning이라는 클래스에 스타일을 적용해 준다고 하자.

이 클래스를 에러 여부에 따라 적용하거나 적용하지 않는 예제를 만들어 보았다.

<body>
  <div id="app">
    <p v-bind:class="isError? 'warning' : ''">Hello</p>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        isError: true
      }
    });
  </script>
</body>

isError라는 data를 정의해 이 값에 따라 class를 적용하거나 적용하지 않을 수 있다.

이보다 더 복잡한 표현식의 경우에는 코드가 비대해지고 유지보수가 어려워진다.
이런 경우 아래처럼 computed를 사용하면 간단하게 코드를 작성할 수 있다.

<body>
  <div id="app">
    <p v-bind:class="errorTextColor">Hello</p>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        isError: false
      },
      computed: {
        errorTextColor: function() {
          return this.isError ? 'warning' : null;
        }
      }
    });
  </script>
</body>

computed는 해당 내부에서 사용하는 data에 의존하기 때문에 data가 변경되면 computed도 자동으로 이를 반영해 준다.

또한, computed는 getter 함수이기 때문에 사이드 이펙트가 없어, 테스트를 하거나 이해하기가 쉽다는 장점이 있다.

아래서 설명할 methods는 함수를 정의한 것인데, 사실 methods를 사용해도 같은 결과를 도출할 수 있다. 차이점은, computed 속성은 종속 대상을 따라 캐싱된다는 점이다.
종속된 대상이 변경될 때만 함수를 실행하고 변경되지 않으면 계산을 다시 하지 않고, 이미 저장된 결과를 즉시 반환해 준다.

 


 

watch

watch는 data에서 정의한 속성이 변화했을 때, 추가 동작을 수행할 수 있게 정의하는 속성이다.

대부분의 경우에는 computed 속성이 더 적합하지만, 데이터 변경에 대한 응답으로 비동기식 또는 시간이 많이 소요되는 조작을 수행할 때는 watch 속성이 더 유용하게 사용될 수 있다.

간단한 예제를 보자.

<body>
  <div id="app">
    {{num}}
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        num: 10
      },
      watch: {
        num: function() {
          console.log('changed');
        }
      }
    })
  </script>
</body>

num이 변경되었을 때, console로 'changed'라는 로그를 찍는 watch를 생성하였다.

이런 식으로 data의 num을 두 번 증가시키면 changed가 두 번 찍히는 것을 볼 수 있다.

 

보다 구체적으로 비동기 예제를 들면, 

<body>
  <div id="app">
    <div>
      {{ num }}
    </div>
    <div>
      {{ doubleNum }}
    </div>
    <div>
      {{ user }}
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        num: 5,
        user: {}
      },
      computed: {
        doubleNum: function() {
          return this.num * 2;
        }
      },
      watch: {
        num: async function(newValue, oldValue) {
          console.log(newValue, oldValue)
          
          this.user = await this.fetchUserByNumber(newValue);
        }
      },
      methods: {
        fetchUserByNumber: async function (num) {
          const res = await axios.get(`https://jsonplaceholder.typicode.com/users/${num}`)
          return res.data;
        }
      }
    });
  </script>
</body>

axios를 통해 num이라는 숫자가 변경되면 해당 숫자에 해당하는 데이터를 받아오도록 하였다.
받아온 데이터는 user에 저장을 해주었다.

num 변경 전

Vue devtools를 사용해 숫자를 증가시키면 아래와 같이 num이 변경되면서 num이 종속된 doubleNum이라는 computed 속성도 변경된 것을 볼 수 있다.

또한, num을 watch 하고 있다가 값이 변경되면 axios로 데이터를 요청하고, 받아온 데이터를 user에 저장, 화면에 보여주게 된다.

 

또한, watch는 두 개의 params가 있는데, 첫 번째는 변경된 값이고 두 번째는 이전의 값이다. 원하는 값으로 조작할 수 있다.

num 변경 후

watch 옵션을 사용하면 비동기 연산을 수행하고, 연산을 얼마나 자주 수행하는지 제한하고, 최종 응답을 얻을 때까지 중간 상태를 설정할 수 있지만, computed 속성은 이러한 기능을 수행할 수 없다.

computed의 경우에는 반환된 값을 그대로 변수로 사용할 수 있는 반면, watch의 경우에는 내부적으로 재할당 시켜주어야 한다는 차이도 있다.

 


 

methods

methods는 우리가 익히 아는 화면의 동작과 이벤트 로직을 제어하는 메서드 영역이다.

다른 속성에서 사용할 함수들을 분리해서 이곳에 넣어둬도 되고, 이벤트에 바인딩할 메서드를 정의할 수도 있다.

<body>
  <div id="app">
    <button v-on:click="logText">click me</button>
    <input type="text" v-on:keyup.enter="logText">
    <button>add</button>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      methods: {
        logText: function() {
          console.log('clicked');
        }
      }
    })
  </script>
</body>

이런 식으로 버튼을 클릭했을 때, 로그가 찍히도록 할 수도 있지만, 

<body>
  <div id="app">
    {{num}}
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        num: 10
      },
      watch: {
        num: function() {
          this.logText();
        }
      },
      methods: {
        logText: function(){
          console.log('changed');
        }
      }
    })
  </script>
</body>

이런 식으로 인스턴스 내부에서 사용할 수도 있다.

 


 

created

각 Vue 인스턴스는 생성될 때 일련의 초기화 단계를 거친다. 예를 들어, 데이터 관찰 설정이 필요한 경우, 템플릿을 컴파일하는 경우, 인스턴스를 DOM에 마운트 하는 경우, 그리고 데이터가 변경되어 DOM를 업데이트하는 경우가 있다. 그 과정에서 사용자 정의 로직을 실행할 수 있는라이프사이클 훅도 호출된다.

created는 인스턴스가 생성된 후에 호출된다.

인스턴스가 생성될 때는 다음과 같은 초기화 작업을 수행한다.

  • 데이터 관찰
  • 템플릿 컴파일
  • DOM에 객체 연결
  • 데이터 변경 시 DOM 업데이트

이 작업들 외에도 커스텀 로직을 추가할 수 있는데, 이 작업을 created에서 할 수 있다.

new Vue({
  data: {
    a: 1
  },
  created: function() {
    // this 는 vm 을 가리킴
    console.log("a is: " + this.a);
  }
});

초기화될 때, 로그가 찍히는 것을 볼 수 있다.

 


REF

https://v2.ko.vuejs.org/v2/guide/instance.html#%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%99%80-%EB%A9%94%EC%86%8C%EB%93%9C

https://joshua1988.github.io/web-development/vuejs/vuejs-tutorial-for-beginner/

 

728x90