인스턴스

Vue 인스턴스는 new Vue() 생성자로 만드는 Vue 애플리케이션의 기본 단위 입니다. 이 인서턴스를 사용하려면 특정 DOM 요소에 연결해야 합니다. Vue가 MVVM 패턴을 완벽히 따르는 것은 아니지만, Vue의 구조가 그것과 유사하기 때문에 인스턴스를 View Model이라고 부르기도 합니다.

인스턴스의 생성자에는 옵션 오브젝트를 파라미터로 사용합니다. 옵션은 여러가지 속성을 가질 수 있는데, 아래와 같은 속성들이 있습니다.

  • el : 인스턴스를 연결할 DOM 요소
  • props : 다른 컴포넌트에서 전달 받은 변수
  • data : 인스턴스 내에서 사용할 변수들
  • computed : 값이 자동으로 계산되는 변수들
  • watch : 변수의 값이 변경되었을 때 호출할 함수
  • methods : 인스턴스 내에서 사용할 함수들
  • components : 인스턴스 내에서 사용할 컴포넌트들
  • 그리고 created, mounted 등의 라이프싸이클 Hook

모든 인스턴스는 각자의 라이프싸이클을 갖고 있습니다. 라이프싸이클의 순서는 아래와 같고, 라이프 싸이클 Hook을 이용하면 특정 단계에서 특정 명령을 실행할 수 있습니다.

Vue Lifecycle

Vue 인스턴스의 라이프싸이클

아래는 예시 인스턴스 생성 코드입니다.

new Vue({
    el: '#app',
    data: {
        message: 'Hello World!'
    },
    created: function() {
        console.log("created");
    },
    mounted: function() {
        console.log("mounted");
        this.message = 'Hello Vue.js!';
    }
});

컴포넌트

컴포넌트는 재사용할 수 있는 인스턴스 입니다. 웹사이트에서 반복적으로 사용하는 요소들을 컴포넌트로 만들어두면 그 안에 데이터만 바꾸면서 편리하게 여러 번 사용할 수 있습니다. 컴포넌트는 글로벌 컴포넌트와 로컬 컴포넌트 2가지 종류가 있습니다. 글로벌 컴포넌트는 여러 인스턴스에서 사용할 수 있지만, 로컬 컴포넌트는 그 인스턴스에서만 사용할 수 있습니다.

아래는 글로벌 컴포넌트와 로컬 컴포넌트를 만드는 예시 코드입니다.

<div id="app">
    <component-global></component-global>
    <component-local></component-local>
</div>

<script>
Vue.component('component-global', {
    template: '<div>Global Component</div>'
});

new Vue({
    el: '#app',
    components: {
        'component-local': {
            template: '<div>Local Component</div>'
        }
    }
});
</script>

위 코드에서 component-globalcomponent-local은 각 컴포넌트의 이름입니다. HTML 부분에서 그 컴포넌트의 이름을 가진 태그를 만들면, 그 태그는 해당 컴포넌트의 template 에 있는 DOM 요소로 대체됩니다.

Single File Components를 이용한다면, 한 vue 파일 당 하나의 컴포넌트를 만드는 것 입니다.

컴포넌트 통신

여러 개의 컴포넌트를 사용할 때 컴포넌트끼리 데이터를 주고 받아야 하는 상황이 있습니다. Vue에서는 기본적으로 부모와 자식 관계를 가진 컴포넌트 끼리 통신을 할 수 있습니다. 부모에서 자식으로 데이터를 전달할 때는 v-bindprops, 자식에서 부모로 데이터를 전달할 때는 $emitv-on을 사용합니다.

<div id="app">
    <component-1 v-bind:propsdata="message"></component-child-1>
    <component-2 v-on:button-clicked="printText"></component-child-2>
</div>

<script>
Vue.component('component-1', {
    props: ['propsdata'],
    template: '<p></p>'
});

Vue.component('component-2', {
    template: '<button v-on:click="buttonClicked">Create event</button>',
    methods: {

        buttonClicked: function() {
            this.$emit('button-clicked');
        }

    }
});

new Vue({
    el: '#app',
    data: {
        message: 'Message from parent'
    },
    methods: {

        printText: function() {
            console.log('Event from child');
        }

    }
});
</script>

위의 예시에서는 component-1component-2 2개의 자식 컴포넌트를 만들었습니다.

component-1v-bind:propsdata="message"라는 속성을 갖고 있습니다. 이는 component-1의 propsdata 에 현재 인스턴스의 message 값을 넣어주겠다는 것 입니다.

component-1을 정의한 부분을 보면 props로 propsdata 변수 하나를 갖고 있습니다. props 는 자식이 부모로부터 받을 수 있는 데이터 입니다.

component-2v-on:button-clicked="printText"라는 속성을 갖고 있습니다. 이는 component-2에서 button-clicked 라는 이벤트가 발생하면 printText 함수를 실행한다는 의미입니다.

component-2의 정의에서 template 부분을 보면 버튼이 v-on:click="buttonClicked"라는 속성을 갖고 있습니다. 이는 버튼이 클릭되었을 때 buttonClicked라는 함수를 실행한다는 의미 입니다.

buttonClicked 함수는 this.$emit('button-clicked');를 실행하고 있습니다. $emit 은 이벤트를 발생시키는 함수입니다.

즉, 자식의 버튼을 클릭하면, buttonClicked가 실행되어 $emit을 하고, 부모의 printText가 실행됩니다.

부모, 자식 관계를 가진 컴포넌트들이 많을 때, 데이터의 흐름이 자유로워지면 프로그램의 작동 방식을 이해하기 어렵기 때문에, 데이터는 부모에서 자식으로만 가는 waterfall 방식을 이용하고, 자식에서 어떤 이벤트가 발생하면 그것을 부모에게 알려서 그 이벤트에 맞게 업데이트할 수 있도록 하는 것이 좋습니다.

Event Bus

위에서 말했듯이, 기본적으로 컴포넌트에서의 통신은 부모 자식 관계를 가진 컴포넌트 사이에서 일어나야 합니다. 하지만 Event Bus를 이용하면 같은 부모를 공유하는 형제 관계의 컴포넌트에서도 통신을 할 수 있습니다.

<div id="app">
    <component-1></component-1>
    <component-2></component-2>
</div>

<script>
let eventBus = new Vue();

Vue.component('component-1', {
    template: '<button v-on:click="buttonClicked">Trigger event bus</button>',
    methods: {

        buttonClicked: function() {
            eventBus.$emit('triggerEventBus', 10);
        }
        
    }
});

Vue.component('component-2', {
    template: '<p>This is sibling component</p>',
    created: function() {
        eventBus.$on('triggerEventBus', function(value) {
            console.log("Event bus triggered. Value : " + value);
        });
    }
});

new Vue({
    el: '#app'
});
</script>

위 코드에서는 component-1component-2 2개의 자식 컴포넌트를 만들었습니다. 그런데, 그 외에도 eventBus라는 Vue 인스턴스를 하나 만들었습니다.

component-1에서는 버튼을 클릭하면 eventBus.$emit('triggerEventBus', 10);를 실행하고 있습니다. component-2에서는 created 라이프싸이클 Hook에서 eventBus.$on을 통해 eventBus에서 triggerEventBus라는 이벤트가 발생하면 어떤 코드를 실행할지 함수로 정의했습니다. (created 에서 정의했기 때문에 이 컴포넌트가 생성되자 마자 적용됩니다.)

이 예시는 eventBus라는 제 3의 인스턴스를 만들어 형제 컴포넌트끼리 데이터를 주고 받을 수 있도록 하는 예시였습니다.