OTTER-LOG

[TIL] script의 async, defer

otter의 TIL기록장by otter2023년 2월 16일에 최종수정되었습니다.
잘못된 내용이 있으면 댓글을 달아주세요.

브라우저 렌더링 과정에서, HTML을 읽어오는 과정 중 script 태그를 만나게 되면 HTML을 파싱하는 과정을 멈추게 된다. 스크립트를 모두 다운받고 실행한 후에야 남은 페이지를 처리할 수 있다.

<p>...스크립트 앞 콘텐츠...</p> <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> <!-- 스크립트 다운로드 및 실행이 끝나기 전까지 아래 내용이 보이지 않습니다. --> <p>...스크립트 뒤 콘텐츠...</p>

위의 예제를 통해 보면, script 태그를 만나는 시점에 DOM 형성은 중단되고 script를 우선 다운로드 받는다. 만약 이 용량이 크게 되면 다운로드가 마무리되는 시점까지 스크립트 뒤에 존재하는 <p> 태그는 보여지지 않을 것이다.

일반적으로 이러한 부작용을 피하기 위해 script 태그를 body 태그의 최하단에 위치시킨다. 그런데 이 또한 완전한 해결책은 아니다. HTML 문서의 용량이 큰 경우를 생각해보면, 모든 HTML 문서를 다운로드 받고 DOM을 그린 다음에야 script 다운로드를 시작한다. 그러면 당연히 페이지는 느려질 것이다.

defer

스크립트에 defer속성을 주게 되면, 백그라운드에서 다운로드를 진행한다. 스크립트를 다운로드 받는 중에도 HTML 파싱은 멈추지 않는다. 그리고 defer 스크립트의 실행은 페이지 구성이 끝날때까지 지연된다. 즉 페이지 구성이 완료된 후에 스크립트가 실행된다.

<p>...스크립트 앞 콘텐츠...</p> <script> document.addEventListener("DOMContentLoaded", () => alert("`defer` 스크립트가 실행된 후, DOM이 준비되었습니다!"), ); // (2) // dom에 접근해야 하는 부분 </script> <script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1" ></script> <p>...스크립트 뒤 콘텐츠...</p>

위의 상황에서의 동작은 다음과 같다.

  • 우선 페이지의 요소들은 바로 출력된다.
  • defer로 지연한 스크립트는 DOM을 파싱하는 과정에서 같이 다운로드된다.
  • HTML 파싱이 마무리 되었다면 defer script가 실행된다.

async

async 스크립트는 페이지와 완전히 독립적으로 작동한다. defer와 마찬가지로 백그라운드에서 다운로드가 진행된다. 그러나, defer와 다른점이 존재한다.

  • HTML 파싱이 마무리 되기 이전 스크립트가 실행될 수 있다.
  • async 스크립트는 다른 스크립트를 기다리지 않는다.
  • async 스크립트는 다운로드가 완료되면 실행된다.

아래의 예제를 통해 확인해 보자.

<p>...스크립트 앞 콘텐츠...</p> <script> document.addEventListener("DOMContentLoaded", () => alert("DOM이 준비 되었습니다!"), ); </script> <script async src="https://javascript.info/article/script-async-defer/long.js" ></script> <script async src="https://javascript.info/article/script-async-defer/small.js" ></script> <p>...스크립트 뒤 콘텐츠...</p>
  • 페이지의 요소가 보이나, small과 long 중 어떤 스크립트가 먼저 실행될지 알 수 없다.
  • 페이지 요소가 보이기 전에 스크립트가 실행될 수 있다. 실제로 예제를 실행해봤을때 다음과 같은 결과를 얻었다.

  • long 스크립트가 실행되었으나 페이지 요소가 보이지 않는다.

정리하기

defer, async 모두 HTML 트리를 파싱할때에 백그라운드에서 병렬적으로 스크립트를 다운로드 한다. 다만 defer는 DOM의 상호작용이 가능한 시점에 실행됨이 보장된다. 반면, async는 다운로드가 완료되면 실행된다. 그리고 다른 스크립트와의 실행 순서가 보장되지 않는다.

Ref