본문 바로가기
JS

브라우저 렌더링 과정, 그만 검색하고 외우자 (feat.MDN)

by 사과넹 2024. 4. 15.
반응형

웹 성능

성능에 관련한 요인

  • 지연시간
  • 브라우저는 대부분 single thread로 동작하는 것

지연시간

빠른 Load를 위해서는 빠르게 요청하는 것도 중요하다. 네트워크 지연시간을 줄여서 리소스를 빠르게 요청하고, 웹 최적화를 통해 페이지 로드를 빠르게 한다.

네트워크 지연시간
네트워크를 통해 컴퓨터로 바이트를 전송하는데 걸리는 시간

웹 최적화
페이지 로드가 최대한 빠르게 이루어지도록 하는 것

브라우저는 single thread

유저는 브라우저 내에서 수많은 상호작용을 시도하고, 브라우저는 이에 맞게 반응을 제공해야 한다. main thread가 요청된 작업을 수행하며 유저의 상호작용에 반응하기 위해서는 렌더링 시간이 중요하다.

브라우저가 singel thread인 것을 미뤄보아 main thread에 책임을 줄여주는 방식으로 프로그래밍하면 웹 성능의 향상을 이룰 수 있고, 상호작용에 대한 응답은 더 빨라질 것이다.

탐색, Navigation

Load의 첫 단계는 Navigation이다.

이는 유저가 URL을 통해 웹 페이지에 접근할 때, 링크를 통해 웹 페이지에 접근 할 때, Form을 제출하는 등 클라이언트에서 서버로 Request를 보낼 때 동작한다.

탐색이 시작되면 먼저 해당 페이지의 자원이 어디에 위치 하는지 찾는다. 자원은 우리가 아는 도메인 주소가 아닌 그 뒤에 있는 어떤 IP 주소의 서버 위치하고 있다. 만약이 IP에 존재하는 서버에 한 번도 방문한 적이 없다면 브라우저는 서버에 DNS 조회 요청을 시도한다. 응답받은 IP 주소는 일정 기간 캐시되어 캐시가 남아있는 한 후속 요청 속도를 빠르게 한다.

DNS (Domain Name System)
웹 브라우저는 IP(Internet Protocol) 주소를 통해 상호작용하기 때문에 인터넷 자원을 로드할 땐 DNS가 도메인 이름을 IP 주소로 변환한다.

 

DNS 조회는 호스트 이름 하나당 한 번만 수행되기 때문에 요청된 페이지 안에 다른 호스트 이름을 참조하고 있는 자원이 있다면(이미지, 폰트, 광고 등), 각각에 대해 모두 조회되어야 한다.

모바일 브라우저의 경우 DNS 조회는 휴대폰 → Cell Tower 를 거치는 과정이 추가되기 때문에 이 거리에 따라 지연시간이 발생될 수 있다.

 

📎 DNS 레코드

A IPv4 주소 레코드는 IPv4 주소로 호스트 이름을 매핑하는 데 사용됩니다.
AAAA IPv6 주소 레코드는 IPv6 주소로 호스트 이름을 매핑하는 데 사용됩니다.
CNAME 정식 이름 레코드는 다른 DNS 레코드를 가리키는 별칭입니다.
NS 네임 서버 레코드는 권한 있는 네임 서버를 가리킵니다.
SOA 신뢰할 수 있는 기록은 DNS 영역에 대해 신뢰할 수 있는 정보가 포함되어 있습니다.
MX 메일 교환 레코드 포인트 봉사 메일.
TXT 텍스트 기록은 임의의 텍스트가 포함되어 있습니다. 그것은 종종 특정 도메인에 대한 전자 메일을 보낼 수있는 사람을 지정하는 도메인 소유권 확인 및 SPF (발신자 정책 프레임 워크)에 사용됩니다.

TCP Handshake

IP 주소 획득 후에는 브라우저는 서버와 TCP 3방향 handshake를 통해 연결을 설정한다.

TCP (Transmission Control Protocol)
전송 제어 프로토콜, 2개의 호스트를 연결하고, data stream을 교환하게 해주는 네트워크 프로토콜이며 데이터와 패킷이 보내진 순서대로 전달하는 것을 보장한다

Data Stream
전송된 정보를 수집하거나 정보를 전송할 때 사용되는 디지털 방식으로 암호화 된 일관된 신호의 흐름

 

TCP 3방향 handshake
SYN, SYN-ACK, ACK 로 불리며 두 컴퓨터가 TCP 세션을 협상하고 시작하기 위해 TCP가 3개의 메세지를 전달하는 것이다. 요청이 보내지기 전에 3개의 추가적인 메세지가 컴퓨터 사이에 주고받아진다는 의미이다. 이 방식은 데이터를 전송하기 전 통신하려는 두 주체(현재는 브라우저와 웹 서버)가 TCP 소켓 연결을 위한 매개변수를 주고 받을 수 있도록 만들어졌다.

TLS 협상, TLS Negotiation

HTTPS의 보안 연결을 위해서는 TCP handshake 외 다른 handshake가 필요하다.

TLS negotitation이라 불리는 handshake는 통신 암호화에 쓰일 암호를 결정하고, 서버를 확인하고, 실제 데이터 전송 전에 안전한 연결이 이루어질 수 있도록 한다.

보안이 있는 연결은 없는 연결보다 페이지 로딩 시간이 길지만 충분히 필요한 과정이다. 브라우저와 웹 서버가 주고 받는 데이터를 제 3자에게 해독당하지 못하게 하기 때문이다.

이렇게 DNS 조회, TCP handshake, TLS negotiation의 일련의 과정들을 거치면 총 8번의 데이터 요청, 응답이 오간다. 이 프로세스를 거치고나면 드디어 브라우저는 웹 서버에 자원을 요청할 수 있다.

Response

웹 서버에 연결은 일단 최초 한 번이 성립되어야 하고, 브라우저는 Get Request를 보내는데, 대개 HTML 파일을 요청한다.

서버는 요청에 응답 헤더와 함께 HTML 내용을 응답하고, 초기 요청에 대한 응답은 첫 바이트 데이터를 포함하고 있다. (TTFB, Time to First Byte)

TTFB, Time to First Byte
사용자가 웹 페이지를 접근하여 보내진 브라우저 요청에 의해 HTML의 첫 패킷을 응답받는데 걸리는 시간 대부분 첫 번째 컨텐츠 청크는 14kb 정도 한다

TCP Slow Start, 14kb rule

TTFB가 14kb인 이유는 네트워크 통신의 속도를 조절하는 알고리즘인 TCP Slow Start에 의해 정해진 것이다.

이는 네트워크 최대 대역폭을 파악하고 네트워크 용량에 적당한 전송속도를 찾기 위해 점진적으로 데이터 전송량을 증가시킨다.

점진적으로 증가되는 패킷의 크기는 이전 패킷 용량의 2배이며 예를 들어 첫 번째 패킷의 크기가 14kb니까 두 번째 패킷의 크기는 28kb이다. 패킷의 크기가 미리 정의한 임계치에 다르거나 혼잡의 징후(아래 설명)가 나타나기 전까지는 계속 2배씩 커진다.

패킷, packet
컴퓨터 네트워크가 전달하는 데이터의 형식화된 블록이며 제어 정보와 사용자 데이터를 포함한다. 이는 Payload라고 부른다. 패킷을 지원하지 않는 컴퓨터 통신 연결은 단순히 바이트, 문자열, 비트를 연속하여 데이터를 전송한다.

혼잡 제어, Congestion control

서버가 TCP 패킷으로 데이터를 보내고, 클라이언트는 데이터를 받았다는 확인 응답을 보내며 데이터의 수신을 확인한다.

만약 서버가 패킷을 너무 빠르게 보내면 그 패킷들은 무시되기 때문에 확인 응답이 없을 것이고, 서버는 확인 응답을 받지 않았기 때문에 누락되었다고 파악한다.

혼잡 제어 알고리즘은 보내진 패킷의 흐름과 확인 응답을 바탕으로 전송 속도를 결정한다.

구문 분석, Parsing

브라우저가 첫 번째 데이터 청크를 받으면 받은 정보를 parsing하기 시작한다. Parcing은 데이터를 DOM과 CSSOM으로 바꾸는 단계이며 이 두 가지는 화면에 페이지를 렌더링하는데 사용된다.

요청된 HTML 파일 크기가 첫 패킷의 고정된 크기인 14kb보다 크더라도 브라우저는 일단 parsing을 시작하여 현재 가지고 있는 수준에서 렌더링을 시도한다. 이것은 웹 최적화가 필요한 이유 중 하나인데, 첫 패킷에 렌더링에 필요한 HTML과 CSS만이라도 (JS는 없더라도) 포함되어야 빠른 렌더링을 통해 유저에게 페이지를 제공할 수 있다.

Building the DOM tree

HTML을 DOM tree로 parsing하는데, 이 때 토큰화와 함께 이루어진다. HTML 토큰은 시작 및 종료 태그, 속성 이름 및 값을 포함하고, 문서화가 잘되어 있는 정도에 따라 parsing 정확도와 속도가 올라간다. parsing기는 토큰화된 입력을 분석하여 DOM tree를 생성한다. 당연한 말이지만 DOM node의 개수가 많은 수록 tree building 시간이 오래 걸린다.

parsing 과정에서 이미지 같은 non-blocking 자원을 발견하면 해당 자원을 HTML 파일을 요청했던 것처럼 요청하고 분석을 계속하고, CSS 파일을 만나도 parsing은 계속된다. 하지만 asycn나 defer같은 설정이 되어 있지 않은 script 태그는 렌더링을 blocking시키고, parsing을 중지시킨다. 브라우저의 preload 스캐너가 이 작업을 가속화하긴 해도 과도한 script는 주요 blocking의 원인이 될 수 있다.

Preload scanner

브라우저가 DOM tree를 building하는 것은 main thread가 담당하기 때문에 preload scanner는 parsing이 필요하고, 우선순위가 높은 자원들(CSS, JS, 폰트 등)을 미리 요청하여 구문 분석기가 해당 부분을 parsing할 때, 요청 후 응답 시간을 최대한 감소하게 한다.

이처럼 preload scanner는 구문 분석 도중 일어날 수 있는 blocking 상황을 최적화해준다.

순서가 중요하지 않은 JS파일은 구문 분석의 blocking 현상을 피하기 위해 async, defer 속성을 사용하는 것이 좋다. 또한 CSS 다운로드는 구문 분석기의 흐름을 방해하지는 않지만 JS 내부에서 CSS를 조작할 수 있기 때문에 JS 실행은 blocking할 수 있다.

Building the CSSOM

DOM tree를 생성했으면 CSS를 처리하고 CSSOM을 생성해야할 차례이다. CSSOM은 DOM과 동일하게 트리구조이지만 서로 종속되지 않고, 독립적인 tree이다.

CSSOM tree 는 user agent stylesheet를 포함하고, 브라우저는 node에 가장 일반적인 규칙부터 적용하여 재귀적으로 더 구체적으로 적용된 규칙에 따라 스타일을 수정해나간다.

user agent stylesheet
브라우저 내에 적용되는 기본 style

 

CSSOM을 생성하는 것은 일반적으로 한 번의 DNS를 조회하는 것보다 더 짧은 시간이 소요되기 때문에 성능 최적화 관점에서 보았을 때, CSSOM은 성능 향상에 큰 영향은 없다.

Other Processes

Javascript compilation

CSSOM이 생성되는 동안 preload scanner로 인해 JS파일이 함께 다운로드 되는데, 이때 JS는 해석, 컴파일되어 구문 분석 및 실행된다.

스크립트는 추상 구문 트리로 구문 분석되는데, 일부 브라우저 엔진에서는 추상 구문 트리를 인터프리터에게 넘긴다. 그 결과 main thread에서 실행되는 byte code가 생성되는데 이 과정이 JS compile 과정이다.

Building the Accessibility Tree, 접근성 트리 구축

브라우저는 접근성 트리를 생성하는데 보조 장치는 이 트리를 이용해 내용을 분석하고 해석한다.

접근성 객체 모델(AOM)은 DOM의 의미 버전이며 DOM이 업데이트되면 AOM도 업데이트되며 이는 직접적으로 수정될 수 없고, 이것이 생성되기 전까지 화면 리더기는 컨텐츠에 접근이 불가하다.

Render

렌더링 과정에는 스타일, 레이아웃, 페인트와 합성이 포함된다. CSSOM과 DOM tree는 구문이 분석되는 과정에서 생성되고 render tree로 합성된다.

render tree는 보이는 요소의 레이아웃을 계산하고 나서 화면에 페인트된다. 어떤 경우 컨텐츠가 자신만의 레이어를 가지도록 조작되고 합성된다. 화면의 일부분을 CPU대신 GPU가 수행하여 main thread의 부담을 줄이고, 성능을 향상시킨다.

Style

DOM과 CSSOM이 완성되면 이 둘을 합성시켜 Render tree로 생성한다. 이는 style tree라고도 불린다.

Render tree는 DOM의 root node부터 시작하여 화면에 보이는 node를 순회하며 만들어진다. “화면에 보이는” 이기 때문에 display:none 인 node는 해당되지 않는다.

각 node에 적용된 CSSOM 규칙이 있기 때문에 모든 node에 관련된 스타일을 모두 맞춰보고 CSS cascade 규칙에 맞게 계산하여 스타일을 결정한다.

Layout

Render tree가 완성되면 Render tree를 기반으로 각 노드의 도형 값을 계산하여 레이아웃을 생성한다. 모든 node의 width, height, position을 결정하는 단계이다.

Render tree에서는 화면에 어떤 node들이 어떤 스타일을 가지고 표시될지는 알고 있으나 어떤 위치나 좌표에 위치해야하는지는 모르기 때문에 정확한 크기와 위치를 결정하기 위해 또다시 render tree의 root node부터 순회하여 레이아웃을 결정한다.

레이아웃 단계에서는 view port의 크기를 고려하여 화면에 표시될 상자의 크기를 결정한다.

처음 node들의 사이즈와 위치가 결정되는 것은 레이아웃이고, 그 이후 node의 크기나 위치가 재 계산되는 것은 reflow라고 한다.

Paint

레이아웃이 생성되면 node를 화면에 painting한다. 이 단계에서 레이아웃이 생성되며 계산된 각 node들이 실제 화면의 pixel로 변환된다. 또한 텍스트, 색상, 경계, 그림자, 버튼이나 이미지 같은 대체 요소를 포함해 모든 시각적인 부분을 화면에 그리는 작업이다.

부드러운 스크롤이나 애니메이션을 위해 스타일 계산, reflow, painting과 같이 main thread를 점유하는 모든 작업은 브라우저를 16.67ms 미만이여야 한다. 매우 많은 픽셀들은 빠른 시간안에 페인팅되어야 하고, 첫 페인팅보다 다시 페인팅하는 것은 더 빠르게 마무리 되어야한다. 그렇기 때문에 화면에 그리는 작업은 일반적으로 몇 개의 레이어로 구분된다. 이것이 일어나면 합성이 필요하다.

페인팅 단계에서 레이아웃 tree의 요소를 레이어로 분리할 수 있고, 컨텐츠를 CPU main thread에서 GPU 레이어로 변경하면 페인트 및 리페인트 성능이 높아진다.

레이어는 성능을 향상시키지만 메모리 관리 측면에서는 비싼 작업이다. 그렇기 때문에 웹 성틍 최적화 전략으로 레이어를 많이 만드는 것은 남용되지 말아야 한다.

Compositing

몇 개의 레이어가 그려졌을 때, 올바른 순서대로 레이어를 겹치는 과정, 즉 정확한 렌더링을 보장하기 위해 합성이 필요하다. 페이지가 계속 자원을 로드하면 리플로우가 일어날 수 있고, 리플로우는 리페인트와 재합성을 일으킬 수 있다.

Interactivity

main thread에서 페이지를 모두 그리면 페이지가 준비되었다고 생각할 수 있지만 defer javascript를 다운하거나 onload 이벤트가 발생한다면 main thread의 일은 아직 끝나지 않았다. 그래서 스크롤링같은 다른 상호작용은 불가능할 것이다.

TTI(Time to Interactive)는 DNS 조회와 SSL 연결이 이루어지는 첫 요청부터 페이지가 상호작용할 준비가 될 때까지 얼마나 걸리는지를 측정하는 단위이다.

첫 번째 콘텐츠가 포함된 페인트 이후 페이지가 유저와의 상호작용에 50ms 이내로 응담할 때 상호작용 가능한 시점으로 보고, main thread가 다른 일을 하고 있다면 50ms 이내로 적절한 반응을 하지 못한다.

ref

728x90
반응형