스터디에서 이번주제로 정해진 브라우저의 렌더링 과정을
블로그를 작성하며 공부를 해보려고한다.
평소에 아무생각없이 들락거렸던 웹사이트들이
어떻게 작동되고 얼마나 복잡한일을 거친뒤 나오는건지
알아보도록하자.
그럼 브라우저에서의 렌더링과 파싱 단어부터 간단하게 무엇인지 알아보자
렌더링 (rendering)
HTML, CSS, Javascript 파일을 파싱해, 브라우저에 시각적으로 출력하는 과정
파싱 (parsing)
프로그래밍 언어로 작성된 파일을 실행시키기 위해
구문 분석(syntax analysis)을 하는 단계
브라우저의 렌더링엔 과정이 있으므로
그 과정을 순차적으로 알아보도록하자
브라우저 렌더링의 과정
1. 브라우저는 HTML, CSS, JS, 이미지, 폰트 등
리소스를 서버에 요청하고, 응답으로 받아온다.
2. 브라우저 렌더링 엔진은 받아온 HTML, CSS를 파싱해
DOM, CSSOM을 생성하고, 이들을 결합해 렌더 트리를 생성한다.
3. 브라우저 JS 엔진은 받아온 JS를 파싱해 AST를 생성하고,
바이트코드로 변환해 실행한다.
4. 렌더트리를 기반으로 HTML 요소의
레이아웃(위치, 크기)을 계산한다.
5. 화면에 HTML요소를
페인팅한다.
1. 리소스를 서버에 요청, 응답
브라우저는 필요한 리소스를 서버에 요청하고, 응답으로 받아와야한다.
그럼 서버에 요청은 어떻게 보내??
브라우저의 주소창 URL이 그것을 해주는 것이다.
URL을 입력하면, DNS를 통해 진짜 주소 IP 주소로 변환이 되고,
그 IP주소를 갖는 서버에게 요청을 보내는 것이다.
브라우저 렌더링 엔진이 HTML 파일을 파싱할 때, 위에서 아래로 한줄씩 파싱,
그러다가 외부 리소스를 가져오는 태그를 만나면 리소스 파일을 서버로 요청
2. HTML 파싱, DOM 파싱
응답으로 받아온 HTML 파일은 텍스트로 이루어져 있다.
이런 텍스트들로 지금 보고있는 화면이 되어야한다.
따라서 문서를 브라우저가 이해할 수 있는 형태로 바꾸는 작업이 필요하다.
0. 서버는 브라우저에게 2진수 형태의 HTML 문서를 응답으로 준다.
1. 문서는 <meta>의 charset속성에 지정된 방법으로 문자열로 인코딩됨. (ex. UTF-8)
2. 문자열 형태의 HTML 문서를 '토큰'단위로 분해.
3. 각 토큰을 객체로 변환해, 노드를 생성한다.
4. HTML 문서의 요소들의 중첩관계를 기반으로 노드들을 트리 구조로 구성. (이 트리를 DOM이라함.)
위의 과정을 통해 HTML 문서가 파싱되고, DOM의 결과물이 생성된다.
그럼 왜 이렇게 파일을 복잡하게 바꿔야하나....?
브라우저는 javascript 언어만 알아듣기 때문에
HTML 태그나 속성 등을 바로 다룰수 없다. 그래서 '객체'형태로 바꾸어
브라우저가 이해할 수 있게 바꿔줘야 한다.
그럼 CSS는??
위의 html을 파싱하다가 <link>, <style> 태그를 만나게 되면
파싱을 잠시 멈추고 리소스 파일을 서버로 요청한다고 했었다.
이렇게 가져온 CSS 파일도 HTML과 똑같이
DOM 트리구조를 만드는 과정을 거쳐 트리가 만들어진다.
이러한 트리구조를 CSSOM이라고 부른다.
하지만 HTML과 조금 다른점은 CSS의 속성은 상속이 되기 때문에
자식은 이를 반영한다.
위의 예를 들면 p태그에 font-size와 font-weight를 가지고있다.
그의 자식요소인 span태그도 font-size와 font-weight를 상속받는 것이다.
그럼 위에서 만든 DOM, CSSOM 두개를 합치는 작업이 필요하다.
그렇게 만들어지는 트리가 렌더트리 이다.
브라우저가 진짜 사용자에게 보여주기 위한 화면을 그리는 과정이기 때문에,
DOM의 meta태그나, CSSOM의 display:none 등의 보이지 않게 해둔 요소들은
랜더트리에서 제외 되어야한다.
여기까지 했다면 텍스트로 구성된 객체를 만든 것이다.
실제로 페이지에 보여야하기 위해서는 '페인팅'이 되어야한다.
그전에 Javascript파일이 해석되는 방식을 봐보자.
3. Javascript 파싱
렌더링 엔진이 HTML파일을 파싱하다가 <script>태그를 만나면 파싱을 잠시 멈추고,
Javascript 파싱은 브라우저 렌더링 엔진이 하지않고
Javascript 엔진이 한다. 이 때 렌더링엔진은 JS엔진에게
완전히 제어권을 넘겨주기 때문에, HTML파싱을 멈췄다가
JS파싱이 완료되면 다시 제어권을 돌려받아 HTML파싱을 다시 시작함.
JS엔진은 js파일의 코드를 컴퓨터가 이해가능한 기계어로 변환하고 실행한다.
간단한 텍스트 문자열인 코드를 '토큰' 단위로 분해한다.
이렇게 분해된 토큰에 문법적인 의미와 구조가 더해져서
AST(추상 구문 트리)가 완성이 된다.
이 AST라는 트리를 실행하도록 해주어야한다.
그 실제 실행을 인터프리터가 한다.
그렇기 때문에 인터프리터가 알아들을 수 있도록 AST트리를 바이트 코드라는
중간 수준의 코드로 변환해야함. 그렇게 바아이트코드가 생성기를 통해 생성된다.
4. 레이아웃 (리플로우)
레이아웃은 요소의 기하학적인 속성들을 찾는 과정.
렌더트리에 들어있는 요소들의 위치나 크기와 관련된 정보들이 있었다.
그 정보들은 각 요소들의 정보이지,
전체화면에서 정확히 어디에 위치하는건지는 알지못한다.
따라서 이런 계산을 하는 단계가 레이아웃 단계이다.
브라우저가 각 요소들이 전체화면에서 어디에, 무슨 크기로
배치되어야하는지 파악하기 위해 렌더트리의 맨 윗부분부터 아래로 내려가며 계산함.
여기에서 모든 값들은 절대적인 값(px)로 변환된다.
5. 페인팅
이제 위치계산도 끝났으니, 화면에 보여주기만 하면된다.
각각 정보를 가진 픽셀들이 하나씩 모여서 하나의 이미지, 화면을 구성
따라서 화면에 색상을 주고, 어떤 요소를 보여주기 위해서는
그 픽셀에 대한 정보가 있어야한다.
'페인팅'이 이러한 픽셀들을 채워나가는 과정인 것이다.
이렇게 브라우저의 화면이 띄어지는 것이다.
만약 사용자가 화면크기를 조정하거나, 어떤 버튼을 눌러서
화면에 요소가 추가되거나 제거된다면??
화면에 나타내는 모습을 바꾸기 위해서는 모든 요소들의 위치나 크기를
다시 계산하고, 다시 그려서 보여줘야한다.
이렇게 앞의 4,5 과정을 반복하는 것을 리플로우, 리페인팅 이라고 한다.
만약 DOM에 요소를 추가한다고 하면,
DOM트리를 다시 구성하고, CSSOM과 합쳐져서 새로운 렌더트리가 생성됨.
그렇기 때문에 리플로우, 리페인팅이 일어나는 것이다.
참고