keyword
JavaScript rendering, DOM, HTML DOM트리, CSS CSSOM트리, 자바스크립트 렌더링, 렌더링 트리, 레이아웃, 리플로우(reflow), 페인트, 리페인트, async, defer, React rendering, re-rendering, virtual DOM
React.js의 렌더링과 리렌더링 방식에 대해 공부하던 중, 자바스크립트의 브라우저 렌더링 방식과 리액트의 가상돔을 이용한 렌더링 방식의 차이가 궁금해서 알아보기로 했다. 리렌더링에 대해서도 더 알아보자.
<요약>
리렌더링 방식
데이터가 업데이트되거나 변경사항이 생기면,
자바스크립트를 사용한 브라우저 : 렌더 트리 다시 생성 -> Reflow 또는 Repaint가 발생
reflow
(=re-레이아웃) : 변경(일부 또는 전체)이 필요한 렌더 트리에 대한 유효성 확인 작업과 함께 노드의 크기와 위치를 다시 계산함. 이 과정을 리플로(Reflow) 또는 레이아웃(Layout), 레이아웃팅(Layouting)이라고 부름re-paint
: 변경 영역의 결과를 표현하기 위해 화면이 업데이트되는 것을 의미합니다. 리페인트(Repaint) 또는 리드로우(Redraw)라고 합니다.리액트를 사용한 브라우저
: 데이터가 업데이트되면 전체UI를 가상DOM에 리렌더링하고, 이전 가상DOM에 있던 내용과 비교하는 과정(diffing)을 거친 뒤, 바뀐 부분을 한번에 실제 DOM에 적용함. (리플로우와 리페인트 최소화)
브라우저
핵심기능
동작과정 - 중요 렌더링 경로 (Critical Rendering Path)
<script>
태그 만나면 DOM생성 프로세스 중지script와 동기적 처리
<동기적 처리>
<script>
태그의 위치에 따라 블로킹이 발생하여 DOM생성이 지연될 가능성이 있음. 렌더링
HTML, CSS, 자바스크립트 등 개발자가 작성한 문서가 브라우저에 출력되는 과정
각 브라우저는 렌더링을 수행하는 엔진을 갖고 있음.
렌더링 과정
3.1.) HTML코드로 DOM트리 생성
과정
: 변환->토큰화->렉싱-> DOM트리 생성
1) 변환: 브라우저가 HTML의 원시 byte를 읽어와서, HTML에 정의된 인코딩(예: utf-8)에 따라 개별 문자로 변환함
2) 토큰화 : 브라우저가 문자열을 W3C 표준에 지정된 고유 토큰으로 변환
3) 렉싱 : 방출된 토큰은 해당 속성 및 규칙을 정의하는 "객체"로 변환됨
4) DOM(트리) 생성: HTML 마크업에 정의된 여러 태그 간의 관계를 해석해서 트리구조로 연결함
브라우저는 HTML 마크업을 처리할 때 마다 위의 모든 단계를 수행함
3.2.) CSS코드로 CSSOM트리 생성
<head>
태그 내부에 <style>
태그를 (internal) 선언<head>
태그 외부에 style.css
파일을 참조하여 (external) 선언<head>
안에 내부 또는 외부에 정의된 스타일을 브라우저가 이해하고 처리할 수 있는 형식으로 변환해야 함과정
: DOM트리 생성과정과 동일하게 CSSOM트리 생성3.3) 자바스크립트 렌더링
<script>
태그를 만나면 자바스크립트 엔진으로 제어권을 넘기며 시작됨 3.4.) 렌더링 트리(Rendenring Tree) 생성
추가:
<script>
의 위치 & 파싱 블로킹
브라우저는 동기적(Synchronous)적으로 HTML, CSS, Javascript를 처리한다.
이것은 script 태그의 위치에 따라 블로킹이 발생하여 DOM의 생성이 지연될 수 있다는 것을 의미한다.
따라서 script 태그의 위치는 중요한 의미를 갖는다.
자바스크립트 파싱에 의한 HTML 파싱의 중단 가능
<script>
태그가 <head>
안에 있게되면 문제 발생<script>
코드는 DOM이 모두 생성된 후 파싱 및 실행이 될 수 있도록 <body>
의 가장 하단에 기입해야 DOM 조작시 오류가 없고, HTML 파싱이 정지없이 한번에 실행되어 페이지 로딩시간이 단축된다.
<script>
태그의 async/defer 속성(Attribute)
자바스크립트의 파싱에 의한 HTML 파싱 블로킹 현상을 근본적으로 해결하기 위해, HTML5부터 async와 defer 속성이 추가됨.
<script async src="script.js"></script>
<script defer src="script.js"></script>
두 속성 모두 HTML파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행되도록 만들어줌.
하지만, 두 속성들 사이에는, 자바스크립트 실행 시점의 차이가 존재함.
async
defer
리액트
가상DOM (Virtual DDOM)
DOM을 빈번히 업데이트하는 것을 좀 더 효율적인 방법으로 업데이트 하기 위해 만들어짐.
DOM (Document Object Model)
웹페이지가 화면에 로딩되면 브라우저는 페이지의 문서객체모델(DOM)을 만든다. (= 애플리케이션의 UI이자, HTML파일의 자바스크립트 표현)
애플리케이션 UI의 상태가 변경될 때 마다 DOM은 트리형태이기 때문에, 특정 노드를 찾아 수정하거나 제거하거나 원하는 곳에 삽입이 가능함.
DOM보다 가상DOM 사용하는 이유? 성능!
DOM을 자주 조작하면 성능에 영향을 주어 속도가 느려짐
가상DOM 작동 원리
Virtual DOM을 사용하면 실제 DOM에 접근하여 조작하는 대신, 이를 추상화한 가상의 DOM을 사용한다.
즉, DOM의 가벼운 사본 형태라고 보면 된다.
데이터를 업데이트하면 전체 UI를 Virtual DOM에 리렌더링 한다.
이전 Virtual DOM에 있던 내용과 현재 Virtual DOM의 내용을 비교한다. 이 과정을 "diffing"이라고 한다.
그러고 나서, 바뀐 부분만 실제 DOM에 적용한다.
간단히 말해서 리액트에게 UI가 어떤 상태를 원하는지 알려주고 DOM이 해당 상태와 일치하는지 확인한다고 생각하면 된다.
가상DOM이 무조건 DOM보다 빠른가? Not always!
Virtual DOM을 사용한다고 해서 사용하지 않을 때와 비교하여 무조건 빠른 것은 아니다.
리액트에서는 지속적으로 데이터가 변화하는 대규모 애플리케이션 구축을 할때 사용을 권장하고 있다.
즉, 이 말은 리액트를 사용하지 않아도 코드 최적화를 열심히 하면 DOM 작업이 느려지는 문제를 개선할 수 있고, 또 작업이 매우 간단할 때는 오히려 리액트를 사용하지 않는 편이 더 나은 성능을 보이기도 한다.
결국에는 적절하게 사용해야 리액트가 지닌 능력을 잘 발휘할 수 있다.
정리
빈번한 DOM 조작은 성능이 무겁고 느려지게 된다.
Virtual DOM은 실제 DOM의 가상의 표현 즉, 사본 같은 형태이다.
상태 변경이 발생하면 Virtual DOM이 업데이트되고 이전 및 현재 버전의 가상 DOM이 비교된다. 이것을 "diffing"이라 부른다.
그런 다음 Virtual DOM은 배치 업데이트를 실제 DOM으로 보내 UI를 업데이트한다.
React는 Virtual DOM을 사용하여 대규모 애플리케이션을 구축할 경우 성능을 향상시킨다.
<추가>
리렌더링
리액트에서 뷰(View)를 업데이트할 때는 "업데이트 과정을 거친다" 라기 보다는 "조화 과정을 거친다"라고 표현하는 것이 더 정확하다.
그 이유는 컴포넌트에서 데이터에 변화가 있을 때마다 뷰(View)가 변형되는 것처럼 보이지만, 실제로는 새로운 요소로 갈아 끼우기 때문이다.
이 작업 또한 render() 함수가 하며 컴포넌트는 데이터를 업데이트했을 때 단순히 업데이트한 값을 수정하는 것이 아니라, 새로운 데이터를 가지고 render 함수를 또 다시 호출한다.
그러면 그 데이터를 지닌 뷰(View)가 새롭게 생성되는 것!
하지만, render 함수가 반환한 새로운 뷰는 곧바로 DOM에 반영하지 않고, 이전 render 함수가 만들었던 컴포넌트 정보와 현재 render 함수가 만든 컴포넌트 정보를 비교한다.
둘의 차이를 비교 후, 원하는 뷰로 리렌더링 즉, 새로운 요소로 갈아 끼운다라고 볼 수 있다.
리액트는 render -> 가상돔에 결과물 저장 -> 원래 돔과 가상돔의 결과물 비교 -> 달라진 점을 최종 돔에 저장
매번 변화되지 않은 부분까지 불필요하게 렌더링 하는 것과 달리 부분만 연산을 할 것 이기에 속도 면에서 성능 향상.
C.f) Svelt 라는 새로운 프레임워크는, 이 가상돔자체도 없애버린다고 한다. 굳이 가상돔에 저장하는 단계를 거치지 않고 반응형으로 변화된 부분을 바로 최종 돔에 적용시킨다고 한다.
소고
공부 자료를 찾다보면 좋은 자료와 심화된 내용들이 끝도 없이 나와서 이 포스트에만 너무 정체되어 진도가 나가지 않는다. 일단은 첫 공부니까 이 정도로 정리하고 다음에 복습할 때 아래 글을 참고해서 심화로 학습하고 내용을 업데이트 하기로 하자.
일단은 여기까지 정리하고, 나중에 다시 일목요연하게 정리해보자.
https://d2.naver.com/helloworld/59361
https://yozm.wishket.com/magazine/detail/1338/
<참고>
자바스크립트 브라우저 렌더링 :
https://medium.com/swlh/what-the-heck-is-repaint-and-reflow-in-the-browser-b2d0fb980c08
https://medium.com/%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98%ED%92%88%EA%B2%A9/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%9D%98-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95-5c01c4158ce https://velog.io/@klloo/JavaScript-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81
리액트 렌더링, 리렌더링:
https://ljtaek2.tistory.com/137?category=898627,
https://ljtaek2.tistory.com/132?category=898627
<더 읽어보기>
자바스크립트 브라우저 렌더링 :
https://d2.naver.com/helloworld/59361
https://yozm.wishket.com/magazine/detail/1338/
https://doqtqu.tistory.com/312
리플로우 최소화 방법 :
https://12bme.tistory.com/140
Ajax, XHR(XML Http Request), Strict 모드 : https://velog.io/@yujinoneill/220308