브라우저 렌더링 (feat. Chrome)

gyomni·2022년 9월 16일
1

Week I Learned

목록 보기
19/20
post-thumbnail

HTML, CSS가 브라우저 렌더링하는데 필요한 전부이고,
JavascriptHTML태그를 동적으로 움직이게 만드는 프로그래밍 언어이다!

생각보다 간단한데..?
과연 그럴지...~ 🤷‍♀️
브라우저가 어떻게 렌더링되는 되는지 자세히 알아보자 !!🔍


브라우저 주요 구성 요소

  • The user interface
    : 주소 표시줄, 이전/다음 버튼, 홈버튼, 새로고침/정지 버튼, 북마크 메뉴 등 요청한 페이지를 보여주는 창을 제외한 사용자가 컨트롤 할 수 있는 나머지 모든 부분.

  • The browser engine
    : 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어.
    자료를 찾기 위해 자료저장소로 감.

    -> 사용자가 주소 표시줄에 URI을 입력했을 때, 입력 받은 URI 값을 렌더링 엔진에게 전달해주는 역할.
    -> 뒤로 가기 버튼을 클릭했을 때 이전 페이지로 가! 라는 명령을 렌더링 엔진에게 전달해주는 역할.

  • The rendering engine
    : 요청한 URI를 브라우저 엔진에게 받아서 서버에게 요청하고 서버로부터 URI에 해당하는 데이터 (HTML, CSS, Javascript..)를 받아서 파싱한 후 렌더링 함.

    -> 요청한 콘텐츠를 화면에 표시.
    -> 사용자가 URI를 입력했을 때 URI에 해당하는 데이터를 통신레이어, 자바스크립트 해석기, UI 백엔드에게 전달해줌. (전달하기 전에 파서들을 통해 HTML과 CSS를 파싱)
    -> 크롬은 웹킷(Webkit) 엔진을 사용

  • Networking
    : 렌더링 엔진으로부터 HTTP 요청을 하라는 명령을 받아서 서버에게 HTTP요청을 하고 (네트워크 처리) 난 뒤 서버에게 응답을 받아서 응답 받은 데이터를 렌더링 엔진에게 반환함. (OS 단에서 실행)

    -> HTTP 요청과 같은 네트워크 호출에 사용됨.
    -> 플랫폼 독립적인 인터페이스이고 각 플랫폼 하부에서 실행됨.

  • UI backend
    : 최종적으로 렌더링 엔진에서 생성된 렌더트리를 브라우저에 그리는 역할을 담당.

    -> 콤보 박스와 창 같은 기본적인 장치를 그림.
    -> 플랫폼에서 명시하지 않은 일반적인 인터페이스로서, OS 사용자 인터페이스 체계를 사용.

  • JavaScript interpreter
    : 자바스크립트 코드를 해석하고 실행.(자바스크립트 파싱)

    -> chrome에서는 V8이라는 javascript엔진 사용

  • Data storage
    : 자료를 저장하는 계층이다. 쿠키등의 모든 종류의 자원을 하드 디스크에 저장한다.

    -> HTML5부터는 브라우저가 지원하는 '웹 데이터 베이스'에 저장 가능


브라우저 렌더링 과정

  1. 사용자가 user interface 중 하나인 주소 표시줄에 URI를 입력한다.
    입력한 순간 URI를 브라우저 엔진에게 전달하고,
    브라우저 엔진은 입력받은 주소값에 해당하는 데이터를 먼저 Data storage에서 찾아본다.

    user interface를 통해 URI를 입력 받을 때마다 서버로 가서 해당하는 데이터를 받아오게 되면, 똑같은 데이터를 계속 서버로부터 받아와야 하는 불필요한 네트워크 통신이 발생한다. 이것은 곧 낭비를 뜻한다. (똑같은 데이터를 받으려고 계속 서버까지 가야하니까)
    이러한 낭비를 방지하기 위해 Data storage에 자주 받아 오는 데이터를 저장해두고 사용한다. 이것을 캐싱이라 칭하는데, 이처럼 캐싱 기법을 사용해서 효율적인 렌더링을 구성한다.

  2. 브라우저 엔진은 URI에 해당하는 자료들을 먼저 Data storage에서 찾고,
    만약 있다면 그것을 바로 렌더링 엔진에게 전달하기 때문에 Networking까지 가지 않아도 된다. (=> 낭비 방지)
    렌더링 엔진은 브라우저 엔진에서 가져온 자료 (HTML, CSS, Image등)를 분석하는 동시에 URI 데이터를 Networking(통신 레이어), JavaScript interpreter(자바스크립트 해석기), UI backend로 전파한다.

  3. 하지만, 처음 데이터를 받아오는 것이라서 Data storage에 저장되어 있지 않다면 서버로 가서 자료들을 받아와야 한다.
    이런 경우는 렌더링 엔진에게 사용자가 입력한 URI값을 전달하는데,
    그러면 렌더링 엔진은 URI의 주소값을 Networking(통신 레이어)와 JavaScript interpreter(자바스크립트 해석기), UI backend에게 전달하게 된다.
    정확히는 Networking(통신 레이어)에게 URI의 주소값을 전달하고 나서 Networking(통신 레이어)는 서버에 요청을 보낸다.

  4. URI 주소에 해당하는 data(HTML,CSS,Javscript)를 달라고 요청하면 서버는 URI에 해당하는 data를 응답한다.

  5. 응답받은 data를 다시 렌더링 엔진에게 전달하면,HTML,CSS는 렌더링 엔진이 파싱하고,

  6. 렌더링 엔진은 받은 Javscript를 JavaScript interpreter(자바스크립트 해석기)에 넣어서 해석을 한다.

  7. JavaScript interpreter는 파싱한 결과를 렌더링 엔진에게 전달해서 파싱한 HTML의 결과인 DOM Tree를 조작한다.

  8. 최종적으로 완성된 DOM node(DOM Tree 구성요소)를 Render Object(Render Tree 구성요소)로 바꾸게 되는데,
    해당 Render Tree가 UI backend에게 전달 된다.

  9. 그러면 UI backend로 인해 화면을 그리게 된다.

렌더링 엔진 동작

렌더링 엔진은 URI를 통해 요청을 받아서 해당 데이터를 렌더링하는 역할을 한다.
-> Chrome, IOS는 webkit 이라는 rendering engine 사용.

대략적인 렌더링 엔진 동작 과정

1) DOM tree 구축을 위한 HTML 파싱
: CSS, Javascript도 파싱을 하게 된다.
HTML문서를 파싱한 후 , content tree 내부에서 tag(a, div...)를 DOM node로 변환한다.
그 다음 CSS파일과 함께 모든 스타일 요소를 파싱해서 스타일 규칙을 만든다.
이 스타일 규칙이 적용된 DOM node가 최종적으로 render tree를 생성한다. (=>render tree의 구성 요소인 render object로 바뀐다)

2) render Tree 구축
: HTML, CSS 를 파싱해서 만들어진 DOM tree가 render tree로 바뀌게 된다.
render tree는 색상, 면적과 같은 시각적 속성을 갖는 사각형을 포함한다.
이러한 사각형 정보를 포함하고 있기 때문에 render tree가 그려질 수 있게 된다.

3) render Tree 배치
: render tree가 생성이 끝나면, 배치정보를 가지고 있기 때문에 순서대로 배치가 된다.
각 node가 정확한 위치에 표시되기 위해 이동한다.

4) render Tree 그리기
: 각 node 배치를 완료하면, UI Backend에서 각 노드들을 돌아다니면서 paint작업하여 그리기를 완료한다.

여기서 1)번과 2),3),4)번은 병렬적으로 진행된다!

DOM tree는 DOM node라는 자식요소로 구성되어 있고, render tree도 render object라는 자식요소로 구성되어 있다.
근데, DOM tree가 다 구축된 다음 render tree로 전환되는 것이 아닌, DOM tree의 구성요소인 하나의 DOM node, 각각의 DOM node들이 새로 생성되자 마자 바로 render object로 전환이 된다.
이렇게 바로바로 변환되는 과정에서 render tree가 바로바로 생성이 된다.

이런 과정의 어떤 부분이 사용자에게 장점으로 다가갈 수 있을까?
=> DOM tree가 완벽히 완성될 때 까지 화면에 아무것도 안보이는 것이 아니라, DOM tree가 완성되는 중간 중간 바로 render tree가 구축되기 때문에 이동안 바로 바로 render tree가 배치되고 그려지게 된다.
따라서! 사용자는 DOM tree가 완벽하게 구축될때까지 기다릴 필요 없이 render tree가 그려짐으로 인해 브라우저가 조금씩 그려지는 것을 확인할 수 있다.

(옛날에 인터넷이 느렸을 때 화면이 위에서 부터아래로 브라우저의 화면이 조금씩 뜨기 시작한 이유!)

webkit의 동작 과정

  1. HTML을 parsing하여 DOM tree를 생성한다.
    : HTML은 Hypertext Markup Language.
    즉, Markup Language는 정보를 표시해주는 언어이다.
    HTML tag를 파싱하게 되면 DOM tree를 생성하게 된다.

    DOM tree는 DOM node들을 tree형태로 표현한건데, tree형태로 표현한 이유는 무엇일까?
    => HTML은 Markup Language여서 정보를 표시하는 역할만 할 수 있다. 따라서 정보가 동적으로 움직이는(프로그래밍 언어로써의 동작)은 불가능하다.
    이 부분을 가능하게 해주기 위해 DOM tree로 만드는 것이다.
    HTML을 DOM tree로 바꾸게 되면 Javascript라는 프로그래밍 언어를 활용해서 HTML tag를 조작하고 움직이게 할 수 있게 된다. (=동적 표현 가능)
    이렇게 되면 사용자와 상호 작용이 가능해진다. (e.g. 버튼 클릭)
<html>
  <body>
    <p>Gyomni Velog</p>
    <div>
      <img src='#'/>
    </div>
    <script></script>
  </body>
</html>

위의 코드처럼 브라우저의 tag들은 parsing을 하면 실행과 동시에 아래의 이미지와 같이 DOM tree로 생성이 된다.

  • HTMLParagraphElement = <p>
  • Text = Gyomni Velog
  • HTMLDivElement = <div>
  • HTMLImageElement = <img>

    브라우저가 렌더링 엔진이 HTML tag들을 DOM tree로 바꿀 때는 tag를 parsing함과 동시에 실행하는 과정을 거친다.
    => <html> parsing하고 실행,
    <body> parsing하고 실행,
    <p>Gyomni Velog</p> parsing하고 실행,
    ....
    이렇게 쭉 실행하다가,
    <script> ( javascript를 가져와서 실행하는 tag )를 만나면 <script>를 parsing하고 실행할 때 까지 다음 tag를 parsing할 수가 없다. (실행 완료 후 다음 tag를 parsing하기 때문)
    이렇기 때문에 <script><body>의 가장 마지막에 위치하는 것이 좋다.

    여기서 HTML5에 추가된 사항이 있는데,
    HTML5에서는 <script>를 비동기로 처리하는 속성인 defer, async가 추가되었다.

  1. CSS를 parsing하여 스타일 규칙을 얻는다.
    CSSHTML과 마찬가지로 onbject model을 가지고 있는데, 그것이 바로CSSOM(style rules, 스타일 규칙)이다.
    Style Sheets를 CSS Parser가 parsing을 하면 Style Rules을 얻게 되는데, Style Rules같은 경우는 어떤 tag(DOM tree에서 어떤 DOM node)에 해당하는 Style Rule인지 DOM node에 대한 정보를 가지고 있다. 따라서 DOM tree안의 구성요소인 DOM node와 Style Rules가 딱 만나게(Attachment) 되면 그릴 준비가 완료된 것이다. 즉, render tree가 만들어지게 되는 것이다.

  2. DOM tree를 생성하는 동시에, 이미 생성된 DOM tree와 style Ryles(CSSOM)을 Attachment한다.
    1.에서 설명하고 있는 "HTML tag들이 DOM tree로 바뀌는 과정"에서 생성된 DOM node는 object이다.
    그리고 object는 attach라는 method를 가진다.
    attach가 실행되면 결과물로 DOM node에서 render object로 변환된다.

    render object는 render tree의 구성요소로써 자신(tag)와 자식 요소를 어떻게 배치하고 그려야 할지 다 안다. 그리고! 모든 DOM node가 전부 render object로 생성되는 것은 아니다
    => HTML tag들은 여러 종류가 있다.
    <Head>를 예를 들면, 브라우저에 그려지는 tag가 아니다. 그래서 render object로 바뀌지 않는다.

    <html><body> DOM node는 render object로 변환이 되는데, 이것들은 특별한 이름을 가진다.
    => render tree root로써 render view라고 부름.

    나머지 DOM node들을 render object로 변환되어 render view에 차근차근 추가되는 형식으로 render tree가 구축된다.

  3. 구축한 render tree를 배치(layout)한다.
    배치는 <html> 요소에 해당하는 최상위 render object (render view)에서 시작한다.
    화면의 왼쪽 위부터 차근차근 DOM node(CSS가 입혀진 HTML tag)들을 그려가기 시작한다.

  4. 배치가 끝난 render tree를 그린다.
    render object에 있는 paint method를 호출함으로 인해 render tree가 그려지게 된다.
    => 이 역할을 UI backed가 담당


*참고 - Gecko 엔진의 reflow 과정을 시각화한 영상


프론트엔드 엔지니어는 결국 브라우저를 통해서 렌더링하는 역할을 프로그래밍 하는 것!

그러니 이 과정을 완전히 내것으로 만들자!!
그 다음은 최적화!! 성능 개선 !!

참고 자료

profile
Front-end developer 👩‍💻✍

0개의 댓글