IT/Front-End

브라우저의 역할과 스크립트의 로드 시점

라임웨일 2022. 6. 15. 16:06
반응형

브라우저는 어떻게 동작하는가? 이전에도 해당 주제로 글을 쓴적이 있어서 오늘은 간단하게 알아보고 스크립트의 로드 시점에 대해 알아보려 합니다

 

사용자가 연결된 주소의 서버에 데이터 요청을 하게 되면 서버로부터 데이터를 다운로드 받은 것을 가지고 웹브라우저가 그것을 해석해서 사용자가 보는 UI 를 완성해 주게 됩니다.

 

 

 

 

📖 웹 브라우저 동작과정 간단히 알아보기

브라우저의 주요 기능은 사용자가 선택한 자원을 서버에 요청하고 브라우저에 표시하는 것입니다. 

 

💡 브라우저의 기본 구조

브라우저의 주요 구성 요소는 다음과 같습니다.

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

 

 

 

 렌더링 엔진

렌더링 엔진의 역할은 요청 받은 내용을 브라우저 화면에 표시하는 일을 수행합니다.

렌더링 엔진은 HTML 및 XML 문서와 이미지를 표시할 수 있고 플러그인이나 브라우저 확장 기능을 이용해 PDF와 같은 다른 유형도 표시할 수 있습니다. 대표적 렌더링 엔진으로 파이어폭스와 웹킷 엔진이 있는데 파이어폭스는 모질라에서 직접 만든 게코(Gecko) 엔진을 사용하고 사파리와 크롬은 웹킷(Webkit) 엔진을 사용합니다.

웹킷은 최초 리눅스 플랫폼에서 동작하기 위해 제작된 오픈소스 엔진인데 애플이 맥과 윈도우즈에서 사파리 브라우저를 지원하기 위해 수정을 가했습니다. 더 자세한 내용은 webkit.org를 참조하기 바랍니다.

 

파싱(Parse or Parsing)이란?

문서를 파싱한다는 것은 브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것을 의미합니다.  파싱 결과는 보통 문서 구조를 나타내는 노드 트리인데 파싱 트리(parse tree) 또는 문법 트리(syntax tree)라고 부릅니다.

 

👉 브라우저가 문서(HTML)를 해석하면서 하는일

  1. 불러오기(Loading) - 불러오기는 HTTP 모듈 또는 파일시스템으로 전달 받은 리소스 스트림(resource stream)을 읽는 과정으로 로더(Loader)가 이 역할을 맡으며 로더는 단순히 읽는 것이 아니라, 이미 데이터를 읽었는지도 확인하고, 팝업창을 열지 말지, 또는 파일을 다운로드 받을 지를 결정
  2. 파싱(Parsing) - 파싱은 DOM(Document Object Model) 트리를 만드는 과정으로 일반적으로 HTML, XML 파서를 각각 가지고 있으며 HTML 파서는 말 그대로 HTML 문서를 해석하는데 사용되고, XML 파서는 XML 형식을 따르는 SVG, MathML 등을 처리하는데 사용함.
  3. 랜더링 트리 만들기 - 파싱으로 생성된 DOM 트리는 HTML/XML 문서의 내용을 트리 형태로 자료 구조화 한 것을 말하는데 DOM 트리는 내용 자체를 저장하고 있고, 화면에 표시하기 위한 위치와 크기 정보, 그리는 순서 등을 저장하기 위한 별도의 트리 구조가 필요한며 이를 일반적으로 렌더링 트리라고 부름.
  4. CSS 스타일 결정 - CSS 는 HTML 문서 내용과 별도로 표현
  5. 레이아웃(Layout) - 렌더링 트리가 생성될 때, 각 렌더(Render) 객체가 위치와 크기를 갖게 되는 과정
  6. 그리기(Painting) - 그리기 단계는 렌더링 트리를 탐색하면서 특정 메모리 공간에 RGB 값을 채우는 과정

 

 스크립트와 스타일 시트의 진행 순서

다음은 일반적으로 많이 사용되는 코드입니다.

- 일반적인 문서 해석
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" type="text/css" href="default.css">
    <link rel="stylesheet" type="text/css" href="pages.css">
    <script src="jquery.js"></script>
    <script src="user.ui.js"></script>
</head>
<body>
    <img src="/images/sample.png" alt="">

    <ul>
        <li>HTML을 파싱</li>
        <li>외부 CSS파일 및 외부 자바스크립트 파일을 로드</li>
        <li>자바스크립트가 전달된 시점에 실행</li>
        <li>DOM 트리의 구축 완료</li>
        <li>이미지 파일 및 플래시 등의 외부 리소스를 로드</li>
        <li>모두 완료</li>
    </ul>
</body>
</html>

 

일반적으로 브라우저가 위와 같은 문서를 만나게 되면 HTML을 파싱하고 외부 자원인 CSS, JS 파일을 로드하게 됩니다.

자바스크립트는 전달된 시점에 실행하게 되고 DOM 트리의 구축을 완료한 이후에 이미지 파일 및 플래시 등의 외부 리소스를 로드하면서 모든 작업이 완료됩니다. 여기서 중요한 점이 "스크립트를 만나게 되면 어떻게 되는 것인가" 입니다.

JS인 <script> 태그를 만나면 스크립트가 해석 및 실행되는 동안 문서의 파싱은 중단되게 됩니다. 스크립트가 외부에 있는 경우 우선 네트워크로부터 자원을 가져와야 하는데 이 또한 실시간으로 처리되고 자원을 받을 때까지 파싱은 중단됩니다.

이 모델은 수 년간 지속됐고 HTML4와 HTML5의 명세에도 정의되어 있습니다. 한편 스타일 시트는 이론적으로 DOM 트리를 변경하지 않기 때문에 문서 파싱을 기다리거나 중단하지 않습니다.

 

위의 코드에서는 스타일을 먼저 불러오고 다음에 스크립트를 로드하지만 만약에 스크립트르 파일을 먼저 로드하게 되는 경우 즉, 스크립트가 문서를 파싱하는 동안 스타일 정보를 요청하는 경우라면 문제가 됩니다.

이 경우에 스크립트가 문서를 파싱하는 동안 브라우저는 다른 작업을 수행하지 않기 때문에 스타일이 파싱되지 않은 상태가 되고 이렇게 되었을 때 화면 레이아웃이 제대로 구성되지 않은 상태로 사용자에게 뷰를 제공하게 될 확률이 높기 때문에 사용자 경험(UX)을 떨어뜨리는 결과를 초래하게 될 것입니다. 이런 문제는 흔치 않은 것처럼 보이지만 매우 빈번하게 발생합니다.

이러한 문제를 야기시키지 않고 사용자 경험을 떨어뜨리지 않기 위해 다음과 같이 스크립트 소스를 body 태그 끝에 두는 것을 권장하고 있습니다.

 
- 권장되는 script 위치
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" type="text/css" href="default.css">
    <link rel="stylesheet" type="text/css" href="pages.css">
</head>
<body>
    <img src="/images/sample.png" alt="">

    <ul>
        <li>HTML을 파싱</li>
        <li>외부 CSS파일 및 외부 자바스크립트 파일을 로드</li>
        <li>자바스크립트가 전달된 시점에 실행</li>
        <li>DOM 트리의 구축 완료</li>
        <li>이미지 파일 및 플래시 등의 외부 리소스를 로드</li>
        <li>모두 완료</li>
    </ul>
    <script src="jquery.js"></script>
    <script src="user.ui.js"></script>
</body>
</html>

 

위와 같이 스크립트 소스를 하단에 두게 되면 HTML 문서를 화면에 표시하는 속도가 빨라지게 되고 사용자가 뷰를 보는데 필요한 왠만한 문서를 해석한 상태이기 때문에 사용자의 불편을 초래하지 않을 수 있습니다.

 

스크립트의 로드 시점 - async, defer

지금까지 알아본 바에 의하면 스크립트를 문서의 마지막(</body>) 이전에 삽입하는 방식이 오래된 브라우저에서도 동일한 효과를 얻을 수 있으면서 사용자 경험 개선은 물론이고 이벤트를 이용한 프로그래밍을 처리할 필요도 줄어들게 합니다.

다만, 문서의 <head> 영역에 스크립트가 삽입되거나 외부의 파일에 정의되어 있다면 이벤트 연결은 문서의 로드시점에 맞게 처리해야 한다는 것입니다.

이렇게 head 에 삽입하는 경우에 모던 브라우저에서는 defer, async 속성을 사용할 수 있는데 이에 대해 알아보고자 합니다.

 

 

크롬 개발자 도구를 열고 네트워크 패널을 보게 되면 웹 패이지를 구성하는데 필요한 자원들을 서버에서 다운로드 받은 결과를 시각적으로 확인할 수 있는데 이 네트워크 패널의 하단에서 모든 자원을 다운받는데 걸린 총 시간과 서버에 요청한 갯수, 전송된 파일 크기 등을 확인하실 수 있습니다.

이 네트워크 패널에서 DOMContentLoadedLoad 를 확인할 수 있는데, 먼저 DOMContentLoaded는 DOM 트리를 완성되는 시점을 말합니다. 쉽게 말해, images 와 같은 외부 자원(iframe, image)을 제외(ex. embedded type)한 HTML Element 를 해석, 구성해 주는 것을 말합니다.

그리고 Load 는 문서의 모든 콘텐츠(images, script, css, etc)가 로드된 상태를 말합니다.

그렇기 때문에 대개 DOMContentLoaded 가 끝나는 시점과 Load 시점 사이 또는 Load 이후에도 image 파일들을 로드하게 됩니다.

 

- 스크립트의 일반적인 실행

asyncdefer의 동작에 대해 알아보기 전에 기본적인 <script>의 실행 과정에 대해 알아봅니다.

기본적으로 <script>는 인라인 코드의 경우 즉시 해석되고 실행될 수 있지만 위에서 언급했듯이 그렇지 않은 경우는 해당 파일을 가져올 때까지 HTML 문서의 구문 분석을 중단합니다.

 

 

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>Title</title>    
</head>
<body>    
  1. HTML 구문을 작성합니다.
    
  <!-- 2. 자바스크립트 구문을 실행합니다. -->
  <script src="jquery.js"></script>
    
  3. HTML 나머지 구문을 작성합니다.    
</body>
</html>

 

위 코드의 실행 순서는 1 -> 2 -> 3번의 순서로 실행됩니다. 이 때 2번 자바스크립트 파일의 용량이 커서 화면을 로드하는데 시간이 오래 걸리게 된다면 HTML 구문 분석이 일시 중지되므로 HTML이 화면에 출력되는 시간이 길어집니다. 이런 이유로 async,defer 속성을 사용합니다.

 

async,defer 속성 정의 및 차이점

async속성은 HTML렌더링을 멈추지 않고 동시에 js 파일을 다운로드 하고 다운로드가 끝난 후에 자바스크립트를 실행합니다.

defer속성은 HTML렌더링을 멈추지 않고 동시에 js 파일을 다운로드 하고 HTML렌더링이 끝난 후에 자바스크립트를 실행합니다.

<script async src="jquery.js"></script>
<script defer src="user.ui.js"></script>

 

- async와 defer를 지원하는 브라우저

async 및 defer 속성은 최신 브라우저를 기준으로 보자면 매우 광범위하게 지원되지만 이러한 속성의 동작은 JavaScript 엔진마다 약간 다를 수 있습니다.

지원하는 브라우저를 알아보기 전에 일부만 지원되는 경우에 대한 동작 과정을 알아봅니다. defer 속성만 있다면 스크립트는 페이지의 파싱이 완료된 후에 실행됩니다. 단, async와 defer 속성이 모두 지정된 경우 async 속성을 지원하는 모던 브라우저는 기본적으로 async 속성을 따릅니다.

하지만 async 속성을 지원하지 않는 구형 브라우저는 defer 속성의 지원 여부에 따라 결과가 다릅니다. defer 속성을 지원하는 경우 defer 속성에 의해 비동기적으로 스크립트를 실행하고 defer 조차도 지원하지 않는 구형 브라우저는 동기적으로 스크립트를 실행합니다.

 

 

IE의 경우가 예외적이긴 하지만 여전히 국내 환경이 뒷받침되고 있지 않지만 IE 10 미만의 점유율이 극히 낮아진 이 시점에서 이제는 고려하지 않아도 될 만한 수준이 되어야 된다고 봅니다.
만약에 굳이 지원해야 한다면 aync, defer 속성을 사용하기 보다 </body> 바로 앞에 스크립트를 선언하는 방법이 가장 권장됩니다.

 

브라우저의 동작에 대해 좀 더 심도있게 알고 싶다면?: https://whales.tistory.com/67

 

브라우저는 어떻게 동작하는가?

이 글은 이스라엘 개발자 탈리 가르시엘(Tali Garsiel)이 html5rocks.com에 게시한 "How Browsers Work: Behind the scenes of modern web browsers"를 번역한 글입니다. 탈리 가르시엘은 몇 년간..

whales.tistory.com

 

출처: https://webclub.tistory.com/630 

반응형
광고차단(애드블록)프로그램이나 브라우저를 사용중이시면 프로그램을 비활성화이나 블로그 주소를 예외 처리 해주시면 광고가 노출됩니다. 귀찮고 번거롭겠지만 광고 클릭은 저에게는 큰 힘이 됩니다. 🙇🏻‍♂️🙇🏻‍♀️
제 블로그의 모든 글은 제가 직접 작성 하고 다른 글을 참고할 때는 이전 글보다 읽기 편하게 수정해서 작성하고 있습니다. 커피 한잔 사먹고 더 열심히 좋은글로 보답하겠습니다.
오늘도 제 블로그에 와 주셔서 감사합니다. :)
//