- Published on
JS 단원 05: 스크립트를 문서 하단에 두는 이유 및 defer/async 비교
1. HTML 렌더링과 JavaScript 실행 순서
브라우저는 HTML을 위에서 아래로 순차적으로 파싱하면서 렌더링합니다.
그 중간에 <script>
태그가 있으면 파싱을 중단하고 스크립트를 먼저 실행합니다.
<!-- HTML 파싱 도중 실행 중단됨 -->
<script src="main.js"></script>
→ 따라서 <head>
안에 스크립트를 두면 DOM이 완성되기 전에 실행되어 오류가 발생할 수 있습니다.
<body>
하단에 위치
2. 전통적인 해결책: <!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 비교
항목 | 일반 script | defer | async |
---|---|---|---|
HTML 파싱 중단 | O | X | X |
로딩 중 실행 | X | X | O |
DOMContentLoaded 이후 실행 | X | O | X |
실행 순서 보장 | O | O | X |
추천 사용 | X | O | 조건부 (단일 스크립트) |
type="module"
)
6. 모듈 스크립트에서의 동작 (<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 구조에 적합합니다.