자바스크립트가 가장 많이 사용되는 분야는 웹 브라우저 환경에서 동작하는 웹 페이지, 애플리케이션의 클라이언트 사이드이다.

웹/앱의 클라이언트 사이드 자바스크립트는 브라우저에서 HTML, CSS와 함께 실행된다. 따라서 브라우저 환경을 고려함으로써 더 효율적인 자바스크립트 프로그래밍이 가능하다.

브라우저는 HTML, CSS, Javascript로 작성된 문서를 파싱(parsing: 구문해석)하여 브라우저에 렌더링(rendering: 시각적으로 출력)한다. 렌더링 과정은 다음과 같다.

브라우저의 렌더링 과정

  1. 브라우저에서 HTML, CSS, JS, 이미지, 폰트 파일 등 렌더링에 필요한 리소스 요청 및 서버로부터 응답 받음

  2. 브라우저의 렌더링 엔진은 서버로부터 응답받은 HTML과 CSS를 파싱하여 DOM, CSSOM을 생성하고 이들을 결합하여 렌더 트리를 생성

  3. 브라우저의 자바스크립트 엔진(ex. V8, 스파이더몽키)은 서버로부터 응답된 자바스크립트를 파싱하여 AST(Abastract Syntax Tree)를 생성하고 바이트코드로 변환하여 실행(이때 자바스크립트는 DOM API를 통해 DOM이나 CSSOM을 변경할 수 있음. 변경된 DOM, CSSOM은 다시 렌더 트리로 결합됨)

  4. 렌더 트리를 기반으로 HTML 요소의 레이아웃을 계산하고, 브라우저 화면에 HTML 요소를 페인팅함

핵심을 요약하자면,

클라이언트는 웹/앱을 실행하기 위해 서버에게 HTML, CSS, JS에 대한 정보를 요청하고, 서버가 응답한 리소스를 parsing하여 rendering하는 과정을 거친다.

이때, <script>를 HTML 파일 내 어디에 위치시키냐에 따라 렌더링 속도에 차이가 있다.

HTML 파일에서 script의 위치

브라우저는 동기적(synchronous)으로, 즉 위에서 아래로 순차적으로 HTML, CSS, Javascript를 parsing하고 실행한다.

이때, script의 위치에 따라 HTML 파싱이 블로킹되어 DOM 생성이 지연될 수 있다.

1. <head><script> 를 위치시키는 방식

브라우저가 파일을 읽을 때 html, css를 한줄씩 읽으면서 parsing한다. 그런데 html을 parsing하는 과정에서 head에 위치한 script를 만나면 parsing을 멈춘다.

그리고는 script를 다운(fetcing)받아서 실행(executing)까지 마친 뒤에, 이어서 html을 parsing 하게 된다.

이러한 방식은 자바스크립트의 크기가 커짐에 따라 읽는 시간이 오래 걸리게 되어 사용자의 페이지 로딩 시간을 지연시킨다.

2. <body><script>를 위치시키는 방식

1번 방식과 달리 html을 전부 parsing할 수 있다. 페이지가 로딩 된 후, script가 fetch & execute된다.

이 경우 사용자는 html을 신속히 받아올 수 있다. 다만, 만약 페이지가 자바스크립트에 의존적일수록, 사용자는 웹페이지의 전체적인 부분을 사용하기 위해 script가 실행될 때까지 기다려야 한다.

두 가지 방식을 알아보며 <head>보다 <body><script>를 위치시키는 것이 이로움을 알게 되었다.

자바스크립트 parsing으로 인해 DOM 생성이 중단(blocking)되는 문제를 해결하기 위해, HTML5부터는 async와 defer 어트리뷰트가 추가되었다.

async와 defer

async와 defer 어트리뷰트는 다음과 같이 src 어트리뷰트를 통해 외부 자바스크립트 파일을 로드하는 경우에만 사용할 수 있다.

다시 말해, src 어트리뷰트가 없는 인라인 자바스크립트에는 사용할 수 없다.

<script async src = "extern.js"></script>
<script defer src = "extern.js"></script>

async 어트리뷰트

  • HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행된다.
  • 단, 자바스크립트의 파싱과 실행은 자바스크립트 파일의 로드가 완료된 직후 진행되며, 이때 HTML 파싱은 중단된다.
  • 여러 개의 script 태그에 async 어트리뷰트를 지정하면, script 태그의 순서와 상관없이 먼저 로드가 완료된 자바스크립트부터 먼저 실행된다.
  • 따라서 순서 보장이 필요한 script 태그에는 async 어트리뷰트를 지정하지 않아야 한다.
  • IE10 이상에서 지원된다.

defer 어트리뷰트

  • async 어트리뷰트처럼 HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행된다.
  • 단, 자바스크립트의 파싱과 실행은 HTML 파싱이 완료된 직후, 즉 DOM 생성이 완료된 직후 진행된다.
  • 따라서 DOM 생성이 완료된 이후 실행되어야 할 자바스크립트에 유용하다.
  • IE10 이상에서 지원되며, IE6~9에서도 지원되긴 하지만 정상작동하지 않을 수도 있다.

본 글의 내용은 '모던 자바스크립트 Deep Dive'와 '드림코딩 by 엘리'의 자료를 바탕으로 정리하였습니다.

0개의 댓글