위어드섹터 공식 블로그

[HTML] script 태그의 위치와 defer, async 속성 본문

Developing/HTML

[HTML] script 태그의 위치와 defer, async 속성

위어드섹터 2023. 5. 10. 01:27

HTML에서 script 태그로 분리한 javascript 파일(.js)을 가져올 때, 

script 태그를 선언한 위치와 비동기 속성 추가로 인해 결정되는 스크립트 실행 순서에 대해서 알아봤습니다.



“script 태그에 defer 속성 사용을 권장한다.” 라는 이유와 차이점에 대해 정리해봤습니다.



## 문제 상황과 원인

테스트를 위해 새로운 HTML 문서에 분리한 javascript 코드를 호출했습니다.

DOM 요소를 찾기 위해 메서드를 사용했지만 null이 반환되어 에러가 발생했습니다.

head에 script 태그를 사용하여, DOM 객체 생성 전에 조작하려고 했기 때문에 발생한 문제였습니다.



## 해결 방법 1 : script 태그의 위치 변경

가장 쉬운 방법으로는 HTML 상단에 정의한 script 태그를 body 태그 하단으로 옮길 수 있습니다.

순차적으로 실행되는 언어이므로 HTML 파싱을 마친 후에 javascript가 실행 될 수 있도록 하는 것입니다.

 

하지만 HTML 코드가 길어지거나, head에 script 태그를 사용해야하는 경우도 있기 때문에 script 태그가 head와 body에 분산되어 있으면 가독성이 좋지 못할 것입니다. 

그래서 아래의 비동기 속성 defer와 async를 사용한 방법을 자세하게 알아봤습니다.



## 해결 방법 2 : defer, async 속성 추가

script 태그를 head 태그 내부에 정의하고, defer와 async 중 하나를 추가하면 HTML을 파싱하면서 파일을 비동기적으로 로드할 수 있습니다.

 

defer와 async의 큰 차이점은 완료 후 즉시 실행 여부 입니다. 

async는 로드 완료 시점에서 HTML 파싱 완료를 기다리지 않고 javascript가 실행됩니다.

만약 실행되는 js의 크기가 크다면 파싱 작업이 남아있는 부분은 한참 동안 화면에서 볼 수 없을 수도 있습니다.

 

그렇기 때문에 async 보다 defer 사용이 권장되기도 합니다.

 

defer 

  • js 로드가 완료 되어도 HTML 파싱 완료(DOM 생성 완료) 후, javascript 실행

 

   HTML 파싱 ---------------------------

   js 로드              ========

   js 실행                              ++++++

 

  • script 태그가 여러개 있다면 선언한 순서대로 실행

e.g. 아래의 순서라면 defer1, 2, 3 순차적으로 동작

<!-- 비동기 defer -->

   <script defer src="/assets/defer1.js"></script>

   <script defer src="/assets/defer2.js"></script>

   <script defer src="/assets/defer3.js"></script>



async

  • js 로드 완료 후, HTML 파싱이 일시 중지되고 javascript 즉시 실행

 

   HTML 파싱 -------------------      --------

   js 로드              ========

   js 실행                      ++++++

 

  • script 태그가 여러개 있다면 먼저 로드한 순서대로 실행 

e.g. 아래의 순서라도 순서를 보장할 수 없음

<!-- 비동기 async -->

   <script async src="/assets/async1.js"></script>

   <script async src="/assets/async2.js"></script>

   <script async src="/assets/async2.js"></script>




위치 별 실행 순서

 

<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <meta http-equiv="X-UA-Compatible" content="IE=edge">

   <meta name="viewport" content="width=device-width, initial-scale=1.0">

   <!-- 비동기 async -->

   <script async src="/assets/defer/async.js"></script>

   <!-- 비동기 defer -->

   <script defer src="/assets/defer/async_defer.js"></script>

   <!-- 동기 -->

   <script src="/assets/defer/sync_in_head.js"></script>

   <title>Document</title>

</head>

<body>

   <p>테스트 페이지</p>

   <!-- 동기 -->

   <script src="/assets/defer/sync_in_body.js"></script>

</body>

</html>





위의 코드로 js 파일에 console.log()를 실행시켰습니다.

 

sync_in_body.js 파일을 기점으로 

HTML 파싱 전 async 속성이 붙은 스크립트가 실행되었고, defer 속성이 붙은 스크립트는 파싱이 완료 된 후에 실행된 것을 확인해 보았습니다.



주의!

 

“The defer and async attributes must not be specified if the src attribute is not present.” 

– 출처 w3.org

 

defer와 async는 HTML5에서 추가된 속성으로 외부 파일(.js)로 분리한 경우에만 사용할 수 있습니다.

즉, 아래와 같이 js 파일을 분리하지 않고 바로 javascript를 실행할 경우에는 작동하지 않습니다.

 

   <script>

       console.log('인라인 스크립트에서는 defer 나 async 속성을 사용하지 마세요')

   </script>




 

이번에는 “script 태그에 defer 속성 사용을 권장한다.” 의 원리를 파악하며

효율적인 javascript 로딩 방법을 통해 프론트 성능 향상의 한 방법에 대해 알아보았습니다.

더 나은 인터렉션을 위해 다음에는 다른 주제로 돌아오겠습니다~



## 참고자료

모던 자바스크립트 튜토리얼 

모던 자바스크립트 Deep Dive

mdn <script>

What Is The Fastest Way To Load JavaScript

 

블로그 구독자 문의 주소 : info@weirdsector.co.kr

그로스 해킹 파트너, LABBIT 바로가기

LABBIT을 운영하는 Team 위어드섹터 만나러 가기

브랜드의 성장박스 Data Nugget