5 컴포넌트 통신 방법 - 기본
source: categories/study/vue-beginner-lv1/vue-beginner5.md
5.1 컴포넌트 통신
이번 시간엔 컴포넌트 통신 방법에 대해서 알아보겠습니다.
컴포넌트 통신 방식
뷰 컴포넌트는 각각 고유한 데이터 유효 범위를 갖습니다.
따라서, 컴포넌트 간에 데이터를 주고 받기 위해선 아래와 같은 규칙을 따라야 합니다.
- 상위 컴포넌트에서 하위 컴포넌트로 통신하는 방법:
props
전달 - 하위 컴포넌트에서 상위 컴포넌트로 통신하는 방법:
event
발생
앞에서 컴포넌트를 살펴봤을 때, 컴포넌트를 등록하면 관계가 생긴다고 말씀드렸었습니다.
뷰 컴포넌트
컴포넌트는 화면의 영역을 구분하여 개발할 수 있는 뷰의 기능입니다.
컴포넌트 기반으로 화면을 개발하게되면 코드의 재사용성이 올라가고 빠르게 화면을 제작할 수 있습니다.
다시 그 부분을 보면, 화면에 따라서 영역별로 컴포넌트를 만들어 코드를 관리한다했습니다.
그런식으로 컴포넌트를 만들었을 때, 컴포너트간의 관계가 생깁니다.
위 이미지를 보시면 연회색으로 보이는 영역이 3개.
오른쪽 그림 보시면 page 1개에서 연회색 영역 3개로 나뉘어지고, 왼쪽아래 연회색 영역은 진회색 영역 2개로 나뉘어지고, 오른쪽 연회색 영역은 진회색 영역 3개로 나뉘어집니다.
이렇게 컴포넌트를 나눌 때마다 컴포넌트가 자연스럽게 하단에 위치하게됩니다. (하위, 자식 컴포넌트)
이렇게 관계가 생기는데, 이러한 관계의 중요한 점은,
컴포넌트 통신 방식!!
바로 규칙이 생긴다는 점입니다.
컴포넌트는 각각 고유한 데이터 유효 범위를 갖습니다. 각 컴포넌트는 데이터를 각각 관리한다는 뜻입니다.
데이터를 상위 컴포넌트, 하위 컴포넌트간 공유하려면, 그 공유하기위한 방법으로 props
라는 속성, 그리고 이벤트 전달방식을 이용해야됩니다.
그래서 다시 정리하면, 상위에서 하위로는 데이터를 내려주는 방식으로, (props
속성으로)
하위에서 상위로는 이벤트를 올려주는(이벤트 발생) 방식으로 서로 통신합니다.
5.2 컴포넌트 통신 규칙이 필요한 이유
앞서 작성했었던 컴포넌트 구조입니다.
app-header
와 app-content
, app-footer
컴포넌트를 등록해봤었습니다.
각각의 컴포넌트 하위에 컴포넌트를 하나씩 더 등록했다고 가정하겠습니다.
(NavigationBar
, LoginForm
, CompanyInfo
)
여기서 app-header
컴포넌트에서 특정 사용자가 로그인을해서 LoginForm
에 어떤 데이터를 전달했다고 해볼게요.
그리고 LoginForm
에서 무언갈 처리하고나서 app-footer
로 무언갈 보냈다고 할게요.
그랬더니 이번엔 app-footer
에서 NavigationBar
로 무언갈 보냈다고 해봅시다.
이렇게 특정 컴포넌트의 변화에 따라서 나머지 컴포넌트가 위와 같은 식으로 유기적으로 데이터를 주고받았을 때, 데이터의 방향을 예측하기 어렵다는 것입니다.
위와 같이 말도안되는 데이터의 관계가 생기기 시작하면, 특정 상태, 데이터가 바뀌었을 때 , 그로인한 버그를 추적하기가 어려운게 N방향 통신의 문제점입니다.
이전 MVC 패턴에서 이런 문제점이 많이 있었습니다.
그런데 이제는 데이터가 위와 같이 상위 컴포넌트에서 하위 컴포넌트, 즉, 아래로만 내려가게됩니다.
따라서 이러한 컴포넌트 통신방식이라는 규칙이 생겼을 때, 저희가 얻는 이점은, 처음엔 좀 불편하겠지만, 데이터의 흐름을 추적할 수 있다, 데이터는 항상 위에서 아래로 흐르고, 이벤트는 아래에서 위로 가기 때문에 흐름을 추적할 수 있다는 것입니다.
위와 같이 상위 컴포넌트에서 하위 컴포넌트로는 props
가, 하위 컴포넌트에서 상위 컴포넌트로는 event
가 흐릅니다.
5.3 props 속성
props
에 대해 실습하기 위해서 playground/props.html
파일을 하나 생성합니다.
가독성을 위해 위와 같이 appHeader
라는 변수생성.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<app-header></app-header>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
template: '<h1>header</h1>'
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
}
})
</script>
</body>
</html>
이제부터 props
에 대해 알아보도록 하겠습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<app-header></app-header>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
template: '<h1>header</h1>'
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
},
// 이제부터 props에 대해 알아보도록 하겠습니다.
// 아래와 같이 data 속성을 활용해 message: 'hi'를 설정해보도록 하겠습니다.
data: {
message: 'hi'
}
})
</script>
</body>
</html>
위와 같이 data
속성을 활용해 message: 'hi'
를 설정해보도록 하겠습니다.
이렇게하면 Root
컴포넌트에 message: "hi"
가 생기기 시작합니다.
이게 Root
컴포넌트에서 관리하는 data
입니다.
이 data
를 app-header
컴포넌트로 내릴겁니다.
내릴 때는 앞서 말씀드렸던 props
라는 속성을 사용하시면 됩니다.
v-bind:프롭스 속성 이름="사우이 컴포넌트의 데이터 이름"
이것이 props 정의 문법입니다.
구조를 보시면 app-header
의 상위 컴포넌트는 Root
가 됩니다.
Root
의 data
의 이름은 message
입니다.
즉, "상위 컴포넌트 데이터 이름"
부분에 "message"
라고 넣습니다.
프롭스 속성 이름
부분은 위와 같이 appHeader
에 props
키에 배열로 등록합니다.
그럼 위와 같이 propsdata
라고 정의할 수 있습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- <app-header v-bind:프롭스 속성 이름="상위 컴포넌트의 데이터 이름"></app-header> -->
<app-header v-bind:propsdata="message"></app-header>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
template: '<h1>header</h1>',
props: ['propsdata']
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
},
// 이제부터 props에 대해 알아보도록 하겠습니다.
// 아래와 같이 data 속성을 활용해 message: 'hi'를 설정해보도록 하겠습니다.
data: {
message: 'hi'
}
})
</script>
</body>
</html>
보시면 app-header
컴포넌트에 propsdata
속성이름으로 "hi"
라는 값이 내려왔습니다.
5.4 props 속성의 특징
저희가 컴포넌트 통신방법 두번째, 이벤트 발생에 대해 알아보기 전에 props
에 대해 조금만 더 짚어보고 가겠습니다.
위와 같이 코드를 작성하시고 페이지를 보시면 app-header
에 props
가 생길겁니다.
여기서 알 수 있는 재밌는 사실은 Root
컴포넌트에 있는 message
있죠? 상위 컴포넌트에 있는 data
속성에 들어있는 것이 message
인데, 여기에 들어있는 값이 바뀌었을 때,
위와 같이 hi hello
로 바뀌었을 때,
바뀐 데이터가 그대로 app-header
로 내려와서 반영됩니다.
이것이 바로 저희가 첫시간에 구현했었던, reactivity
가 그대로 props
에도 반영이 된다라는 사실을 알 수가 있습니다.
저희가 후반부에 배울 Vue.js
의 기본적인 문법 중에 data binding
이라는 것이 있는데,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- <app-header v-bind:프롭스 속성 이름="상위 컴포넌트의 데이터 이름"></app-header> -->
<app-header v-bind:propsdata="message"></app-header>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
template: '<h1>{{propsdata}}</h1>',
props: ['propsdata']
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
},
// 이제부터 props에 대해 알아보도록 하겠습니다.
// 아래와 같이 data 속성을 활용해 message: 'hi'를 설정해보도록 하겠습니다.
data: {
message: 'hi'
}
})
</script>
</body>
</html>
위와 같이 mustache
문법을 통해
{{ }}
이렇게 넣어주고 그 안에 propsdata
변수를 넣어주면, propsdata
값이 무엇이 되었든간에 반영이되어 화면에 나타나게됩니다.
지금 기준으로는 'hi'
를 내려보냈기 때문에 h1
태그 안에 hi
가 출력될 것입니다.
확인해보시면 위와 같이 hi
가 출력되는 것을 볼 수 있습니다.
Root
컴포넌트에서 message
로 hi
를 보냈기 때문에 app-header
에 hi
가 나타난 것입니다.
위와 같이 Root
컴포넌트에서 또 hi
대신에 hi props
로 바꿔주면,
상위 컴포넌트의 data
를 바꾸는 순간 하위 컴포넌트의 data
가 바뀌면서
하위 컴포넌트의 props
속성에 반영되면서 화면이 다시 그려지는 것을 볼 수 있습니다.
5.5 실습 안내 - props 속성 실습
앞시간에 말씀드린 것처럼 이번시간엔 props
등록 실습을 해보겠습니다.
위 num이라는 data
를 props
로 appContent
에 내려보내보시고 mustache(콧수염) 문법
을 활용해서
{{ }}
num
에 들어있는 값을 화면에 표현해보시길 바랍니다.
props
를 위와 같이 등록하시고
num
을 app-content
컴포넌트에 v-bind
를 활용해 내려보내면됩니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- <app-header v-bind:프롭스 속성 이름="상위 컴포넌트의 데이터 이름"></app-header> -->
<app-header v-bind:propsdata="message"></app-header>
<app-content v-bind:propsdata="num"></app-content>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
template: '<h1>{{propsdata}}</h1>',
props: ['propsdata']
}
var appContent = {
template: '<div>{{propsdata}}</div>',
props: ['propsdata']
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
'app-content': appContent,
},
// 이제부터 props에 대해 알아보도록 하겠습니다.
// 아래와 같이 data 속성을 활용해 message: 'hi'를 설정해보도록 하겠습니다.
data: {
message: 'hi',
num: 10,
}
})
</script>
</body>
</html>
5.6 실습 풀이 - props 속성 실습 풀이
위와 같이 propsdata
라고 동일하게 쓰셔도 각 컴포넌트에 속해있는 것이기 때문에 유효범위가 각 컴포넌트로 제한되어있어서 아무 문제 없다.
그리고 컴포넌트 태그에 위와 같이 설정해준다.
위와 같이 작성하면 div
태그 안에 10이 출력될 것이다.
화면을 보시면 app-header
와 app-content
가 각각 propsdata
를 가지고 있고,
Root
컴포넌트(인스턴스)의 data
에서 num
이라는 내용이 app-content
로 내려왔다는 것을 알 수 있습니다.
여기서 다시 복습을 해보면, 위와 같이 Root
컴포넌트의 num
값이 바뀌었을 때, 그 값이 바뀐 상태로 아래 하위 컴포넌트로 전달이 될 것이고
전달이된 값이 화면에 바로 반영돼서 그려지는 것이 바로 Reactivity라고 말씀을 드렸습니다.
5.7 event emit
위에서 아래로는 data(props)를 전달하고, 아래서 위로는 data를 올리는 것이 아니라 event를 올립니다.
이것이 바로 event emit입니다.
이는 컴포넌트 통신의 흐름을 보다 더 파악하기 쉽도록 하게하기 위함입니다.
plaground/event-emit.html
파일을 만들어줍니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<app-header></app-header>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
template: '<button>click me</button>'
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
}
})
</script>
</body>
</html>
위와 같이 컴포넌트를 작성해주시고 페이지를 확인해봅니다.
위와 같이 app-header
컴포넌트가 등록된 것을 볼 수 있습니다.
이제부터 event emit
을 등록해보겠습니다.
위와 같이 Vue.js 문법 중에 하나인 v-on:click=""
이라는 문법으로 클릭 이벤트를 등록할 수 있습니다.
이렇게 하면, 위 컴포넌트를 클릭했을 때, 위 컴포넌트의 상위 컴포넌트로 event
를 보낼 수 있습니다.
즉, app-header
를 클릭했을 때, Root
로 event
를 보낼 수 있습니다.
위와 같이 passEvent
라는 메소드를 정의합니다.
그리고 method
는 위와 같이 정의할 수 있습니다. method의 passEvent 프로퍼티에 해당 컴포넌트를 클릭했을 때 실행되는 콜백 함수를 정의할 수 있습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<app-header></app-header>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
// v-on 문법을 통해 event를 등록할 수 있습니다.
// 이렇게하면, 해당 컴포넌트를 클릭했을 때, 해당 컴포넌트의 상위 컴포넌트로 event를 보낼 수 있습니다.
// 아래와 같이 passEvent라는 메소드를 정의합니다.
template: '<button v-on:click="passEvent">click me</button>',
// 그리고 methods를 아래와 같이 정의합니다.
// 이 컴포넌트를 클릭했을 때 passEvent 라는 콜백함수가 실행되는데, 그 passEvent 콜백함수를 여기서 정의합니다.
methods: {
passEvent: function () {
// this.$emit() 코드로 pass라는 이벤트를 한번 보내보도록 하겠습니다.
this.$emit('pass');
}
}
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
}
})
</script>
</body>
</html>
this.$emit('pass');
코드로 pass
라고 한번 이벤트를 보내보도록 하겠습니다.
vue 개발자도구를 보시면 Events 탭이 있습니다.
이 Events 탭에서 event emit에 대한 로그들을 확인할 수 있습니다.
app-header
컴포넌트를 클릭하시면 위와 같이 pass
가 계속 발생이됩니다.
app-header
에서 event
가 발생이되는데 pass
라고하는 event
가 발생이 되었다는 뜻입니다.
여기까지 event
발생을 시켜봤구요, 이 다음 시간엔 이 event emit
을 받아서 어떤 것들을 할 수 있는지 이어서 알아보도록 하겠습니다.
5.8 event emit으로 콘솔 출력하기
앞시간까지 this.$emit()
이라는 API
를 활용해서 pass
라는 이벤트를 발생시켰습니다.
그래서 click
했을 때 위와 같이 event
가 발생하는 것을 볼 수 있습니다.
그런데 아직은 상위 컴포넌트에서 받을 수 있게 조작은 해주지 않은 상태입니다.
이를 위해선 app-header 컴포넌트에서 발생한 event를 잡아줘야됩니다.
문법은 위와 같습니다.
v-on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메서드 이름"
app-header에서 pass라는 이벤트가 발생했었죠? (코드를 그렇게 작성함)
app-header
의 상위 컴포넌트는 Root
인스턴스죠?
이 Root
인스턴스에서 실행할 method
를 저희가 정의하고 그거를 돌려보면됩니다.
하위 컴포넌트에서 발생한 이벤트 이름은 pass
입니다.
상위 컴포넌트의 메소드 이름
은 위 부분에 작성을 해보겠습니다.
new Vue()
부분이 바로 Root
컴포넌트, 즉, app-header
컴포넌트의 상위 컴포넌트 입니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 하위 컴포넌트에서 발생한 이벤트를 상위 컴포넌트에서 받게해줘야합니다. -->
<!-- <app-header v-on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메소드 이름"></app-header> -->
<!-- 하위 컴포넌트에서 발생한 메소드 이름은 pass 였습니다. this.$emit('pass') -->
<app-header v-on:pass="logText"></app-header>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
// v-on 문법을 통해 event를 등록할 수 있습니다.
// 이렇게하면, 해당 컴포넌트를 클릭했을 때, 해당 컴포넌트의 상위 컴포넌트로 event를 보낼 수 있습니다.
// 아래와 같이 passEvent라는 메소드를 정의합니다.
template: '<button v-on:click="passEvent">click me</button>',
// 그리고 methods를 아래와 같이 정의합니다.
// 이 컴포넌트를 클릭했을 때 passEvent 라는 콜백함수가 실행되는데, 그 passEvent 콜백함수를 여기서 정의합니다.
methods: {
passEvent: function () {
// this.$emit() 코드로 pass라는 이벤트를 한번 보내보도록 하겠습니다.
this.$emit('pass');
}
}
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
},
// app-header의 상위 컴포넌트는 바로 이 Root 컴포넌트(Vue 인스턴스)입니다.
// 여기에 methods를 작성하겠습니다.
methods: {
logText: function () {
console.log('hi');
}
}
})
</script>
</body>
</html>
누를 때마다 event log
는 계속 쌓이고 콘솔창에 hi
는 계속 출력되는 것을 볼 수 있습니다.
이게 바로 event emit이고 하위 컴포넌트에서 상위 컴포넌트로 대화(통신)하는 방식입니다.
5.9 실습 안내 - event emit 실습 안내
new Vue()
의 components
에 app-content
컴포넌트를 등록합니다.
이렇게 반복하시면서 코드나 문법에 익숙해지실 거 같습니다.
위와 같이 appContent
변수를 만들고 template
속성을 정의해줍니다.
appContent
컴포넌트를 클릭했을 때 실행할 함수 이름을 addNumber
라고 정의해보겠습니다.
그럼 위와 같이 v-on:click="addNumber"
코드를 통해 appContent
컴포넌트를 클릭했을 때, addNumber
함수를 실행할 수 있습니다.
v-on
이라고 하는 것은 Vue
에서 제공하는 속성이고, 이러한 문법들은 나중에 문법에 대해 볼 때 더 자세하게 설명드리겠습니다.
여튼 위 appContent
컴포넌트를 클릭하면 addNumber
함수를 실행하겠다는 것.
위와 같이 this.$emit()
코드를 넣어줍니다.
여기서 직접 해보셔야될게 뭐냐면, 위 appContent
컴포넌트의 add 버튼을 클릭했을 때, addNumber
함수를 실행시키고, 실행된 addNumber
의 this.$emit()
코드를 통해 상위 컴포넌트에 event
를 보내시고 (이벤트 이름은 this.$emit()
여기 소괄호 안에 작성해주면 된다), 그리고나서
다시 new Vue()
에 data
속성을 정의하고 그 안에 num
속성을 정의합니다.
여튼 위와 같이 하위 컴포넌트에서 발생한 event
를 상위 컴포넌트로 올려서 (app-content
의 상위 컴포넌트는 Root(new Vue())
) data.num
값을 1 증가시켜보세요.
그러니까 최종적으로 결과는 11이 되어야겠죠?
클릭할 때마다 12, 13, 14… 이렇게 증가하게 만드셔도됩니다.
여튼 num
값을 1을 증가시키는 event
를 상위 컴포넌트로 올리고,
상위 컴포넌트의 methods에 정의해서 num에 접근하게끔 해보세요.
조금 힌트를 드리면 this.num
으로.. methods
안에서 data
의 num
에 this.num
으로 접근하실 수 있으니까, 참고하셔서 위의 methods
안에 메소드를 만들고 연결을 해보시면 되겠습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 하위 컴포넌트에서 발생한 이벤트를 상위 컴포넌트에서 받게해줘야합니다. -->
<!-- <app-header v-on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메소드 이름"></app-header> -->
<!-- 하위 컴포넌트에서 발생한 메소드 이름은 pass 였습니다. this.$emit('pass') -->
<app-header v-on:pass="logText"></app-header>
<app-content v-on:add="addNumber" v-bind:propsdata="num"></app-content>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
// v-on 문법을 통해 event를 등록할 수 있습니다.
// 이렇게하면, 해당 컴포넌트를 클릭했을 때, 해당 컴포넌트의 상위 컴포넌트로 event를 보낼 수 있습니다.
// 아래와 같이 passEvent라는 메소드를 정의합니다.
template: '<button v-on:click="passEvent">click me</button>',
// 그리고 methods를 아래와 같이 정의합니다.
// 이 컴포넌트를 클릭했을 때 passEvent 라는 콜백함수가 실행되는데, 그 passEvent 콜백함수를 여기서 정의합니다.
methods: {
passEvent: function () {
// this.$emit() 코드로 pass라는 이벤트를 한번 보내보도록 하겠습니다.
this.$emit('pass');
}
}
}
var appContent = {
template: '<button v-on:click="addNumber">add {{propsdata}}</button>',
methods: {
addNumber: function () {
// 상위 컴포넌트에 이벤트를 올리는 방법, this.$emit();
this.$emit('add');
}
},
props: ['propsdata']
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
'app-content': appContent,
},
// app-header의 상위 컴포넌트는 바로 이 Root 컴포넌트(Vue 인스턴스)입니다.
// 여기에 methods를 작성하겠습니다.
methods: {
logText: function () {
console.log('hi');
},
addNumber: function () {
this.num++;
}
},
data: {
num: 10,
}
})
</script>
</body>
</html>
5.10 실습 풀이
세번째 탭(이벤트 탭)
app-content
라는 컴포넌트에서 increase
라는 이벤트가 발생한 것을 확인할 수 있습니다.
그리고 위에 app-content
태그에 v-on:increase
를 적어주시고(하위 컴포넌트에서 발생한 이벤트이름)
상위 컴포넌트의 메서드 이름까지 적어줍니다.
이제 상위 컴포넌트에서 event
를 받아서 상위 컴포넌트의 increaseNumber
함수를 실행시켰으니, 여기서 num
값을 증가시키면됩니다.
add
버튼을 누를 때마다 위와 같이 num
값을 증가시키면됩니다.
위와 같이 add
버튼을 누를 때마다 num
값이 1씩 증가됩니다.
num
을 화면상에 표현해봅시다.
p
태그를 만드시고 num을
{{ }}
mustache
문법으로 넣어줍니다.
add
버튼을 누르면 increase
가 나오면서 숫자가 올라가는 것을 볼 수 있습니다.
그리고 data
의 num
값도 반영되는 것을 확인할 수 있습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{num}}</p>
<!-- 하위 컴포넌트에서 발생한 이벤트를 상위 컴포넌트에서 받게해줘야합니다. -->
<!-- <app-header v-on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메소드 이름"></app-header> -->
<!-- 하위 컴포넌트에서 발생한 메소드 이름은 pass 였습니다. this.$emit('pass') -->
<app-header v-on:pass="logText"></app-header>
<app-content v-on:add="addNumber" v-bind:propsdata="num"></app-content>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
// v-on 문법을 통해 event를 등록할 수 있습니다.
// 이렇게하면, 해당 컴포넌트를 클릭했을 때, 해당 컴포넌트의 상위 컴포넌트로 event를 보낼 수 있습니다.
// 아래와 같이 passEvent라는 메소드를 정의합니다.
template: '<button v-on:click="passEvent">click me</button>',
// 그리고 methods를 아래와 같이 정의합니다.
// 이 컴포넌트를 클릭했을 때 passEvent 라는 콜백함수가 실행되는데, 그 passEvent 콜백함수를 여기서 정의합니다.
methods: {
passEvent: function () {
// this.$emit() 코드로 pass라는 이벤트를 한번 보내보도록 하겠습니다.
this.$emit('pass');
}
}
}
var appContent = {
template: '<button v-on:click="addNumber">add</button>',
methods: {
addNumber: function () {
// 상위 컴포넌트에 이벤트를 올리는 방법, this.$emit();
this.$emit('add');
}
},
props: ['propsdata']
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
'app-content': appContent,
},
// app-header의 상위 컴포넌트는 바로 이 Root 컴포넌트(Vue 인스턴스)입니다.
// 여기에 methods를 작성하겠습니다.
methods: {
logText: function () {
console.log('hi');
},
addNumber: function () {
this.num++;
}
},
data: {
num: 10,
}
})
</script>
</body>
</html>
그런데 위 this
에 대해서 좀 헷갈리실 분들이 계실 거 같습니다.
다음 시간에 이 this
에 대해서 좀 더 설명드리고 위 코드들에 대해 이해하실 수 있게 코드 예시도 보여드리겠습니다.
5.11 Vue 인스턴스에서의 this
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{num}}</p>
<!-- 하위 컴포넌트에서 발생한 이벤트를 상위 컴포넌트에서 받게해줘야합니다. -->
<!-- <app-header v-on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메소드 이름"></app-header> -->
<!-- 하위 컴포넌트에서 발생한 메소드 이름은 pass 였습니다. this.$emit('pass') -->
<app-header v-on:pass="logText"></app-header>
<app-content v-on:add="addNumber" v-bind:propsdata="num"></app-content>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
// v-on 문법을 통해 event를 등록할 수 있습니다.
// 이렇게하면, 해당 컴포넌트를 클릭했을 때, 해당 컴포넌트의 상위 컴포넌트로 event를 보낼 수 있습니다.
// 아래와 같이 passEvent라는 메소드를 정의합니다.
template: '<button v-on:click="passEvent">click me</button>',
// 그리고 methods를 아래와 같이 정의합니다.
// 이 컴포넌트를 클릭했을 때 passEvent 라는 콜백함수가 실행되는데, 그 passEvent 콜백함수를 여기서 정의합니다.
methods: {
passEvent: function () {
// this.$emit() 코드로 pass라는 이벤트를 한번 보내보도록 하겠습니다.
this.$emit('pass');
}
}
}
var appContent = {
template: '<button v-on:click="addNumber">add</button>',
methods: {
addNumber: function () {
// 상위 컴포넌트에 이벤트를 올리는 방법, this.$emit();
this.$emit('add');
}
},
props: ['propsdata']
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
'app-content': appContent,
},
// app-header의 상위 컴포넌트는 바로 이 Root 컴포넌트(Vue 인스턴스)입니다.
// 여기에 methods를 작성하겠습니다.
methods: {
logText: function () {
console.log('hi');
},
addNumber: function () {
this.num++;
}
},
data: {
num: 10,
}
})
</script>
</body>
</html>
위에서 보신 this에 대해 궁금하실텐데, 일단 이를 이해하기위해서 우선적으로 간단한 코드를 보겠습니다.
var obj = {
num: 10,
getNumber: function () {
console.log(this.num);
}
}
obj.getNumber(); // 10
객체 안에서 this
를 사용하면 그 this
는 해당 객체를 가리키게됩니다.
Vue 인스턴스 구조는 일단 자세하게 들여다보진 마세요. 예시일 뿐이니까 최대한 간단하게 작성한겁니다.
일단 이전에 말씀드렸던 관점에서 보면,
this
가 Vue라는 객체를 가리킨다는 것은 이해하실 수 있으실겁니다.
물론 이 data
는 Vue
내부적으로 new Vue()
를 실행하면 기타 동작들을 거쳐서 조금 변환이 있겠지만, 기본적으로 methods
속성 안의 함수에서 this
는 Vue 인스턴스를 가리키고, this.num
은 Vue 인스턴스의 data
의 num
을 가리킨다고 보면됩니다.
// vue 인스턴스를 최대한 간소화해서 작성해봤습니다.
// 예시일 뿐이니까 최대한 간단하게 작성한겁니다.
// 아래 this가 Vue라는 객체를 가리킨다는 것은 이해하실 수 있으실겁니다.
// 물론 아래와 같은 상황에서 this가 Vue를 가리킬 순 없습니다.
// 실제 new Vue() 동작을 거쳐 아래와는 다른 모습으로, this가 Vue를 가리킬 수 있도록 객체 구조가 바뀔 것입니다.
// 여튼 기본적으로 methods 속성 안의 함수에서 this는 Vue 인스턴스를 가리키고, this.num은 Vue 인스턴스의 data의 num을 가리킨다고 보면됩니다.
var Vue = {
el: '',
num: 10,
data: {
num: 10,
},
methods: {
getNumber: function () {
this.num
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=no">
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{num}}</p>
<!-- 하위 컴포넌트에서 발생한 이벤트를 상위 컴포넌트에서 받게해줘야합니다. -->
<!-- <app-header v-on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메소드 이름"></app-header> -->
<!-- 하위 컴포넌트에서 발생한 메소드 이름은 pass 였습니다. this.$emit('pass') -->
<app-header v-on:pass="logText"></app-header>
<app-content v-on:add="addNumber" v-bind:propsdata="num"></app-content>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
// v-on 문법을 통해 event를 등록할 수 있습니다.
// 이렇게하면, 해당 컴포넌트를 클릭했을 때, 해당 컴포넌트의 상위 컴포넌트로 event를 보낼 수 있습니다.
// 아래와 같이 passEvent라는 메소드를 정의합니다.
template: '<button v-on:click="passEvent">click me</button>',
// 그리고 methods를 아래와 같이 정의합니다.
// 이 컴포넌트를 클릭했을 때 passEvent 라는 콜백함수가 실행되는데, 그 passEvent 콜백함수를 여기서 정의합니다.
methods: {
passEvent: function () {
// this.$emit() 코드로 pass라는 이벤트를 한번 보내보도록 하겠습니다.
this.$emit('pass');
}
}
}
var appContent = {
template: '<button v-on:click="addNumber">add</button>',
methods: {
addNumber: function () {
// 상위 컴포넌트에 이벤트를 올리는 방법, this.$emit();
this.$emit('add');
}
},
props: ['propsdata']
}
var vm = new Vue({
el: '#app',
components: {
'app-header': appHeader,
'app-content': appContent,
},
// app-header의 상위 컴포넌트는 바로 이 Root 컴포넌트(Vue 인스턴스)입니다.
// 여기에 methods를 작성하겠습니다.
methods: {
logText: function () {
console.log('hi');
},
addNumber: function () {
this.num++;
}
},
data: {
num: 10,
}
})
</script>
</body>
</html>
Vue 인스턴스를 vm
이라는 변수에 할당합니다.
펼쳐서 조금 내리시면 num
이라는게 바로 보입니다.
분명 저희는 data
안에 선언을 했지만 실제론 그 바깥 레벨로 나와있습니다.
Vue 인스턴스를 생성할 때 이러한 처리가 된다는 것을 추측할 수 있습니다.
따라서 this.num
은 data
의 num
속성을 가리킨다라고 보면 되겠습니다.