• 2021. 11. 16.

    by. 문익점

    반응형

    웹 브라우저는 웹 서버와 통신하여 HTML 문서나 파일을 출력하는 그래픽 사용자 인터페이스입니다. 웹 서버에서 파일을 받아와서 사용자에게 시각적으로 보여주고 소통하는 소프트웨어라고 생각하면 됩니다. 이 브라우저는 웹 서버에서 가져온 웹 페이지를 어떻게 시각적으로 보여줄까요? 이 포스팅은 브라우저의 동작 원리에 대해 설명합니다.

    랜더링 엔진

    브라우저의 구성요소 중 하나인 랜더링 엔진은 웹 페이지를 그리는데 핵심적인 역활을 합니다. 랜더링 엔진은 웹 페이지를 랜더링 하기 위해 요청 받은 HTML 및 XML 문서등을 파싱하고 트리 구조로 만드는데요. 이 트리 생성이 끝나면 어떻게 화면에 위치 할 것인지를 트리를 따라 계산하게 되고 마지막으로 화면에 픽셀을 그리며 사용자가 웹 페이지를 볼 수 있도록 합니다. 여기서 브라우저가 하나의 화면을 그려내는 과정을 중요 렌더링 경로(Critical Rendering Path)라고 부릅니다.

    중요 렌더링 경로(Critical Rendering Path)

    브라우저가 HTML, CSS, Javascipt를 화면에 픽셀로 변화하는 일련의 단계를 중요 렌더링 경로라고 부릅니다. 브라우저는 첫 번째로 HTML를 파싱하여 DOM(Document Object Model) tree를 만들고 css를 파싱하여 CSSOM tree를 만드는데요. 최종적으로 이 두개의 트리는 Render Tree로 탄생하게 됩니다. 그리고 레이아웃 단계를 거쳐서 모든 것이 페이지의 어느 위치에 갈 것인지 결정됩니다. 마지막으로 실제 화면에 픽셀을 그리는 것 입니다.

    아래에서 단계 별로 하나하나 분석 해보겠습니다.

    DOM (Document Object Model)

    말그대로 HTML 문서를 객체 기반으로 표현으로 변환한 것이 DOM입니다. 객체 모델로 변환이 된다면 다양한 환경과 애플리케이션에서 사용할 수 있게 됩니다. 랜더링 엔진은 HTML을 어떻게 DOM으로 변환 할까요?

    <html lang="ko">
      <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link href="style.css" rel="stylesheet" />
      </head>
      <body>
        <p>Hello Word</p>
      </body>
    </html>

    HTML 마크업의 표준안은 <>로 감싸진 것은 태그라고 정했습니다.,같은 것들을 말합니다. 브라우저는 이 태그들을 만날 때마다 Tokenizer가 토큰을 생성합니다. 위 와 같은 예제라면 StartTag: html - StartTag :head - Tag:meta - Tag:link - EndTag:head 이런식으로요. 이러한 토큰들이 만들어 졌다면 이 토큰들을 다시 node 객체로 변환 하는 과정을 겪는데요. 이 토큰들은 Start, End가 구별이 되어 있기 때문에 자식과 부모관계를 설정 할 수 있습니다. 어느 태그 언제 시작하고(열리고) 끝나는 지(닫히는지) 또 그 안에 어떤 태그가 존재하는지 알 수 있기 때문입니다. 이제 토큰들이 모두 node로 변환됩니다.

    이제 노드 간의 관계, HTML의 내용, 속성을 가지는 트리구조로 완성이 됩니다. 즉 DOM tree의 완성인겁니다.

    CSSOM(Cascading Style Sheets Object Model)

    위와 같이 DOM은 이제 페이지의 내용을 가지게 됩니다. 하지만 이제 이 내용을 어떻게 그릴 것인가?를 알아야합니다. 그럴려면 CSSOM을 만들어야 합니다.

    HTML과 마찬가지로, 수신된 CSS 규칙을 브라우저가 이해하고 처리할 수 있는 형식으로 변환해야 합니다. css를 받은 후 토큰화처리를 한 뒤 브라우저가 이해가능한 CSSOM이 생성 됩니다.

    body 요소 내에 있는 span 태그 안에 포함된 모든 텍스트의 글꼴 크기는 16픽셀이고 색상은 빨간색입니다. font-size 지시문은 body에서 span으로 하향식으로 적용되기 때문입니다. CSS는 Cascading Style Sheets의 준말데요. cascading의 뜻이 위에서 아래로 흐르는, 계단식의라는 뜻입니다. 이는 이렇게 CSSOM으로 그려보면 왜 최상단의 스타일이 아래 자식까지 영향을 미치는지 비로서 알 수 있습니다.

    Render Tree

    DOM에는 페이지의 모든 내용이 포함되고, CSSOM은 페이지의 모든 스타일이 포함되었습니다. 이제 화면에 표현하기 위해 앞선 두 가지 Model를 결합한 Render Tree가 필요합니다.

    랜더 트리는 DOM, CSSOM을 모두 합치지 않습니다. 페이지를 렌더링하는 데 필요한 노드만 포함되는데요. 이 말을 쉽게 표현하자면 사용자 눈에 보이는 내용만 가지게 됩니다. 즉 DOM과 CSSOM을 결합 할 시에 display: none 값을 가진 태그는 랜더트리에 결합되지 않습니다. 사용자 눈에 보이지 않아 랜더링하는데에 필요가 없기 때문입니다. 위 사진을 보면 랜더 트리에는 span 태그가 포함이 되지 않을 걸 확인 할 수 있습니다. 또 한 html의 head 태그는 눈에 보이지 않는 태그인데요. 그래서 랜더 트리에 포함되지 않은 것을 확인 할 수 있습니다.

    여담으로, visibility: hiddendisplay: none과 동작 방식이 다르다는 걸 아실텐데요. 보통 둘 다 사용자 눈에 보이지 않게되지만 차이점으론 visibility: hidden 값은 해당 공간은 존재하지만 보이지 않는다고 많이 얘기됩니다. 이제 그 이유를 알게됬습니다. visibility: hidden는 랜더 트리에 포함되지만 display: none는 랜더트리에 포함되지 않아서 입니다.

    Layout (Reflow)

    랜더 트리를 빌드하므로 써 이제 웹 페이지에 어떠한 정보가 필요한 지, 어떻게 스타일을 적용 할 지에 대한 정보가 준비 되었습니다. 하지만 모든 요소가 어디에 어떻게 위치에 해야 되는지는 알 지 못합니다. 아직 레이아웃 단계가 남아있기 때문입니다. 어떻게 레이아웃에서 정확한 크기와 위치를 파악하는지 보겠습니다.

    <html>
      <head>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>Critial Path: Hello world!</title>
      </head>
      <body>
        <div style="width: 50%">
          <div style="width: 50%">Hello world!</div>
        </div>
      </body>
    </html>

    레이아웃은 브라우저 크기와 관련 되어 있습니다. 크기에 따라 뷰 포트가 변하기 때문인데요. div 태그를 보시면 width: 50%으로 설정하고 있는데요. 이는 뷰 포트의 기준으로 설정하게 됩니다. 위 예제를 보시면 meta 태그로 뷰 포트는 device-width에 따라 폭을 설정하도록 하는 것을 볼 수 있습니다. 결국 기기의 viewport에 따라 위치와 폭이 계산 됨을 알아 낼 수 있습니다.

    여기서 하나 알아야 할 것이 있습니다. 뷰 포트에 따라 정해진다는 것에 주목해야 하는데요. 그 말은 즉 뷰 포트가 바뀔 때마다 브라우저는 레이아웃 단계를 다시 시행해야 합니다. 즉 모바일에서 세로 가로 화면 전환 시 뷰 포트는 변경되고 레이아웃은 다시 실행되게 됩니다. 또 스타일이나 내용이 변경 될 때마다 랜더트리는 다시 그리게 되고 레이아웃도 다시 실행되게 됩니다.

    자 이제 랜더트리를 배치하는 일까지 맞췄습니다.

    Paint (Repaint)

    페이지에 대한 정보, 스타일, 위치 정보를 얻은 랜더트리를 이용하여 개별 노드를 화면에 페인트하는 단계입니다!

    Refs

    https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model#css-object-model-cssom

    반응형