A:
브라우저 랜더링 과정
DOM트리 ▶︎ CSSOM트리 ▶︎ redner tree ▶︎ layout ▶︎ paint
JS 엔진과 렌더링 엔진은 직렬적으로 실행되는데 이 때문에 html 중간에 script태그를 만나면 자바스크립트 엔진으로 제어권이 넘어가게 됩니다.
DOM이 완성되지 않은 상태(렌더트리가 다 구성되지 않았을 때)에서 script로 제어권이 넘어가면 JS 엔진이 DOM 트리 변경시킬 수도 있고 에러가 발생할 수 있어 script태그는 body의 끝부분에 위치시키는 것이 좋습니다.
단 body에 넣는 경우 사용자가 기본적인 HTML컨텐츠를 빨리 본다는 장점은 있지만
자바스크립트 의존성이 큰 페이지인 경우, 사용자가 정상적으로 작동하는 웹 페이지를 보지는 못합니다.
이를 해결하기 위한 방법으로 HTML5부터 async와 defer 추가됩니다.
<p>...스크립트 앞 콘텐츠...</p>
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- 스크립트 다운로드 및 실행이 끝나기 전까지 아래 내용이 보이지 않습니다. -->
<p>...스크립트 뒤 콘텐츠...</p>
async는 boolean 타입이기 때문에 선언하는것만으로도 true로 쓸 수 있다.
브라우저가 HTML을 파싱하다가 async을 확인하고 병렬로 다운로드를 명령만 해놓고 다시 파싱을 하고 JS 파일이 다운로드되면 그때 파싱을 멈추고
다운로드된 JS 파일을 실행하게된다. 이후 실행을 다하고나서 나머지 HTML을 파싱하게된다.
다운로드받는속도를 절약할 수 있지만 HTML이 파싱되기도전에 실행이 되기 때문에 querySelector로 DOM 요소를 조작하려 한다면
위험할 수 있으며 HTML을 파싱하는동안 언제든지 자바스크립트를 실행하기 위해서 멈출 수 있기때문에
사용자가 페이지를 보는데 여전히 시간이 조금 걸릴 수 있는 단점이 있다.
즉, 자바스크립트 파일의 로드는 동기적으로 진행할 수 있으나, 바로 자바스크립트 파일이 실행되므로 실행 순서를 보장할 수는 없음.
<body>
... 스크립트 위 콘텐츠들 ...
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
</body>
HTML을 파싱을하다가 defer를 확인하고 JS 파일 다운로드를 명령시킨후 나머지 HTML을 끝까지 파싱하게 된다. 이후 마지막
파싱을 끝낸 다음에 다운로드되어진 JS 파일을 실행하도록한다.
참고자료
(https://velog.io/@eojine94/JavaScript-%EC%8B%A4%ED%96%89%EB%90%98%EB%A9%B4-html-%ED%8C%8C%EC%8B%B1-%EC%A4%91%EB%8B%A8%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0)
(https://velog.io/@cherrycock/script-async%EC%99%80-defer%EC%9D%98-%EC%B0%A8%EC%9D%B4)
(https://ko.javascript.info/script-async-defer)