HTML 문서에서 외부 JavaScript 파일을 어떻게 불러오느냐에 따라
버튼 클릭이 안 되거나, 에러가 나거나, 성능이 나빠질 수 있습니다.
이 글에서는 간단한 버튼 클릭 예제를 통해
자바스크립트 파일을 언제, 어떻게 로드하는 게 좋은지 알아봅니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="index.js"></script>
<title>Document</title>
</head>
<body>
<h1>제목</h1>
<p>내용</p>
<div>
<button id="btn">버튼</button>
</div>
</body>
</html>
let btn = document.querySelector('#btn');
btn.addEventListener('click', () => {
alert('하하하');
});

브라우저는 HTML을 위에서 아래로 순차적으로 해석(파싱)합니다.

<script src="index.js">를 만나면,
HTML 파싱을 멈추고 JS 파일을 가져와서 즉시 실행합니다.

이때 버튼 태그(<button id="btn">)는 아직 만들어지지 않음 → null 발생
이걸 console.log('btn:', btn);으로 찍어보면...

null 찍히는 것을 확인할 수 있다.
<body> 최하단에서 script 로드<body>
<button id="btn">버튼</button>
<script src="index.js"></script>
</body>
window.onloadwindow.onload = () => {
const btn = document.querySelector('#btn');
btn.addEventListener('click', () => {
alert('하하하');
});
};
DOMContentLoaded (✅ 추천)document.addEventListener('DOMContentLoaded', () => {
const btn = document.querySelector('#btn');
btn.addEventListener('click', () => {
alert('하하하');
});
});
🚀 실제로 비교해보면 DOMContentLoaded가 window.onload보다 더 빠르게 실행됩니다.

defer vs asyncHTML5에서는 defer async 속성을 통해 비동기 script 로드가 가능해져 이 문제를 근본적으로 해결되었습니다.
defer<head>
<script src="index.js" defer></script>
</head>

async<head>
<script src="index.js" async></script>
</head>


defer async📚 참고 및 출처
본 글은 짐코딩님의 자바스크립트 강의를 기반으로 정리하였습니다.(클릭하면 들어가짐)
실제 강의에서는 시각적 예시와 설명이 더 풍부하니, 공부에 많은 도움이 됩니다. 🙏