<script>의 알맞은 위치
1. 개요
- 브라우저는 HTML 문서를 파싱할 때 문서의 맨 위에서부터 차례대로 태그를 하나씩 파싱한다. 이때, 파싱 과정에서 <script> 태그를 만나면 <script> 태그 안의 스크립트를 모두 실행하고 그 다음에 그 뒤에 있는 태그들을 파싱한다. (<script> 태그에서 src 링크가 지정돼있으면 해당 링크의 .js 파일을 다운받고 실행하고 그 다음에 그 뒤에 있는 태그들을 파싱한다.)
- 브라우저가 이러한 방식으로 HTML 문서의 자바스크립트 코드를 처리하므로 다음 문제가 발생할 수 있다.
1) <script> 실행 시점 기준으로는 <script> 태그 뒤에 있는 태그들이 DOM 트리에 아직 없기 때문에, <script> 태그 내에서 그 <script> 태그 뒤에 있는 태그 요소를 조작하려 하면 에러가 발생할 수 있다.
2) 브라우저가 스크립트를 다운받고 이를 실행하기까지 하는 데는 시간이 다소 걸릴 수 있는데, 이 경우 이 페이지의 방문자가 HTML 파싱을 하다 만 상태인 페이지를 오래 보고 있어야 한다.
2. 문제 해결 방법
1) </body> 태그 바로 위에 <script> 태그를 쓰기
- 이 경우 사용자에게 HTML 파싱을 하다 만 페이지를 보여줄 일이 없다는 장점이 있으나, 만약 그 페이지의 사용이나 렌더링에 자바스크립트가 필수적이라면 HTML 파싱이 다 끝날 때까지 이러한 작업에 제한이 발생한다.
- <script> 태그에 src 링크가 지정돼있을 경우, 이 방법을 쓰면 HTML 파싱이 다 끝난다음 그제서부터 링크로부터 .js 파일을 다운받기 시작한다는 단점이 또 있다. 다운로드가 HTML 파싱 작업에 방해가 되는 것도 아닌데 <script>가 HTML 문서 하단에 있다고 그 부분 파싱을 시작할 때 비로소 다운을 시작한다는 건 엄청난 시간낭비가 아닐 수 없다.
* <script> 태그에 defer 속성 넣기
1
<script defer src=""></script>
- <script> 태그를 <head> 태그 안에 넣되 <script> 태그의 속성으로 defer를 추가하면 위 단점을 어느 정도 해소할 수 있다. <script> 태그에 defer 속성을 추가하면, 브라우저가 HTML 파싱 과정에서 이 태그를 만나면 일단 그 시점부터 곧바로 .js 파일을 다운받는다(그리고는 HTML 파싱을 계속 한다). 그러나 다운이 끝난다고 해서 곧바로 그 .js 파일을 실행한다거나 HTML 파싱을 중단하거나 하지는 않고, 문서 전체의 HTML 파싱이 끝나면 그 다음에 비로소 다운받아둔 .js 파일의 내용을 실행한다.
2) <head> 태그 안에 넣되 실행할 스크립트를 함수에 담아 그 함수로 window.onload() 메서드를 재정의 하기
- window.onload()는 그 페이지의 모든 로드가 끝난 이후에 실행되는 메서드로, 다음 방식으로 재정의할 수 있다.
1
2
3
4
5
6
var func_name = function()
{
...
}
window.onload = func_name;
- <script> 태그를 굳이 불편하게 HTML 문서의 최하단까지 옮기지 않아도 된다는 장점이 있으나, 이 경우 HTML 파싱은 물론 HTML 파싱 후의 페이지 내 이미지 로드까지 다 끝난 다음에 스크립트가 실행되게 한 것이기 때문에 1번보다 결과가 더 나빠진다.
3) DOM 트리가 로드됐을 때 콜백함수를 실행하는 event listener를 window 객체에 추가하고, 실행할 스크립트를 담은 함수를 이 event listener의 인자로 전달하기
- window 객체의 addEventListener() 메서드에 “DOMContentLoaded” 문자열과 콜백함수를 인자로 해서 메서드를 호출하여 위 기능을 하는 event listener를 추가할 수 있다.
1
2
3
4
5
6
var func_name = function()
{
...
}
window.addEventListener("DOMContentLoaded", func_name);
- 이 경우 HTML 파싱이 끝나고 막 DOM 트리를 만든 시점에 바로 func_name 함수가 호출되므로 위 2번의 단점이 개선된다.
4) <script> 태그에 async 속성을 추가
1
<script async src=""></script>
- <script> 태그에 async 속성을 추가해서 쓰면, 브라우저가 그 태그를 만나더라도 거기서 HTML 파싱을 중단하지는 않고 HTML 파싱을 계속 진행은 하되, src로 지정된 링크에서 .js 파일을 다 다운받으면 그때 곧바로 HTML 파싱을 중단하고 해당 .js 파일 내 스크립트를 실행한다. 이처럼 async 속성을 사용하면 .js 파일의 다운로드로 인해 낭비되는 시간을 아낄 수 있다.
- async 속성을 사용하는 경우 다음 단점이 있을 수 있다.
1) .js 파일을 다운로드 받은 다음에 HTML 파싱을 중단하는 것은 마찬가지이므로, 만약 스크립트 실행으로 시간이 너무 오래 걸리게 된다면 이로 인해 페이지를 방문한 사람이 HTML 파싱을 하다 만 페이지를 너무 오래 봐야 하는 경우가 생길 수 있다.
2) 수개의 .js 파일을 모두 async 속성으로 로드되게 하는 경우, 각 .js 파일이 다운로드가 완료된 시점이 서로 다를 수 있다. 이 경우, 먼저 다운로드된 .js 파일에서 나중에 다운로드된 .js 파일에 있는 함수나 변수를 호출해 에러가 발생할 수 있다.