2021년 12월 20일

모던 웹브라우저에서 돌아가는 스크립트들은 대부분 HTML보다 ‘무겁습니다’. 용량이 커서 다운로드받는 데 오랜 시간이 걸리고 처리하는 것 역시 마찬가지이죠.

브라우저는 HTML을 읽다가 <script>...</script> 태그를 만나면 스크립트를 먼저 실행해야 하므로 DOM 생성을 멈춥니다. 이는 src 속성이 있는 외부 스크립트 <script src="..."></script>를 만났을 때도 마찬가지입니다. 외부에서 스크립트를 다운받고 실행한 후에야 남은 페이지를 처리할 수 있습니다.

이런 브라우저의 동작 방식은 두 가지 중요한 이슈를 만듭니다.

  1. 스크립트에서는 스크립트 아래에 있는 DOM 요소에 접근할 수 없습니다. 따라서 DOM 요소에 핸들러를 추가하는 것과 같은 여러 행위가 불가능해집니다.
  2. 페이지 위쪽에 용량이 큰 스크립트가 있는 경우 스크립트가 페이지를 ‘막아버립니다’. 페이지에 접속하는 사용자들은 스크립트를 다운받고 실행할 때까지 스크립트 아래쪽 페이지를 볼 수 없게 됩니다.
<p>...스크립트 앞 콘텐츠...</p>

<script src="<https://javascript.info/article/script-async-defer/long.js?speed=1>"></script><!-- 스크립트 다운로드 및 실행이 끝나기 전까지 아래 내용이 보이지 않습니다. -->

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

이런 부작용들을 피할 수 있는 몇 가지 방법이 있습니다. 아래 예시처럼 스크립트를 페이지 맨 아래 놓는 것이 하나의 방법이 될 수 있죠. 이렇게 하면 스크립트 위에 있는 요소에 접근할 수 있습니다. 또한, 페이지 콘텐츠 출력을 막지 않게 됩니다.

<body>
  ... 스크립트 위 콘텐츠들 ...

  <script src="<https://javascript.info/article/script-async-defer/long.js?speed=1>"></script>

</body>

그런데 이 방법은 완벽한 해결책이 아닙니다. HTML 문서 자체가 아주 큰 경우를 가정해봅시다. 브라우저가 HTML 문서 전체를 다운로드 한 다음에 스크립트를 다운받게 하면 페이지가 정말 느려질 겁니다.

네트워크 속도가 빠른 곳에서 페이지에 접속하고 있다면 이런 지연은 눈에 잘 띄지 않습니다. 하지만 아직도 네트워크 환경이 열악한 곳이 많습니다. 모바일 네트워크 접속이 느린 곳도 많죠.

다행히도 이런 문제를 해결할 수 있는 <script> 속성이 있습니다. 바로 defer와 async입니다.

defer

브라우저는 defer 속성이 있는 스크립트(이하 defer 스크립트 또는 지연 스크립트)를 '백그라운드’에서 다운로드 합니다. 따라서 지연 스크립트를 다운로드 하는 도중에도 HTML 파싱이 멈추지 않습니다. 그리고 defer 스크립트 실행은 페이지 구성이 끝날 때까지 지연 됩니다.

위쪽 예시와 동일한 코드인데 스크립트에 defer만 붙여보겠습니다.

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

<script defer src="<https://javascript.info/article/script-async-defer/long.js?speed=1>"></script><!-- 바로 볼 수 있네요! -->

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