자바스크립트 script 태그 async & defer 속성

@JasonLee · February 22, 2022 · 6 min read

서론
본론
body 내부에 script 태그 작성
head 태그 내부에 async 를 사용
head 태그 내부에 defer 를 사용
위 세개의 방식의 함축
결론

서론

일반적으로 나는 보통 Vanilla JS 로 프로젝트를 진행할때
<script> 태그를 사용하여 HTML 파일에 Javascript 파일을
import 하는 방식을 일방적으로 떠올렸다.
하지만, 보통 <head> 태그 내부에

<!DOCTYPE html>
<html lang="ko"
      <head>
        <meta charset="UTF-8" />
        <title>이승환</title>
        <script src="app.js"></script>
      </head>
</html>

위와 같이 일반적인 방법으로 쓰곤 하였는데, 이때 브라우저의 동작방식은
"엔진 자체가 위에서부터 HTMLparsing 하다가 script 태그를 만나면,
HTML 의 Parsing 을 잠시 멈추고 JS 파일을 fetchingexecute 한다."

정도인 것으로 알고 있었다. 이를 단순하게 보면,

parse HTML =>
HTML parsing temporarily blocked =>
fetch js =>
execute js =>
continue parsing HTML =>
Done!!

라고 생각할 수 있다.
이때 만일 해당 web applicationjs file
지나치게 많이 의존하고 있는 상황이거나, 인터넷의 속도가 느려 js file 의 fetchingexecuting process 시간이 길어지면 사용자는 아무 화면조차 볼 수 없는 상황에 직면할 수 밖에 없다.

이에따른 해결점에 대해 구글링을 해보았다.


본론

body 내부에 script 태그 작성

<!DOCTYPE html>
<html lang="ko"
      <head>
        <meta charset="UTF-8" />
        <title>이승환</title>
      </head>
      <body>
        <p>이승환입니다</p>
        <script src="app.js"></script>
      </body>
</html>

그렇다면 body 태그 내부에, 또한 최하단에 script 태그를 사용하는 방식을
고려해 볼 수 있다. 이때의 웹브라우저의 동작 방식을 생각해본다면,

parse HTML =>
when HTML is all ready then fetch js =>
execute js =>
Done!!

위와 같이 HTML parsing 이 끝나면,
js fetching 을 시작하고, 해당 작업이 끝나면,
js file 을 실행한다.

이때, 인터넷이 느린 사용자는 긴 시간을 기다릴 필요 없이
비교적 짧은 시간안에 페이지를 볼 수 있지만,
문제는 사용자의 actionjs file 과 깊은 연관성이 있거나, 요소의 stylingjs 로 이루어져 있다면,
그야말로 빈 껍대기 뿐인 화면을 보는 상황에 직면하게 된다는 점이다.

head 태그 내부에 async 를 사용

<!DOCTYPE html>
<html lang="ko"
      <head>
        <meta charset="UTF-8" />
        <title>이승환</title>
        <script async src="app.js"></script> //async
      </head>
      <body>
        <p>이승환입니다</p>
      </body>
</html>

head 태그 내부에, async 를 사용하여 js fileimport 한다면,
웹 브라우저의 동작 방식은

parse HTML & fetch js =>
when js is all fetched block HTML parsing and execute js =>
continue parsing HTML =>
Done!

위의 방식을 설명하자면,
초기 HTMLparsing 하다 async property 가 붙은 script 태그를 발견하면, 병렬적으로 HTML parsingjs fetching동시에 한다.
그후, js fetching 이 끝난다면, HTML parsing 을 멈춘뒤 js file 을 실행한다.
이후에 마지막으로 멈췄던 HTML parsing 을 지속한다.

뭔가 프로세스 자체가 굉장히 혁신적으로 병렬적 처리를 하며, 완벽해 보이지만, 이 방식은 HTML parsing 이 끝나기도 전에, js file 이 실행되므로, js file 이 HTML DOM 요소를 컨트롤 하는 코드가 있다면, 이는 실행되지 않거나 에러를 유발할 수 있다.

더 이상적인 방식은 없을까?

head 태그 내부에 defer 를 사용

<!DOCTYPE html>
<html lang="ko"
      <head>
        <meta charset="UTF-8" />
        <title>이승환</title>
        <script defer src="app.js"></script> //defer
      </head>
      <body>
        <p>이승환입니다</p>
      </body>
</html>

head 태그 내부에, defer 를 사용하여 js fileimport 한다면,
웹 브라우저의 동작 방식은

parse HTML & fetch js =>
when HTML is all parsed execute js =>
Done!

일단 한눈에 보기에도 process 자체가 확실히 줄었다!
설명을 하자면, HTML parsing 을 하던 중 script defer 를 만나게 되면, 병렬적으로 HTML parsing 과 js fetching 을 진행한다.
그후, HTML parsing 이 완료되면, js file 을 실행한다.

이 얼마나, 획기적인 방식인가. 사용자가 아무것도 못보는 시간을 단축하면서도, js 파일을 병렬적으로 fetching 하면서도, HTML parsing 이 끝나서야 js file 을 실행한다니.

위 세개의 방식의 함축

2c239f8e


결론

하다못해 js file 을 import 하는 과정에도 상황에 맞는 방식을 택해야 하며, 고려할 상황이 꽤 많다는 점을 캐치하게 되었다. 해당 글을 작성했다고 무조건 defer 를 써야지! 라고 생각할 것이 아닌, 모든 방식에 각각의 장단점과 동작원리를 알고 사용하는것이 중요하다고 다시 한번 생각한다.

@JasonLee
Hello :) I'm Jason Lee and currently focusing on React.js.