스크립트의 로드 시점을 알기전에 브라우저가HTML
문서를 해석(Parsing)할 때 일어나는 일들에 대해서 이해하면 좋을것 같다.
HTTP
모듈 또는 파일시스템으로 전달받은 리소스 스트림(resource stream)을 읽는 과정을 거치고 파싱을 통해 DOM(Document Object Model)
트리를 생성한다.파싱(Parsing )?
문서를 파싱한다는 것은 문서에서 읽어낸 데이터를 이해하고 사용할 수 있는 형태로 변환하는 것을 의미한다. 이러한 동작을 하는 기능또는 프로그램을 파서(Parser) 라고 한다.
브라우저에서 HTML 문서를 파싱한 결과를 파싱트리(parse tree) 또는 문법트리(syntax tree)라고 부른다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<link rel="stylesheet" type="text/css" href="default.css" />
<link rel="stylesheet" type="text/css" href="dark.css" />
<script src="jquery.js"></script>
<script src="main.js"></script>
</head>
<body>
...
</body>
</html>
위와 같은 문서를 불러올 때 브라우저는 먼저 HTML
을 파싱하고 외부자원인 CSS, JS 파일을 로드하게 된다.
DOM 트리가 완성되면 이미지 파일이나 외부 리소스를 로드하면서 모든 작업이 완료된다.
여기서 중요한 점은 브라우저가 script
를 해석할 때 어떻게 되는 것인가 인데, 일반적인 경우에는 자바스크립트는 인라인 코드의 경우 즉시 실행되고 파일로 전달했을 때는 전달이 완료된 시점에 실행된다. 문제는 아래의 그림과 같이 브라우저가 자바스크립트 다운로드 및 실행하는 동안은 문서의 파싱은 중단된다.
한편 CSS는 DOM트리를 변경하지 않기 때문에 문서파싱을 기다리거나 중단하지 않는다.
위의 코드에서는 스타일을 먼저 불러온 뒤 스크립트를 로드하지만 만약 스크립트 파일을 먼저 로드하게 될 경우에 스크립트에서 스타일 정보를 요청하게 된다면 잘못된 결과를 내놓기 때문에 결과적으로 사용자경험(UX)을 떨어뜨리게 될 것이다. 이런 문제는 흔치않은 것처럼 보이지만 매우 빈번하게 발생한다.
따라서 위 코드를 다음과 같이 스크립트 태그를 body 태그 끝에 두는것을 권장한다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<link rel="stylesheet" type="text/css" href="default.css" />
<link rel="stylesheet" type="text/css" href="dark.css" />
</head>
<body>
...
<script src="jquery.js"></script>
<script src="main.js"></script>
</body>
</html>
async
속성을 사용하여 스크립트 파일이 비동기적으로 실행될 수 있음을 나타낼때 사용할 수 있다. 즉, 브라우저가 async
속성이 있는 스크립트 태그를 만났을 때 HTML 파싱을 멈추지 않고 스크립트가 실행할 준비가 되었을 때 실행시킨다.
<script async src="main.js"></script>
스크립트의 실행순서가 다운로드 완료된 시점에서 결정이 되므로 실행 순서가 중요한 스크립트들에 async
속성을 사용할 때는 유의해야한다.
defer
속성은 HTML 파싱이 종료되었을 때 스크립트 파일을 실행하도록 브라우저에게 지시하게 된다.
<script defer src="main.js"></script>
위 그림과 같이 브라우저가 스크립트를 다운로드를 완료하더라도 스크립트는 바로 실행되지 않는다. 또한 async
와 다른점은 호출된 순서대로 실행이 된다는 점이다.
스크립트의 의존성 여부에 따라 결정하면 유용하게 사용할 수 있을 것이다. 의존성이 없는 스크립트의 파일을 실행할 때는 async
를 적절하게 사용하고, 의존성과 실행순서가 중요하다면 defer
를 사용해 주면 유용할 것이다.