자바스크립트 script async/defer 어트리뷰트

미음제

·

2022. 10. 21. 22:05

 

async/defer 어트리뷰트

 

브라우저 렌더링 동작에 대해 공부하다 async/defer 어트리뷰트를 처음 접하게 되었고, 생소한 부분이라 다시 따로 공부한 것을 정리했다.

 

 

 

브라우저 렌더링 과정 정리

브라우저 렌더링 자바스크립트가 가장 많이 사용되는 곳은 웹 페이지/애플리케이션 클라이언트 사이드이다. 웹 페이지/애플리케이션의 클라이언트 사이드 상의 자바스크립트는 브라우저에서

mieumje.tistory.com

 

 

async/defer 어트리뷰트의 등장 배경

 

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>Documents</title>
    <script>
      const $animals = document.querySelector(".animals");
      $animals.style.color = "blue";
    </script>
  </head>
  <body>
    <div class="animals">Cats</div>
  </body>
</html>

 

위와 같은 html이 있다고 가정해보자. 브라우저 렌더링 동작 방식에 의해 위에서부터 아래로 차례로 내려오며 DOM과 CSSOM을 그린다. head tag 내부에서 script tag를 만나면, DOM 생성 과정을 중단하고 자바스크립트 엔진이 브라우저 렌더링 엔진에게서 제어권을 넘겨받고 자바스크립트 파싱과 실행을 한다.

 

script 내부를 보면, 'animals'라는 class를 갖는 요소에 접근하고, 해당 요소의 style 프로퍼티의 color 속성을 'blue'로 변경하는 DOM API 조작이 있다. DOM을 생성하는 과정에서 head 부분에서 script tag를 만나 DOM 생성이 중단되었고, 아직 생성되지 않은 DOM(class='animals'인 div)을 조작하려 했기 때문에 자바스크립트 실행 과정에서 에러가 발생하게 된다.

 

DOM API 조작 에러

 

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>Documents</title>
  </head>
  <body>
    <div class="animals">Cats</div>
    <script>
      const $animals = document.querySelector(".animals");
      $animals.style.color = "blue";
    </script>
  </body>
</html>

 

이와 같은 에러를 방지하기 위해 DOM이 모두 생성되고 DOM을 조작하는 script tag를 body의 하단부에 선언함으로 이 문제를 해결할 수 있다. DOM이 완성되지 않은 시점에서 DOM을 조작하는 문제를 피할 수 있고, 자바스크립트가 실행되기 이전에 DOM 생성이 완료되어 렌더링 되기 때문에 페이지 로딩 시간도 단축할 수 있다.

 

그러나 근본적인 해결책은 아니다. 만약 HTML 파일이 말도 안 되게 크다고 생각해보면, script를 로드하고 실행하는 데까지 지연 시간이 길어지게 된다. 이 같은 문제를 해결하고자 HTML5부터 등장한 것이 async/defer 어트리뷰트이다.

 

async/defer 동작

 

우선 async/defer 어트리뷰트는 src 어트리뷰트로 외부 자바스크립트 파일을 로드하는 경우에만 사용할 수 있다. 앞선 예와 같이 in-line으로 script를 작성하는 경우에는 사용할 수 없다.

 

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>Documents</title>
  </head>
  <body>
    <div class="animals">Cats</div>
    <script async src="./aysnc.js"></script>
    <script defer src="./defer.js"></script>
  </body>
</html>

 

src 어트리뷰트로 script를 불러오는 경우 위와 같이 async/defer 어트리뷰트를 추가해 사용하면 된다. async/defer는 모두 HTML 파싱과 자바스크립트 로드가 비동기적으로 동작하게 해주는 키워드이다.

 

async

 

 

async는 HTML 파싱과 자바스크립트 로드를 비동기적으로 실행시켜 준다. 자바스크립트 파싱/실행 동작은 자바스크립트 파일이 모두 로드가 되면 실행한다(이때 HTML 파싱 동작은 중단).

 

<div>script 이전</div>
<script async src='index1.js'></script>
<script async src='index2.js'></script>
<div>script 이후</div>

 

async 어트리뷰트는 javascript 로드 과정을 비동기적으로 처리할 수 있게만 도와준다. 자바스크립트 파일이 로드가 완료되면 바로 해당 자바스크립트를 실행하고 이때 HTML파싱(DOM 생성)이 잠시 중단된다.

 

위에 작성한 것처럼 script 순서를 적었더라도 순서가 보장되지는 않는다.

 

<div>script 이전</div>
<script async src='index1.js'></script> <!--로드 시간 10초-->
<script async src='index2.js'></script> <!--로드 시간 1초-->
<div>script 이후</div>

 

가령 위처럼 'index2' 자바스크립트가 로드되는데 1초가 걸린다면, 해당 자바스크립트 파일이 먼저 로드가 되고 실행된다. 따라서 실행 순서가 의존적인 스크립트라면 순서가 보장되지 않는 async는 피하는 것이 좋다.

 

또한, 스크립트를 비동기적으로 부르기 때문에 DOM이 모두 생성된 후 발생하는 DOMContentLoaded 이벤트 실행을 보장할 수 없다.

 

<!-- Google Analytics는 일반적으로 다음과 같이 삽입합니다. -->
<script async src="https://google-analytics.com/analytics.js"></script>

 

DOM을 직접 조작하거나 의존성이 발생하지 않는 독립적인 서드파티에 접근할 때 사용하기 유용하다.

 

defer

 

 

defer 역시 async와 마찬가지로 HTML 파싱과 자바스크립트 로드를 비동기적으로 실행시켜준다. 차이점은 자바스크립트의 실행 시점이다. defer의 경우 HTML이 파싱이 완료되고 난 후에 자바스크립트가 실행된다.

 

<div>script 이전</div>
<script async src='index1.js'></script> <!--로드 시간 10초-->
<script async src='index2.js'></script> <!--로드 시간 1초-->
<div>script 이후</div>

 

async의 경우 실행 순서가 보장되지 않았지만, defer는 입력된 순서대로 실행 순서가 보장된다.

 

DOM 트리가 완성된 후 자바스크립트가 실행되기 때문에 DOM 조작이 가능하고, 순서를 보장하기 때문에 의존적인 스크립트를 사용해야 하는 경우 적합하다고 할 수 있다.

 

DOMContentLoaded 

 

DOMContentLoaded : DOM 트리가 완성된 직후(그러나 img와 같은 외부 소스가 로드되지 않은 시점) 발생

onload : 모든 소스(img, css, scripts, ...)가 로드된 직후 발생

 


 

이 내용은 '모던 자바스크립트 Deep Dive' 책을 보고 공부한 내용을 다시 정리한 것입니다.

 

참고

 

 

defer, async 스크립트

 

ko.javascript.info

 

 

스크립트의 실행 시점을 조절하는 Async와 Defer 속성 - 재그지그의 개발 블로그

로드한 스크립트의 실행 시점을 조절할 수 있게 만들어주는 Async와 Defer 속성에 대해 알아봅니다.

wormwlrm.github.io

 

 

script async와 defer의 차이

3월 28일부터 4월 3일 일주일간 자바스크립트 위주의 프로젝트를 만들며 드림코딩 엘리님의 자바스크립트 강의와 자바스크립트 파워북이라는 제목의 책으로 복습한 내용을 이번주말 (04.03 ~ 04.04)

velog.io

 

 

문서의 로드시점 - onload, DOMContentLoaded

https://webdir.tistory.com/515 [WEBDIR] onload 문서의 모든 콘텐츠(images, script, css, etc)가 로드된 후 발생하는 이벤트이다(load 이벤트라고들 한다). window.onload \= function() { //실행될 코드 } 문..

valuefactory.tistory.com

 

반응형

'Developer > TI' 카테고리의 다른 글

Babel, Webpack(바벨, 웹팩)  (0) 2022.11.22
프로미스, 콜백 함수  (0) 2022.11.08
이벤트 버블링/캡쳐링, 이벤트 위임  (1) 2022.10.30
브라우저 렌더링 과정 정리  (2) 2022.10.11
REST API, RESTful API 정리  (0) 2022.09.29