ES modules의 추가와 함께 이제 JS 코드를 로드하기 위한 24가지 이상의 방법이 생겼습니다: (inline|not inline) x (defer|no defer) x (async|no async) x (type=text/javascript | type=module | nomodule) -- 그리고 이들은 미묘하게 다릅니다.
이 문서에서는 속성 값들에 따라 HTML의 <script>
태그가 처리되는 여러가지 방법을 비교했습니다.
만약 <script async type="module">
혹은 <script nomodule defer src="...">
을 언제 써야 할지 궁금해하셨다면 잘 오셨습니다!
Note 이 문서는 <script>
태그들이 HTML에 추가되었을 때를 다룹니다; 런타임에 추가되는 <script>
태그들의 동작은 사뭇 다르니 Jake Archibald (2013)님의 Deep dive into the murky waters of script loading를 참고해주세요.
그림 하나가 천 마디보다 가치있기 때문에 좋은 시작점으로서 이 섹션이 존재합니다: (credit: https://developers.google.com/web/fundamentals/primers/modules#module-vs-script)
위 그림에서 볼 수 있듯, async
는 부분적으로 꽤 이상할 수도 있습니다: 오래된 스크립트에서 당신은 주로 일들이 나중에 일어나게 하기 위해 사용했고 모듈로써는 더 빨리 일이 일어나게 하기 위해 사용하곤 했습니다 (기본적으로 모듈 스크립트는 지연되어 시작되기 때문입니다).
defer
는 좀 오래되었고, async
는 비교적 최신의 것입니다. 그리고 둘 다 잘 지원됩니다.
async
와 defer
간의 직관적인 차이는 async
는 더 빠르게 실행하고자 하는 것입니다. (이들은 HTML이 모두 파싱되고 형태를 갖출 때까지는 물론 다른 스크립트들이 모두 다운로드되기까지 기다리지 않아도 됩니다)
-
모듈이 아닌 "표준"
<script>
(type=text/javascript
내포,async
사용 안 함,defer
사용 안 함)- ❌ HTML 파서를 지연시킵니다
- ℹ️ HTML의 다른
<script>
들 전에, 즉시 다운로드되며, 해석되고 실행됩니다 - ✔️ 순서대로 실행됨이 보장됩니다
- ❌
DOMContentLoaded
이벤트를 지연시킵니다 - ❌ 위와 같이, "유일한 실패 원인"이 될 수 있고 렌더링 병목이 발생하여 다이내믹 웹 앱의 시작을 지연시키므로, 중요하지 않은 코드에는 적합하지 않습니다.
-
defer
스크립트:- ℹ️ (모듈이 아닌) 인라인 스크립트에서는,
defer
는 무시되고 아무 효과를 가지지 않게 됩니다. (이들은 즉시 실행됩니다)- 💡 만약 당신이 정말로, 정말로 필요로 하다면, the base64 hack을 사용할 수 있습니다.
- ℹ️ (모듈) 인라인 스크립트에서는,
defer
가 (자동으로) 내포되게 됩니다. - ✔️ HTML 파서를 지연시키지 않고 다운로드됩니다.
- ✔️ 여러
defer
스크립트들 간에 상대적 스크립트 실행 순서가 보장됩니다. (만약 모두src
속성을 가지고 있다면)⚠️ 다만, IE9-에서는 버그가 있습니다.
- ℹ️ DOM이 파싱된 후에 실행됩니다. (하지만
DOMContentLoaded
전에요) - ❌
DOMContentLoaded
이벤트를 지연시킵니다. (스크립트가async defer
가 아닌 이상)
- ℹ️ (모듈이 아닌) 인라인 스크립트에서는,
-
async
스크립트:- ℹ️ (모듈이 아닌) 인라인 스크립트에서는,
async
는 무시되고 아무 효과를 가지지 않게 됩니다. - ℹ️ (모듈) 인라인 스크립트에서는,
async
는 지원됩니다. 출처 - 순서없이, 가능한대로 즉시 실행됩니다. - ✔️ HTML 파서를 지연시키지 않고 다운로드됩니다.
⚠️ 순서없이 실행됩니다; 가능한대로 바로 실행됩니다⚠️ async
스크립트들 사이에 상대적 실행 순서는 보장되지 않습니다. (또한async, type=module
스크립트에도 적용됩니다)⚠️ HTML 파싱이 끝날 때까지 기다려주지 않습니다; DOM 빌드를 방해할 수 있습니다 (특히 웹 브라우저의 캐시에서 로드되었을 때)⚠️ load
이벤트를 지연시킵니다. (DOMContentLoaded
이벤트는 아닙니다)⚠️ IE9-에서는 지원되지 않습니다.
- ℹ️ (모듈이 아닌) 인라인 스크립트에서는,
-
async defer
스크립트:async
로 해석됩니다;async
를 지원하지 않는 오래된 웹 브라우저에 한해서 (IE9...)defer
로 대체됩니다.
-
type=module
스크립트 -type=text/javascript
스크립트들과의 차이점:- ℹ️
defer
를 내포합니다. - ℹ️ 인라인 스크립트에서도,
defer
를 내포합니다. (모듈이 아닌 스크립트들과는 다르게요!) - ✔️ 그 후,
async
가 아닌 모듈 스크립트들 사이에 상대적 실행 순서를 보장해줍니다. (인라인과src
모두) - ✔️ 같은
src
가 여러번 로드되어도, 한 번만 실행됩니다 - ℹ️ 다른 모듈 스크립트에 있는 의존성을 정의하기 위해
import
를 사용할 수 있습니다. (이것은 왜 모듈이 지연(defer)되는지에 대한 한 가지 이유입니다) - ℹ️ CORS 확인 대상입니다. (교차 출처의 모듈들은
Access-Control-Allow-Origin: *
가 필요합니다) - ✔️ 지원하지 않는 웹 브라우저들에서는 실행되지 않습니다.
⚠️ 그러나 IE11, Firefox 52 ESR 등에 의해 여전히 다운로드되는 것으로 보입니다.
- ℹ️
-
<script nomodule>
- ✔️
<script type="module">
를 지원하는 웹 브라우저들에서는 다운로드되거나 실행되지 않습니다.⚠️ 그러나 몇몇 최신 웹 브라우저들에서는 버그가 존재하며 여전히 다운로드됩니다. (Safari 10.3가 그렇습니다 - 다만 대안이 있습니다)
- ✔️
-
인라인 스크립트 (
src
없이):- ℹ️ 모듈이 아닌 인라인 스크립트:
async
와defer
는 모두 무시됩니다; 스크립트는 HTML 파서와 DOM을 빌드하는 것을 지연시키고 즉시 실행됩니다 - ℹ️ 모듈 인라인 스크립트:
defer
를 내포합니다;async
를 지원하며 - ❌ 웹 브라우저에서 캐싱할 수 없습니다.
- ℹ️ 모듈이 아닌 인라인 스크립트:
-
src
스크립트:- ✔️ (올바른 응답 헤더가 주어졌을 때) 웹 브라우저가 캐싱할 수 있고, 이후 페이지 이동에서 네트워크에서 다운로드될 필요없이 계속 재사용될 수 있습니다.
종류 | 사용 예 |
---|---|
script src | 이후에 실행될 인라인 스크립트에서 필요로 하는 레거시 라이브러리 |
script src defer | 지연된 실행, 순서를 지키는 경우; e.g. 다른 지연된 스크립트에서 필요로하는 라이브러리; 점진적 향상 코드 |
script src async | 지연된 실행, 순서없이 (독립된 스크립트); e.g. 스스로 초기화되는 애널리틱스 라이브러리 |
script src async defer | 위와 같이, 다만 IE9 지원과 함께 |
script inline | 1) 이후에 실행될 코드들 전에 즉시 실행되어야 하는 작은 코드 (인라인 polyfill, 타이머, 서버에서 생성된 설정), 혹은 되는대로 실행되어야 하는 이벤트 리스너를 등록할 때; 2) 캐싱이 불가능한 코드 (동적으로 생성되었거나 자주 바뀌는 것들 등); 3) 경험 상 중요한 작고 나누어 다운로드하는 것이 오래걸리는 코드 |
script src module | 라이브러리 및 앱, 최신 웹 브라우저에 한하여 |
script src module async | 점진적 향상 코드, 최신 웹 브라우저에 한하여 |
script inline module | 작거나 캐싱할 수 없는 코드, 최신 웹 브라우저에 한하여; 아마도 이후에 정의될 동기식의 모듈에서 필요로 할 인라인 설정 파일에 사용 |
script inline module async | 작거나 캐싱할 수 없는 점진적 향상 코드 (독립적 스크립트), 최신 웹 브라우저에 한하여; 잘 캐싱될 수 있는 라이브러리들을 import 할 수도 있는 |
script nomodule ... | 최신 웹 브라우저에 ES 모듈을 배포하는 경우, 오래된 웹 브라우저들을 위한 대체 스크립트 |
script src async defer module nomodule | 독자를 위한 연습으로 남겨두어주세요* |
- https://addyosmani.com/blog/script-priorities/
- https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/
- https://jakearchibald.com/2017/es-modules-in-browsers/
- https://developers.google.com/web/fundamentals/primers/modules
- https://gist.github.com/jakub-g/5286483ff5f29e8fdd9f
- https://bitsofco.de/async-vs-defer/