[자바스크립트] - load , defer , async 의 차이

1️⃣ script 로드 문제점

HTML에서 자바스크립트를 가져올 올 때 <script> 태그를 통해서 가져올 수 있습니다.

하지만 브라우저에서는 HTML 파일의 위에서 부터 아래로 순차적으로 파싱하기 때문에 HTML이 파싱되지 않은 상태에서 자바스크립트를 파싱할 경우 문제가 생길 수 있다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <script>
        let btn = document.querySelector('#btn'); // button을 참조할 수 없습니다.
        btn.addEventListener('click', function() {
            alert('Hello world!');
        })
	</script>
  </head>
  <body>
    <button id="btn">버튼</button>
  </body>
</html>

위 코드를 실행하게 되면 HTML의 버튼 태그가 파싱되지 않은 상태에서 스크립트 파일이 실행이 되서 오류가 뜨게 된다. 

그림으로 보면 HTML이 다 파싱되기도 전에 스크립트 파일이 파싱되고 실행되는 모습을 볼 수 있다. 이 과정이 완료될 때 동안 HTML 파싱은 잠시 멈추기 때문에 시간상으로 비효율적이다. 

2️⃣ 해결 방법

✅ body 최 하단에서 script 로드하기

HTML을 모두 파싱한 후 스크립트 파일이 실행될 수 있도록 body태그 최 하단에 스크립트 파일을 이동시키는 방법이다.

<!DOCTYPE html>
<html lang="ko">
  <head> </head>
  <body>
    <button id="btn">버튼</button>
    <script>
      let btn = document.querySelector('#btn'); // button을 참조할 수 없습니다.
      btn.addEventListener('click', function () {
        alert('Hello world!');
      });
    </script>
  </body>
</html>

가장 쉬운 방법이긴 하지만 모든 HTML 요소를 파싱한 후 실행되기 때문에 시간 측면에서 비효율적이다.🤔

 

✅ window.onload 이벤트 사용하기

HTML의 모든 요소(DOM, images, script, css, etc)가 로드된 후 발생하는 이벤트다. 

<!DOCTYPE html>
<html lang="ko">
  <head>
    <script>
      window.onload = function () {
        let btn = document.querySelector('#btn'); // button을 참조할 수 없습니다.
        btn.addEventListener('click', function () {
          alert('Hello world!');
        });
      };
    </script>
  </head>
  <body>
    <button id="btn">버튼</button>
  </body>
</html>

이것또한 모든 요소가 로드된 후 발생하는 이벤트이기 때문에 시간측면에서는 비효율적이다.🤔

 

✅ DOMContentLoaded 이벤트 사용하기

window.onload와는 달리 DOM만 생성 후 발생하는 이벤트다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <script>
      document.addEventListener('DOMContentLoaded', function () {
        let btn = document.querySelector('#btn'); // button을 참조할 수 없습니다.
        btn.addEventListener('click', function () {
          alert('Hello world!');
        });
      });
    </script>
  </head>
  <body>
    <button id="btn">버튼</button>
  </body>
</html>

window.onload 보다는 빠를 수 있으나 DOM요소를 모두 파싱한다는 점에서 아직까지는 만족스럽지 않다.😅

 

ES5 부터는 defer, async 속성을 통해 비동기 스크립트 로드가 가능해져 이 문제가 해결되었다. 😀 

✅ defer 속성

HTML 파싱과 함께, 비동기로 JavaScript 파일을 불러온다.

이전과는 달리 스크립트 파일이 로드 될 때에도 HTML 파싱이 멈추지 않기 때문에 시간상에서도 매우 효율적이고, 스크립트 실행은 HTML파싱이 모두 완료된 후 실행되기 때문에 에러없이 사용할 수 있다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <script src="script.js" defer></script>
  </head>
  <body>
    <button id="btn">버튼</button>
  </body>
</html>

 

✅ async 속성

HTML 파싱과  함께, 비동기로 JavaScript 파일을 불러온다.

defer와는 달리 HTML 파싱이 완료되지 않았더라도, 먼저 로딩되는 JavaScript 파일부터 실행된다. 

그리고 JavaScript 파일을 실행할 때는 HTML 파싱이 중단된다. 

❗순서에 따라서 실행이 잘 안될 수도 있다❗

<!DOCTYPE html>
<html lang="ko">
  <head>
    <script src="script.js" async></script>
  </head>
  <body>
    <button id="btn">버튼</button>
  </body>
</html>

👉 async는 꼭 필요할 때만 쓰자..