[Javascript] DOM(Document Object Model)

Gyuhan Park·2022년 12월 16일
0

javascript deepdive

목록 보기
8/11

브라우저의 핵심 기능

"필요한 리소스(HTML, CSS, JS,이미지, 폰트, 서버가 동적으로 생성한 데이터)를 서버에 요청하고 서버로부터 응답받아 브라우저에 시각적으로 렌더링하는 것"

브라우저의 렌더링 엔진이 HTML을 파싱하는 도중에 외부 리소스를 로드하는 link태그, img태그, script태그를 만나면 파싱을 중단하고 리소스 파일을 서버로 요청한다.

HTML문서는 문자열로 이루어진 순수한 텍스트다.
순수한 텍스트를 브라우저에 시각적인 픽셀로 렌더링하려면 브라우저가 이해할 수 있는 자료구조인 DOM을 생성한다.

브라우저 렌더링 과정(CRP)

  1. 브라우저는 렌더링에 필요한 리소스를 요청하고 서버로부터 응답
  2. 브라우저의 렌더링엔진은 응답된 HTML과 CSS를 파싱하여 DOMCSSOM 생성
  3. 렌더트리 생성
  4. 브라우저의 JS엔진은 JS를 파싱하여 AST를 생성하고 인터프리터가 실행가능한 바이트코드로 변환하여 실행
  5. DOM API를 통해 DOM이나 CSSOM 변경 가능
  6. 렌더 트리를 기반으로 레이아웃 계산, 화면에 페인팅

DOM 생성 과정

  1. 서버는 브라우저가 요청한 HTML 파일을 바이트로 응답.
  2. meta태그의 charset 속성을 기준으로 바이트를 문자열로 변환. (response header에 담긴다)
  3. 문자열로 변환된 HTML 문서를 토큰 단위로 분해.
    #토큰 : 문법적 의미를 갖는 코드의 최소 단위#
  4. 각 토큰들을 객체로 변환하여 노드 생성.(문서 노드, 요소 노드, 어트리뷰트 노드, 텍스트 노드)
  5. HTML 요소가 부모관계를 가지며 이를 바탕으로 구성된 트리 자료구조 : DOM

렌더 트리

DOM + CSSOM = 렌더 트리

렌더 트리는 브라우저 화면에 렌더링되는 노드만으로 구성 된다.
따라서 meta태그, script태그, display: none 등이 적용된 노드들은 포함되지 않는다.

DOM이나 CSSOM을 변경하는 DOM API 가 사용된 경우, 변경된 DOM과 CSSOM은 다시 렌더 트리로 결합되고, 리플로우, 리페인트 과정을 거쳐 브라우저의 화면에 리렌더링 된다.

리플로우 : 레이아웃 계산을 다시 하는 것. ex)노드 추가/삭제, 요소 크기/위치 변경
리페인트: 재결합된 렌더 트리를 기반으로 다시 페인트 하는 것.

레이아웃에 영향이 없으면 리플로우없이 리페인트만 실행된다.

리렌더링은 성능에 악영향을 주는, 코스트가 많이 드는 작업이므로 불필요한 리렌더링이 일어나지 않도록 주의할 필요가 있다.

async/defer

HTML을 파싱하다가 중간에 <link> 또는 <script> 태그를 만나면 파싱을 중단한다.
이처럼 브라우저는 동기적으로 HTML, CSS, JS를 파싱하고 실행한다. 따라서,
1) 태그 위치에 따라 DOM 생성이 지연될 수 있다.
2) DOM API를 사용한다면 태그 위치에 따라 예상대로 동작하지 않을 수 있다.

<head>
  <script>
    const APPLE = document.getElementById('apple');
    APPLE.style.color = 'red';
  </script>
</head>
<body>
  <li id='apple'>apple</li>
  <li id='banana'>banana</li>
</body>

이렇게 script 태그가 header에 존재하면 HTML 파싱이 중단되고 JS엔진이 JS를 파싱하기 시작한다.
또한 id='apple'인 노드를 찾지만 아직 DOM이 생성되지 않았기 때문에 APPLE에는 null값이 들어간다.

그래서 보통 script 태그는 DOM 생성이 완료된 이후인, body 태그 가장 아래에 넣는다.

DOM 생성이 중단되는 문제를 해결하기 위해 async/defer 태그가 추가되었다.
script 태그를 통해 외부 자바스크립트 파일을 로드하는 경우에만 사용할 수 있다.
async와 defer 어트리뷰트를 사용하면 HTML 파싱과 외부 JS파일 로드가 비동기로 진행된다.

async

<script async src="extern.js"></script>

자바스크립트의 파싱과 실행은 자바스크립트 파일의 로드가 완료된 직후 진행되며, 이때 html 파싱 중단
async 태그가 여러개면 로드가 완료된 순서로 실행되므로 순서가 보장되지 않는다.

defer

<script defer src="extern.js"></script>

자바스크립트의 파싱과 실행은 HTMl 파싱이 완료된 직후 진행된다.(DOMContentLoaded)
DOM 생성이 완료된 이후 실행되어야할 자바스크립트에 유용하다. (DOM API)
HTML 파싱과 JS 파싱이 병렬적으로 진행되므로 header에 선언하여 로딩시간을 줄일 수 있다.

profile
단단한 프론트엔드 개발자가 되고 싶은

0개의 댓글