이 포스팅은 Javascript 기초 시리즈 7 편 중 2 번째 글 입니다.

  • Part 1 - 01: Introduction
  • Part 2 - This Post
  • Part 3 - 03. 변수, ES5 문제
  • Part 4 - 04. Functions
  • Part 5 - 05. Class
  • Part 6 - 06. Object
  • Part 7 - 07. Array
▼ 목록 보기

js file의 로드 위치에 따른 차이 이해

어느 위치에서 js 파일을 로드하느냐에 따라 페이지가 렌더링될 때 양상이 달라질 수 있다.

01.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="main.js"></script>
</head>
<body>
    
</body>
</html>

브라우저는 한 줄씩 코드를 읽고 해석한다. 이 떄 script 태그를 만나게 되면 main.js 파일을 읽게 된다. 이 때, 비동기 방식으로 진행하지 않고 동기 방식으로 진행되기 때문에 페이지를 렌더링하는 메인 스레드는 block 처리 된다.

이 때, 추가적으로 알아야 하는 사실이 있는데, main.js를 실행하기 위해서는 총 2개의 단계를 거치게 된다.

  1. fetching
    • 해당 main.js 파일을 메모리에 로드하는 과정이다.
  2. executing
    • 로드된 파일을 실행하는 과정이다.

그렇기 때문에 main.js를 로드하고, 이를 실행하는 과정을 모두 진행한 후에야 아래 코드를 읽으면서 html 컨텐츠를 로딩할 수 있다.

02.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <script src="main.js"></script>
</body>
</html>

그런 단점 때문에 body 태그 근처에 script 태그를 위치시킨다. 이럴 경우 먼저 html이 렌더링이 되기 때문에 화면은 보여진다. 그런데 만약에 페이지 자체가 js에 굉장히 의존적이라면, (보기 위해서는 사용자의 정보를 서버에서 받아와야 한다던지, 동적인 요소가 난무한다던지, 게임이라던지?) js 파일을 받고 실행하기 전까지는 제대로 된 화면을 볼 수 업다는 문제가 생긴다.

03.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script async src="main.js"></script>
</head>
<body>
    
</body>
</html>

이렇게 쓰게되면, 브라우저가 script 태그를 만날 경우 main.js를 다른 스레드를 사용하여 fetching 하라고 명령하게 된다. fetching이 끝나면 렌더링을 하는 main 스레드는 block 처리가 되고, 다운로드된 js를 실행한다. 그리고 난 후 나머지 html를 렌더링한다.

확실히 시간적인 이득은 볼 수 있다. 하지만 html이 렌더링 되기 전에 실행되기 때문에, 어떠한 컨텐츠냐에 따라서 제대로된 화면을 제공할 수 없을 수 있다. 만약 쿼리셀렉터로 html의 요소를 제어한다고 할 경우, 해당 html 요소가 아직 렌더링이 되지 않은 상태라면 굉장히 위험하다.

또한 여러개의 js 파일을 로딩한다면 여전히 중간중간에 렌더링을 멈추기 때문에 전체적인 화면을 보는데 까지 시간이 소요된다는 단점 역시 존재한다.

04.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script defer src="main.js"></script>
</head>
<body>
    
</body>
</html>

이러한 문제를 해결할 수 있는 것이 바로 defer이다. defer는 다운로드와 실행을 따로 진행한다. 그래서 script 태그를 만나면, 일단 다운로드를 다른 스레드로 병렬로 진행하고, 이 다운로드가 끝날 경우 대기하다가 html 렌더링이 끝나고 난 후에 js를 실행한다.

async, defer

추가적으로 여러개의 js 파일을 로딩한다고 가정해보자. 그렇다면 async 같은 경우 다운로드가 먼저되는 시점에 실행이 이루어지기 때문에 js 파일의 처리 과정이 계속해서 변경된다. a, b, c 순으로 항상 로딩되는 것이 아니기 때문에 각 파일의 의존성이 있을 경우 문제가 발생한다.

하지만 defer는 다운로드 시점과 실행 시점이 다르기 때문에 이를 원하는 순서대로 실행할 수 있다. 따라서 좀더 안전하고 좋은 방법이라고 생각할 수 있다.

Reference

자바스크립트 2. 콘솔에 출력, script async 와 defer의 차이점 및 앞으로 자바스크립트 공부 방향 | 프론트엔드 개발자 입문편 (JavaScript ES5+)