* 이 글은 How the browser renders a webpage? - DOM, CSSOM and Rendering을 번역하였습니다.
브라우저의 Rendering (1) - construction에서는 DOM, CSSOM, Render Tree에 대해서 살펴봅니다.
여기서는 어떻게 브라우저가 페이지를 렌더하는지 이해하기 위해 DOM과 CSSOM을 깊게 파헤쳐 봅니다. 브라우저는 특정 리소스가 먼저 로드되고 다른 리소스가 비동기 적으로 로드 될 때까지 웹 페이지의 일부 렌더링을 차단합니다.
웹 사이트를 개발할 때 UX는 매우 중요하고 필수적입니다. 웹사이트에서 초기 렌더가 될 때 불 필요한 파일들을 기다림으로 인해 리소스들의 로드가 느려지는 공통의 문제가 존재합니다. 이러한 문제를 피하기 위해 우리는 브라우저가 특정 웹사이트를 렌더링 할 때의 생애주기를 이해해야합니다.
첫 번째로, DOM이 무엇인지 알아야합니다. 브라우저는 HTML 문서를 가져올 수 있도록 요청을 서버로 보내고, 서버는 기본적으로 응답 헤더에 Content-Type이 text/html; charset:UTF-8 값으로 설정된 2진수 스트림 형식인 텍스트 파일의 HTML 페이지를 반환합니다.
여기서 text/html은 브라우저에게 HTML 문서를 알리는 MIME 유형이며 charset=UTF-8은 브라우저에게 UTF-8문자 인코딩 형식으로 변환되었음을 알려줍니다. 이러한 정보들을 사용함으로써 브라우저는 2진수 형식을 읽을 수 있는 텍스트 파일로 변환할 수 있습니다. 이 모든 부분은 아래의 설명되어져 있습니다.
만약 이러한 헤더가 없었다면, 브라우저는 어떻게 파일을 이해하고, 텍스트 형식으로 렌더링을 하는지에 대해 인지하지 못했을 것입니다. 이런 변환이 성공적으로 이뤄지면, 브라우저는 HTML 문서를 읽기 시작합니다. 보통의 HTML 문서는 아래와 같습니다.
<!DOCTYPE HTML>
<html>
<head>
<title>Rendering Test</title>
<!-- stylesheet -->
<link rel="stylesheet" href="./style.css"/>
</head>
<body>
<div class="container">
<h1>Hello World!</h1>
<p>This is a sample paragraph.</p>
</div>
<!-- script -->
<script src="./main.js"></script>
</body>
</html>
위에 문서를 보면 HTML 요소에 스타일적인 부분을 제공하는 style.css와 main.js 로 JavaScript 문서가 할당되어 있습니다. CSS style이 적용된 페이지는 아래와 같은 모습으로 나타납니다.
그러나 의문점은 여전히 존재합니다. 어떻게 브라우저가 text로만 씌여있는 파일인 단순한 HTML 문서를 이렇게 보기좋은 웹페이지로 렌더링을 했을까요? 그렇기에 DOM, CSSOM 그리고 Render Tree에 대해서 한번 살펴봅시다.
Document Object Model(DOM)
브라우저가 HTML 코드를 읽을 때 HTML 요소들(html, body, div 등등)은 노드라고 불리우는 JavaScript 객체로 만들어집니다. 즉, 모든 HTML 요소들은 JavaScript 객체로 변환되는 것입니다.
모든 HTML 요소들은 다른 속성들을 가지고 있기 때문에, 노드 객체는 다른 클래스(생성자 함수)에서 생성됩니다. 예를 들어 div 요소는 Node 클래스를 상속하는 HTMLDivElement로 변환됩니다. 이전 HTML 문서의 경우, 아래와 같이 단순한 테스트를 하여 Node들을 시각화할 수 있습니다.
브라우저는 HTMLDivElement, HTMlScriptElement, Node 등과 같은 내장 클래스와 함께 제공됩니다.
브라우저가 HTML 문서에서 Node 들을 변환시킨 후의 Node 객체의 형태는 마치 트리구조와도 같습니다. HTML파일의 HTML 요소들 서로가 중첩되어 있기 때문에, 브라우저는 이전에 생성한 Node 객체를 사용하여 이를 복제합니다. 이를 통해 브라우저는 효과적으로 렌더를 할 수 있고, 웹페이지 전체적인 생애주기를 다룰 수 있습니다.
위에 구조는 DOM 트리입니다. DOM 트리는 최상의 요소 html부터 시작되어 문서내에 HTML 요소들을 중첩하며 뻗어나갑니다. HTML 요소가 발견될 때마다 각각의 클래스로부터 DOM 노드 객체는 만들어집니다.
* DOM 노드는 HTML 요소만을 가지고 있지 않습니다. 브라우저가 DOM 트리를 만들때, 주석, 속성, 텍스트와 같은 것들은 트리에 별도의 노드로 저장합니다. 위 그림은 단순화 하기 위하여, HTML 요소의 AKA DOM 요소에 대한 DOM 노드만 고려합니다.
아래는 모든 DOM 노드 타입의 리스트들을 참조한 사이트입니다.
아래 보이는 것처럼 Google Chrome DevTools Console에 있는 DOM 트리를 시각화 할 수 있습니다. 각각의 DOM요소들의 속성과 함께 DOM 요소들의 계층들을 보여줍니다.
JavaScript는 DOM이 무엇인지 알지 못하며, 이는 JavaScript의 명세의 한 부분도 아닙니다. DOM은 웹페이지를 효율적으로 렌더링하고 개발자가 다양한 목적으로 DOM요소를 동적으로 조작할 수 있도록 공개적으로 노출하기 위해 브라우저에 의해 제공되는 Web API 입니다.
* DOM API를 사용함으로써 개발자들은 HTML 요소들을 추가 및 삭제, 이벤트 리스너를 바인딩 하거나 요소들의 노출을 변경할 수 있습니다. 또한 HTML 요소들을 새로 만들거나 메모리상에 복제할 수 있고, 렌더된 DOM 트리의 영향을 받지 않게끔 조작할 수 있습니다. 이러한 부분들은 개발자들은 풍부한 사용자 경험으로 동적인 웹페이지를 구성할 수 있습니다.
CSS Object Model(CSSOM)
웹사이트를 디자인할 때, 우리들의 목적은 가능한 보기 좋게 만드는 것입니다. 그리고 우리는 특정 스타일을 HTML 요소에게 제공함으로써 이를 충족시킵니다. HTML 페이지에서 HTML 요소들에게 Cascading Style Sheets의 약자인 CSS를 이용함으로써 맟춤 스타일을 제공합니다. CSS 선택자는 DOM 요소들을 찾고 color 나 font-size 같은 디자인 속성들을 설정합니다.
HTML 요소에 스타일을 적용하는 방법은 외부 CSS파일을 사용하거나 <style>태그를 HTML 페이지에서 직접사용, 그리고 HTML 요소의 스타일 속성을 인라인 방법으로 사용하거나 JavaScript를 이용하여 사용하는 다양한 방법이 존재합니다. 하지만 결국에는 브라우저는 적용한 CSS 스타일들을 DOM 요소들에 적용하는 무거운 작업을 수행해야 합니다.
아래 코드처럼 CSS를 사용한다고 가정해봅시다. 단순하기 하기 위해 CSS를 HTML 페이지에 어떻게 Import 했는지는 생략하겠습니다.
html {
padding: 0;
margin: 0;
}
body {
font-size: 14px;
}
.container {
width: 300px;
height: 200px;
color: black;
}
.container > h1 {
color: gray;
}
.container > p {
font-size: 12px;
display: none;
}
DOM이 만들어진 후, 브라우저는 CSS의 모든 소스들을 읽고 CSSOM으로 구조화 하기 시작합니다. CSSOM은 CSS Object Model의 약자로써 DOM과 같은 트리입니다.
이 트리의 각 노드에는 DOM 요소에 적용될 CSS 스타일 정보가 포함되어 있습니다. 하지만 CSSOM에는 <meta>, <script>, <title>과 같은 화면과 관련이 없는 DOM 요소들은 포함하지 않습니다.
알다시피, 대부분의 브라우저는 user agent stylesheet 라고 불리우는 기본 style을 가지고 있습니다. 먼저 브라우저는 개발자 속성에서 제공하는 CSS로 user agent style을 재 정의하여 DOM 요소에 대한 최종 CSS속성을 계산한 뒤 노드를 구성합니다.
display같은 CSS 속성이 개발자나 브라우저에 의해 정의되어있지 않더라도, W3C CSS 기준에 따라 정의된 속성이 default 값으로 적용됩니다. CSS 속성의 default 값을 선택됨과 동시에 W3C 문서에 언급되어 있는 상속자격이 있는 속성들을 상속 특정 규칙이 적용됩니다.
예를 들어 color나 font-size 는 HTML 요소에서 누락된 경우 부모의 값을 상속합니다. 그러므로 HTML 요소와 이를 상속하는 모든 하위 요소에 이러한 속성이 있을 것이라고 예상할 수 있습니다. 이를 cascading of style이라고 하며 CSS가 Cascading Style Sheets의 줄임말입니다. 이것이 브라우저가 CSS의 cascading 규칙을 기반으로 스타일을 계산하기 위해 트리와 같으 구조인 CSSOM을 구성하는 이유입니다.
* Element 패널에 Chrome DevTools console을 이용하면 HTML 요소들의 계산된 스타일을 볼 수 있습니다. 왼쪽 패널에 HTML 요소를 선택하고 오른쪽 패널에 computed 탭을 클릭하세요.
아래의 다이어그램을 사용하여 CSSOM을 시각화 해보았습니다. 단순명료하기위해, user-agent style은 제외하였고 이전에 작성된 CSS 스타일만 나타냈습니다.
위에 다이어그램을 보면, CSSOM 트리는 <link>, <title>, <script>등 화면과 관계없는 태그는 포함하지 않습니다.
gray에 있는 속성 값은 상속 된 값을 재 정의하는 반면에 black에 있는 CSS 속성의 값들은 위에서 아래로 계단식으로 적용됩니다.
Render Tree
렌더 트리는 DOM과 CSSOM 트리를 결합하여 구성한 트리입니다. 브라우저는 렌더트리를 사용하여 각각의 보이는 요소들의 레이아웃을 계산해야하고 화면에 그려야 합니다. 그러므로 렌더 트리가 구성되지 않는다면 화면에 아무것도 그려지지 않으므로 DOM과 CSSOM 트리가 모두 필요합니다.
렌더트리는 화면에 그려질 항목의 low-level representation이므로 픽셀에 영역을 포함하지 않는 노드를 포함하지 않습니다. 예를 들어 display:none;인 요소는 0px 0px의 크기를 가지므로 렌더 트리에 포함하지 않습니다.
위 다이어그램처럼 렌더트리는 화면에 그려지는 요소들을 포함된 구조를 트리모양으로 만들기 위해 DOM과 CSSOM을 결합합니다.
CSSOM 이후, div 내 있는 p 요소는 display:none; 으로 적용 되어있고, 화면에 공간을 차지하지 않기 때문에 p요소의 자식들도 물론 렌더트리에 포함되지 않습니다. 하지만 p요소가 visiblility:hidden 또는 opacity:0 으로 적용되었다면 화면에 공간을 점유하므로 렌더트리에 포함됩니다.
브라우저에 의해 구성된 DOM 트리의 요소에 대한 접근을 제공하는 DOM API와는 다르게 CSSOM은 유저에게 보이지 않습니다. 하지만 브라우저가 렌더트리를 형성하기 위해 DOM과 CSSOM을 결합하면, 브라우저는DOM 요소의 High-level API를 제공하여 DOM 요소의 CSSOM을 노출합니다. 그렇기에 개발자는 CSSOM 노드에 CSS 속성을 바꾸거나 접근할 수 있습니다.
* JavaScript를 사용하여 요소의 스타일을 조작하는 부분은 여기서 다루지 않기 때문에, CSSOM API의 대한 모든 정보를 제공하는 아래의 링크를 추천드립니다.
* 각 요소 스타일의 계산하는 정확한 방법을 제공하는 CSS Typed Object API의 소개글도 추천글로 공유드립니다.
다음은 브라우저의 Rendering (1) - Operation에 대해 다루겠습니다.
긴 글 읽어주셔서 감사합니다.
< 참고자료 >
[사이트] #medium
medium.com/jspoint/how-the-browser-renders-a-web-page-dom-cssom-and-rendering-df10531c9969
<JavaScript> 브라우저의 Rendering (1) - Construction
'Language & Framework & Library > JavaScript' 카테고리의 다른 글
웹 개발자가 알아야 할 7가지 디자인 패턴 (0) | 2021.04.04 |
---|---|
브라우저의 Rendering (2) - Operation (0) | 2021.04.03 |
Script async, defer (0) | 2020.08.01 |
ES2020 - 10 New javaScript Features (0) | 2020.07.26 |
ES5 .Intro(12) - DOM(Document Object Model) (0) | 2019.12.29 |