브라우저 렌더링

leekoby·2023년 3월 16일
0

WEB

목록 보기
7/7
post-thumbnail

🔧변경내용🔨

제목날짜내용
발행일23.03.16

📌들어가기에 앞서

해당 포스트는 브라우저 렌더링에 대해 학습한 내용을 정리하며 기록한 내용입니다.




📖 브라우저 렌더링

브라우저 렌더링에서 렌더링(rendering)이란?

  • HTML, CSS, JavaScript 등 개발자가 작성한 문서가 브라우저에서 출력되는 과정을 의미

  • 현존하는 브라우저마다 다름

  • 브라우저는 기본적으로 렌더링을 수행하는 렌더링 엔진을 가지고 있음




📖 브라우저 렌더링 과정

아래는 브라우저가 렌더링이 되는 과정을 간단히 도식화한 것

[그림] 브라우저 렌더링 과정 도식화

  1. 사용자가 브라우저를 통해 웹 사이트에 접속

  2. 브라우저는 서버로부터 HTML, CSS, JavaScript와 같은 웹사이트에 필요한 리소스를 다운받음

  3. 렌더링 엔진은 전달받은 HTML 문서를 파싱(parsing)해 DOM(Document Object Model, 문서 객체 모델) 트리를 만듦

  4. 다운받은 외부 CSS 파일과 함께 포함된 스타일 요소를 파싱(parsing)해 CSSOM(CSS Object Model, CSS 객체 모델) 트리를 만듦

  5. 만든 DOM 트리와 CSSOM 트리를 결합해 Render 트리를 구축

  6. 레이아웃 과정을 통해 각 요소를 어디에 배치할지 결정

  7. 레이아웃 과정이 끝나면 UI 백엔드에서 Render 트리를 화면에 그리기 시작, 이 과정을 paint라고 합니다.

이제부터 렌더링 과정에 쓰이는 개념들을 알아보도록 하자




📖 파싱(Parsing)

파싱이란?

프로그래밍 언어로 작성된 파일을 실행시키기 위해 구문 분석(syntax analysis)을 하는 단계

  • 파싱을 파서(parser)가 진행

    • 일종의 인터프리터나 컴파일러 구성 요소 가운데 하나

    파서는 HTML 파일의 코드를 문법적 의미를 갖는 최소 단위인 토큰(token)으로 한 번 분해하고, 이 토큰들을 문법적 의미와 구조에 따라 노드(node)라는 요소로 바꿈

    노드들은 상하 관계에 따라 하나의 트리를 형성하는데 이를 파스 트리(parse tree), 혹은 문법 트리(syntax tree)라고 부름

문서 파싱(document parsing)은 브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것을 의미

렌더링 과정에서는 HTML 파일을 바탕으로 DOM 트리를 구축하고 및 CSS 파일로 CSSOM 트리를 만드는 것을 파싱한다고 표현

브라우저는 HTML 문서를 받아 들자마자 DOM 트리로 파싱

  • 이때 HTML 토큰이 만들어지는데, 이 토큰에는 시작 태그와 마침 태그가 포함되고, 속성 이름과 값도 포함

  • 이런 토큰으로 변한 입력값은 파서에 의해 노드가 되고, 최종적으로 트리 구조의 DOM으로 구성

브라우저는 HTML 문서를 파싱하면서 CSS 스타일을 만날 경우

  1. 텍스트를 CSS 스타일링 레이아웃과 페인팅에 사용하는 데이터 구조인 CSSOM 트리로 파싱

  2. <script> 태그를 만날 경우

    • 렌더링을 차단하면서 HTML 파싱 또한 중단
  3. <script> 태그 내 src 속성으로 연결된 파일을 다운받아 파싱하고 실행시킨 뒤 다시 HTML 파일을 파싱하기 시작

파싱은 문서에 작성된 언어 또는 형식의 규칙에 따름

파싱할 수 있는 모든 형식은 정해진 용어와 구문 규칙에 따라야함

  • 따라서 형식을 잘 갖춘 문서라면 파싱 과정은 직관적이고 빠르게 진행됩니다.

이제부터 한 HTML 파일을 통해 DOM 트리, CSSOM 트리, Render 트리에 대해 살펴보도록 하자

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="code.css">
    <title>코드스테이츠</title>
</head>
<body>
    <h1>Section4</h1>
    <p>Unit1은 <span>HTML</span><span>CSS</span> 심화다.</p>
    <section>어렵지 않아요!</section>
</body>
</html>

[코드] HTML 파일의 예시




📖 DOM Tree

DOM은 HTML문서의 요소들의 중첩 관계를 기반으로 노드들을 트리 구조로 구성한 것을 의미

Document Object Model의 줄임말

브라우저는 JavaScript 언어만 알아듣기 때문에 HTML의 태그나 속성들을 이해하지 못함

응답으로 받아온 HTML 문서는 텍스트로만 이뤄져 있음

그래서 이해할 수 있는 형태인 객체로 바꿔준 것이 바로 DOM트리

[그림] HTML 파일을 DOM 트리로 변환하면 이렇게 나옵니다.




📖 CSSOM Tree

html 파일을 DOM 트리로 파싱하던 브라우저는 <link>, <style> 태그를 만나게 되면 파싱을 잠시 멈추고 해당 리소스 파일을 서버로 요청

이렇게 요청한 파일을 html 파일과 마찬가지로 파싱

파일을 파싱해 만든 트리를 CSSOM

CSS Object Model의 줄임말

CSSOM 트리를 구축하고 나면 브라우저는 다시 html 파일의 파싱을 멈췄던 부분으로 돌아가서 마저 DOM 트리를 구축

[그림] CSS 파일을 CSSOM 트리로 변환하면 이렇게 나옵니다.

하나 알아둬야 할 부분은 CSS는 부모의 속성을 자식이 상속 받는다는 점

예를 들어 body가 부모 요소이고 font-size16px인 속성을 가지고 있는데,

  • 그 밑에 있는 p는 자식 요소이기 때문에 부모 요소인 body가 갖고 있던 속성을 상속

  • 동시에 자신이 가지고 있는 속성인 font-weight 속성까지 가지므로 2개의 속성을 갖게 된다




📖 렌더 트리(Render Tree)

DOM 트리와 CSSOM 트리는 트리 구조로 되어 있기 때문에 비슷하게 생김

하지만 애초에 리소스부터 틀린 서로 다른 속성을 가진 독립적인 트리

그렇기 때문에 브라우저 위에 웹사이트를 표시하기 위해서는 이 둘을 합치는 작업이 반드시 필요함

렌더 트리는 이름처럼 렌더링을 목적으로 만들어지는 트리

렌더링은 사용자에게 브라우저가 보여주고자 하는 화면을 그리는 과정

  • 보이지 않을 요소들은 이 트리에 포함시키지 않음

[그림] 결국 사용자의 눈에 보여야 하는 것만 렌더하기 때문에 보이지 않아야 하는 요소나 태그는 제외됩니다.

예를 들어

  • DOM 트리의 <meta>
  • CSSOM 트리의 display:none

위와 같이 사용자에게 보여주지 않아도 되거나, 보여주지 말아야 할 태그나 요소는 렌더 트리에서 제외




📖 레이아웃

브라우저 렌더링에서 “레이아웃” 과정은 우리가 웹 개발에서 평소 말하는 웹 페이지 레이아웃 짜기, 디자이너들의 레이아웃과는 다른 개념

여기서 말하는 레이아웃은 렌더트리를 기반으로 HTML 요소의 레이아웃(위치, 크기 등)을 계산하여 브라우저 화면 어디에 배치할 지 결정하는 과정

[그림] 결국 사용자의 눈에 보여야 하는 것만 렌더하기 때문에 보이지 않아야 하는 요소나 태그는 제외됩니다.

위의 그림에서도 볼 수 있듯이 DOM, CSSOM에 있던 속성들이 합쳐서 렌더 트리를 구성하는 것을 볼 수 있다.

그러나 아직까지 렌더 트리는 텍스트로 구성된 객체로만 보인다.

페인팅이라는 작업을 거쳐야지 브라우저 위의 화면으로 그려지게 된다.

렌더 트리에는 CSSOM 트리에 있던 속성들이 합쳐져 있다.

따라서 렌더 트리에는 요소들의 크기, 혹은 위치에 관련된 정보들이 들어있다.

하지만 아직까지 이 정보들은 그저 각 요소에 관련된 정보일 뿐, 전체 화면에서 정확히 어디에 위치하는가에 대해서는 알지 못한다.

이런 계산을 브라우저의 렌더링 엔진이 한다. 브라우저는 각 요소가 전체 화면에서 어디에, 어떤 크기로, 어떻게 배치가 되어야 하는지 파악하기 위해 렌더트리를 위에서 아래로 읽어 내려가고 모든 값은 절대적인 단위인 px 값으로 변환된다.




📖 페인팅

위치에 대한 계산을 마치면 이제 화면에 보여주기 위해 브라우저는 화면 위에 레이아웃에서 결정된 대로 그림을 그리기 시작

브라우저 화면은 픽셀이라고 하는 작은 점들로 구성되어 있다.

각 정보를 가진 픽셀들이 모여 하나의 화면을 구성하는 것

페인팅은 이런 픽셀에 대한 정보들을 바탕으로 픽셀을 채워나가는 과정

이 과정까지 해내야 텍스트에 불과했던 HTML 파일의 내용들이 이미지화된 모습으로 브라우저 화면에 띄워진다.

[그림] 페인트 과정을 통해 최종적으로 출력된 결과물

단순히 주소창에 주소를 검색해서 웹 사이트를 모니터로 눈으로 확인할 뿐이지만, 그 내부에서 브라우저가 어떤 과정을 통해 웹 사이트를 띄우는지 살펴보면 굉장히 복잡한 과정이 빠르게 수행되고 있다는 것을 알 수 있게 되었다.

프론트엔드 엔지니어로서 브라우저 렌더링 과정을 알아둔다면 렌더링 최적화 시 어떤 단계에서 최적화시켜야 할지 알 수 있다.




📖 리플로우와 리페인트


브라우저 렌더링

만약에 사용자가 브라우저 화면을 늘리거나 줄이는 등 크기를 조절하거나, 다른 사이트로 이동을 하는 등 화면에 요소가 추가 되거나 삭제, 혹은 아예 다른 요소들을 불러와야 하는 상황이 생기면 당연히 화면에 있던 요소들의 크기가 바뀌게 됩니다.

사용자의 입장에서는 당연한 과정이지만, 이렇게 화면에 나타나는 모습을 바꾸기 위해서는 모든 요소의 위치와 크기를 다시 계산하고, 다시 그려 보여주어야 합니다. 이런 식으로 어떤 웹 인터랙션으로 인해 앞서 보았던 렌더링 과정의 레이아웃을 반복해 수행하는 것을 리플로우, 페인트 과정을 반복해 수행하는 것을 리페인트라고 합니다.

const div = document.createElement('div');
document.body.append(div);
div.textContent = 'hello world';

위의 코드는 div요소를 생성하고 body에 해당 요소를 붙인 뒤 ‘hello world’라는 텍스트를 추가하는 간단한 코드입니다. 이미 지난 섹션에서 학습하셨듯이, 여러분은 JavaScript를 통해 DOM을 조작하는 방법을 알고 있으며, CSS 또한 이런 방식으로 조작할 수 있다는 것 또한 알고 계실 겁니다. 이렇게 JavaScript 조작을 통해 변경이 일어나면, DOM 트리를 다시 구성하는 것을 시작해 CSSOM과 합쳐 새 렌더 트리를 생성합니다. 그리고 레이아웃과 페인트 과정을 또다시 거쳐 화면에 보여줍니다. DOM 조작은 리플로우, 리페인트가 일어나는 대표적인 예시입니다.


리플로우와 리페인트의 최적화

사용자의 눈에 일련의 과정이 부드럽게 처리되려면 초당 60프레임은 반드시 유지시켜야 한다.

이는 TOAST에서 WebRender를 소개하면서 나오는 개념

TOAST에서도 WebRender를 설명하기 위해 사용한 예시지만, 너무나 찰떡인 예시기 때문에 예시로 플립 북(flip book)을 보자.

여기 책장을 빠르게 넘기자 그림이 움직이는 것처럼 보이는 책이 있다.

이 책은 라이카 스튜디오의 한 애니메이터가 만든 ‘어벤져스: 인피니티 워' 플립북(flip book)이다.

플립북(flip book)은 부드러운 움직임을 위해서 수많은 종이의 양이 필요하고, 빠르게 넘기는 속도가 필요

이는 브라우저도 마찬가지인데, 사용자인 여러분의 눈에 한 화면을, 혹은 어떤 애니메이션을 끊김없이 부드럽고 연속적으로 보여주기 위해서 브라우저는 약 60장의 미세한 변화가 있는 그림들을 전부 준비해놓고 그것을 1초 안에 빠르게 보여주는 것이다.

즉 1초라는 짧은 시간 안에 브라우저는 60장가량의 레이아웃과 페인트 과정을 동시에 처리해야만 하는 것

여기서 중요한 점이 있다.

DOM은 변경되면 렌더 트리를 다시 구축하기 때문에, 변경될 때마다 리플로우와 리페인트를 다시 해야 한다는 것

리플로우는 렌더링을 다시 하는 것이기 때문에 배치를 위한 연산을 해야 해 실제로 CPU를 많이 차지

리페인트는 페인트를 다시 하는 것이라 픽셀을 다시 화면에 찍어 그려야 하므로 GPU를 많이 차지

그렇기 때문에 프레임 드랍(Frame Drop) 현상과 직접적인 연관이 있다.

파싱이란

프로그래밍 언어로 작성된 파일을 실행시키기 위해 구문 분석(syntax analysis)을 하는 단계

파싱을 파서(parser)가 진행하며, 일종의 인터프리터나 컴파일러 구성 요소 가운데 하나

파서는 HTML 파일의 코드를 문법적 의미를 갖는 최소 단위인 토큰(token)으로 한 번 분해

이 토큰들을 문법적 의미와 구조에 따라 노드(node)라는 요소로 바꿈

노드들은 상하 관계에 따라 하나의 트리를 형성하는데 이를 파스 트리(parse tree), 혹은 문법 트리(syntax tree)라고 부름.

그렇다면 어떻게 최적화를 시킬 수 있을까?


불필요한 레이아웃을 줄인다

레이아웃 과정에서 불필요하게 계산해야 할 것이 많아지면 엔진도 결국 컴퓨팅 파워에 기대기 때문에 과부하가 불가피하게 생기게 된다.

따라서 불필요한 레이아웃 하나만 줄여도 렌더링 퍼포먼스를 최적화할 수 있다.

CSSOM 트리의 CSS 속성 중에 레이아웃을 발생시키는 속성들이 존재하고 있다.

이 속성을 사용하게 되면 그때마다 변경이 되어 렌더 트리를 만들고, 레이아웃을 발생시키고, 페인트를 하는 과정이 연속적으로 발생한다.

따라서 이런 레이아웃을 발생시키는 속성을 피하면 해당 과정이 발생하는 횟수를 줄일 수 있다.


CSS에서 레이아웃, 페인트를 발생시키는 속성들

아래는 각각 리플로우, 리페인트가 일어나는 CSS 속성들이다.

레이아웃과 페인트 과정은 별개의 과정처럼 보일 수 있으나 리플로우 시 리페인트는 필연적으로 일어나므로 가능하다면 리플로우가 발생하는 속성보다 리페인트만 발생하는 속성을 사용하는게 좋다.

페인트 과정은 유저에게 화면을 보여주기 위해 그리는 과정이라 필수적인 과정이기 때문

따라서 프론트엔드 개발자가 되어 브라우저 렌더링의 최적화 과정을 수행하고자 한다면 레이아웃이 일어나는 상황을 최대한 피하는 것이 좋기 때문에 해당 속성을 알아두는 것이 굉장히 중요


리플로우가 일어나는 대표적인 속성

positionwidthheightlefttopright
bottommarginpaddingborderborder-widthclear
displayfloatfont-familyfont-sizefont-weightline-height
min-heightoverflowtext-alignvertical-align

위치, 혹은 너비와 관련된 속성이 많다.

left 속성 중 left-top, left-bottom 속성을 사용하면 위치가 변경이 된다. (position 속성과 같이 씀)

이 left 속성은 레이아웃을 발생시키는 속성.

따라서 이 속성을 가지고 애니메이션을 만들 때는 프레임 유지를 보장하기 어려워진다.

그래서 이 속성을 피해 transform이라는 속성을 사용

transform에 있는 translate를 사용하면 좌표 값을 사용해 위치를 이동하지만, 레이아웃을 발생시키지 않고 페인트만 다시 발생시키는 쪽으로 렌더링 과정이 일어나기 때문에 유지하고자 하는 프레임 수를 기대할 수 있다.

리페인트가 일어나는 대표적인 속성

backgroundbackground-imagebackground-positionbackground-repeatbackground-sizeborder-radius
border-stylebox-shadowcolorline-styleoutlineclear
displayfloatfont-familyfont-sizefont-weightoutline-color
visibility...

리페인트가 일어나지 않게 해주는 opacity라는 속성도 있습니다.

visibility/display 보다 opacitiy를 사용하는 것이 성능 개선에 도움이 된다.

영향을 주는 노드를 줄입니다.

JavaScript + CSS를 조합한 애니메이션이 많거나, 레이아웃 변화가 많은 요소의 경우 positionabsolute 또는 fixed를 사용

  • 영향을 받는 주변 노드들을 줄일 수 있다.

fixed와 같이 영향을 받는 노드가 전혀 없는 경우 리플로우 과정이 필요 없기 때문에 리페인트 연산 비용만 들일 수 있다.




최근 모던 웹 브라우저는 성능이 매우 뛰어나므로 대부분의 경우 크게 성능 개선을 고려할 필요성을 느끼지 못한다.

하지만 프론트엔드 개발을 하다보면 다양하고 복잡한 요구 사항에 대응해야 하는 경우도 많아지고, 화면이 라이브 데이터에 따라 실시간으로, 빠르게 수많은 변경을 일으켜야 하는 경우도 생기게 된다.

해당 내용들은 실력 있는 웹 프론트엔드 개발자가 되기 위해 필수로 알아야하는 내용이며, 기술 면접에서도 자주 물어보는 내용이기 때문에 꼭 숙지해야겠다.




📚 레퍼런스

코드스테이츠 수업자료

0개의 댓글