이 글은 Vue 2 버전을 사용합니다. Vue 3 버전과 다른 부분이 있을 수 있습니다.
Vue Application
개발의 가장 첫 시작점은 Vue Instance
를 생성하는 것이다.
let app = new Vue({...});
이 생성자를 통해서 받아내는 객체가 바로 Vue Instance
인데,
이 인스턴스는 다음 목차에 배울 Vue Component
를 담을 수 있는 그릇이로써 사용된다.
이런 이유로 Vue Instance
를 Root Component
라고도 표현한다.
일단 여기서 봐둘 것은 생성자의 인자로 넘어간 options object
정도이다.
이 option object
에는 아래와 같은 속성을 갖을 수 있다.
각 속성들에 대한 설명은 지금하지 않겠다.
계속 코딩하면서 자연스럽게 알게되므로, 지금은 "이런 게 있구나~" 하고 넘어가자.
오늘 날의 프론트 웹 개발에 사용되는 React, Vue 등의 라이브러리( 또는 프레임워크 )들은
기본적으로 화면을 나누고, 각 나눈 구역 안에서 또 구역을 나누어 각각을 코드로 관리한다.
그리고 이렇게 각각의 구역(또는 화면 요소)를 코드로 만든 것을 컴포넌트
라고 부른다.
컴포넌트의 핵심은 "재사용성" 이다. 중요한 점이니 잊지 말길 바란다.
이미지 출처: https://v2.vuejs.org/v2/guide/components.html#Organizing-Components
그렇다면 Vue 에서는 컴포넌트를 어떻게 생성할까?
기본적은 생성법은 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Component</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
<div id="app">
<app-section></app-section>
</div>
<script>
// ******* 전역 컴포넌트 생성 *******
Vue.component(
'app-section', // 컴포넌트 태그명
{ template: '<h1>Section</h1>'} // 컴포넌트의 내용
);
new Vue({
el: '#app',
components: {
// ******* 지역 컴포넌트 생성 *******
'app-footer': {
template: '<footer>FOOTER</footer>'
}
}
});
</script>
</body>
</html>
일단 위 html 을 봐서 알겠지만 서로 생성 방식이 다르다.
전역은 Vue.component
를 사용하고, 지역 컴포넌트는 Vue instance 의 Options Object
의 components 라는 속성에 넣는 객체의 key-value
를 넣음으로서 생성한다.
하지만 단순히 생성 방식에만 차이가 있는 게 아니다.
전역 컴포넌트와 지역 컴포넌트에는 사실 각각의 제약사항이 있다.
<div id="app">
<app-header></app-header>
<app-footer></app-footer>
</div>
<div id="app2">
<app-header></app-header> <!-- 전역 컴포넌트는 모든 인스턴스에서 접근 가능 -->
<app-footer></app-footer> <!-- 로컬 컴포넌트는 무조건 components 명시해야 한다 -->
</div>
<script>
// global component
Vue.component('app-header', {
template: '<h1>Header</h1>'
});
new Vue({
el: '#app',
components: {
"app-footer": {
template: '<footer>FOOTER</footer>'
}
}
});
new Vue({
el: '#app2'
// 로컬 컴포넌트를 생성하지 않았다! 에러가 발생할 것이다.
});
</script>
에러 발생내역 :
이러한 제약 사항과 관련해서는 공식 문서에서도 볼 수 있는 내용이니,
사용에 있어서 주의하도록 하자.
그런데 우리가 개발할 때는 어떤 걸 더 주로 쓸까?
우리는 "지역 컴포넌트"를 쓰는 경우가 더 많다.
그렇다면 전역 컴포넌트는 언제 쓸까?
전역 컴포넌트는 다른 라이브러리에서 만들고, 우리가 그 라이브러리를 사용할 때
해당 컴포넌트를 호출하여 사용하게 된다.
컴포넌트들은 각각 고유한 데이터
를 갖고 있다.
그리고 이 데이터들의 스코프는 자신들이 속한 컴포넌트이다.
그런데 이 데이터를 상위 혹은 하위 컴포넌트에게 넘기려면 어떻게 해야할까?
다행히도 Vue 에는 이러한 데이터 교환 규칙이 정해져있다!
상위 ==> 하위
데이터 전달: props
를 전달하는 방식으로 데이터를 넘긴다.하위 ==> 상위
데이터 전달: 이벤트를 발생시키는 방식(event-emit
)으로 데이터를 넘긴다.한번 코드를 보면서 이해해보자.
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<title>Props</title>
</head>
<body>
<div id="app">
<!-- v-bind:{props 배열에 지정한 명칭}="{부모 컴포넌트 데이터 이름}"-->
<app-header v-bind:props_data="message"></app-header>
</div>
<script>
let appHeader = {
template: '<h1>header {{props_data}}</h1>',
props: ['props_data'] // 상위 컴포넌트로 부터 데이터를 받기 위한 props 설정
};
// 만약 상위 컴포넌트로부터 받은 것을 곧바로 자식 컴포넌트에 나오게 하고 싶다면
let appHeader = {
template: '<h1>{{props_data}}</h1>', // 이렇게 해주면 된다! 이건 추후에 배움.
props: ['props_data']
};
new Vue({
el: '#app',
components: {
'app-header': appHeader
},
data: {
message: 'hi' // 하위 컴포넌트가 받을 data
}
});
</script>
</body>
</html>
Vue 개발자 도구를 통해서 확인해보면 아래와 같다.
👉 상위 컴포넌트
👉 하위 컴포넌트
정상적으로 상위 컴포넌트의 data.message
가 하위 컴포넌트의
props.props_data
에 전달된 것을 확인할 수 있다.
여기서 눈여겨볼 사항은 props
또한 reactive
하게 동작한다는 것이다.
다시 말해 상위 컴포넌트가 하위 컴포넌트에게 준 data.message 에 변경이 일어나면,
그 값을 전달받은 props
의 값 또한 변경이 전파되는 것이다!
이번에는 하위 컴포넌트에서 상위 컴포넌트로 데이터를 보내보자.
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<title>Event-Emit</title>
</head>
<body>
<div id="app4">
<!-- v-on:{하위 컴포넌트에서 발생한 이벤트의 "이름"}="상위 컴포넌트 메소드 이름" -->
<app-header v-on:pass="printText"></app-header>
{{fromChildText}}
</div>
<script>
let appHeader = {
template: '<button v-on:click="passEvent">click me</button>',
methods: {
passEvent: function () {
this.$emit(
'pass', // 이벤트 이름을 인자로 준다.
{childData: 1} // 이벤트와 함께 부모 컴포넌트에 보내고 싶은 데이터
);
}
}
}
new Vue({
el: '#app4',
components: {
'app-header': appHeader
},
methods: {
printText: function (passedData) {
console.log(passedData); // {childData: 1} 출력...
this.fromChild = passedData;
}
},
data: {
fromChildText: ''
}
})
</script>
</body>
</html>
난해하다.
코드 작성 방식의 순서를 숙지하면서 이해해보자.
appHeader.template
에 <button v-on:click="passEvent">click me</button>
을 줘서 클릭이 발생했을 때, passEvent 메소드가 실행되도록 한다.appHeader.methods
에 하나 추가한다.<app-header>
에 v-on 속성을 다음과 같이 작성해준다.v-on:{자식 컴포넌트가 this.$emit 으로 날린 이벤트 이름}="{부모 컴포넌트의 methods}"
v-on:
속성의 값에 사용된 함수를 작성한다.좀 복잡하지만 이렇게 규칙을 만들었으니 그냥 지켜주자.
참고로 이후에v-model
이라는 것을 배우면 이러한 복잡함이 많이 사라진다.
결국은 component
도 Vue Instance 의 한 종류이다.
다만 우리가 Root 로서 사용하는 Vue Instance 와 Component 은 서로 애플리케이션
안에서 맡는 역할
이 다르다. 그래서 서로 사용법과 생성 시 사용되는 option 들이
조금씩 차이가 난다.
stackoverflow 에서는 아래와 같은 글을 찾았는데, 이것도 차이점을 이해하기에
좋은 문구인거 같아서 추가 했다.
What are the differences?
- Since components are reusable Vue instances,
they accept the same options as new Vue, such as data, computed,
watch, methods, and lifecycle hooks.
The only exceptions are a few root-specific options like el.
- A root Vue instance is a Vue application launcher,
Vue component is an extension of the Vue instance.
참고링크: