자바스크립트와 리액트의 렌더링, 리렌더링 비교

Sujeong Ji·2022년 4월 13일
3

React

목록 보기
5/9
post-thumbnail

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에 적용함. (리플로우와 리페인트 최소화)


브라우저

핵심기능

  • 웹페이지를 서버에 요청(request)하고, 서버로부터 응답(response)를 받아 브라우저에 표시하는 것

동작과정 - 중요 렌더링 경로 (Critical Rendering Path)

  1. 파싱 (Parsing)
    • HTML파일, CSS파일이 파서에 의해 파싱
    • HTML 파서가 <script>태그 만나면 DOM생성 프로세스 중지
    • 자바스크립트 엔진으로 제어권한 넘김
    • 자바스크립트 실행 완료되면, 다시 HTML 파서로 제어권한 넘김
    • DOM생성 재개
  2. 렌더트리 (Render Tree) 구축
    • HTML DOM트리, CSSOM 트리로 변환됨
    • HTML DOM트리 + CSSOM 트리 결합 => 렌더 트리로 형성
  3. 레이아웃(Layout) 또는 리플로우(Reflow)
  4. 페인트(Paint)
  5. 리렌더링(렌더트리 변경->리플로우->리페인트)

script와 동기적 처리

<동기적 처리>

  • 브라우저는 동기적으로 HTML, CSS, JS를 처리함.
  • 따라서, 이로 인해 <script>태그의 위치에 따라 블로킹이 발생하여 DOM생성이 지연될 가능성이 있음.
  • 각 단계에서 리소스를 로드하는 순서나 작성한 스크립트의 내용에 따라 웹 페이지의 반응 속도가 달라질 수 있음.
  • 최적화를 통해 프론트엔드 개발자는 렌더링에 걸리는 시간을 개선시킬 수 있음.


렌더링

HTML, CSS, 자바스크립트 등 개발자가 작성한 문서가 브라우저에 출력되는 과정

각 브라우저는 렌더링을 수행하는 엔진을 갖고 있음.

  • 크롬 : 블링크(Blink)
  • 사파리 : 웹킷 (Webkit)
  • 파이어폭스 : 개코 (Gecko)

렌더링 과정

  1. 사용자가 브라우저를 통해 웹사이트 접속
  2. 서버로부터 HTML, CSS 등 웹사이트에 필요한 리소스 다운로드 받음
  3. 브라우저가 페이지를 렌더링 하려면, HTML코드는 DOM 트리, CSS는 CSSOM 트리를 생성해야 함
  • 3.1.) HTML코드로 DOM트리 생성

    • 과정 : 변환->토큰화->렉싱-> DOM트리 생성

    • 1) 변환: 브라우저가 HTML의 원시 byte를 읽어와서, HTML에 정의된 인코딩(예: utf-8)에 따라 개별 문자로 변환함

    • 2) 토큰화 : 브라우저가 문자열을 W3C 표준에 지정된 고유 토큰으로 변환

    • 3) 렉싱 : 방출된 토큰은 해당 속성 및 규칙을 정의하는 "객체"로 변환됨

    • 4) DOM(트리) 생성: HTML 마크업에 정의된 여러 태그 간의 관계를 해석해서 트리구조로 연결함

    • 브라우저는 HTML 마크업을 처리할 때 마다 위의 모든 단계를 수행함

  • 3.2.) CSS코드로 CSSOM트리 생성

    • CSS의 3가지 선언 방법
      • HTML 마크업 내에 직접(inline) 스타일 선언
      • <head> 태그 내부에 <style>태그를 (internal) 선언
      • <head> 태그 외부에 style.css 파일을 참조하여 (external) 선언
        : HTML과 마찬가지로, <head> 안에 내부 또는 외부에 정의된 스타일을 브라우저가 이해하고 처리할 수 있는 형식으로 변환해야 함
    • 과정 : DOM트리 생성과정과 동일하게 CSSOM트리 생성

  • 3.3) 자바스크립트 렌더링

    • 자바스크립트 렌더링은 렌더링엔진이 아닌 자바스크립트 엔진이 처리함
    • 자바스크립트 파싱은 CSS파싱과 마찬가지로, 렌더링 엔진이 HTML문서를 한 줄씩 읽어나가다가, <script> 태그를 만나면 자바스크립트 엔진으로 제어권을 넘기며 시작됨
    • 자바스크립트 엔진은 CPU가 이해할 수 있는 저수준 언어로 자바스크립트 코드를 파싱
    • 이후, DOM, CSSOM같은 AST(추상적 구문 트리)를 생성하여 이를 기반으로 바이트 코드를 생성하고, 인터프리터가 실행될 수 있도록 만듬
    • 자바스크립트의 실행이 완료되면 다시 HTML파서에 제어 권한을 넘겨 브라우저가 중지했던 시점부터 DOM 생성을 재개
  • 3.4.) 렌더링 트리(Rendenring Tree) 생성

    • DOM트리와 CSSOM트리가 만들어지면, 이 둘을 결합해서 렌더링 트리 생성
    • 렌더링 트리에는 페이지를 렌더링하는데 필요한 노드만 포함됨
  1. 레이아웃(Layout)/리플로우(Reflow) 단계
  • 레이아웃 단계에서는 뷰포트(웹페이지가 사용자에게 보여지는 영역) 내에서 각 요소의 정학한 위치와 크기를 정확하게 캡쳐한 Box모델이 출력됨. 모든 상대적인 측정값은 화면에서 절대적인 픽셀로 변환됨.
  1. 페인팅 (Paint) 단계
  • 마지막으로 렌더링 트리의 각 노드를 화면의 실제 픽셀로 변환함. 레이아웃 단계에서 모든 계산이 완료가 되면, 화면에 요소들을 그리게 됨. 이 단계를 '페인팅' 또는 '래스터화'라고 함
  • 이미 레이아웃 단계에서 각 노드들의 위치, 크기, 색상 등 스타일이 모두 계산되었기 때문에 화면에서 실제 픽셀로 변환하게 됨.
  • 레이아웃 VS. 페인팅
  1. 리플로우(Reflow), 리페인트(Re-paint)
  • 리플로우
    • 사용자가 웹 페이지에 처음 접속하면, 렌더링 과정을 거쳐서 화면의 모든 요소가 그려지게 됨
    • 이후 사용자는 다양한 액션을 수행하게 되고, 여기에서 발생되는 이벤트로 인해서 새로운 HTML 요소가 추가되거나, 기존 요소의 스타일이 변경이 발생함.
    • 이런 변경으로 영향을 받게 되는 모든 노드에 대해서 렌더링 트리생성과 레이아웃 과정을 다시 수행하게 됨.
    • 리플로우는 단지 변경사항을 반영하기 위해 렌더링 트리를 생성하고 레이아웃 과정을 다시 수행하는 것임.
  • 리페인트
    • 리페인트는 새로운 결과를 화면에 그려지기 위해서 다시 페인팅 단계를 수행함.
    • 기존 요소에 변경 사항이 생겼다고 해서 항상 리플로우-리페인트가 일어나는 것은 아님.
    • 레이아웃에 영향을 미치지 않는 단순한 색상 변경 같은 변경사항은 리풀로우 수행없이, 바로 리페인트만 수행하게 됨 (반면, 리플로우가 일어나면 반드시 리페인트가 일어남)
    • 특이사항
      • 리플로우가 일어나는 대표적인 속성들: position, width, height, margin, padding, border, border-width, font-size, font-weight, line-height, text-align, overflow
      • 오직 리페인트만 일어나는 대표적인 속성들: background, color, text-decoration, border-style, border-radius

추가: <script>의 위치 & 파싱 블로킹

브라우저는 동기적(Synchronous)적으로 HTML, CSS, Javascript를 처리한다.

이것은 script 태그의 위치에 따라 블로킹이 발생하여 DOM의 생성이 지연될 수 있다는 것을 의미한다.

따라서 script 태그의 위치는 중요한 의미를 갖는다.

자바스크립트 파싱에 의한 HTML 파싱의 중단 가능

  • 렌더링엔진과 자바스크립트 엔진 코드는 직렬적으로 파싱함 (= 위에서 아래로 한줄씩 동기적으로 읽음)
  • 만약, <script> 태그가 <head>안에 있게되면 문제 발생
  • DOM이 완성되지 않은 상태에서 자바스크립트 코드가 DOM API를 통해 DOM을 조작하면 에러가 발생 가능
  • 자바스크립트의 로딩, 파싱, 실행으로 인해, 전체 페이지의 로딩 시간이 길어질 수 있음.
  • 따라서, <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

    • 공통: HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행됨.
    • 차이 : 자바스크립트의 파싱과 실행은, 자바스크립트 파일이 로드되면 바로 진행됨. (이때, HTML 파싱은 중단됨)
  • defer

    • 공통: HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행됨.
    • 차이: 자바스크립트의 파싱과 실행은, HTML 파싱이 완료되어 DOM이 생성된 이후에 진행됨.
      따라서, DOM생성이 완료된 이후 실행되어야 하는 자바스크립트 파일에 유용하게 사용됨.


    리액트

    가상DOM (Virtual DDOM)

    DOM을 빈번히 업데이트하는 것을 좀 더 효율적인 방법으로 업데이트 하기 위해 만들어짐.

    DOM (Document Object Model)

    웹페이지가 화면에 로딩되면 브라우저는 페이지의 문서객체모델(DOM)을 만든다. (= 애플리케이션의 UI이자, HTML파일의 자바스크립트 표현)

    애플리케이션 UI의 상태가 변경될 때 마다 DOM은 트리형태이기 때문에, 특정 노드를 찾아 수정하거나 제거하거나 원하는 곳에 삽입이 가능함.

DOM보다 가상DOM 사용하는 이유? 성능!

DOM을 자주 조작하면 성능에 영향을 주어 속도가 느려짐

  • DOM이 느려지는 것은 아님!
    : DOM자체는 빠르고, 읽고 쓸때의 성능은 자바스크립트 객체를 처리할 때의 성능과 비교하여 다르지 않음.
  • 그러나! 웹브라우저에서 DOM에 변경사항이 있을시,
    웹브라우저가 다시 HTML,CSS를 연산하고, 레이아웃을 구성하고, 페인트를 하는 과정에서 많은 시간이 허비되며 성능 저하가 발생함.

가상DOM 작동 원리

Virtual DOM을 사용하면 실제 DOM에 접근하여 조작하는 대신, 이를 추상화한 가상의 DOM을 사용한다.

즉, DOM의 가벼운 사본 형태라고 보면 된다.

  1. 데이터를 업데이트하면 전체 UI를 Virtual DOM에 리렌더링 한다.

  2. 이전 Virtual DOM에 있던 내용과 현재 Virtual DOM의 내용을 비교한다. 이 과정을 "diffing"이라고 한다.

  3. 그러고 나서, 바뀐 부분만 실제 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

profile
Studying Front-end Development 🌼

0개의 댓글