브라우저 렌더링 Flow

sunn·2022년 4월 28일
0
post-thumbnail

브라우저의 구성요소

먼저 브라우저의 구성요소를 알아보면,

브라우저 구성요소

  • 사용자 인터페이스(User Interface) - 주소표시줄, 이전/다음 버튼, 북마크 메뉴 등 요청한 페이지를 보여주는 창을 제외한 나머지 모든 부분
  • 브라우저 엔진(Browser Engine) - 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어
  • 렌더링 엔진(Rendering Engine) - 사용자 인터페이스에서 요청한 콘텐츠를 표시. 예를 들어 HTML을 요청하면 HTML을 파싱하여 화면에 표시
  • 통신(Networking) - HTTP Request와 같은 네트워크 호출에 사용. 플랫폼에 독립적인 인터페이스고 플랫폼 하부에서 실행
  • UI 백엔드(UI Backend) - 콤보박스와 창같은 기본 장치를 그림. 플랫폼에서 명시하지 않은 일반적인 인터페이스로서, OS의 UI체계를 사용
  • 자바스크립트 해석기(JavaScript Interpreter) - 자바스크립트 코드를 해석하고 실행
  • 자료 저장소(Data Persistence) - 쿠키를 저장하는 것과 같은 모든 종류의 자원을 하드디스크에 저장하는 공간. HTML5 명세에는 브라우저가 지원하는 '웹 데이터 베이스'가 정의되어 있다.

그래서 렌더링이 뭔데?

요청받은 내용을 브라우저 화면에 표시하는 일을 렌더링이라고 하며,
이를 진행하는 프로그램을 렌더링 엔진이라고 한다.

렌더링 엔진 기본 구동 과정

  • 서버로부터 받은 HTML 문서를 파싱하여 DOM 트리 구축
  • DOM 트리를 바탕으로 렌더 트리 구축 (DOM 트리가 구축되는 동안 브라우저는 렌더 트리를 구축함)
    DOM 트리를 화면에 보여지게 트리를 구축하는 것이 렌더트리!
    head 요소와 같은 비시각적인 DOM요소는 렌더트리에 추가되지 않음!
  • 구축된 렌더 트리를 바탕으로 각 요소를 화면에 배치
  • 우리가 보는 화면이 출력됨 (렌더트리 페인팅)

그렇다면, HTML 파싱이란?

파싱은 렌더링 엔진의 메일 쓰레드에서 진행되는데, script tag를 만나면 파싱을 일시중지하고 js 코드를 로딩하고 파싱해 실행하기를 기다린다.

따라서,

  1. HTML을 읽는 과정에 스크립트를 만나면 중단 시점이 생기고 그만큼 Display에 표시되는 것이 지연된다.
  2. DOM 트리가 생성되기전에 자바스크립트가 생성되지도 않은 DOM의 조작을 시도할 수 있다.

위와 같은 상황을 막기 위해 script 태그는 body 태그의 최하단에 두는 것이 script의 최적 위치라고 할 수 있다.


하지만 script 태그를 body 태그 최하단에 놓기 싫거나, 그럴 수 없는 상황에 있을 때 활용할 수 있는 다른 방법도 있습니다.

script의 로딩 순서 제어하기

1. script 태그의 속성으로 로딩 순서 제어하기

script 태그의 async와 defer 속성을 활용하면 스크립트 로딩 순서를 제어할 수 있습니다.

1-1. async

<script async src="script.js">

script 태그의 async 속성은 스크립트가 나머지 페이지와는 비동기적으로 실행됨을 나타내며, 브라우저가 페이지를 파싱하는 동안에도 스크립트가 사용가능해지면 곧바로 실행됨을 명시합니다.

위와 같이 asnyc 속성이 더해진 script 태그가 HTML 태그 사이에 있는 경우 아래 그림과 같이 작동합니다.

script async flow

1. HTML parsing 도중 script 태그를 만나도 Script fetch(script 로드)와 HTML parsing이 함께 이루어지다가
2. script 로드가 끝나면 Script excution(script가 실행되는 시점)에 HTML parsing이 잠시 중단되고 실행이 끝나면
3. HTML parsing이 재개됩니다.

1-2. defer

<script defer src="script.js">

script 태그의 defer 속성은 페이지가 모두 로드된 후에 해당 외부 스크립트가 실행됨을 명시합니다.

위와 같이 defer 속성이 더해진 script 태그가 HTML 태그 사이에 있는 경우 아래 그림과 같이 작동합니다.

script defer flow

HTML parsing 도중 script 태그를 만나도 script 로드의 시작부터 끝까지 html parsing이 중단되지 않으며, html parsing이 끝나고 난 뒤에 script가 실행됩니다.

2. script 내부에서 로딩 순서 제어하기

DOMContentLoaded 와 onload

DOMContentLoaded와 onload를 활용하면 javascript 자체에서 로딩 순서를 제어할 수도 있습니다.

DOMContentLoaded 내부의 코드는 DOM 생성이 끝난 후에 실행되고 onload 내부의 코드는 문서에 포함된 모든 콘텐츠(images, script, css, ...)가 전부 로드된 후에 실행됩니다.

따라서,

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>DOMContentLoaded</title>
</head>
<body>
    <script>
    	// window.onload가 가장 앞에 위치!
        window.onload = function(){
            console.log("afterwindowload");
            var target = document.querySelector("#test");
            console.log(target);
        }
		// DOMContentLoaded가 두번째에 위치!
        document.addEventListener("DOMContentLoaded", function() {
            console.log("afterdomload");
            var target = document.querySelector('#test');
            console.log(target);
        });
		// 일반 script 코드가 가장 끝에 위치
        console.log("바로시작")
        var target = document.querySelector('#test');
        console.log(target);
    </script>
    <div id="test">test</div>
</body>
</html>

위 코드의 console에 출력된 결과는

https://velog.velcdn.com/post-images%2Ftakeknowledge%2Fd3891230-240a-11ea-b1b8-23d7a1c01321%2Fimage.png

일반 script 코드 -> DOMContentLoaded 안의 코드 -> window.onload 안의 코드 순으로 출력됩니다.


주소창에 url을 입력하고 이동하면 어떤 일이 일어날까?

  1. 주소창에 URL을 치고 엔터 키를 누르면 서버를 찾아간다.
  2. DNS (실제 서버가 어디에 있는지 알고있는 서버)가 연결해줄 곳을 찾음
  3. 여기서 HTTPS라면 HTTPS 방식으로 통신함
  4. 서버의 기본설정인 index.html을 서버에서 클라이언트로 보냄
  5. 브라우저는 텍스트로 이루어진 index.html 파일을 파싱.
  6. 한 줄씩 읽어나가면서 DOM 트리를 만들고,
  7. 중간에 link 태그를 만나 CSS 요청이 발생하면 요청과 응답을 거치고 CSS 파싱함
  8. CSS 파싱이 끝나면 중단된 HTML을 다시 읽고 DOM 트리를 완성
  9. 완성된 DOM 트리와 CSS 객체 모델(CSSOM) 트리를 합쳐 Render Tree를 만들고 그림
  10. 중간에 HTML 파서는 스크립트 태그를 만나게 되면 자바스크립트 코드를 실행하기 위해 파싱을 중단
  11. 제어권한을 자바스크립트 엔진에 넘기고 자바스크립트 코드 또는 파일을 로드해서 파싱하고 실행

Source:
https://velog.io/@grinding_hannah/Web-브라우저-작동-원리

https://d2.naver.com/helloworld/59361

profile
:-)

0개의 댓글