본문으로 건너뛰기
개발 머꼬
개발 노트HTML
hohyeon.dev18

스크립트에 async를 붙였더니 라이브러리가 undefined가 된 이유

  • #Engineering Note
  • #HTML
  • #Performance

문제 발생

<head>의 스크립트 두 개가 파싱을 막아 첫 화면이 늦게 그려졌습니다.

<head>
  <script src="/vendor.js"></script>
  <script src="/app.js"></script>
</head>

찾아본 대로 둘 다 async를 붙였더니 화면은 빨라졌는데, 이번에는 가끔 app.js에서 vendor is not defined가 났습니다. 로컬에서는 재현되지 않고 배포 환경에서만 났습니다.

원인 분석

두 속성이 해결하는 문제는 같지만 보장하는 것이 다릅니다. MDN은 양쪽 모두 parser-blocking JavaScript를 없앤다고 설명하면서, 실행 순서에 대해서는 정반대로 적습니다.

async에 대해서는 이렇게만 말합니다 — "You can't rely on the order the scripts will load in." 다운로드가 먼저 끝난 쪽이 먼저 실행됩니다. 그래서 파일 크기와 네트워크 상태에 따라 결과가 달라지고, 캐시가 따뜻한 로컬에서는 거의 재현되지 않습니다.

defer는 반대입니다 — "Scripts with the defer attribute will execute in the order in which they appear in the document." 그리고 "Scripts with the defer attribute will prevent the DOMContentLoaded event from firing until the script has loaded and finished evaluating." 즉 문서 순서가 유지되고, DOM 초기화 코드가 기대하는 시점 이전에 실행이 끝납니다.

인라인 스크립트에 붙인 것도 문제였습니다. MDN은 경고로 못박습니다 — src 속성이 없으면(인라인 스크립트) 이 속성을 쓰면 안 되며, 아무 효과가 없습니다.

해결 방안

  1. 서로 의존하는 스크립트는 defer입니다. 순서가 곧 계약입니다.
<head>
  <script src="/vendor.js" defer></script>
  <script src="/app.js" defer></script>
</head>
  1. async는 진짜로 독립적인 것에만 씁니다. 다른 스크립트를 참조하지도, 참조당하지도 않는 측정·분석 스크립트가 여기 해당합니다.

  2. 인라인 스크립트에는 붙이지 않습니다. 효과가 없는데 붙어 있으면 다음 사람이 "순서가 보장되는 줄" 오해합니다. 순서를 미루고 싶으면 파일로 빼서 defer를 주거나 type="module"로 바꿉니다.

  3. 모듈은 이미 defer입니다. 문서 그대로 "The defer attribute has no effect on module scripts — they defer by default." type="module"defer를 덧붙이는 건 의미가 없고, 여기에 async를 주면 그 모듈만 순서를 포기하는 셈입니다.

  4. </body> 직전 배치와 defer는 다릅니다. 둘 다 파싱을 막지 않지만, defer는 파싱과 병렬로 내려받는 반면 본문 끝 스크립트는 그 지점에 도달해야 요청이 시작됩니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.