본문 바로가기
Vue.js

[Vue] Virtiual DOM과 Vue 렌더링 원리 알아보기 ✏️

by devebucks 2021. 6. 27.
728x90

개요

이번 6월에 회사에서 제가 속한 팀을 대상으로 Vue 라이프 사이클에 대한 기술 공유를 제가 맡게 되었었는데요.

단순히, 라이프 사이클에 대한 개념과 사용 방법 공유보다는 라이프 사이클이 어떻게 렌더링으로 작용하는지를 공유하고 싶었습니다.

그 이유는 저와 팀 원들에게 원리와 동작을 충분히 이해하고 프로그래밍을 하자는 메시지를 던지기 위해서였습니다.

공유를 준비하면서 공부한 내용을 총 3개의 시리즈 포스팅으로 작성해봤습니다.

✏️ [웹 렌더링 개념-1] DOM 이해하기
✏️ [웹 렌더링 개념-2] webkit 렌더링 엔진 작업 순서- 브라우저에 화면이 그려지는 과정
👉 [Vue] Virtual DOM과 Vue 렌더링 원리 알아보기

이번 내용은  Vue가 브라우저에서 렌더링되는 원리를 알아보겠습니다.

 

그럼 순서대로 브라우저에서 어떤 과정으로 렌더링이 일어나는지 알아보겠습니다.

  • Vue 라이프 사이클 개념
  • Reactivity System 개념
  • Virtual DOM 개념
  • Vue가 브라우저에 렌더링되는 원리
  • 공유를 하면서 받은 질문: 렌더링 원리를 알게 되었으니, React 대신 Vue를 사용하게 된 이유.
  • 정리

 

 

🔥 Vue 라이프 사이클 개념

컴포넌트가 브라우저 화면에 그려지고 사라지기까지의 과정을 말합니다. 라이프 사이클은 여러 단계가 정의되어 있고, Vue 프레임워크는 개발자가 단계마다 로직을 사용해서 컴포넌트를 조작할 수 있도록 훅(hook)이라는 함수를 지원합니다. 훅에는 beforeCreated, Created, beforeMount, Mounted, destroyed 훅이 있습니다. 가장 많이 사용하는 Created와 Mounted훅만 집고 넘어가겠습니다.

 

Created

Created는 Vue 인스턴스가 생성되고, 인스턴스에 reactivity가 적용되서 컴포넌트의 data property가 setter와 getter로 정의된 후에 작동하는 훅입니다. data property가 getter와 setter로 변했기 때문에, 반응성 시스템이 관리하는 데이터가 되었고, 그래서 Created 훅부터 컴포넌트의 data에 접근할 수 있습니다.

그래서,

주로 컴포넌트가 생성되는 시점인 Created훅에서 서버로 데이터를 요청을 합니다. 컴포넌트의 data property에 서버에서 받은 데이터를 대입하는 로직이 컴포넌트 상태 값을 초기에 대입해 줄 수 있습니다. 아직 virtual DOM에 반영되지 않은 인스턴스이고, 실제 DOM에도 없는 컴포넌트여서, 컴포넌트 내부의 노드를 접근하는 것은 불가능한 단계입니다.

 

 

Mounted

virtual DOM이 실제 브라우저 DOM에 Mounted되고 난 후에 동작하는 훅입니다. 당연히 data property 접근이 가능합니다. 완전히 Mount된 상태를 보장하지 않기 때문에 Mounted 훅에서 DOM 구조를 조작하는 로직을 사용할 수 없습니다. nextTick 함수를 통해서 로직을 작성하라고 공식문서에서는 가이드하고 있습니다.

 

 

 

🔥 Reactivity System 개념

참고
- How Reactivity works in Vue.js
- Building a 0.7KB Reactivity System Similar to Vue
- Vue.js 문서- 반응형에 대해 깊이 알아보기

정말 좋은 내용이 있어서 그 블로그 내용을 기반으로 작성했습니다.

Reactivity System은 쉽게 말해서 Component data property의 상태나 변화된 상태를 virtual DOM에 반영하는 작업을 자동으로 해줍니다. Reactivity System은 일반 Javascript 프레임워크에는 없고, React나 Vue에서 패키지로 함께 제공되고 있습니다.

 

Reactivity System이 있을 때와 없을 때를 Vue와 Vanilla Javascript로 확인해 보겠습니다.

 

아래 코드는  Vue 로 작성된 코드입니다. input에 text를 입력하면, 화면에 자동으로 입력한 텍스트가 표시됩니다. 문자 하나만 쳐도 화면에 바로 표시가 됩니다.

이 일을 해주는게 Reactivity System입니다.

 

 

 

 

 

 

 Vanilla Javascript 도 아래 코드를 예시로 보여드리겠습니다. 보시면, 입력에 반응해서 화면에 입력된 값이 렌더링되지 않습니다.

기본적으로 Vanilla Javascript는 반응성이 없습니다. 직접 이벤트 메서드를 장황하게 작성해서 반응성을 인위적으로 손 수 만들어 줘야 합니다.

 

 

 

 

 

 

이제 보면, 아래 그림이 Reactivity system이 동작하는 순서입니다.

Vue 문서 제공

Getter

1. 처음 렌더링이 동작하면, Data property에 touched합니다.

2. 그럼, getter함수가 호출됩니다. 종속적으로 data property의 상태값들을 모으기 위해서 watcher를 호출합니다.

3. watcher는 Component Render Function을 호출합니다. Render Function은 data property들을 Virtual DOM Tree에 반영합니다.

 

Setter

Component data property 변경이 Reactivity System의 핵심 기능입니다. 

1. data property가 변경되면, Setter 함수가 실행됩니다.

2. Setter함수는 변경된 데이터에 대해서 watcher에 알립니다.

3. 그리고, 다시 watcher는 변경된 data property에 대해서 Component Render Function을 실행해서 Virtual DOM에 변경 상태를 반영합니다.

 

Reactivity System은 Vue 라이프 사이클에서 Created 훅 전에 Vue 인스턴스에 주입됩니다. 이 주입이 Component의 data property들을 Object.defineProperty()를 사용해서 getter()와 setter()메소드로 정의하는 단계입니다. 초기에 컴포넌트가 생성될 때, setter와 getter로 정의되었기 때문에 Reactivity System이 데이터를 인지할 수 있고, 위에 보여드린 그림처럼 getter로 작동해서 Watcher로 종속적으로 데이터를 가져다 줄 수 있게됩니다. 그래서,  Created훅부터 Vue Component의 data property에 접근해서 로직을 작성할 수 있게 되는 겁니다. 

 

 

🔥 Virtual DOM 개념

참고
React: Virtual DOM

virtual DOM 개념

virtual DOM은 실제 DOM을 너무 많이 업데이트하는 비효율적인 연산을 줄이고자 만들어진 가상의 DOM입니다. virtual DOM은 메모리에 저장되어 사용됩니다.

 

virtual DOM 장점

실제 DOM을 조작하는 것은 느리지만, virtual DOM을 조작하는 건 꽤 빠릅니다. 그 이유는 virtual DOM은 노드의 변경 사항만을 DOM에서만 변경하지, 화면을 그리지 않거든요. virtual DOM을 사용하면 데이터 변경이나 DOM 구조 변경에 대한 브라우저에서의 비효율적인 재렌더링 작업이 줄어듭니다.

virtual DOM이 변경되는 것은 꽤 빠르고, 변경이 일어나면 Reactivity System은 실제 DOM 혹은 업데이트 되기 전의 virtual DOM snapshot과 업데이트가 된 부분을 비교합니다. 그리고, 실제 DOM에 반영합니다.

 

 

🔥 Vue가 브라우저에 렌더링되는 원리

빌드를 하면, 파일 번들링이 되요. vue-cli로 프로젝트를 생성하면, @vue/cli-plugin-babel 패키지가 자동으로 설치가 되요. 기본 스펙인 거에요. 
이 babel이 Template와 script를 컴파일해서 Virtual DOM 객체화를 진행해요. 그게, 바로, dist/js/ 아래로 만들어진, js 파일들이에요.
브라우저에서 vue-router로 설정한 경로로 서버에 요청이 들어오면, 트랜스파일링된 자바스크립트 파일이 브라우저로 로드가 되요. 

브라우저는 이 자바스크립트 파일로 아까 보여드렸던, 렌더링 엔진 작업이 진행되는 겁니다. 
위에서 렌더링 엔진 작업에서 제일 앞 단에 보시면, HTML과 CSS 처리 과정이 있는데, virtual DOM 개념을 사용하는 VUE의 경우, 이미 DOM처리가 된 객체를 브라우저로 전달하기 때문에 파싱단계를 거쳐서 DOM Tree를 만드는 단계가 생략이 되고, Attachment 부터 진행이 되요.
Attachment되고, Render Tree 만들어서 layout 계산하고 페인트가 되는 거에요.

CSS도 궁금하실 수 있는데, CSS는 따로 분리가 되요. 별도의 CSS파일로 번들링이 되요. 트랜스파일링이 아니라 번들링이라고 말한 이유는 단일 파일 컴포넌트에 있는 CSS들을 하나의 파일로 묶기 때문에 번들링이라고 하는거에요.
CSS는 별도로 렌더링이 되는거에요. 

이게 가상 돔이고요.  index.html에 할당이 되고, 다른 css도 함꼐 로드를 해서 Render tree가 되는거에요.  Render Tree가 이제 Layout이 계산되고, paint가 되는거에요.

style적인 부분이 변경이 일어나서 주변 style에 영향을 주면, Virtual DOM이 작동하지 않아요. 브라우저 렌더링 엔진이 reflow와 repaint 작업만 다시 할 뿐이에요.
그런데, vue 컴포넌트의 state 상태가 변경이 감지되면, Vue 인스턴스의 리액트 시스템이 메모리에 있는 Virtual DOM을 조작을 해요. 그리고나서 리액티비티 시스템이 실제 DOM에 MOUNT하게 되는 거에요.

이 과정이 

근거: https://kr.vuejs.org/v2/guide/render-function.html#%EB%B2%84%EC%B6%94%EC%96%BC-DOM

 

 

🔥 받은 질문: Vue를 사용하게 된 이유가 뭔가요?

왜 Vue를 썼는지에 대한 질문이 있었습니다. 마침 이번 공유를 준비하면서 이 부분에 대해서 많이 생각하고 있어서 쉽게 질문에 답을 할 수 있었습니다.

 

개발 생산성 이점.

코드 관리와 유지보수가 JQuery나 Vanilla 보다 쉽습니다. 프로젝트의 규모가 커질 수록 이 장점은 더 명확해 집니다.

하나의 Vue 파일에 HTML, CSS 그리고 Javascript를 관리할 수 있고, 이를 컴포넌트라고 단위를 줄 수 있습니다.

자동 프로젝트 생성.

- vue-cli를 사용하면, babel과 webpack 그리고 라이브러리 관리까지 하는 프로젝트를 쉽게 구축할 수 있고 개발자는 개발에 만 집중할 수 있습니다.

virtual DOM을 사용해서 렌더링의 속도의 이점.

DOM을 빈번하게 변경하는 작업을 효율적으로 작업하기 위해서 나왔습니다.

DOM을 직접 수정하는 작업이 많이 발생할 경우 브라우저에서 처리하는 연산이 많아짐에 따라서 사용자가 보는 화면에 변경된 DOM이 렌더링되는 속도가 느립니다. 데이터의 변경이 일어나면, 조작할 DOM을 샐렉트해야 하는데, 만약 DOM의 노드가 정말 많은 경우는 이를 찾는 시간이 걸리기 때문에 그 만큼, 렌더링 속도가 느려집니다. 

 

그런데, virtual DOM을 사용하는 Vue와 React는 실제 사용자가 보는 DOM이 아니라 virtual DOM을 조작합니다. 변경된 부분을 DOM에 반영합니다. 

Virtual DOM은 DOM을 직접 핸들링하지 않게 하기 위한 방법입니다.

한번에 여기서 virtual DOM은 사용자의 컴퓨터 메모리에 저장됩니다. 

virtual DOM에 막 변경사항이 반영이 됩니다. 그러다가, 마지막에 변경된 값만, 실제 DOM에 렌더링합니다.

Vue.js 프레임워크는 Virtual DOM과 DOM을 변경된 부분을 비교해서 변경된 부분만 mount합니다.

과정 비교

데이터 변경 -> dom 찾기 -> element 생성 -> DOM에 반영

데이터 변경 -> Virtual DOM에 반영 -> DOM에 변경된 element만 변경

 

예를 들어 다음과 같은 DOM에 있는 모든 p태그 요소의 텍스트를 모두 변경해 본다고 합시다.

{
  html: {
    body: {
       #1: '팝콘',
       #2: '카스타드',
       #3: '감자튀김'
    }
  }
}

순수 웹 페이지

 DOM에서 변경할 element를 찾습니다. #1 element를 찾았습니다. -> #1 element의 값을 업데이트합니다. -> DOM이 업데이트됩니다. -> #2 element를 찾습니다. -> #2 element의 값을 업데이트합니다. -> DOM이 업데이트됩니다. -> #3 element를 찾습니다. -> #3 element의 값을 업데이트합니다. -> DOM이 업데이트 됩니다.

 

Vue, React

virtual DOM이 DOM에 변경사항을 한 시점에 반영합니다. #1,#2,#3의 변경된 내용이 virtual DOM에 반영됩니다. -> virtual DOM이 DOM에 한 번에 반영됩니다.

 

위의 차이를 보면, 화면에서 변경되는 요소가 많을 수록 virtual DOM을 통해서 DOM에 마운트되는 방법이 순서난 연산적인 면에서 순수 웹 페이지보다 렌더링이 빠르게 됩니다.

반응형 시스템을 사용하기 위해서

Vanilla Javascript나 JQuery로 반응성을 구현한다고 생각해 보세요. 정말 많은 이벤트 코드가 들어가야 할 겁니다. Vue를 사용해서  컴포넌트에 data property에 상태 데이터를 넣어주고 v-model 디렉티브를 input property로 주면 간단하게 반응성을 구현할 수 있습니다. 개발생산성에 큰 장점을 가질 수 있습니다.

 

 

🔥 받은 질문 : React가 아닌, Vue를 사용하는 이유.

1. React는 프레임워크보단 라이브러리에 가깝습니다. React는 함수형 컴포넌트로 사용되므로, 오로지 스크립트에 많이 의존해서 개발합니다. 프로그래밍 방법이 정말 자유롭다고 합니다. 팀 간의 코드 작성 방식에서 많은 협의가 필요할 수 있습니다. 반면, Vue 프레임워크는 자유도가 덜하지만, 그만큼 제약된 조건에서 프로그래밍이 되므로 팀간에 컨벤션 협의가 많이 필요없습니다. Vue의 인스턴스가 존재하고, 그 인스턴스에는 제한된 property가 제공됩니다. 개발자는 Vue.js 프레임워크가 제공하는 스펙에 맞춰서 개발을 해야 하므로, 팀원간에 협의가 많이 필요하지 않습니다.

 

2. 무한 요청을 신경쓰지 않아도 됩니다. React는 useEffect같은 훅을 사용해서 서버의 데이터를 요청할 때, 요청에 대한 조건을 프로그래밍하지 않으면, 무한 요청을 날리게 됩니다. Vue는 이 부분을 신경쓰지 않고 개발할 수 있습니다.

 

 

 

 

 

총 한 시간에 걸쳐서 쉬지 않고 장황한 설명을 했고, 질문도 꽤 받았습니다. 열정적으로 강의한 덕분인지 전원에게 박수를 받고 시원하게 공유를 마무리할 수 있었습니다.

728x90

댓글