모던 자바스크립트 딥 다이브 스터디중!
웹 애플레이션의 클라이언트 사이드 자바스크립트는 브라우저에서 HTML, CSS와 함께 실행한다. 따라서 브라우저 환경을 고려할 때 보다 더 효율적인 클라이언트 사이드 자바스크립트 프로그래밍이 가능하다.
브라우저는 HTML, CSS, 자바스크립트로 작성된 텍스트 문서를 어떻게 파싱해서 브라우저에 렌더링 하는 걸까?
💡 AST Abstract Syntax Tree
컴퓨터 과학에서 추상 구문 트리(abstract syntax tree, AST), 또는 간단히 구문 트리(syntax tree)는 프로그래밍 언어로 작성된 소스 코드의 추상 구문 구조의 트리이다. 이 트리의 각 노드는 소스 코드에서 발생되는 구조를 나타낸다. 구문이 추상적이라는 의미는 실제 구문에서 나타나는 모든 세세한 정보를 나타내지는 않는다는 것을 의미한다.
코드 텍스트에서 트리구조의 데이터 스트럭처를 만들어낸다. 코드에 있는 아이템이 각 트리에 있는 노드와 매치된다. 먼저, 렉시컬 분석을 통해 코드의 문자들을 읽어서 정해진 룰에 따라 토큰으로 만들어야 한다. 그런 후 syntax 분석에서는 앞서 나온 토큰 목록을 트리구조로 만든다.
http://www.example.com
와 같이 루트 요청을 하게 되면 일반적으로 index.html을 응답하도록 기본 설정이 되어 있다.💡DNS
DNS는 IP 주소 및 기타 데이터를 저장하고 이름별로 쿼리할 수 있게 해주는 계층형 분산 데이터베이스입니다. 즉, DNS는 컴퓨터가 서로 통신하는 데 사용하는 숫자 IP 주소로 변환되는, 쉽게 읽을 수 있는 도메인 이름의 디렉터리입니다.
HTTP(HyperText Transfer Protocol)는 웹에서 브라우저와 서버가 통신하기 위한 요청/응답 프로토콜이다. 1990년대 초에 설계된 HTTP는 거듭하여 진화해온 확장 가능한 프로토콜이다. HTTP는 애플리케이션 계층의 프로노콜로 신뢰 가능한 전송 프로토콜이라면 이론상이로는 무엇이든 사용할 수 있으나 TCP 혹은 TLS를 통해 전송된다.
HTTP1.0과 1.1의 차이
커넥션 유지 (Persistent Connection)
HTTP 프로토콜은 응용 계층의 프로토콜이다. HTTP를 통한 데이터 전송은 TCP 세션 기반에서 이루어진다.
1.0은 매번 데이터를 요청하고 수신할 때 마다 TCP 세션을 맺어야한다.
반면, 1.1은 한번의 TCP 세션에 여러 개의 요청을 보내고 응답을 수신할 수 있다.
TCP 세션 처리 부하를 줄이고 응답속도를 개선할 수 있다.
파이프라이닝 (Pipelining)
명령어를 순차적으로 실행하는 프로세서에 적용되는 기술로 여러개의 명령어를 실행하는 기법이다.
HTTP 요청은 순차적으로 이루어진다. 파이프라이닝 기능이 없는 경우에는 요청에 대한 응답이 오고 나서 다음 요청을 할 수 있다. 만약 첫번째 요청이 실패한다면 2,3번째 요청은 진행하지 못한다는 단점이 있다.
파이프라이닝을 통하면 동시에 요청 1,2,3을 보내고 이에 대한 각각의 응답을 받아 처리할 수 있다.
proxy-authentication
과 proxy-authorization
2개의 헤더가 추가되었다.15년만에 등장한 HTTP 2.0
1.1의 문제점을 개선한 2.0의 차이에 대해 알아보자.
2.0에서는 더 나아가 multiplexed 라는 기술을 도입해 1개의 세션으로 여러 개의 요청을 순서 상관없이 Stream으로 받아서 동시다발적으로 처리하고 응답할 수 있다. 이는 1.1의 HOL 블로킹 (Head-Of-Line Blocking) 문제를 해결한다.
서버가 응답한 HTML 문서는 문자열로 이루어진 순수한 텍스트이다. 그 문서를 브라우저에서 시각적인 픽셀로 렌더링하려면 HTML 문서를 브라우저가 이해할 수 있는 객체로 변환하여 메모리에 저장해야 한다.
렌더링 엔진은 DOM을 한 줄씩 파싱하여 DOM을 생성해가다 CSS를 로드하는 코드를 만나면 DOM생성을 일시 중단한다. CSS를 HTML과 동일한 파싱과정을 거쳐 CSSOM(css object model)을 생성한다.
HTML, CSS을 각각 파싱하여 DOM과 CSSOM을 생성한 후 렌더링을 위한 렌더 트리 render tree로 결합된다.
렌더 트리는 렌더링을 위한 트리구조의 자료구조로, 화면에 렌더링되지 않는 노드(ex. script tag) 나 css에 의해 비표시 (ex. display:none) 되는 노드들은 포함하지 않는다.
렌더트리는 각 HTML 요소의 레이아웃을 계산하는 데 사용되며 브라우저 화면에 픽셀을 렌더링하는 페인팅 처리에 입력된다.
- 자바스크립트에 의한 노드 추가 또는 삭제
- 브라우저 창의 리사이징에 의한 뷰포트 크기 변경
- HTML 요소의 레이아웃 변경을 발생시키는 스타일 변경
위와 같은 변경은 리플로우와 리페인팅을 발생시켜 성능에 악영향을 주니 주의해야 한다.
HTML 문서를 파싱한 결과물로 생성된 DOM은 문서의 구조와 정보 뿐 아니라 요소를 조작할 수 있는 DOM API도 제공한다. 자바스크립트 코드에서 DOM API를 사용하면 이미 생성된 DOM을 동적으로 조작할 수 있다.
CSS 파싱과 마찬가지로 DOM을 생성해다가 자바스크립트 파일 로드 코드를 마주하면 DOM 생성을 일시 중지한다. script 태그 내 코드를 파싱하기 위해서는 렌더링 엔진 -> 자바스크립트 엔진으로 제어권을 넘긴다. 따라서 다시 렌더링 엔진으로 제어권이 돌아왔을 때 DOM을 이어 생성한다.
자바스크립트 엔진은 자바스크립트 코드를 파싱하여 CPU가 이해할 수 있는 저수준 언어로 변환하고 실행하는 역할을 한다. V8, SpiderMonkey, JavascriptCore 등이 존재하며 모두 ECMAScript를 준수한다. 또, 자바스크립트 엔진은 자바스크립트 코드를 해석하여 AST(Abstract Synctax tree)를 생성하고 그걸 기반으로 인터프리터가 실행할 수 있는 중간코드인 바이트 코드를 생성하여 실행한다.
Javascript > Token > AST > ByteCode > 인터프리터에 의해 실행
한번 생성한 DOM, CSSOM 을 다시 변경시키면 다시 Render tree를 결합하고 그 렌더트리를 기반으로 레이아웃과 페인팅 과정을 거쳐 다시 브라우저 화면에 렌더링한다. 레이아웃계산을 다시 하는 것을 리플로우, 재결합된 렌더트리를 다시 페인팅 하는 것은 리페인팅 이라고 한다.
무조건 같이 실행되는 것은 아니고 레이아웃의 영향이 없는 변경은 리페인팅만 실행된다.
브라우저는 동기적으로 HTML 코드를 읽어 순차적으로 파싱되기 때문에 script태그의 위치에 따라 에러가 발생될 수 있다. DOM이 생성되기 전에 자바스크립트에서 DOM을 조작한다면 에러가 발생할 것이다. 이런 에러를 방지하기 위해 script 코드는 아래에 위치 시키는 것이 좋다.
DOM 생성이 중단되는 문제를 근본적으로 해결하기 위해 HTML5부터 script 태그에 async와 defer 어트리뷰트가 추가되었다.
두 어트리뷰트 모두 파일의 로드가 비동기적으로 진행된다는 공통점이 있다. 하지만 자바스크립트의 실행 시점에 차이가 있다.
<script async src="x.js"/>
자바스크립트의 파싱과 실행은 자바스크립트 파일의 로드가 완료된 직후 진행되면 이 때 HTML 파싱이 중단된다. 여러 개 사용 시 순서가 보장되지 않는다. 순서 보장이 필요하면 async를 지정하지 않아야 한다.
<script defer src="x.js"/>
자바스크립트의 파싱과 실행은 HTML 파싱이 완료된 직후, DOM 생성이 완료된 직후 (DOMContentLoaded event 실행) 진행된다. DOM 생성이 완료된 이후 실행되어야 할 자바스크립트에 유용하다.
https://yceffort.kr/2021/05/ast-for-javascript
https://developer.mozilla.org/ko/docs/Web/HTTP/Overview
https://withbundo.blogspot.com/2021/02/http-http-10-http-11.html
https://jaehoney.tistory.com/281
https://ko.javascript.info/dom-nodes
잘 읽었습니다. 좋은 정보 감사드립니다.