
웹 브라우저는 서버로부터 웹 페이지를 이루고 있는 여러 파일을 전달 받은 후 HTML 문서를 위에서부터 한 줄씩 읽으며(Parsing) 화면에 보여준다.
HTML에 적용하는 CSS, JS 같은 파일들은 해당 파일을 링크하는 코드가 읽힌 후 다운로드된다.
<head>
<title>Document</title>
<script src="js/app.js"></script>
</head>
<body>
<div></div>
</body>
head 태그부터 차례대로 파싱하다가 script 태그를 만나면 해당 파일을 로드하기 위해 잠시 HTML 파싱 과정을 멈춘다.
JS 다운로드가 완료되면 해당 JS 파일을 실행한 후 이어서 나머지 HTML 파일을 파싱한다.
이러한 경우 JS의 파일 용량이 매우 크다면 웹 페이지를 화면에 나타내기까지 비교적 오랜 시간이 소요된다.
<head>
<title>Document</title>
</head>
<body>
<div><div>
<script src="js/app.js"></script>
</body>
HTML 파싱을 모두 마친 후 JS 파일을 다운로드한다. head에 있는 것보다 빠르게 페이지 화면에 나타낼 수 있다. 하지만 해당 웹 사이트의 주된 컨텐츠가 JS를 통해 생성되는 등 JS에 의존적인 경우 정상적인 웹 페이지를 화면에 나타내기까지 시간이 소요된다는 단점이 있다.
HTML에서 자바스크립트를 포함할때 어떻게 포함하는게 더 효율적인가??
결과 반환 시점 지정으로 Defer과 Async가 있다.
웹 페이지를 렌더할 때 브라우저는 HTML 파일 위에서 아래 순서로 읽으며 DOM Tree를 생성한다.
중간에 script 태그를 만나면 이것도 DOM요소 이기 때문에 해석하고 실행한다. 즉 해당 스프립트 파일을 모두 해석하기 전까지 나머지 HTML 렌더를 일시적으로 멈춘다.
문제점
서버간 통신할 때의 방식으로 실시간성과 비실시간성.
동기 요청 : 작업이 완료될 때까지 하던 작업 멈추고 대기.
응답이 즉각적이어야 하는 경우
비동기 요청 : 작업이 완료되든말든 하던 작업 그대로 진행.
- 작업이 완료되면, 완료되었다는 응답을 받음 -> Callback이라고 함.
응답이 즉각적이지 않아도 되는 경우. (요청에 대한 응답이 절대 누락되어서는 안됨.)


병렬 : HTML 파싱 -> 스크립트 호출 -> HTML 파싱 후 실행
브라우저가 페이지의 파싱을 모두 끝낸 후에 스크립트가 실행된다.
예시)



병렬 : HTML 파싱 -> 스크립트 호출 + 바로 실행 -> 스크립트 호출이 끝나면 HTML 파싱 을 잠시 멈추고, 스크립트 실행
특정 코드가 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행!!
브라우저가 페이지를 파싱되는 동안에도 스크립트가 실행됨.


외부 JS 파일들을 가져옴. 모듈에 export, import를 적용하면 다른 모듈을 불러와 함수를 호출할 수 있다고 함.
defer과 async에 대해 배워봤으니 실제 코드에서 어떻게 진행이 되는지 확인해보자.
<head>
<title>Document</title>
<script async src="js/app.js"></script>
</head>
<body>
<div></div>
</body>
HTML 파싱 도중 script 태그를 만나면 HTML 파싱과 JS 파일 다운로드를 병렬적으로 수행한다.
다운로드가 완료된 이후 파싱을 멈추고 해당 JS를 실행한다. 이어서 JS실행이 종료된 후 파싱을 재개한다.
파일 다운로드를 병렬적으로 수행하여 다운로드 시간을 절약할 수 있다. 하지만 JS 파일은 파싱이 완료되기 전에 실행되므로 아직 파싱되지 않는 DOM에 접근하는 경우 오류를 발생시킬 수 있다.
웹 페이지를 화면에 나타내기까지 시간이 걸리는 건 여전함.
async처럼 병렬적으로 수행하지만 다운로드가 완료된 이후 JS 파일을 실행하지 않고 HTML파싱을 마친다.
그 다음 렌더링 준비가 완료된 이후 다운로드 받은 JS를 실행한다.
<head>
<title>Document</title>
<script defer src="js/app.js"></script>
</head>
<body>
<div></div>
</body>
웹 페이지를 화면에 보여주고, 바로 JS를 실행시키므로 가장 효율적인 방법이다.
<head>
<title>Document</title>
<script async src="js/a.js"></script>
<script async src="js/b.js"></script>
<script async src="js/c.js"></script>
</head>
<body>
<div></div>
</body>
다운로드는 병렬 수행, 다운로드가 완료된 파일을 바로 실행시키므로 먼저 다운로드 완료된 순으로 JS파일 실행
😵 근데 위와 같은 코드에서 JS 파일이 a->b->c 가 아닌 c->a->b 순으로 다운로드가 된다면 어떻게 될까??
c.js가 a.js에 의존적인 경우 아직 a.js를 불러오지 못했으므로 오류 발생.
의존적?? :
진행 방향
HTML 파싱 시작
파싱 끝
페이지 Render
HTML 파싱과 JS 다운로드가 병렬 수행, HTML 파싱을 완료한 이후에 JS를 실행.
그러면 HTML에 명시한 순서대로 코드 실행된다.
결론
HTML에서 JS를 적용할 때 head 태그 안에 defer 속성을 적용시키는게 가장 효율적이다.
참고 자료
🔗 https://choewy.tistory.com/146 - JavaScript 위치 및 async/defer 속성에 따른 HTML 렌더링 효율성
🔗 https://fe-j.tistory.com/entry/async-defer-javascript-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%88%9C%EC%84%9C-%EB%B0%94%EA%BF%94-%EC%84%B1%EB%8A%A5%EB%86%92%EC%9D%B4%EA%B8%B0 - async , defer - javascript 렌더링 순서 바꿔 성능높이기
🔗 https://www.youtube.com/watch?v=tJieVCgGzhs - 콘솔에 출력, script async 와 defer의 차이점 및 앞으로 자바스크립트 공부 방향