<script> 태그의 async와 defer

1. 위치에 따른 렌더링 시간

웹 브라우저의 역할은 웹 페이지를 이루고 있는 여러 파일을 다운로드 받은 후 HTML 문서를 위에서부터 한 줄씩 읽으며(parsing) 화면에 보여주는 것이라고 할 수 있습니다. HTML 파일에는 CSS, Javascript와 같은 여러 파일이 링크된 상태로 포함되어 있는데, 이러한 파일들은 해당 파일을 링크하는 코드가 읽힌 후 다운로드 됩니다. 먼저, <head>태그에서 Javascript 파일이 링크된 경우를 살펴보겠습니다.

<head>
  <title>Document</title>
  <script src="js/app.js"></script>
</head>
<body>
  <div></div>
</body>

웹 브라우저는 <head>태그부터 차례대로 파싱하다가 <script> 태그를 만나면 해당 파일을 다운로드 하기 위해 잠시 HTML 파싱 과정을 멈춥니다. 다운로드가 완료되면 해당 Javascript 파일을 실행한 후 이어서 나머지 HTML 파일을 파싱합니다. 만약, 해당 Javascript 파일의 용량이 매우 큰 상황인 경우 웹 페이지를 화면에 나타내기까지 비교적 오랜 시간이 소요될 수 있습니다.

→ Parsing HTML
→ Stop Parsing HTML
    → fetching JS
    → executing JS
→ Continue Parsing HTML
→ End Parsing
→ Render Page

그렇다면, <body> 태그의 맨 끝 부분에서 Javascript를 불러오는 경우를 생각해봅시다.

<head>
    <title>Document</title>
</head>
<body>
    <div><div>
    <script src="js/app.js"></script>
</body>

이 경우에는 웹 브라우저가 HTML 파싱을 모두 마친 후 Javascript 파일을 다운로드하기 때문에 앞의 상황에 비해 훨씬 빠르게 웹 페이지를 화면에 나타낼 수 있습니다. 그러나, 해당 웹 사이트의 주된 컨텐츠가 Javascript를 통해 생성되는 등 Javascript에 의존적인 경우 정상적인 웹 페이지를 화면에 나타내기까지 어느정도 시간이 소요된다는 단점이 있습니다.

→ Parsing HTML
→ End Parsing
    → fetching JS
    → executing JS
→ Render Page

2. async와 defer 속성에 따른 렌더링 시간

이번에는 ascync로 Javascript 파일을 불러오는 경우를 살펴보겠습니다.

<head>
  <title>Document</title>
  <script async src="js/app.js"></script>
</head>
<body>
  <div></div>
</body>

이 경우에는 웹 브라우저가 HTML 파싱 도중 <script> 태그를 만나면, HTML 파싱과 해당 Javascript 파일 다운로드를 병렬적으로 수행하고, 다운로드가 완료된 이후 파싱을 멈추고 해당 Javascript 실행합니다. 이어서, Javascript 실행이 종료된 후 파싱을 재개합니다. 이는 앞의 두 상황과는 다르게 Javascript 파일 다운로드를 병렬적으로 수행함으로써 파일 다운로드 시간을 절약할 수 있다는 장점이 있는 반면, 해당 Javascript 파일은 파싱이 완료되기 전에 실행되므로 아직 파싱되지 않는 DOM에 접근하는 경우 오류를 발생시킬 수 있습니다. 또한, 여전히 Javascript 실행은 독립적으로 수행되므로 웹 페이지를 화면에 나타내기까지 어느정도 시간이 소요된다는 단점도 있습니다.

→ Parsing HTML
    → fetching JS
→ Stop Parsing HTML
    → executing JS
→ Continue Parsing HTML
→ End Parsing
→ Render Page

그렇다면, defer 속성으로 Javascript 파일을 불러오는 경우를 살펴보겠습니다.

<head>
  <title>Document</title>
  <script defer src="js/app.js"></script>
</head>
<body>
  <div></div>
</body>

이 경우에는 async와 마찬가지로 HTML 파싱과 Javascript 파일 다운로드를 병렬적으로 수행하되, 다운로드가 완료된 이후 Javascript 파일을 실행하지 않고 HTML 파싱을 진행합니다. 그리고, 웹 페이지 렌더링 준비가 완료된 이후 다운로드 받은 Javascript를 실행합니다. 이 과정을 보면 알 수 있듯이 먼저 웹 페이지를 화면에 보여주고, 곧바로 Javascript를 실행시키므로 위의 다양한 방법들 중에서 가장 효율적인 방법이라고 할 수 있습니다.

→ Parsing HTML
    → fetching JS
→ End Parsing
→ Render Page
    → executing JS

3. async와 defer의 차이점

위에서는 async와 defer 속성을 통해 Javascript가 포함된 웹 페이지를 렌더링하는 부분에 대해서 살펴보았습니다. 이번에는 async과 defer가 각각 어떠한 방식으로 동작하는지 간단하게 정리해 보았습니다. 먼저, async를 통해 여러 개의 Javascript 파일을 실행시키는 순서를 살펴봅시다. 위에서 언급한 – HTML 문서를 위에서부터 한 줄씩 읽으며… – 내용을 토대로 아래 코드를 보면 a.js → b.js → c.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>

그러나, async는 위의 경우에서 살펴보았듯이 다운로드는 병렬적으로 수행하고, 다운로드가 완료된 파일을 바로 실행시키므로 먼저 다운로드가 완료된 순으로 Javascript 파일을 실행시킵니다. 가령, 위의 Javascript 파일이 c.js → a.js → b.js 순으로 다운로드 된 경우 그 과정을 나타내면 다음과 같습니다.

→ Parsing HTML
    → fetching a.js
    → fetching b.js
    → fetching c.js
→ Stop Parsing HTML
    → executing c.js
→ Continue Parsing HTML
→ Stop Parsing HTML
    → executing a.js
→ Continue Parsing HTML
→ Stop Parsing HTML
    → executing b.js
→ Continue Parsing HTML
→ End Parsing
→ Render Page

이 경우, 먼저 실행된 c.jsa.js에 의존적인 경우 아직 a.js를 불러오지 못했으므로 오류를 발생시킬 수 있습니다. 반면, defer 속성을 적용시킨 경우에는 HTML 파싱과 Javascript 다운로드를 병렬적으로 수행하고, HTML 파싱을 완료한 이후에 Javascript를 실행시키므로 HTML에 명시한 순서대로 코드가 실행됩니다.

→ Parsing HTML
    → fetching a.js
    → fetching b.js
    → fetching c.js
→ End Parsing
→ Render Page
    → executing a.js다
    → executing b.js
    → executing c.js

따라서, HTML에서 Javascript를 불러올 때 <head> 태그 안에서 defer 속성을 적용시킨 방법이 가장 안정적이며, 효율적인 방법이라고 할 수 있습니다.

"" 개의 결과를 찾았습니다.

    ""에 대한 결과를 찾을 수 없습니다.