Node.js 등장으로 자바스크립트는 웹 브라우저 이외에 서버 사이드 애플리케이션 개발에도 사용할 수 있는 범용 개발 언어가 되었다. 하지만 가장 많이 사용되는 분야는 여전히 웹 브라우저 환경에서 동작하는 웹 애플리케이션의 클라이언트 사이드이다.
대부분의 프로그래밍 언어는 운영체제를 기반으로 실행되지만 웹 애플리케이션의 클라이언트 사이드 자바스크립트는 브라우저 환경에서 HTML과 CSS와 함께 실행된다.
우리는 브라우저가 HTML, CSS, 자바스크립트로 작성된 텍스트 문서를 어떻게 파싱 하여 렌더링 하는지 알아야 한다.
사용자의 웹 요청에 따라 서버에 HTML, CSS, Javascript, 이미지, 폰트 등 렌더링에 필요한 리소스를 요청하고 응답받는다. 브라우저의 렌더링 엔진은 HTML, CSS를 파싱 하여 DOM과 CSSOM을 생성하고 결합하여 렌더 트리를 생성한다.
브라우저의 자바스크립트 엔진은 서버의 응답으로 온 자바스크립트를 파싱 하여 AST (Abstract Syntax Tree)를 생성하고
바이트 코드로 변환하여 실행한다.
자바스크립트는 DOM API를 통해 DOM을 변경할 수 있는데 이 경우 변경된 DOM은 다시 렌더 트리로 결합된다.
렌터 트리 기반으로 HTML요소의 레이아웃을 계산하고 브라우저 화면에 HTML 요소를 렌더링 한다.
브라우저의 개발자 도구에 Network 탭에는 서버로부터 요청 및 응답하는 리소스가 로그로 남는다.
이 과정을 잘 보면 요청한 html페이지 외에도 css, font, image 파일 등 다양한 리소스를 받는 것을 확인할 수 있다.
이는 HTML을 파싱 하는 도중에 외부 리소스를 로드하는 태그인 link, img태그, script 태그 등을 만나면 HTML 파싱을 중단하고 해당 리소스 파일을 서버로 요청하기 때문이다.
브라우저의 렌더링 엔진은 서버가 내려준 바이트를 문자로 변경하고 브라우저가 이해할 수 있는 자료구조인 DOM
(Document Object Model)을 생성한다. 이러한 바이트에는 meta 태그의 charset 어트리뷰트에 지정된 인코딩을 기준으로 문자열로 변환한다.
<html>
<head>
<meta charset="UTF-8">
...
컴파일러를 공부할 때 문법을 해석하기 위해 문법적 의미를 갖는 최소 단위인 토큰을 공부한 적이 있다.
토큰을 통해 사용자가 입력한 문법이 맞는지 아닌지 다양한 방법으로 해석할 수 있다고 했다.
HTML도 마찬가지다. HTML 문서를 읽어 들이고 토큰으로 분해한다. 문법적 검사가 끝난 후 후처리가 진행된다.
이 경우 토큰들을 객체로 변환하여 node를 생성한다.
토큰의 내용에 따라 문서 노드, 텍스트 노드, 요소 노드, 어트리뷰트 노드가 생성된다고 한다.
이러한 노드가 DOM을 구성하는 기본 요소가 된다.
각 HTML 태그의 시작 태그와 종료 태그 내부에는 텍스트뿐만 아니라 html태그도 존재한다. 따라서 이러한 요소를 반영하기 위해 모드 노드들을 트리 자료구조로 구성한다.
이러한 노드들로 구성된 트리 자료구조를 DOM이라고 한다.
(여태껏 각 노드들로 구성된 객체를 DOM인 줄 알았는데 좀 더 넓은 개념이었다.)
재밌는 건 CSS 또한 바이트, 문자, 토근, 노드, CSSOM을 거치며 CSSOM을 생성한다. CSS의 노드들로 구성된 트리 자료구조를 CSSOM이라고 하는 것 같다.
이 CSSOM을 보면 최상위의 있는 css의 요소 값을 하위로 점차 상속하면서 생성된다.
위에 보이는 Render Tree는 렌더링 엔진이 DOM과 CSSOM이 결합된 결과이다.
렌더 트리는 렌더링을 위한 트리 구조의 자료구조이다. 따라서 브라우저 화면에 렌더링 되지 않는 노드와 CSS에 의해 비 표시되는 노드들은 포함되지 않는다. 즉 브라우저 화면에 렌더링 되는 노드만으로 구성된다.
이렇게 완성된 렌더 트리는 각 HTML 요소의 레이아웃을 계산하는 데 사용되며 브라우저 화면에 픽셀을 렌더링 하는 페인팅 처리에 입력된다.
이러한 과정은 다음과 같은 경우 반복해서 레이아웃 계산과 페인팅이 재차 실행된다.
- 자바스크립트에 의한 노드 추가 또는 삭제
- 브라우저 창의 resizing에 의한 viewport 크기 변경
- HTML 요소의 레이아웃에 변경을 발생시키는 스타일의 변경
( width/height, margin, padding, border, display, position, top/right/bottom/left )
이러한 브라우저 렌더링 과정은 비용이 큰 작업이다. 따라서 리 렌더링이 빈번하게 발생하지 않도록 주의할 필요가 있다.
(아 오늘 position, z-index 자바스크립트로 변경시켰는데..)
자바스크립트 관점에서 자바스크립트 파싱은 렌더링 엔진이 아닌 자바스크립트 엔진이 처리한다.
자바스크립트 코드를 파싱 하여 저수준 언어로 변환하고 실행하는 역할을 한다.
렌더링 엔진은 script 태그를 만나면 자바스크립트 엔진에게 제어권을 넘기고 자바스크립트 엔진은 추상적 구문 트리를 생성한 후 AST를 기반으로 인터프리터가 실행할 수 있는 중간 코드인 바이트코드를 생성하여 실행한다.
AST는 컴파일러만 사용하는 줄 알았는데 이게 또 그런 것은 아닌가 보다. 자바스크립트도 사실 인터프리터가 바이트 코드를 읽어 실행하는 구조다 보니 인터프리터를 위해 토큰의 구문 분석 후에 AST 생성하여 활용하는 것 같다.
리플로우와 리 페인드
reflow, repaint 처음 듣는 생소한 용어이다.
자바스크립트 코드에 DOM이나 CSSOM을 변경하는 DOM API가 사용된 경우
( DOM은 문서의 구조와 정보를 제공하는 것뿐만 아니라 프로그래밍 인터페이스로 DOM API를 제공한다.)
DOM이나 CSSOM이 변경된다. 이렇게 변경된 DOM이나 CSSOM은 다시 렌더 트리로 결합되고 변경된 렌더 트리를 기반으로 레이아웃과 페인트 과정을 거쳐 다시 브라우저 화면에 다시 렌더링 한다.
이를 리 페인트, 리플로우라 한다.
여기서 reflow는 레이아웃 계산을 다시 하는 것을 말하며, 노드 추가/삭제, 요소의 크기/위치 변경, 윈도 리사이징 등 레이아웃에 영향을 주는 변경이 발생한 경우에 한하여 실행된다.
repaint는 재결합된 렌더 트리 (DOM + CSSOM)를 기반으로 다시 페인트를 하는 것을 말한다.
만약 레이아웃에 영향이 없는 변경이 일어난 경우 리플로우 없이 리 페인트만 실행된다.
음.. 예를 들면 backgroud-color, visibillty, outline 등의 스타일 변경 시에는 레이아웃 수치가 변경되지 않을 것이다. 따라서 이 경우에는 리 페인트만 실행된다.
HTML 파싱
브라우저의 렌더링 엔진과 자바스크립트 엔진은 제어권을 넘겨받으면서 실행된다.
이 말은 동기적으로 , 위에서 아래로 순차적으로 파싱 하고 실행함을 뜻한다.
이는 DOM의 생성이 일시적으로 중단됨을 뜻하며 자바스크립트의 파싱 및 실행 이후 다시 DOM을 생성한다는 이야기이다.
이러한 blocking 문제를 해결하기 위해서 HTML5부터 async와 defer 속성이 추가되었고 IE10 이상을 권장한다.
따로 정리하긴 했는데 간단히 설명드리면 async와 defer는 비동기적으로 HTML 파싱과 자바스크립트 파일의 로드가 진행된다. 여기서 async는 자바스크립트 파일의 로드가 완료되면 자바스크립트 파싱 및 실행을 진행하고 HTML파싱을 중단한다.
defer는 DOM 생성 이후 자바스크립트를 파싱 및 실행하기에 순서가 보장된다.
'웹 프로그래밍 기초' 카테고리의 다른 글
DOM (2) (0) | 2022.03.12 |
---|---|
DOM (1) (0) | 2022.03.12 |
[css] z-index (0) | 2022.03.11 |
Canvas (0) | 2021.10.17 |
파일 다운로드 회고 (2) | 2021.09.08 |
댓글