본문 바로가기

내직업은 IT종사자/HTML|CSS

[html] <head>태그 <script> async, defer 차이점, 어떻게 사용해야 할까요?

반응형

 

<script></script> normal, async, defer의 차이점

 

 목차

 

 

 

 

 

1. <script>의 normal방식이 야기할 수 있는 문제점


 

<!-- #1 -->
<script>
console.log("script 실행">
</script>

<!-- #2 -->
<script src="{script url}"></script>

브라우저는 HTML을 읽다가 #1과 같이 <script>...</script>태그를 만나면 스크립트를 먼저 실행하고 DOM 생성을 멈춥니다. 또 #2와 같은 경우도 외부 스크립트를 다운받고 실행한 후에야 DOM생성을 하기 시작합니다. 

 

이런 브라우저 동작방식은 

  1. 스크립트에서 스크립트 아래에 있는 DOM요소에 접근을 할 수 없습니다. 
  2. 페이지 위쪽에 용량이 큰 스크립트가 있는경우 스크립트 다운 및 실행할때까지 아래쪽 페이지를 볼 수 없습니다.
<div id="beforeScriptDiv">
	<span> 스크립트 앞 콘텐츠 </span>
</div>

<script>
    const beforeScriptDiv = document.getElementById('beforeScriptDiv');
    const afterScriptDiv = document.getElementById('afterScriptDiv');
    console.log(beforeScriptDiv); //<div id="xxx">...</div>
    console.log(afterScriptDiv); // null
</script>

<div id="afterScriptDiv">
	<span> 스크립트 뒤 콘텐츠 </span>
</div>

 

위의 문제를 해결하기 위해서 <script>...</script>를 화면 요소(<body></body>) 안의 맨 아래에 위치 시켜놓는 방법도 있습니다.

 

<body>
...화면 요소들

<script>....</script>
</body>

이러한 방법은 간단하고 가벼운 페이지들은 문제가 되진 않지만 HTML문서 자체가 아주 큰 경우,

브라우저가 HTML문서 전체를 다운로드 한다음에 스크립트를 다운 및 실행해서 페이지가 느려질 수 있습니다. 

네트워크 속도가 빠른곳에서 접속하고 있다면 이러한 문제점은 체감하기에 어렵지만

네트워크 환경이 느린 곳에서 접속하면 느껴질 수 도있습니다. 

 

이러한 문제는 <script>속성 async와 defer로 해결할 수 있습니다!

 

 

 

2. <script>의 파싱 및 실행순서 


https://www.growingwiththeweb.com/images/2014/02/26/script.svg

 

async, defer속성을 사용하지 않은 기본 normal 방식은 파싱 및 실행순서가 위에 그림과 같습니다.

 

HTML parsing 시작 -> <script>를 만나면  HTML parsing 중단 -> <script> 다운 및 실행 한 후 -> HTML parsing을 이어서합니다.

 

 

3. <script  asnyc>의 파싱 및 실행순서


 

https://www.growingwiththeweb.com/images/2014/02/26/script-async.svg

 

<script> asnyc 속성을 사용하면 페이지 파싱과 완전히 독립적으로 작동합니다.

defer스크립트와 마찬자기로 백그라운드에서 다운로드 됩니다.

또한, asnyc속성으로 불러오는 <script>가 여러개라면 순서에 상관없이 먼저 다운이 끝나는 순으로 실행되는 비동기 성격을 갖고 있습니다. 

 

HTML parsing 시작 -> <script>를 만나도  HTML parsing 계속 -> 단, <script>  실행 할 때는 parsing 멈춤 -> HTML parsing을 이어서합니다.

 

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

<script>
  document.addEventListener('DOMContentLoaded', () => alert("DOM이 준비 되었습니다!"));
</script>

<script async src="heavy.js"></script>
<script async src="light.js"></script>

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

 

위에 코드를 예로 설명하자면,

  • DOMContentLoaded 이벤트는 상황에 따라 비동기 스크립터 전이나 후에 실행됩니다. 정확한 순서를 예측할 순 없습니다. 
  • async속성은 비동기 성격으로 서로 기다리지 않습니다. 위치상으로 light.js가 heavy.js보다 밑에 있지만 light.js가 더 먼저 다운로드 되었기 때문에 먼저 실행됩니다.  이를 'load-first order'라고 부릅니다. 

 

async 비동기 속성은 '방문자 수 카운터 나 광고 관련 스크립트' 처럼 각각 독립적인 역할을 하는 서드파티 스크립트에서 

많이 사용합니다. 

 

 

4. <script defer>의 파싱 및 실행순서


 

https://www.growingwiththeweb.com/images/2014/02/26/script-defer.svg

 

 

defer속성은 스크립트를 async와 마찬가지로 백그라운드에서 다운로드 합니다. 

defer속성은 외부 스크립트에서만 유효합니다. (<script>에 src속성이 없으면 defer속성은 무시됩니다.)

 

HTML parsing 시작 -> <script>를 만나도  HTML parsing 계속 -> HTML parsing이 끝난 후 -> <script>실행 (DOMContentLoaded 이벤트 발생 전에 실행)

 

 

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

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

<script defer src="https://heavy.js"></script>
<script defer src="https://light.js"></script>

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

 

다운로드는 작고 가벼운 스크립트 먼저 완료 되지만 실행은  정의한 순서대로 됩니다. 

즉, defer 속성을 쓰면 순서가 지켜집니다.  

heavy.js -> light.js -> DOMContentLoaded 순서대로 실행됩니다. 

 

 

 

 

 

참고글: https://ko.javascript.info/script-async-defer

 

 

잘못된 정보에 대한 피드백은 언제나 환영입니다  (´▽`ʃƪ)♡

반응형