문서와 리소스 로딩 (defer, async)

개요

모던 웹브라우저에서 돌아가는 스크립트들은 대부분 HTML보다 무겁다.

브라우저는 HTML을 읽다가 <script> 태그를 만나면 스크립트를 먼저 실행해야 한다.
src 속성이 있는 외부 스크립트 또한 마찬가지인데, 외부에서 스크립트를 다운받고 실행한 후에 남은 페이지를 처리하게 된다.


문제점

스크립트문을 먼저 실행시키면 몇가지 문제가 생기는데

  1. script문에서 자신보다 아래에 위치한 DOM 요소에 접근할 수 없다.
  2. script문이 로딩될 때까지 아래쪽 페이지를 볼 수 없다.

그래서 우리는 이러한 단점을 해결하기 위해 HTML 최하단에 script문을 위치시킨다.

<body>
  ... 스크립트 위 콘텐츠들 ...

  <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
</body>

하지만 이 방법은 완벽한 해결책이 아니다.
HTML 문서 자체가 아주 큰 경우를 가정해보자.
브라우저가 HTML 문서 전체를 다운로드 한 다음에 스크립트를 다운받게 하면 정말 느려질 것이다.

이런 상황에 <script>속성으로 defer 나 async를 사용하면 해결할 수 있게 된다.


defer

defer는 지연 스크립트라고도 불리며, 브라우저는 defer 속성이 있는 스크립트를 백그라운드에서 다운로드한다.
따라서 스크립트를 다운로드 하는 도중에도 HTML 파싱이 멈추지 않는다.
파싱이 끝나고 DOM이 준비된 후에 스크립트문을 실행시킨다.

<p>...스크립트 앞 콘텐츠...</p>

<script>
  document.addEventListener('DOMContentLoaded', () =>
    alert('`defer` 스크립트가 실행된 후, DOM이 준비되었습니다!')
  ); // (2)
</script>

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<p>...스크립트 뒤 콘텐츠...</p>

예시를 통해 살펴보자면, DOMContentLoaded는 지연 스크립트가 실행될 때 해당 이벤트를 실행시킨다.

script문은 html에 추가된 순서대로 실행된다. 크기가 작은 스크립트는 먼저 다운되지만 큰 스크립트보다 코드상 뒤에 있다면 실행순서는 후순위로 밀려난다.

또한 가장 중요한 사실은 defer 속성은 src 속성을 사용하는 외부 스크립트에만 유효하다!


async

async 속성이 붙은 스크립트는 비동기 스크립트 라고도 불리며 페이지와 완전히 독립적으로 동작한다.

defer와의 차이점

  • defer와 마찬가지로 백그라운드에서 다운되지만 HTML 파싱이 멈춘다.
  • DomContentLoaded 이벤트나 다른 스크립트가 async가 끝날때까지 기다리지 않는다.
  • 실행은 스크립트 다운로드가 끝난 순으로 진행된다.

동적 스크립트

let script = document.createElement('script');
script.src = '/article/script-async-defer/long.js';
document.body.append(script); // (*)

위와 같이 동적으로 스크립트를 추가해줄 수도 있다.
이렇게 추가한 경우 해당 스크립트는 'async' 스크립트 처럼 행동한다.

동적 스크립트를 정적으로 실행시키려면 아래처럼 해주면 된다.

function loadScript(src) {
  let script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.body.append(script);
}

// async=false이기 때문에 long.js가 먼저 실행됩니다.
loadScript('/article/script-async-defer/long.js');
loadScript('/article/script-async-defer/small.js');

Reference

https://ko.javascript.info/script-async-deferopen in new window

Last Updated: