script의 위치와 속성

Front_H·2020년 10월 17일

javascript

목록 보기
4/5
post-thumbnail

script의 위치에 대해 언급하기 전에 html,css,javascript가 브라우저에 표시되는 과정을 다시 한번 떠올려 보자!

브라우저가 서버에서 페이지에 대한 HTML응답을 받으면, 화면에 표시되기 전에 많은 단계를 거쳐야한다. 브라우저가 페이지의 초기 출력을 위해 실행해야하는 이 순서를 'Critical Rendering Path(CRP)'라고 한다.

CRP의 단계

  1. DOM트리 구축

    DOM트리는 완전히 구분 분석된 HTML페이지의 Object표현이다. 루트 요소 <html> 로 시작하여 페이지의 각 요소에 대한 노드가 만들어진다.

  2. CSSOM트리 구축

    CSSOM(CSS Object Model)은 DOM과 연관된 스타일의 Object표현이다. css는 "렌더링 차단 리소스"로 간주된다.즉, 먼저 리소스를 완전히 파싱하지 않으면 렌더링 트리를 구성할 수 없다.

  3. javascript 실행

    Javascript는 "파서 차단 리소스"로 간주된다. 즉, HTML문서 자체의 구분 분석은 Javascript에 의해 차단된다.
    파서가 내부 태그이든 외부 태그이든 <script>태그에 도달하면 fetch를 중단하고 실행한다. 따라서 문서 내의 요소를 참조하는 Javascript파일이 있는 경우, 해당 문서가 표시된 이후에 배치해야 된다.

  4. 렌더링 트리 구축

    렌더링 트리는 DOM과 CSSOM의 조합이다. 페이지에서 최종적으로 렌더링될 내용을 나타내는 트리다. 즉, 표시되는 내용만 캡쳐하기 때문에 display:none을 사용하여 css로 숨겨진 요소는 포함하지 않는다.

  5. 레이아웃 생성

    레이아웃은 뷰포트의 크기에 따라 css스타일에 대한 컨텍스트에 의해 뷰포트의 크기를 결정한다.

  6. 페인팅

    마지막으로 Painting단계에서 페이지의 가시적인 내용을 픽셀로 변환하여 화면에 표시할 수 있다.


    우리가 여기서 눈여겨보아야할 단계는 3번째 javascript실행 단계다. 위에서 언급했듯이,웹브라우저는 html을 렌더링하는 과정에서 script를 만나면 동기적으로 처리한다. 다시 말해서, 해당 내용이 해석되고 실행되기 전에는 뒤에 나오는 내용을 처리하지 않는다는 것이다. 이 부분은 화면의 렌더링 속도에 큰 영향을 줄 수 있다.(사용자 경험 측면에서 큰 영향을 주며 SEO와도 관련된다.)

그렇다면 우리는 script를 어떤 식으로 넣어야할까..??

script위치와 속성

1. <head></head>안에 위치시키기

<!DOCTYPE html>
<html lang=ko">
<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>


HTML을 파싱하다가 <script>태그를 만나면 HTML파싱을 중단하고 script파일을 다운받고 실행을 끝마친 후에야 다시 HTML파싱을 재개한다.

문제 : script파일의 용량이 크거나 인터넷이 엄청 느리다면 다운받는데도 많은 시간이 소요

2. <body></body>의 끝부분에 위치시키기

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

이 방법은 HTML을 전부 파싱하여서 준비를 끝낸 후에 script파일을 다운받고 실행한다

문제 : 웹사이트가 javascript에 의존적이라면 즉, 사용자가 의미있는 컨텐츠를 보기위해서 서버에 있는 데이터를 받아온다던지 DOM요소를 더 꾸미던지 등의 정상적인 페이지를 보려면 사용자가 스크립트를 fetching하는 시간까지 기다려야하는 불편함이 있다.

3. <head></head>안에 위치시키기 + async

<!DOCTYPE html>
<html lang="ko">
<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>
    <div></div>
</body>
</html>                      


async는 boolean타입의 속성으로 위의 코드처럼 선언만 해줘도 true로 설정이 되어서 바로 쓸 수 있다. 이 방법은 HTML을 파싱하다가 script태그를 만나면 HTML파싱과 script파일을 fetching하는 단계가 병렬로 이루어진다. 그리고 다운로드가 완료되면 HTML파싱은 중단되고 스크립트 파일만 실행된다. 스크립트 파일의 실행이 끝난 후에 다시 HTML파싱이 재개된다.

문제 : 우선, HTML을 파싱하는 도중에 스크립트가 실행되므로 만약 querySelector로 요소를 찾으려고 한다면 아직 정의되지않은 요소일 수 있다. 그리고 자바스크립트를 실행하는 동안에 파싱이 중단되기때문에 시간이 아직도 여전히 걸리는 방법이다.

async 옵션으로 다수의 script파일을 선언한 경우

<!DOCTYPE html>
<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="a.js"></script>
    <script async src="b.js"></script>
</head>
<body>
    <div></div>
</body>
</html>                               


이런 경우 script를 순차적으로 다운로드를 받지만 먼저 다운로드된 script파일이 먼저 실행된다. 즉, 선언한 순서에 상관없이 다운로드가 빨리된 것이 먼저 실행된다는 말이다.

스크립트의 순서가 중요하다면 이 방법은 쓰지않는 것을 권장

4. <head></head>안에 위치시키기 + defer

<!DOCTYPE html>
<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>
    <div></div>
</body>
</html>                                                         


이 방법은 HTML파싱을 하다가 script태그를 만나면 HTML파싱과 스크립트파일의 fetching을 병렬적으로 수행하고 HTML파싱이 끝난 후 스크립트가 실행된다.

defer 옵션으로 다수의 script파일을 선언한 경우

<!DOCTYPE html>
<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="a.js"></script>
    <script defer src="b.js"></script>
</head>
<body>
    <div></div>
</body>
</html>                                                         


이 방법은 HTML을 파싱하는 동안에 각각의 script파일이 fetching되고 파싱이 끝나면 선언한 순서대로 실행된다.

가장 권장하는 방법!! 그리고 알아둬야 할 것은 defer옵션은 외부스크립트에만 유효하다.

profile
drop the bit 0 and 1

0개의 댓글