Populating the page: how browsers work

se·2023년 1월 26일
1
post-thumbnail

MDN Populating the page: how browsers work 아티클을 읽고 정리한 글


웹에서 좋은 사용자 경험을 만드는 요소로 두 가지를 강조할 수 있다.

  1. 빠른 로드 속도
  2. 부드러운 상호작용

💡 따라서 성능에 있어 중요한 두가지 이슈를 꼽아본다면

  1. Latency

    네트워크를 통해 다른 머신으로 데이터를 전송하는 데 걸리는 시간이다. 개발자는 요청된 정보를 최대한 빠르게, 또는 빨라 보이게 전달해야 한다.

    페이지 로드 시간을 최대한 짧게

  2. Single-threaded Browser Render time

    브라우저는 싱글 스레드이다. 개발자의 역할은 메인 스레드가 모든 작업을 완료할 수 있는 동시에 언제나 부드러운 스크롤, 터치 등 유저와의 상호작용이 가능하도록 만드는 것이다.

    메인 스레드의 책임은 최소화하고 렌더링은 부드럽게, 상호작용은 즉각적이게


이 이슈들을 생각하면서 브라우저의 동작 방식을 살펴보자.

✅ 브라우저는 [Navigation → Response] → [Parsing → Rendering]의 흐름으로 웹 페이지를 보여주게 된다. 간단하게 설명하면 리소스를 요청해서 응답을 받고, 응답으로 받은 데이터를 화면에 그릴 수 있도록 프로세싱해서 그려낸다. 이 아티클에서는 각 단계에 대해 자세하게 살펴보고 있다.


유저가 주소창에 URL 입력, 링크 클릭, 폼 제출 등을 통해 요청을 보내면 웹 페이지 로드가 시작된다. 이 navigation에 걸리는 시간을 최소화 하는 것도 web performance 목표 중 하나이다.

  1. DNS Lookup

    첫번째 단계는 페이지를 구성하는 asset들이 어디에 위치해있는지 찾는 것이다.

    https://example.com 이라는 페이지는 사실 IP 주소 93.184.216.34를 가진 서버에 위치한다. 이 페이지를 방문한 적이 없다면 DNS lookup이 발생한다. 이 때 name server에서 응답받은 IP 주소는 한동안 캐시로 저장되고, 이후 요청들은 서버에 다시 연결하는 대신 캐시 정보를 사용해 더 빠르게 처리된다.

    일반적으로 페이지를 로드할 때 hostname 별로 한번씩만 발생하는데, 페이지가 참조하는 고유한 hostname이 더 있다면 추가로 발생한다. fonts, images, scripts, ads, metrics 등이 다른 origin에 위치하는 경우이다. 따라서 필요한 애셋이 있다면 최대한 같은 origin에서 제공하는 것이 좋다. 특히 lookup에 많은 네트워크 자원을 사용하는 모바일 네트워크 환경에서는 성능 이슈가 존재할 수 있다.

          
  2. TCP Handshake

    IP 주소를 탐색한 브라우저는 3-way handshake를 통해 서버와의 연결을 생성한다.

  3. TLS Negotiation

    요청 데이터 전송 전 3번의 round trip이 추가되는데, 각각은 아래와 같은 역할을 한다.

    1. 암호화 방식 결정
    2. 서버 인증
    3. 데이터 전송 전 보안 연결 생성

Response

  1. 웹 서버와의 연결이 생성되면 브라우저는 최초의 HTTP GET 요청을 보낸다.

    대부분의 웹 사이트에서 응답은 HTML 파일이고, 적합한 헤더와 함께 전송된다.

    유저의 요청 시점부터 첫번째 응답 packet을 받는 시점까지의 시간을 Time To First Byte이라고 부른다. 14KB Rule에 따라 이 첫번째 청크의 크기는 보통 14KB이다.

    14KB Rule
    첫번째 패킷은 14KB, 전송이 완료되면 다음 패킷은 두 배로 크기를 증가시키는 규칙이다. 정해진 threshold를 넘기거나 혼잡이 발생하기 전까지 크기를 계속해서 증가시킨다.
    TCP 혼잡 제어를 위한 Slow Start policy에 기인한다. 하드웨어와 네트워크 상태에 따라 limited capacity를 가지는 네트워크 속도를 조정하는 알고리즘으로, 최대 대역폭이 확인되기 전까지 전송 데이터의 크기를 점진적으로 늘려가는 방식을 채택한다.

Parsing

  1. 첫번째 청크를 받으면 브라우저는 parsing을 시작한다. 이 때 DOM, CSSOM이 만들어진다.

    Document Object Model은 브라우저 내부에서 HTML 마크업을 표현하는 방식으로, JavaScript API를 통해 접근이 가능하다.

    요청 페이지가 첫 14KB 패킷보다 큰 경우에도 브라우저는 가지고 있는 데이터로 파싱을 시작한다. 이것이 첫번째 패킷에 렌더링에 필요한 모든 정보, 적어도 페이지 템플릿을 담는 것이 웹 최적화에 중요한 이유이다.

Critical Rendering Path

DOM Tree → CSSOM Tree → Style → Layout → Paint

  1. Building the DOM Tree

    HTML markup → tokentree

    우선 HTML 마크업을 토큰으로 파싱한 뒤 DOM 트리로 만든다. 태그와 속성명, 속성값으로 이루어지는 토큰을 파싱하는 과정은 노드가 많을수록 오래 걸린다.

    이 때 <img /> 같은 non-blocking resource가 있으면 자원을 요청한 뒤 다시 파싱하게 된다. CSS 파일도 메인 스레드를 블로킹하지 않지만 async, defer 속성이 없는 <script> 태그는 렌더링과 파싱을 블로킹한다. preload scanner가 이 과정의 최적화를 돕기는 하지만, 과도한 스크립트 태그는 보틀넥으로 작용할 수 있다.

    Preload Scanner

    DOM tree 만드는 작업은 브라우저 메인 스레드를 점유한다. 이 동안 preload scanner는 가능한 컨텐츠들을 스캔하며 CSS, JS, web fonts 같은 자원을 요청해 메인 파서에게 필요한 자원을 사전에 다운로드한다.

    <link rel="stylesheet" href="styles.css" />
    <script src="myscript.js" async></script>
    <img src="myimage.jpg" alt="image description" />
    <script src="anotherscript.js" async></script>

    CSS 요청은 HTML 파싱이나 다운로드를 블로킹하지 않지만, JS에서 CSS 프로퍼티를 사용하고 있는 경우 블로킹이 발생할 수 있다.

  2. Building the CSSOM Tree

    CSS 파일을 스타일 맵으로 변환하는 과정이다. CSSOM은 selector 기반으로 parent, child, sibling 관계를 가지는 노드들의 트리이다.

    브라우저 기본 스타일인 user agent style sheet를 포함하며, 개발자 도구의 “Recalculate Style” 도구로 CSS parse, CSSOM construction, recursive computed style calculation에 걸리는 시간을 확인할 수 있다.

    CSSOM 구성은 대부분 DNS lookup보다 빠른 시간 안에 일어난다.

    ➕ JS Compilation

    • where JavaScript files are interpreted, compiled, parsed into AST and executed

    ➕ Building Accessibility Tree

    • used by assistive device to parse and interprete content
    • DOM이 업데이트되면 함께 업데이트되면 외부에서는 수정이 불가능
    • AOM이 만들어지기 전에는 스크린 리더가 컨텐츠에 접근할 수 없음

Render

style → layout → paint → composition

  1. Style

    DOM, CSSOM이 렌더 트리로 만들어진다. 이 단계에서는 DOM root에서 시작해 보여질 노드들을 탐색하며 스타일을 계산한다.

    각 노드에는 CSS cascade 규칙에 따라 해당하는 CSSOM 규칙이 적용되며, 그려질 필요가 없는 <head> display: none 등은 제외된다.


  2. Layout

    렌더 트리 노드들의 기하학적 특성(너비, 높이, 위치)를 계산하고 페이지 내에서 어디에 어떤 크기로 나타나야 하는지가 처음으로 결정되는 단계이다. 디바이스의 viewport 사이즈가 고려된다.

    Reflow 레이아웃 이후 일어나는 페이지 일부 또는 문서 전체의 크기 또는 위치 변화를 말한다.


  1. Paint

    각각의 노드를 화면에 그리는 작업이다. First Meaningful Paint는 처음으로 그려지는 화면을 의미한다.

    이 단계에서는 레이아웃 단계에서 계산한 박스를 스크린 픽셀로 변환한다. 부드러운 사용자 경험을 위해 reflow, repaint에서 발생하는 스타일 계산 등 메인 스레드의 모든 작업은 빠르게 이루어져야 한다.


    Compositing

    To ensure repainting can be done even faster than the initial paint, the drawing to the screen is generally broken down into several layers

    • 성능을 위해 일부 작업을 CPU 메인 스레드 대신 GPU layer로 이동시킬 수 있다.
    • <video> <canvas> opacity transform will-change … 등의 요소는 자신만의 레이어를 가지게 된다. 성능 상 이점을 가지지만 메모리 관점에서는 비싸기 때문에 적절한 수준에서 사용되어야한다.
    • 이렇게 document section들이 서로 다른 레이어에 그려지게 되면 레이어들을 오버래핑해 최종 뷰를 그리는 작업이 필요하다. 이런 경우 reflow가 발생하면 repaint & re-composite의 과정이 추가로 필요하기 때문에 이미지 사이즈를 명시하는 것이 중요하다.

0개의 댓글