Welcome :っ)

Devlog/JavaScript

[JavaScript] script의 속성 async와 defer

lazy.won 2022. 7. 12. 12:26
728x90
반응형

 

 

 

 

 

 

 

들어가며..

HTML에 <script>태그를 이용하여 자바스크립트 파일을 로드하는데, 이때, script 태그의 위치와 속성에 따라 로드되는 방식에 차이가 있다. 

상황에 맞게 script 태그를 우리가 원하는대로 동작하게 하려면 이 차이를 알아야 한다. 

 

 

 

 

 

일반적인 script 태그

렌더링 엔진과 자바스크립트 엔진은 병렬적으로 파싱을 실행하지 않고 직렬적으로 파싱을 수행한다.

이처럼 브라우저는 동기적으로 위에서 아래 방향으로 순차적으로 HTML, CSS, 자바스크립트를 파싱하고 실행한다.

 

이것은 script 태그의 위치에 따라 HTML 파싱이 블로킹되어 DOM 생성에 영향을 미칠 수 있다는 것을 의미한다. 

그래서 script 태그의 위치는 중요하다 !

 

 

 

<head> 안에 script 위치시키기

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <script src="main.js"></script>
  </head>
  <body>
    <div id="hello">hello</div>
  </body>
</html>

html 파일을 다운 받았을 때, 브라우저가 한줄 한줄 분석(파싱) 한다.

파싱한 것을 css와 병합하여 DOM 요소로 변환하게 되는데, html을 쭉 파싱하다가 렌더링 엔진이 main.js 파일을 로드하는 script 태그를 만나면 html 파싱을 멈추고, main.js 파일을 서버에서 다운 받아 실행하고, 그 다음에 다시 html 파싱을 진행한다. 

 

이 방법의 단점은 아래와 같다. 

  1. 만약 자바스크립트 파일의 사이즈가 엄청 크고 느리면 사용자가 웹 사이트를 보는데까지 많은 시간이 소요된다.
  2. 자바스크립트 코드(main.js)에서 DOM이나 CSSOM을 변경하는 DOM API를 사용하는 경우, 렌더링 엔진이 해당 HTML 요소를 파싱하지 않아 DOM이나 CSSOM이 아직 생성되어 있지 않기 때문에 에러가 발생한다.

그래서 <head> 안에 script를 포함하는 것은 좋은 방법이 아니다. 

 

 

 

 

<body> 가장 아래에 script 위치시키기

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
  </head>
  <body>
    <div id="hello">hello</div>
    <script src="main.js"></script>
  </body>
</html>

그 다음으로 많이 사용하는 방법으로, body 안에 마지막 줄에 script를 추가하는 방법이다. 

 

이 방식을 이용하면 브라우저가 html 파일을 다운받아 파싱해서 페이지가 준비된 다음, 서버에서 자바스크립트 파일을 다운 받아 실행하게 된다. 

 

즉, 렌더링 엔진이 script 태그를 파싱할 때는 이미 렌더링 엔진이 HTML 요소를 모두 파싱하여 DOM 생성이 완료된 이후가 된다. 

따라서 DOM 이 완성되지 않은 상태에서 DOM을 조작하는 에러가 발생할 우려가 없다.

 

또한 자바스크립트 파일이 실행되기 전에 DOM 생성이 완료되어 렌더링 되므로, 페이지 로딩 시간이 단축된다는 이점이 있다. 

 

하지만, 이런 경우에도 단점은 있다. 사용자가 기본적인 html 파일을 빨리 볼 수 있다는 장점은 있지만, 자바스크립트를 이용해서 서버에서 데이터를 받아온다던지, DOM 요소를 조작한다던지의 일을 해야한다면, 사용자가 정상적인 페이지를 받아오는데까지는, 서버에서 자바스크립트 파일을 받아오는 것까지 기다려야 한다.

 

 

 

 

 

 

 

 

script 태그의 asycn, defer 

앞서 살펴본 자바스크립트 파싱에 의한 DOM 생성이 중단되는 문제를 해결하기 위해 HTML5부터 script 태그에 async와 defer 속성이 추가되었다. 

 

async와 defer 속성을 사용하면 HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 실행된다. 하지만 이 둘은 자바스크립트 실행 시점에 차이가 있다. 

 

 

 

async

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <script async src="mainA.js"></script>
    <script async src="mainB.js"></script>
    <script async src="mainC.js"></script>
  </head>
  <body>
    <div id="hello">hello</div>
  </body>
</html>

<head> 태그 안에 async 속성이 포함된 script를 위치시킨다. 

브라우저가 html 파일을 다운 받아 파싱하다가, async를 보고 병렬로 자바스크립트 파일을 다운로드 받는다. 다운이 완료되면 파싱을 멈추고 자바스크립트 파일을 실행한다. 이후에 다시 파싱을 진행하여 페이지를 보여주게 된다. 

 

이처럼 HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행된다. 하지만 자바스크립트 파싱과 실행은 자바스크립트 파일 로드가 완료된 직후에 진행되며 이때 HTML 파싱이 중단된다. 

 

여러 개의 script 태그에 async 속성을 지정하면 script 태그 순서와 상관없이 로드 완료된 자바스크립트부터 먼저 실행되어, 순서가 보장되지 않는다. 따라서 body 태그 끝에 script를 두는 것 보다는 자바스크립트 파일 다운이 병렬적으로 일어나기 때문에 다운로드 받는 시간을 절약할 수는 있지만, 자바스크립트 실행이 HTML 파싱 완료 전에 실행되기 때문에, 자바스크립트 파일이 html에 의존적이라면 정상적으로 작동하지 않을 수 있다. 

 

따라서 순서 보장이 필요한 script 태그에는 async 속성을 지정하지 않아야 한다. 

(async 속성은 IE10 이상에서 지원됨)

 

 

 

 

defer

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <script defer src="mainA.js"></script>
    <script defer src="mainB.js"></script>
    <script defer src="mainC.js"></script>
  </head>
  <body>
    <div id="hello">hello</div>
  </body>
</html>

async 속성과 마찬가지로 HTML 파싱과 외부 자바스크립트 파일 로드가 비동기적으로 진행된다. 

 

html 파일을 받아 파싱하다가 defer를 보고 자바스크립트 파일을 병렬적으로 다운 받는다. 그리고 파싱을 다시 진행 후 완료된 이후에 먼저 페이지를 준비한다. 그 다음 다운 받은 자바스크립트 파일을 실행한다. 

 

즉, 자바스크립트의 파싱과 실행은 HTML 파싱이 완료된 직후, 즉, DOM 생성 완료 직후 (DOMContentLoaded 이벤트가 발생) 진행된다. 

 

 

따라서 DOM 생성이 완료된 이후, 실행되어야 할 자바스크립트에 유용하다.

(defer 속성은 IE10 이상에서 지원됨)

 

 

 

 

 

📝 정리

  • 정의된 스크립트 순서와 상관 없이 다운로드가 먼저 끝난 것들 순서로 실행하기 때문에 자바스크립트가 순서에 의존적이라면 async를 이용한다면 문제가 될 수 있다.
  • 반대로 defer는 html 파싱하는 동안 병렬적으로 자바스크립트를 다 다운 받아놓은 다음에 정의된 순서대로 실행하기 때문에 원하는대로 실행될것을 예측할 수 있다.
  • 따라서 defer를 사용하는 것을 가장 추천한다. 

 

 

 

 

 

 

 

 

참고

드림코딩 유튜브 (자바스크립트 강의)

https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html

https://velog.io/@yongjin9660/js-defer-async-%EC%B0%A8%EC%9D%B4

 

[JavaScript] JS 파일 로드 방식 비교 (async vs defer)

html src 로드 방식 비교

velog.io

 

320x100
반응형