22sook00 logo
SookDev

defer and async

tag
javascript
date
Feb 11, 2023

 🪢 script 작동원리

브라우저에서 HTML 읽을때 중간에 script 를 만나면 DOM 생성을 멈추고 script 를 먼저 실행하려고 한다.
<script src=’’> 와 같이 외부에서 가져오는 자바스크립트를 만났을때도 DOM 생성은 멈춘다.
→ 해당 스크립트 밑에 DOM 요소가 있다면 하위 DOM 에 접근할 수 없기 때문에 DOM 요소에 이벤트핸들러 추가가 불가능해진다.
그러므로 만약에 용량이 큰 스크립트가 DOM 위에 있다면 아예 스크립트가 페이지를 막아버리기 때문에 스크립트가 다운받고 실행될때까지 스크립트 밑의 페이지를 볼 수 없는것
? 리액트에서 html 파일에 카카오맵 src 를 script에 넣는것 생각해보기
<p>...스크립트 앞 콘텐츠...</p> <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> //스크립트 다운로드 및 실행이 끝나기 전까지 아래 내용이 보이지 않습니다. <p>...스크립트 뒤 콘텐츠...</p>
이러한 문제를 막기위해 우리는 보통 script 를 맨 하단에 위치시킨다.
<body> <p>... 스크립트 위 콘텐츠들 ...</p> <p>... 스크립트 위 콘텐츠들 ...</p> <p>... 스크립트 위 콘텐츠들 ...</p> <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> </body>
하지만 html 파일이 크고, 네트워크 상태가 느릴수록 맨 하단에 위치시키는 것은 문제를 야기시킬 수 있다.
DOM 을 다 그린 후 스크립트를 다운받기 때문에 페이지가 느려질 수 있기 때문이다.
(네트워크 속도가 빠르면 큰 차이는 없어 보이지만 여전히 느린곳이 많다.)

 🪢  defer

defer는 지연 스크립트 라고도 하며 백그라운드 에서 다운로드한다. 지연 스크립트를 다운로드 하는 도중에도 HTML 파싱이 멈추지 않아 script defer 하단에 있는 DOM 도 그려주게 된다. defer 스크립트 실행은 페이지 구성이 끝날때까지 지연된다.
즉, defer 를 쓰게되면 페이지생성을 막지 않으며 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>
defer 는 외부스크립트(src) 에서 가져올때만 적용 가능하고 크기에 상관없이 html에 추가된 순으로 실행된다.
만약 자바스크립트파일 사이즈가 작은파일이 긴파일보다 뒤에있다면,
다운로드는 작은파일이 먼저 되지만 실행은 긴파일이 먼저 된다.
//long 스크립트 먼저실행 <script defer src="https://javascript.long.js"></script> //short 스크립트 다운로드는 먼저되지만 실행은 다음 <script defer src="https://javascript.small.js"></script>
defer를 사용시 유의해야 할 점은 DOM 은 이미 그려진 상태이지만 이벤트 등 자바스크립트가 실행되기 전에 출력이 되기 때문에 동작하지 않을 수 있다. 그러므로 loading indicator 나 버튼의 disabled 처리를 해주므로서 사용자가 어떤것은 사용가능한지 아닌지 알려줄 수 있어야 한다.

DOMcontentLoaded 란?

html 생명주기 3가지 이벤트 중 하나
브라우저가 HTML을 전부 읽고 DOM 트리를 완성하는 즉시 발생한다.
<img> , stylesheet 등의 기타자원은 기다리지 않는다.
만약 img, stylesheet 까지 기다린 후 DOM 트리를 완성한 후 발생 시키려면 load 를 사용하면 된다.
window.addEventListener("DOMContentLoaded", (event) => { console.log("DOMContentLoaded"); }); window.addEventListener("load", (event) => { console.log("DOMContentLoaded"); });

 🪢  async

async 는 비동기 스크립트로 페이지와 완전히 독립적으로 동작한다.
defer 와 마찬가지로 백그라운드에서 다운로드 되어 DOM은 스크립트가 다운로드될때까지 기다리지 않고 페이지가 그려지나, async 스크립트가 실행중일때는 html 파싱이 멈춘다.
 
비동기적으로 동작하기 때문에 defer 와 달리 DOMContentLoaded 와 async 는 서로 기다리지 않고 다운로드 속도에 따라 먼저 실행결과가 달라질 수 있다.
만약 DOM을 다 그린 후 async 스크립트 다운로드가 끝난다면 DOMContentLoaded 는 async 스크립트 실행전에 발생할 수 있고,
async 스크립트가 짧아 DOM을 그리기도 전에 다운로드가 끝나거나 스크립트가 캐싱처리 된 경우, DOMContentLoaded 는 async 스크립트 실행 후에 발생할 수 있다.
즉, 비동기로 진행되는 async 는 자바스크립트 다운로드 속도에 따라 순서가 제각각 실행될 수 있다.

 🪢  동적스크립트

자바스크립트를 동적으로 넣는것을 말하며 dynamic script 라고도 한다.
동적 스크립트는 의도적으로 스크립트 로딩을 지연시킨다.
동적스크립트는 기본적으로 async 스크립트처럼 먼저 다운로드가 되면 실행이 되는 성질이 있다. 그 어떠한것도 기다리지 않는다.
let script = document.createElement('script'); script.src = "/article/script-async-defer/long.js"; document.body.append(script); // (*)
하지만 script.async = false; 처리를 해주면 다운로드에 상관없이 먼저 위치한 스크립트가 먼저 실행된다.

 🧶 결론

async와 defer 스크립트는 다운로드 시 페이지 렌더링을 막지 않는다는 공통점이 있다. 따라서 async와 defer를 적절히 사용하면 사용자가 오래 기다리지 않고 페이지 콘텐츠를 볼 수 있게 할 수 있다.
순서
DOMContentLoaded
async
load-first order. 문서 내 순서와 상관없이 먼저 다운로드된 스크립트가 먼저 실행. 방문자 수 카운터나 광고 관련 스크립트같이 독립적인 스크립트에 혹은 실행 순서가 중요하지 않은 경우에 적용
비동기 스크립트는 HTML 문서가 완전히 다운로드되지 않은 상태라도 로드 및 실행. 스크립트 크기가 작거나 캐싱 처리 되어있을 때 혹은 HTML 문서 길이가 아주 길 때 이런 일이 발생합니다.
defer
DOM 전체가 필요한 스크립트나 실행 순서가 중요한 경우에 적용
문서 다운로드와 파싱이 완료된 후 DOMContentLoaded 이벤트 발생 전에 실행됩니다.
참조 👇🏻