브라우저 렌더링 + Script 태그 위치

김재도·2022년 1월 26일

자바스크립트 강의를 듣다보면 script의 태그 위치가 다른 경우가 있습니다. 어떨 때는 head에 어떨 때는 body에 넣는 script 태그, 위치에 따라 어떻게 달라지는지 알아보겠습니다.

script 태그의 위치를 알아보기 전에 우리는 브라우저가 어떤 과정으로 렌더링되는지를 알아야할 필요가 있습니다. 흐름에따라 브라우저 렌더링 => script 태그의 위치 순으로 설명을 이어가겠습니다.

브라우저의 기본구조

  1. 사용자 인터페이스 : 주소 표시줄, 이전/다음/새로고침 버튼 등 요청한 페이지를 보여주는 창을 제외한 나머지 모든 부분
  2. 브라우저 엔진 : 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어.
  3. 렌더링 엔진 : (HTML, XML 문서 또는 이미지 등)요청받은 콘텐츠를 표시.
  4. 통신 : HTTP 요청과 같은 네트워크 호출에 사용.
  5. UI 백엔드 : 기본적인 위젯을 그리는 인터페이스.
  6. 자바스크립트 해석기 : 자바스크립트 코드를 해석하고 실행.
  7. 자료 저장소 : Cookie, Local Storage 등과 같이 브라우저 메모리를 활용하여 데이터를 저장

렌더링 순서

  1. 사용자가 브라우저의 인터페이스에 URI(통합 자원 식별자)를 입력

  2. 브라우저 엔진은 우선 자료 저장소에서 URI에 해당하는(캐싱 된) 자료를 찾아 렌더링 엔진에 전달. 자료가 없다면 저장이 되어있지 않다면 렌더링 엔진에게 URI를 전달

  3. 렌더링 엔진은 브라우저 엔진에서 가져온 자료를 분석. 동시에 통신레이어에게 URI 주소값을 전달

  4. 통신레이어가 서버에 URI에 해당하는 서버에 데이터를 요청 후 응답 대기

  5. 응답받은 데이터에서 HTML, CSS는 렌더링 엔진이 파싱. JavsScript는 자바스크립트 해석기로 전달해 파싱. HTML 파싱을 통한 DOM Tree 구축, CSS 파싱을 통한 CSSOM Tree 구축.

    * 파싱 : 일련의 문자열을 의미있는 token(어휘 분석의 단위) 으로 분해하고 그것들로 이루어진 Parse tree를 만드는 과정. 즉, 브라우저가 페이지를 렌더링 하기 위해 HTML, css와 같은 코드들을 해석하여 tree로 만드는 과정
  1. 자바스크립트 해석기는 파싱한 결과를 렌더링 엔진에 전달하여 DOM Tree를 조작

  2. 조작이 완료된 DOM Node(DOM Tree의 구성요소)는 Render Object(Render Tree의 구성요소)로 변함. DOM Tree와 CSSOM Tree를 합쳐서 Render Tree를 생성

  3. Layout (Reflow) 배치
    뷰포트 내에서 노드들의 정확한 위치와 크기를 계산하는 과정.
    텍스트나 요소의 박스가 화면에서 차지하는 영역이나 여백, 스타일 속성이 계산됩니다. 렌더 트리의 각 노드에 대해서 화면 상에서 어디에 배치할 지 결정합니다.
    (요소의 크기나 위치가 바뀔 때, 브라우저 창 크기가 바뀔때 다시 발생)

  1. Paint
    각 노드 배치를 완료하면 UI 벡엔드에서 Layout 단계에서 계산된 값을 이용해 Render Tree의 각 노드를 화면상의 실제 픽셀로 변환한다. 이때 픽셀로 변환된 결과는 하나의 레이어가 아니라 여러 개의 레이어로 관리된다.
    (Reflow가 일어나거나 배경 이미지, 텍스트 색상, 그림자 등 레이아웃의 수치를 변화시키지 않는 스타일의 변경이 일어났을 때 다시 발생)

  2. Composite
    Paint 단계에서 생성된 레이어를 합성하여 실제 화면에 나타낸다. 우리는 화면에서 웹 페이지를 볼 수 있다.

해당 과정을 그림으로 표현하면 다음과 같습니다.

위 과정을 보면 script 태그를 만나면 HTML파서는 DOM 트리 생성을 멈추고 자바스크립트 엔진(자바스크립트 해석기)에게 권한을 넘겨줍니다. 자바스크립트 엔진은 script 안에 코드 혹은 외부 JS파일을 다운받아 파싱한 후에 실행까지 완료하면 권한을 다시 HTMl파서에게 넘겨주고 DOM트리는 이어서 생성됩니다. 이런 동작원리를 가지고 있기 때문에 script 태그의 위치는 중요하다고 볼 수 있습니다.

script 태그의 위치에 따른 비교

1. head 태그에 위치할 때

code

위와 같은 코드가 있을 때
자바스크립트 파일은 다음과 같은 순서로 실행됩니다.

HTML 문서는 하향식으로 읽기 때문에 html 파싱 중 script 코드를 만나면 html 파싱을 멈추기 때문에 스크립트 파일이 클 경우 사용자는 파싱 되다만 화면을 보게 될 것 입니다. 또한 html 파싱이 다 진행되기 전에 script 코드가 실행되기 때문에 존재하지 않는 DOM 요소에 접근하려는 시도로 예상치 못한 문제가 발생할 수 있습니다.

2. body 태그 하단에 위치할 때

위와 같은 코드가 있을 때
자바스크립트 파일은 다음과 같은 순서로 실행됩니다.

위 방법은 html 파싱이 끝난 후 자바스크립트를 실행하기 때문에 1번 같은 문제도 일어나지 않고 속도도 빠르고 사용자가 파싱되다만 html을 볼 일은 없지만 자바스크립트에 의존적인 웹이라면 html이 파싱되더라도 자바스크립트가 실행되지 않기 때문에 의미가 없는 화면이 출력됩니다.

3. async 사용

async는 html을 파싱하다가 자바스크립트 코드를 만나면 html 파싱을 멈추지 않고 동시에 다운 받으면서 html파싱을 합니다. 그리고 자바스크립트 파일을 실행시키는데 이때 html파일이 멈추게 됩니다. 이는 자바스크립트에 의존적인 웹을 좀 더 빨리 실행시킬 수 있지만 1번에서 발생한 제대로 다 파싱되지 않은 화면이 사용자에게 제공될 수 있습니다. 그리고 실행시켜야 할 script 태그가 여러개인 경우 순서에 상관없이 먼저 다운받아지는 script 먼저 실행되므로 script 실행순서에 영향을 받는 코드일 경우 문제가 발생할 수 있습니다.

4. defer 사용


defer는 html을 파싱하다 script 태그를 만나면 html 파싱과 동시에 script를 다운시키는데 async와는 다르게 다운로드가 끝나면 바로 실행되지 않고 html 파싱이 완료된 후에 script를 실행시킵니다. 그렇기 때문에 html 파싱이 중간에 멈출 일도, 자바스크립트에 의존적인 웹을 실행시키는 일도, async에서 문제였던 자바스크립트가 순서에 맞지 않게 실행되는 일도 없게 됩니다. 때문에 defer가 가장 이상적인 방법이라고 말할 수 있습니다.

결론적으로 script 태그를 사용할 때는 defer를 사용하는 것이 제일 이상적이며, 오류를 막을 수 있는 방법입니다.

Reference

https://minemanemo.tistory.com/121
https://upcount.tistory.com/97
https://jae04099.tistory.com/entry/HTML-script-%ED%83%9C%EA%B7%B8%EB%8A%94-%EC%96%B4%EB%94%94%EC%97%90-%EC%9C%84%EC%B9%98-%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C
https://blog.leehov.in/25
https://na27.tistory.com/230
https://velog.io/@grinding_hannah/Web-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC
https://tecoble.techcourse.co.kr/post/2021-10-24-browser-rendering/

profile
프론트엔드 개발자입니다.

0개의 댓글