티스토리 뷰

HTML을 다운로드하다가 스크립트 태그를 만나면 HTML은 파싱하는 과정을 중단하고 스크립트를 즉시 실행한다. 만약 외부 스크립트라면 그 스크립트 파일을 다운로드하는 동안 HTML 파싱이 중단된다.

여기에 2가지 문제점이 있다.

  • 스크립트는 스크립트 코드 밑에 있는 DOM element들을 보지 못하므로 핸들링하지 못한다.
  • 만약 스크립트가 아주 크고 이 태그가 페이지 상단에 위치한다면 로딩되는 동안 렌더링 블로킹이 일어나 유저들은 페이지 컨텐트을 보지 못한다.

여기에 대한 workaround로 스크립트 태그를 바디 태그 안의 최하단에 위치시키는 방법도 있다. 그러면 최소한 페이지 컨텐트가 보이고 나서 스크립트 로딩이 시작될 것이다. 다만 이는 미봉책일 뿐이고 HTML이 아주 긴 경우에는 그 만큼 스크립트 로드 시작점이 느려지므로 문제가 될 수 있다. 그래서 이러한 문제점들을 해결하기 위해 asyncdefer 이라는 attribute가 있다.

Async랑 Defer의 공통점

  • 렌더링을 블로킹하지 않는다. 브라우저는 이 키워드를 attribute으로 갖고 있는 스크립트 태그를 만나면 백그라운드에서 로드를 시작하고, 기다리지 않고 바로 다음 코드로 넘어간다.

Defer의 특징

  • DOM이 모두 ready된 다음에 실행된다.
  • 단, DOMContentLoaded 이벤트는 스크립트 실행을 기다린 다음 실행된다. (즉 순서를 나타내면 DOM ready -> defer 스크립트 실행 -> DOMContentLoaded 이벤트 실행)
  • 일반적인 스크립트와 같이 relative order를 지킨다. 즉 태그의 선언 순서를 지킨다. 아래와 같은 경우 long.js와 small.js 모두 평행적으로 로딩을 하기 시작하는데, small.js가 먼저 다운로드가 완료되더라도, 선언 순서를 지키기 때문에 long.js가 다 다운로드 및 실행될 때까지 기다린 후 실행한다.
  • <script defer src="https://javascript.info/article/script-async-defer/long.js"></script> <script defer src="https://javascript.info/article/script-async-defer/small.js"></script>

그러므로 앞에 라이브러리 스크립트가 있고, 뒤에 그 라이브러리 코드를 사용하는 스크립트가 있는 경우와 같이 순서 디펜던시가 있을 때 defer를 쓰면 좋을 듯하다.

그리고 참고로 defer는 external script에서만 동작한다. 즉 src attribute이 없는 스크립트 태그의 defer는 무시된다.

Async의 특징

  • Async는 아무 것도 기다리지 않고, 아무 것도 Async를 기다리지 않는다. (= 로드 및 실행이 완전히 독립적이다)
  • defer는 스크립트간의 relative order를 지키는 반면, async는 서로 완전히 독립적이어서 로드가 되고 나서 바로 실행된다. (load first, run first)
  • 그러므로 디펜던시가 없는 스크립트를 삽입할 때 쓰기 용이하다.
  • 예를 들어 위처럼 DOMContentLoaded 이벤트는 defer가 붙은 스크립트의 실행을 기다리는데, async가 붙은 스크립트의 실행은 기다리지 않는다.
  • async는 DOM의 준비도 기다리지 않는다.

또한 참고로 async는 non-module 스크립트에서는 defer와 마찬가지로 external script일 때만 동작하지만, module 타입의 스크립트에서는 external이 아닌 인라인으로 적었을 때에도 동작한다.

<!-- all dependencies are fetched (analytics.js), and the script runs -->
<!-- doesn't wait for the document or other <script> tags -->
<script async type="module">
  import {counter} from './analytics.js';

  counter.count();
</script>

Dynamic Scripts

async와 defer외에도 스크립트를 선언할 수 있는 또 다른 방법이 하나 있는데, 바로 동적인 스크립트 삽입이다.

let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script); // 스크립트는 도큐먼트에 추가되자마자 로딩을 시작한다. 

동적 스크립트 삽입은 디폴트로 async처럼 동작한다. 이 동작을 변경하려면 스크립트의 async 프로퍼티에 false를 할당하면 된다. async에 false를 할당하면 defer처럼 동작한다.

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

// long.js runs first because of async=false
loadScript("/article/script-async-defer/long.js");
loadScript("/article/script-async-defer/small.js");

Ref

https://javascript.info/script-async-defer

'공부일지(TIL) > Web' 카테고리의 다른 글

package.json의 resolutions  (0) 2021.10.14
Yarn workspaces  (0) 2021.07.28
Window vs Document  (0) 2021.07.20
쿠키  (0) 2021.03.18
NPM & Yarn Dependency model  (0) 2021.03.10
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함