대부분 프로그래밍 언어는 운영체제나 가상 머신 위에서 실행되지만
웹 애플리케이션 클라이언트 사이드 자바스크립트는 브라우저에서 HTML , CSS
와 함께 실행된다.
따라서 브라우저 환경을 고려할 때 더 효율적인 프로그래밍이 가능하다.
브라우저가 HTML , CSS , 자바스크립트로 작성된 텍스트 문서를 어떻게 파싱하여 렌더링 하는지 살펴보자
텍스트 문서의 문자열을 token
으로 분해하고, 문법적 의미와 구조를 반영하여 트리구조의 자료구조인 parse tree / sysntax tree
를 생성하는 일련의 과정을 의미한다.
일반적으로 파싱이 완료된 후에는 중간 언어인 bytecode
를 생성하고 실행한다.
파싱 된 parse tree
를 이용하여 다음과 같은 가정으로 렌더링 한다.
브라우저는 HTML , CSS , 자바스크립트, 이미지, 폰트 파일 등 렌더링에 필요한 리소스를 요청하고 응답을 받는다.
HTML
을 파싱하여 DOM
, CSS
를 파싱하여 CSSOM
을 생성하고 이를 결합하열 렌더 트리를 생성한다.
자바스크립트 엔진은 서버로부터 자바스크립트를 파싱하여 AST (Abstract Syntax tree)
를 생성하고 바이트 코드로 변환하여 실행한다. 이 때 자바스크립트는 동적으로 DOM , CSSOM
을 변경 할 수 있으며 변경된 DOM , CSSOM
은 렌더 트리로 다시 결합된다.
렌더 트리를 기반으로 HTML
요소의 레이아웃을 계산하고 브라우저 환경에 HTML 요소를 페인팅 한다.
요청과 응답에 대한 자세한 내용은 HTTP 공부 시리즈를 통해 보도록 하자
브라우저의 요청과 응답은 개발자 도구 - network
에서 살펴 볼 수 있는데 이를 보면 index.html
에 해당하는 www.naver.com
뿐이 아니라 부수적인 많은 것들도 요청과 응답을 받은 모습을 볼 수 있다.
이는 브라우저 렌더링 엔진이 index.html
을 파싱하는 도중 외부 리소스를 로드하는 태그 link , a , script
등을 만나면 HMTL
파싱을 중단하고 해당 리소스 파일들도 서버로 요청을 보내기 때문이다.
HTTP 1.1
과 HTTP 2.0
이 내용도 HTTP 공부 에서 자세히 살펴보기로 하고 간단히 말 하면
HTTP 1.1
은 요청과 응답이 하나씩만 가능한 1:1
대응이였지만 HTTP 2.0
부터는 N:N
대응으로 렌더링 속도가 더욱 빨라졌다.
HTML
과 DOM
생성브라우저 요청에 의해 서버가 응답한 HTML
문서는 문자열로 이뤄진 순수한 텍스트이다.
순수한 텍스트를 브라우저에 시각적인 픽셀로 시각화 하려면 브라우저가 이해 할 수 있는 자료구조로 변환하여 메모리에 저장해야 한다.
meta
태그의 charset
어트리뷰트에 의해 지정된 인코딩 방식으로 문자열로 변환된다. 토큰
으로 분해한다.토큰
들을 객체로 생성하여 노드
를 생성한다. (문서 노드
, 요소 노드
, 어트리뷰트 노드
, 텍스트 노드
) 이 노드들은 DOM
을 구성하는 기본 요소가 된다.HTML
요소는 부모 자식 형제 관계를 갖는 중첩 관계를 갖는다. 이런 관계를 반영하여 트리 자료 구조로 구성하고 이렇게 구성된 트리 자료 구조를 DOM
이라 부른다.즉
DOM
은HTML
문서를 파싱한 결과물이다.
CSS
와 CSSOM
생성위와 동일
파싱 하여 만든 자료구조인 DOM
과 CSSOM
은 렌더링을 위해 렌더 트리
로 결합된다.
화면에 렌더링 되지 않는 노드 (meta , display : none , script 태그
)를 제외하고 생성한다.
즉 렌더 트리는 브라우저에 렌더링 되는 노드만으로 구성된다.
구성한 렌더 트리를 가지고 Layout -> paint
단계를 통해 브라우저에 렌더링 한다.
렌더링 과정은 다음과 같은 경우 반복 실행된다.
DOM
은 HTML 문서를 파싱한 결과물로, 이러한 자료구조를 통해 HTML 요소와 스타일
을 변경 할 수 있는 프로그래밍 인터페이스로 DOM API
를 제공한다.
자바스크립트에서는
document
라는 객체를 통해DOM
에 접근하여 수정하거나 추가, 제거 할 수 있다.
HTML
파싱 도중 script
태그를 만나면 HTML
파싱을 중단 하고 script
에 담긴 자바스크립트를 파싱 및 실행한다.
자바스크립트의 파싱 및 실행이 종료되면 HTML
파싱을 재개한다.
자바스크립트의 파싱은 브라우저의 렌더링 엔진이 아닌 브라우저 별 자바스크립트 엔진이 파싱한다.
다음과 같은 단계를 따른다.
단순한 문자열인 자바스크립트 소스 코드를 어휘 분석 하여 문법적 의미를 갖는 코드의 최소 단위인 토큰들로 분해한다.
토큰들의 집합을 구문 분석 하여 AST (Abstact Syntax tree , 추상적 구문 트리)
를 생성한다.
function isOddes(arr) {
arr.forEach((num) => {
if (num % 2) return true;
return false;
});
}
const arr = [1,2,3,4,5]
isOddes(arr)
다음과 같은 소스코드를 이용해 AST
를 만들면
{
"type": "Program",
"start": 0,
"end": 142,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 9,
"column": 12
}
},
"comments": [],
"range": [
0,
142
],
"sourceType": "module",
"body": [
{
"type": "FunctionDeclaration",
"start": 0,
"end": 104,
"loc": {
"start": {
"line": 1,
"column": 0
},
....
다음과 같은 파싱을 거쳐 자료구조인 트리를 생성한다.
파싱의 결과물로 생성된 AST
는 인터프리터가 실행 할 수 있는 중간 코드인 바이트 코드로 변환되고 인터프리터에 의해 실행된다.
자바스크립트 코드에 의해 DOM , CSSOM
을 변경하는 DOM API
가 사용된 경우 렌더 트리가 변경되며 브라우저의 화면이 다시 렌더링 된다.
이를 Reflow , Repaint
라고 한다.
HTML
파싱 중단HTML
요소를 파싱하다가 자바스크립트를 파싱해야 하면 HTML
파싱을 중단하고 자바스크립트를 우선적으로 파싱한다고 하였다.
따라서 자바스크립트 파싱을 의미하는 script
태그의 위치는 중요한 의미를 갖는다.
또한 파싱 된 자바스크립트의 AST
는 여태 파싱 된 DOM
을 DOM API
를 통해 조작하는데, 생성이 되지 않은 DOM
의 노드에 접근하는 것은 불가능하기 때문에 더더욱 script
태그의 위치는 중요하다.
이러한 문제를 해결하기 위해 body
태그 뒤에 script
태그들을 위치시키는 것은 좋은 아이디어이다.
페이지 렌더링 시간이 빨라질 뿐더러 (
렌더링이 우선적으로 종료되어 브라우저에 렌더링 됨으로
) 생성되지 않은DOM
의 노드에 접근하려고 하는 에러를 방지 할 수 있다.
게시글을 작성 후에 관련 주제들이 하단에 나오는데 나보다 훨씬 많은 내용을 정리한 링크가 있어 공유한다.