크리티컬 렌더링 패스란 브라우저가 HTML, CSS, JavaScript를 스크린에 픽셀로 나타내는 과정을 의미한다. 이 과정을 최적화하는 것이 렌더링 퍼포먼스를 향상시킨다.
HTML 코드에서 꺾쇠(<>
) 를 만나면 그건 태그라는 뜻인데, 브라우저는 태그를 만날 때마다 토큰(의미있는 단위의 문자열)을 만든다. 예를 들어 <head>
를 만나면 start tag가 head라는 토큰을 만든다. 이 과정은 Tokenizer가 한다. 예를 들어 아래 같은 HTML 코드를 읽으면 그 밑의 토큰으로 해석할 수 있다.
<html>
<head>
<meta name="viewport" ... />
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
...
</html>
Start Tag: HTML
Start Tag: head
Tag: meta
Tag: link
EndTag: head
이 토큰들은 다시 노드로 변환된다. 토큰들의 위계(hierarchy)에 기반해서 노드로 변환하고 연결하면 DOM(Document Object Model)
이 생성된다. 노드는 HTML 엘리먼트가 갖고 있는 관련 정보들은 모두 들고 있다.
그리고 중요한 점은 DOM은 점진적으로 생성된다는 것이다. HTML이 한꺼번에 반환되는 것이 아니라 점진적, 부분적으로 반환되는데 그것들이 먼저 렌더링될 수 있다. 이러한 특성은 성능 최적화에 유리하다.
CSSOM(CSS Object Model)
도 CSS Parser가 코드를 보고 유효한 토큰이 있는지 확인한 다음 토큰을 노드로 바꾼다.(HTML처럼 꺾쇠<>
는 없지만 CSS가 토큰을 변환하는 방식은 따로 있다) CSSOM이 DOM과 다른 것은 Cascading 한다는 것인데, 예를 들어 body {font size: 16px} p { font-weight: bold}
라는 코드가 있으면 p는 폰트 크기가 16px이라는 body의 속성을 상속받는다.
이러한 속성 때문에 HTML과 달리 partial CSSOM 트리는 사용할 수 없다. 스타일 속성은 트리 내에서 얼마든지 재정의될 수 있고, 상속되기 때문에, 전체 트리를 완성할 때까지 속성을 확정할 수 없기 때문이다. 그렇기 때문에 브라우저는 모든 CSS를 받을 때까지 페이지 렌더링을 차단한다. → CSS is render blocking!
또한 CSS에서 선택자를 중첩적으로 사용할 경우 calculate이 더 오래 걸릴 수 있다. 예를 들어 div 안에 있는 p 태그에 어떤 속성을 적용한다고 했을 때, p 태그를 찾은 다음 그 부모가 div 태그가 맞는지 다시 한번 거슬러 올라가야 하기 때문이다.
이렇게 DOM이 웹페이지의 content를 갖고 있고, CSSOM이 스타일 정보를 갖고 있을 때, 컨텐츠와 스타일을 합쳐 실제 스크린에 렌더링하게 만드는 기반이 렌더 트리다.
렌더 트리는 눈에 보이는 내용(only visible content
)만 갖고 있다. 예를 들어 DOM의 head 노드의 자식인 meta나 link 노드는 눈에 보이는 속성이 아니다. 그러므로 이 노드들은 렌더 트리에서 제외된다. 그리고 display:none 속성을 가진 노드가 있다면 이 또한 렌더 트리에서 제거된다. 눈에 보이는 노드들을 DOM과 CSSOM을 비교하고 복사를 해오면서 컨텐츠와 스타일을 동시에 가진 렌더 트리가 완성된다.
렌더 트리를 기반으로 리플로우가 일어나는데, 이 단계에서는 뷰포트를 확인하고 엘리먼트의 크기와 위치를 확인해서 레이아웃을 계산한다.
그리고 레이아웃을 다 그리면 렌더 트리의 각 노드를 페이지 픽셀로 전환하여 실제로 화면에 렌더링하는 페인트 단계를 거친다. 처음에는 전체 화면을 다 그리고, 그 다음은 필요한 부분만 리페인트를 한다.