Front-end/JavaScript
[JavaScript] defer, async 스크립트
Nave
2022. 6. 20. 18:50
모던 웹브라우저에서 돌아가는 스크립트들은 대부분 HTML보다 무겁다. 용량이 커서 다운로드받는 데 오랜 시간이 걸리고 처리하는 것 역시 마찬가지.
브라우저는 HTML을 읽다가 <script>...</script> 태그를 만나면 스크립트를 먼저 실행해야 하므로 DOM 생성을 멈춘다. 이는 src 속성이 있는 외부 스크립트 <script src="..."></script>를 만났을 때도 마찬가지인데, 외부에서 스크립트를 다운받고 실행한 후에야 남은 페이지를 처리할 수 있다.
이런 브라우저의 동작 방식은 두 가지 중요한 이슈를 만듭니다.
- 스크립트에서는 스크립트 아래에 있는 DOM 요소에 접근할 수 없다. 따라서 DOM 요소에 핸들러를 추가하는 것과 같은 여러 행위가 불가능해진다.
- 페이지 위쪽에 용량이 큰 스크립트가 있는 경우 스크립트가 페이지를 막아버린다. 페이지에 접속하는 사용자들은 스크립트를 다운받고 실행할 때까지 스크립트 아래쪽 페이지를 볼 수 없게 된는 것이다.
<p>...스크립트 앞 콘텐츠...</p>
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- 스크립트 다운로드 및 실행이 끝나기 전까지 아래 내용이 보이지 않는다. -->
<p>...스크립트 뒤 콘텐츠...</p>
이런 문제를 해결할 수 있는 <script> 속성이 defer 과 async 이다.
1. defer
브라우저는 defer 속성이 있는 스크립트(defer 스크립트 또는 지연 스크립트)를 '백그라운드'에서 다운로드 한다. 따라서 지연스크립트를 다운로드 하는 도중에도 HTML파싱이 멈추지 않는다. 그리고 defer 스크립트 실행은 페이지 구성이 끝날 때까지 지연된다.
<p>...스크립트 앞 콘텐츠...</p>
<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- 바로 볼 수 있음 -->
<p>...스크립트 뒤 콘텐츠...</p>
- 지연 스크립트는 페이지 생성을 절대 막지 않는다.
- 지연 스크립트는 DOM이 준비된 후에 실행되긴 하지만 DOMContentLoaded 이벤트 발생 전에 실행된다.
(( 예시 ))
<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. <p>로 감싸진 페이지 콘텐츠는 바로 출력된다.
2. DOMContentLoaded 이벤트는 지연 스크립트 실행을 기다리기 때문에, 얼럿창은 DOM트리가 완성되고 지연 스크립트가 실행된 후 에 뜬다.
**defer 속성은 외부 스크립트에만 유효하다
--> <script>에 src가 없으면 defer 속성은 무시된다.
2. async
async 속성이 붙은 스크립트(이하 async 스크립트 또는 비동기 스크립트)는 페이지와 완전히 독립적으로 동작한다.
- async 스크립트는 defer 스크립트와 마찬가지로 백그라운드에서 다운로드된다. 따라서 HTML 페이지는 async 스크립트 다운이 완료되길 기다리지 않고 페이지 내 콘텐츠를 처리, 출력한다.
- DOMContentLoaded 이벤트와 async 스크립트는 서로를 기다리지 않습니다.
- 페이지 구성이 끝난 후에 async 스크립트 다운로딩이 끝난 경우, DOMContentLoaded는 async 스크립트 실행 전에 발생할 수 있다.
- async 스크립트가 짧아서 페이지 구성이 끝나기 전에 다운로드 되거나 스크립트가 캐싱처리 된 경우, DOMContentLoaded는 async 스크립트 실행 후에 발생할 수도 있다.
- 다른 스크립트들은 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>
- 비동기 스크립트 다운로드는 페이지 로딩을 막지 않기 때문에 페이지 콘텐츠가 바로 출력된다.
- DOMContentLoaded 이벤트는 상황에 따라 비동기 스크립트 전이나 후에 실행된다. 때문에 정확한 순서를 예측할 수 없다.
- 비동기 스크립트는 서로를 기다리지 않는다. 위치상으론 small.js가 아래이긴 하지만 long.js보다 먼저 다운로드되었기 때문에 먼저 실행된다. 이렇게 먼저 로드가 된 스크립트가 먼저 실행되는 것을 'load-first order’라고 부른다.