웹 개발, 특히 Vue.js와 같은 프런트엔드 프레임워크에서 "마운트"라는 용어는 일반적으로 웹 페이지의 DOM(Document Object Model, 문서 객체 모델)에 JavaScript 객체(예: Vue 인스턴스)가 연결되는 과정을 의미합니다.
DOM은 웹 페이지의 구조를 표현하는 방식으로, HTML 태그들은 DOM의 요소 또는 노드로 표현됩니다. 예를 들어, <div> 태그는 하나의 DOM 요소를 생성하며, 이 요소는 자식 요소를 가질 수 있습니다. Vue.js에서 Vue 인스턴스가 생성되면, 이 인스턴스는 특정 DOM 요소에 마운트(mount) 될 수 있습니다. 이는 Vue 인스턴스가 DOM 요소를 관리하고, 해당 요소와 그 하위 요소에 대한 렌더링을 제어한다는 것을 의미합니다. 예를 들어, 다음과 같은 Vue 인스턴스가 있다고 가정해 봅시다:
여기에서 el: '#app'은 Vue 인스턴스가 마운트(mount) 될 DOM 요소를 지정합니다. 이 경우, id가 'app'인 DOM 요소에 Vue 인스턴스가 마운트(mount) 됩니다. 따라서 "DOM에 마운트(mount) 된다"는 말은 Vue 인스턴스가 특정 DOM 요소를 관리하고 제어하게 된다는 것을 의미합니다. 반대로 "DOM에 마운트(mount) 되지 않았다"는 Vue 인스턴스가 아직 DOM 요소를 관리하고 제어하지 않는다는 것을 의미합니다.
이런 방식으로 Vue 인스턴스가 DOM 요소에 마운트(mount) 되면, Vue 인스턴스의 데이터와 메소드는 해당 DOM 요소와 그 하위 요소에 연결되어, 데이터의 변경이 자동으로 DOM에 반영되게 됩니다. 이렇게 함으로써 우리는 DOM을 직접 조작하지 않고도 웹 페이지의 동적인 부분을 쉽게 제어할 수 있습니다.
라이프사이클 훅(lifecycle hook) - created 와 mounted
Vue.js에서 created와 mounted는 라이프사이클 훅(lifecycle hook)으로, 컴포넌트가 생성되고 동작하는 동안 특정 시점에 자동으로 호출되는 메소드입니다.
created: 이 훅은 Vue 인스턴스가 생성되고 데이터 관찰, 계산된 속성, 메소드, watch 등이 설정된 직후에 실행됩니다. 이 시점에는 인스턴스가 DOM에 마운트되지 않았기 때문에 DOM 요소에 접근할 수 없습니다. 그러나 데이터에 접근하거나 변경하는 것은 가능합니다.
mounted: 이 훅은 Vue 인스턴스가 DOM에 마운트(mount) 된 직후에 실행됩니다. 이 시점에는 인스턴스가 DOM에 연결되어 있으므로 DOM 요소에 접근하거나 조작할 수 있습니다. 간단한 예제를 통해 이 두 라이프사이클 훅의 차이를 살펴보겠습니다:
위 예제에서 created hook 에서는 this.$el이 null이지만, mounted 훅에서는 this.$el이 DOM 요소를 참조하고 있습니다. 이는 created hook 이 실행되는 시점에는 Vue 인스턴스가 DOM에 마운트되지 않았으므로 DOM 요소에 접근할 수 없음을 보여줍니다.
반면에 mounted hook 이 실행되는 시점에는 Vue 인스턴스가 DOM에 마운트되어 있으므로 DOM 요소에 접근할 수 있습니다. 또한, 두 hook 모두 this.message를 통해 데이터에 접근할 수 있습니다. 이는 두 hook 이 실행되는 시점에 모두 Vue 인스턴스의 데이터가 설정되어 있음을 보여줍니다.
당연히 실행순서도 created 다음에 mounted 가 호출됩니다.
event bus 활용해 보기
app1 에서 data 초기화하는 과정에서 app2 로도 data 를 전달하고자 할 수도 있습니다. click 이벤트라면 methods 함수를 통해 전달하면 됩니다. 하지만 click 이벤트와 상관없이 바인딩 되고나서, 이는 watch 를 통해 처리도 가능하지만, event bus 를 통해 아래와 같이 구현도 가능합니다. 다만, 잠시 주의해야 할 사항도 존재합니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue.js 이벤트 버스 예제</title>
<!-- Vue.js CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>
<body>
<!-- 첫 번째 Vue.js 인스턴스 -->
<div id="app1">
<p>App 1의 데이터: {{ message }}</p>
<button @click="sendMessage">App 2로 메시지 보내기</button>
</div>
<!-- 두 번째 Vue.js 인스턴스 -->
<div id="app2">
<p>App 2의 데이터: {{ receivedMessage }}</p>
<button @click="sendMessage">App 2로 데이터 검증하기</button>
</div>
<script>
// 이벤트 버스를 위한 새 Vue 인스턴스 생성
var EventBus = new Vue();
// 첫 번째 Vue.js 인스턴스
new Vue({
el: '#app1',
data: {
message: '초기 데이터 설정'
},
mounted: function () {
this.$nextTick(function () {
// mounted 훅에서 이벤트를 발행합니다.
EventBus.$emit('event-from-app1', this.message);
});
},
methods: {
sendMessage: function () {
// 이벤트 버스를 통해 메시지를 발행합니다.
EventBus.$emit('event-from-app1', this.message);
}
}
});
// 두 번째 Vue.js 인스턴스
new Vue({
el: '#app2',
data: {
receivedMessage: ''
},
mounted: function () {
// 이벤트 버스를 구독하여 메시지를 수신합니다.
EventBus.$on('event-from-app1', function (message) {
this.receivedMessage = message;
}.bind(this));
},
methods: {
sendMessage: function () {
// 이벤트 버스를 통해 메시지를 발행합니다.
EventBus.$emit('event-from-app1', '데이터 검증용 메시지 from app2');
}
}
});
</script>
</body>
</html>
위의 코드를 살펴보면, App1의 mounted 라이프사이클 hook 에서 이벤트를 발생시키고 있습니다. 그러나, 이 이벤트는 App2의 mounted 라이프사이클 hook 에서 수신하려고 시도하고 있습니다.
Vue.js의 라이프사이클에 따르면 mounted hook 은 Vue 인스턴스가 DOM에 마운트된 직후에 실행됩니다. 그러나 App1과 App2 두 인스턴스가 동시에 DOM에 마운트되므로, App1에서 이벤트를 발생시키는 시점과 App2에서 이벤트를 수신하는 시점 사이에 차이가 존재할 수 있습니다.
따라서 App2에서 receivedMessage가 빈 값으로 나타나는 문제가 발생할 수 있습니다. 이 문제를 해결하려면 이벤트 발생과 수신의 시점을 조정해야 합니다.nextTick 함수를 사용하여 Vue의 렌더링이 완료된 후에 이벤트를 발생시키는 방법을 사용할 수 있습니다.