[JAVASCRIPT] defer, async 스크립트

Yuri Lee·2023년 1월 4일
0

JAVASCRIPT

목록 보기
1/6

모던 웹브라우저에서 돌아가는 스크립트들은 대부분 HTML 보다 '무겁다.'
용량이 커서 다운로드 받는 데 오랜 시간이 걸리고 처리하는 것 역시 마찬가지이다.

브라우저는 HTML을 읽다가 <script>...</script> 태그를 만나면 스크립트를 먼저 실행해야 하므로 DOM 생성을 멈춘다. 이는 src 속성이 있는 외부 스크립트 <script src="...">...</script>를 만나도 마찬가지이다. 외부에서 스크립트를 다운받고 실행한 후에야 남은 페이지를 처리할 수 있다.

이런 브라우저의 동작 방식은 두 가지 중요한 이슈를 만든다.

  1. 스크립트에서는 스크립트 아래에 있는 DOM 요소에 접근할 수 없다. 따라서 DOM 요소에 핸들러를 추가하는 것과 같은 여러 행위가 불가능해진다.

  2. 페이지 위쪽에 용량이 큰 스크립트가 있는 경우 스크립트가 페이지를 '막아버린다.' 페이지에 접속하는 사용자들은 스크립트를 다운받고 실행할 때까지 스크립트 아래쪽 페이지를 볼 수 없게 된다.

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

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

<!-- 스크립트 다운로드 및 실행이 끝나기 전까지 아래 내용이 보이지 않습니다. -->
<p>...스크립트 뒤 콘텐츠...</p>

이런 부작용 들을 피할 수 있는 몇가지 방법이 있다.
아래 예시처럼 스크립트를 페이지 맨 아래 놓는 것이 하나의 방법이 될 수 있다. 이렇게 하면 스크립트 위에 있는 요소에 접근할 수 있다. 또한, 페이지 콘텐츠 출력을 막지 않게 된다.

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

네트워크 속도가 빠른 곳에서 페이지에 접속하고 있다면 이런 지연은 눈에 띄지 않지만, 아직도 네트워크 환경이 열악한 곳이 많다. 모바일 네트워크 접속도 마찬가지이다.

다행히도 이런 문제를 해결할 수 있는 <script> 속성이 defer와 async이다.

defer

브라우저는 defer 속성이 있는 스크립트(지연 스크립트)를 '백그라운드'에서 다운로드한다. 따라서 지연 스크립트를 다운로드하는 와중에도 HTML 파싱이 멈추지 않는다. 그리고 defer 스크립트 실행은 페이지 구성이 끝날 때까지 지연된다.

  • 지연 스크립트는 페이지 생성을 절대 막지 않는다.
  • 지연 스크립트는 DOM이 준비된 후에 실행되기는 하지만 DOMContentLoaded 이벤트 발생 전에 실행된다.
    java
<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>
  1. 페이지 콘텐츠는 바로 출력된다.
  2. DOMContentLoaded 이벤트는 지연 스크립트 실행을 기다린다. 따라서 얼럿창은 DOM 트리가 완성되고 지연 스크립트가 실행된 후에 뜬다.

지연 스크립트는 일반 스크립트와 마찬가지로 HTML에 추가된 순(상대순, 요소순)으로 실행된다.
따라서 길이가 긴 스크립트가 앞에, 길이가 짧은 스크립트가 뒤에 있어도 짧은 스크립트는 긴 스크립트가 실행될때까지 기다린다.

작은 스크립트는 먼저 다운되지만, 실행은 나중에 됩니다.

브라우저는 성능을 위해 페이지에 어떤 스크립트들이 있는지 쭉 살펴본 후에야 스크립트를 병렬적으로 다운로드합니다. 위 예시에서도 스크립트 다운로드가 병렬적으로 진행되었습니다. 그런데 이 때 크기가 작은 small.js이 long.js보다 먼저 다운로드 될 수 있습니다.

하지만 명세서에서 스크립트를 문서에 추가한 순서대로 실행하라고 정의했기 때문에 small.js는 long.js 다음에 실행됩니다.

defer 속성은 외부 스크립트에만 유효합니다.

<script>에 src가 없으면 defer 속성은 무시됩니다.

async

async 속성이 붙은 스크립트는 페이지와 완전히 독립적으로 작동한다.

  • async 스크립트는 defer 스크립트와 마찬가지로 백그라운드에서 다운로드된다. 따라서 HTML 페이지는 async 스크립트 다운이 완료되길 기다리지 않고 페이지 내 콘텐츠를 처리, 출력한다.

  • DOMContentLoaded 이벤트와 async 스크립트는 서로를 기다리지 않는다.

    • 페이지 구성이 끝난 후에 async 스크립트 다운로딩이 끝난 경우, DOMContentLoaded는 async 스크립트 실행 전에 발생할 수 있다.
    • async 스크립트가 짧아서 페이지 구성이 끝나기 전에 다운로드되거나,스크립트가 캐싱처리 된 경우, DOMContentLoaded는 async 스크립트 실행 후에 발생할 수도 있다.
  • 다른 스크립트들은 async 스크립트를 기다리지 않는다. async 스크립트 역시 다른 스크립트들을 기다리지 않는다.

요약

async와 defer 스크립트는 다운로드 시 페이지 렌더링을 막지 않는다는 공통점이 있다. 따라서 async와 defer를 적절히 사용하면 사용자가 오래 기다리지 않고 페이지 콘텐츠를 볼 수 있게 할 수 있다.

순서DOMContentLoaded
asyncload-first order. 문서 내 순서와 상관없이 먼저 다운로드 된 스크립트가 먼저 실행된다.비동기 스크립트는 HTML 문서가 완전히 다운로드되지 않은 상태라도 로드 및 실행될 수 있다. 스크립트 크기가 작거나 캐싱 처리 되어있을 때 혹은 HTML 문서 길이가 아주 길 때 이런 일이 발생한다.
defer문서에 추가된 순지연 스크립트는 문서 다운로드와 파싱이 완료된 후에, DOMContentLoaded 이벤트 발생 전에 실행된다.

스크립트 다운로드가 끝나지 않았어도 페이지는 동작해야한다.

defer를 사용하게 되면 스크립트가 실행되기 전에 페이지가 화면에 출력된다는 점에 항상 유의해야 한다.
사용자는 그래픽 관련 컴포넌트들이 준비되지 않은 상태에서 화면을 보게 될 수도 있다.
따라서 지연 스크립트가 영향을 주는 영역엔 반드시 '로딩 인디케이터'가 있어야한다. 관련 버튼도 사용불가처리를 해줘야한다. 이렇게 해야 사용자에게 현재 어떤 것은 사용할 수 있는지, 어떤 것은 사용할 수 없는 지 알려줄 있다.

실무에서는 defer를 DOM 전체가 필요한 스크립트가 실행 순서가 중요한 경우에 적용한다. async 는 방문자 수 카운터나 광고 관련 스크립트 같이 독립적인 스크립트에 혹은 실행 순서가 중요하지 않은 경우에 적용한다.

REFERNCES

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

profile
개발자 이유리

0개의 댓글