Published on

JS 단원 05: 스크립트를 문서 하단에 두는 이유 및 defer/async 비교

1. HTML 렌더링과 JavaScript 실행 순서

브라우저는 HTML을 위에서 아래로 순차적으로 파싱하면서 렌더링합니다.
그 중간에 <script> 태그가 있으면 파싱을 중단하고 스크립트를 먼저 실행합니다.

<!-- HTML 파싱 도중 실행 중단됨 -->
<script src="main.js"></script>

→ 따라서 <head> 안에 스크립트를 두면 DOM이 완성되기 전에 실행되어 오류가 발생할 수 있습니다.


2. 전통적인 해결책: <body> 하단에 위치

<!DOCTYPE html>
<html>
  <head><title>문서</title></head>
  <body>
    <div id="app"></div>
    <script src="main.js"></script>
  </body>
</html>
  • HTML 파싱이 모두 끝난 뒤 JS 실행
  • DOM 접근 시점이 안전함

3. defer 속성

<script src="main.js" defer></script>
  • HTML 파싱과 동시에 JS 로드
  • HTML 파싱 완료 후 JS 실행 (DOMContentLoaded 직전에 실행)
  • 여러 스크립트도 순서대로 실행됨

4. async 속성

<script src="main.js" async></script>
  • HTML 파싱과 동시에 JS 로드
  • 로드 완료 시 즉시 실행 (HTML 파싱 중단됨)
  • 스크립트 간 순서 보장 안 됨

5. defer vs async 비교

항목일반 scriptdeferasync
HTML 파싱 중단OXX
로딩 중 실행XXO
DOMContentLoaded 이후 실행XOX
실행 순서 보장OOX
추천 사용XO조건부 (단일 스크립트)

6. 모듈 스크립트에서의 동작 (type="module")

<script type="module" src="main.js"></script>
  • 자동으로 defer 동작 포함
  • import/export 문법 사용 가능
  • strict 모드 자동 적용

7. 실습 예제

script 위치에 따른 실행 타이밍

<script>
  document.getElementById("target").textContent = "Hello";
</script>
<p id="target"></p> <!-- 오류 발생 -->

→ DOM이 아직 준비되지 않았기 때문

해결 방법

  • body 하단에 스크립트 배치
  • DOMContentLoaded 이벤트 사용
document.addEventListener("DOMContentLoaded", function () {
  document.getElementById("target").textContent = "Hello";
});

8. 성능 최적화 관점에서의 선택

  • script 위치나 속성 설정은 렌더링 차단 여부에 큰 영향
  • 외부 스크립트는 defer 기본, 내부 동적 삽입은 type="module" 권장

요약

  • script 태그를 <body> 하단에 두는 이유는 DOM이 모두 파싱된 후 안전하게 실행되기 위해서
  • defer는 비동기로 로드되지만 실행은 DOM 완성 후
  • async는 즉시 실행되며 순서 보장이 없기 때문에 주의 필요
  • 모듈 스크립트는 자동 defer 동작 포함

심화학습

Q1. async로 여러 스크립트를 로드하면 어떤 문제가 발생할 수 있나요?
A1. 네트워크 상황에 따라 실행 순서가 달라져 의존성이 있는 스크립트 간 충돌이 발생할 수 있습니다.


Q2. defer와 body 하단 스크립트의 차이는 무엇인가요?
A2. 기능상 유사하지만, defer는 명시적이며 script 태그 위치에 상관없이 실행 시점을 제어할 수 있습니다.


Q3. 모듈 스크립트의 defer 동작은 어떤 점에서 유리한가요?
A3. 자동 defer 처리와 함께 모듈화, 스코프 격리, strict 모드까지 적용되어 현대적인 JS 구조에 적합합니다.