프론트엔드 면접을 복기하다가 예전에 정리해 놓은 부분에서 질문이 나왔다는 것을 확인해버린 것이다.. 이번 참에 확실히 개념을 짚고 넘어가고자 한다.
브라우저는 HTML을 읽다가 <script>
를 만나면 스크립트를 먼저 실행해야 하므로 DOM 생성을 멈추며, 이는 src
속성이 있는 외부 스크립트를 만난 경우도 동일하게 작동한다. 외부에서 스크립트를 다운받고 실행한 후에야 남은 페이지를 이어서 처리하게 된다.
이 때, 스크립트 파싱 시에는 스크립트 아래에 있는 DOM 요소에 접근할 수 없기 때문에 DOM 요소에 핸들러를 추가하는 것과 같은 여러 행위가 불가능해진다. 따라서, 페이지 상단에 용량이 큰 스크립트가 있는 경우, 해당 스크립트가 페이지 생성을 막기 때문에 페이지에 접속하는 사용자들은 스크립트를 다운받고 실행할 때까지 스크립트 아래쪽 페이지를 볼 수 없게 되는 상황이 생긴다. 그렇다면 이러한 문제를 어떻게 해결할 수 있을까?
HTMLScriptElement - Web APIs | MDN
index.html
파일의 <script>
태그가 어디에 위치하느냐에 따라 파싱 과정의 차이가 생긴다.
<head>
내 <script>
<script>
진입 시, html Parsing(분석)을 잠시 중단한다.<body>
끝 <script>
<body>
내 마지막에 Javascript 파일을 서버에서 다운로드한 후 실행하므로, 웹페이지의 동작이 Javascript에 존재하는 코드에 의존적일 경우에는 사용자로 하여금 의미있는 컨텐츠를 보게 하는 것이 어렵다.
이제 이 부분이 면접에서 나온 부분이다. 사실 파싱 과정은 브라우저 렌더링 과정의 일부인데, 결국 이 부분을 defer나 async를 적절히 사용하여 최적화할 수 있다.
defer는 사전적 의미로 '지연하다'라는 뜻이다. 브라우저는 defer
속성이 있는 스크립트(이하 defer 스크립트)를 백그라운드에서 다운로드한다. defer 스크립트를 다운로드 하는 도중에도 HTML 파싱이 멈추지 않으며, defer 스크립트 실행은 페이지 구성이 끝날 때까지 지연된다.
DOMContentLoaded
이벤트 발생 전에 실행된다.<script>
에 src가 없으면 defer 속성은 무시된다.async
속성이 붙은 스크립트(이하 async 스크립트)는 페이지와 완전히 독립적으로 동작한다.
특징
DOMContentLoaded
이벤트와 별개로 동작한다. (기다리지 않는다.)순서
<script>
진입 시, 병렬적으로 Javascript 파일을 서버에서 다운로드한다.