웹 성능 최적화에 대해 공부를 하려다보니 브라우저가 어떻게 동작하는지 알아야 될꺼같아 차근차근 공부해 나갑니다.
[브라우저]
브라우저란 웹 페이지, 이미지, 비디오 등의 콘텐츠를 수신, 전송 및 표현하는 소프트웨어입니다.
우리가 인터넷에 접속하기위해 사용하는 크롬, 사파리, 엣지, 파이어폭스, 익스플로어 등이 바로 브라우저 입니다.
각 브라우저마다 해석과정이 다르기 때문에 웹표준이 존재합니다.
원래는 W3C가 담당했으나 앞으로 HTML5 & DOM 표준은 WHATWG가 담당한다고 합니다.
(출저 기사: zdnet.co.kr/view/?no=20190531184644 )
[브라우저의 기본 구조]
- 사용자 인터페이스(User Interface)
- 주소 표시줄, 이전/다음 버튼, 북마크 메뉴 등 요청한 페이지를 보여주는 창을 제외한 나머지 모든 부분이다.
- 브라우저 엔진(Browser Engine)
- 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어한다.
- 렌더링 엔진(Rendering Engine)
- 요청한 콘텐츠를 표시. ex) HTML을 요청하면 HTML과 CSS를 파싱하여 화면을 표시
- 통신(Networking)
- HTTP 요청과 같은 네트워크 호출에 사용된다. 이것은 플랫폼 독립적인 인터페이스이고 각 플랫폼 하부에서 실행된다.
- 자바스크립트 해석기(Javascript Interpreter)
- 자바스크립트 코드를 해석하고 실행한다.
- UI 백엔드(UI Backend)
- ComboBox, Window 등의 기본적인 위젯을 그려줍니다. OS에서 제공하는 UI메서드를 사용합니다.
- 자료 저장소(Data Persistence)
- 로컬/세션 스토리지, 쿠키, WebSQL 등의 다양한 브라우저 API로 이루어져 있습니다.
위에 기본 요소중 우리는 렌더링에 대해 공부를 할 예정이니 렌더링 엔진을 좀 더 파해쳐봅시다.
[렌더링 엔진]
렌더링 엔진의 역할은 요청 받은 내용을 브라우저 화면에 표시하는 일입니다.
렌더링 엔진은 HTML, XML, 이미지를 표시할 수 있습니다. 추가적으로 플로그인이나 브라우저 확장 기능을 이용해 PDF와 같은 다른 유형도 표시 가능합니다.
[렌더링 엔진 종류]
- Webkit(웹킷) : 크롬, 사파리 사용
- Gecko(게코) : 파이어폭스 사용
[Webkit 기준 동작과정]
- HTML을 파싱해(HTML Parser) DOM 트리를 구성한다.
- CSS를 파싱해(CSS Parser) CSSOM 트리(Style Rules)를 구성한다.
- DOM트리 + CSSOM트리 합쳐서(Attachment) 렌더 트리를 만든다.
- 렌더 트리를 기반으로 렌더 트리 배치(Layout)을 실행해 각 노드의 형태를 계산한다.
- 렌더 트리의 각 노드를 화면에 그린다(Paint).
동작 과정은 알았다 이제 주요 처리 기능인 Parser, Attachment, Layout, Painting의 개념을 파해쳐보자
[동작과정 주요 처리 기능 설명]
Parser
: 서버로부터 전송받은 문서의 문자열을 브라우저가 이해할 수 있는 구조로 변환하는 과정
위에서 DOM Tree 와 CSSOM Tree (Style Rules)가 만들어지는 과정을 보자
- DOM(Document Object Model) Tree 생성 과정
- Bytes -> Characters [변환][Conversion]
- HTML의 원시 바이트를 읽어와 해당 파일에 지정된 인코딩(UTF-8 등)에 따라 문자열로 변환하는 과정
- Characters -> Tokens [토큰화][Tokenizing]
- 문자열을 HTML5 표준에 따라 토큰별로 규칙이 있는 고유 토큰(<>)으로 변환
- Tokens -> Nodes [렉싱][Lexing]
- 토큰을 해당 속성 및 규칙을 정의한 객체(Nodes)로 변환한다.
- Nodes -> DOM [DOM생성][DOM construction]
- 생성된 노드들을 트리 구조로 변환
- CSSOM( CSS Object Model ) Tree 생성 과정
위에 CSSOM Tree 생성 과정은 DOM트리 생성과정과 유사하다.
브라우저는 DOM을 생성하는 동안 외부 CSS를 참조하는 <link> 태그를 만나는 순간 브라우저에 리소스를 요청합니다. 이때 아래 그림처럼 CSSOM Tree 구조를 생성하게 됩니다.
브라우저는 해당 노드에 적용할 수 있는 가장 일반적인 규칙부터 시작합니다.
예를 들면 가장 상위에 있는 body에 font-size : 16px 는 바로 하위 노드들에게 적용되게되고 그 하위에 <p><span><img> 태그들은 각각 자신이 가지고있는 속성을 추가적으로 적용하게 됩니다.
한편, 모든 브라우저는 'User agent style'이라는 기본 스타일 세트를 제공하고 있습니다. 우리가 'reset.css' 등으로 오버라이딩하지 않으면 이 기본 스타일이 적용됩니다.
주의
- 여기서 팁으로 캐스케이딩이 영 좋지 못한 사례가 있습니다.
여기서 적용 속도로 보자면 h1이 div p 보다 빠릅니다. 그 이유는 div p 는 트리에서 p를 찾고 그 부모가 div인지 판단해야 하기 때문입니다. 참고(www.youtube.com/watch?v=gkTy6G4FtMg)
- 선 파서, 후 렌더링
CSSOM을 만드는 동안에는 렌더링이 일어나지 않습니다. 위 그림을 예를 들면 h1에 color:blue를 적용하고 div p는 무시하고 끝날순 없기 때문입니다. 그래서 CSS가 완전히 파싱이 되기전엔 브라우저는 렌더링을 작업하지 않습니다.
이 말은 CSS를 잘 압축해서 제공하면 렌더링 최적화가 된다는 뜻입니다.
렌더링을 막는 또 다른 경우가 있는데 HTML, JavaScript도 페이지 렌더링을 막습니다.
HTML은 돔이 만들어져야 렌더링을 할 수 있으니 큰 의미는 없지만
JavaScript의 경우 HTML 파서가 <script> 태그를 만나면 JavaScript코드를 실행하기 위해서 DOM트리 생성 프로세스를 중단하고 JavaScript Engine에 제어 권한을 넘기게 됩니다. (즉, html 파싱을 중지)
이러한 이유로 <body> 태그 제일 하단에 작성하지 않고 중간에 작성하거나 <head>에 작성하는경우 렌더링이 끊어지면서 사용자에게는 완성되지않은 화면을 순간 보여지게 됩니다. 심리적으로 느려보이거나 보기 안좋게 느껴집니다.
JavaScript Engine은 <script> 태그 내의 js 코드와 src 속성에 정의된 js파일을 로드하고 파싱하여 실행합니다.
실행이 완료되면 다시 HTML 파서로 제어 권한을 넘기고 브라우저가 중단된 시점부터 DOM 생성을 재개합니다.
렌더링 과정에서 성능을 개선하기 위해서는 <script> 태그에 defer 속성을 주면, 문서 파싱은 중단되지않고 문서 파싱이 완료된 이후에 JavaScript가 실행됩니다. HTML5에서 스크립트 비동기로 처리하는 속성(defer, async) 설명
Attachment
: DOM Tree + CSSOM Tree = Render Tree를 만드는 과정
브라우저는 이 과정에서 '사용자 눈에 보이는 노드' 들만 렌더링합니다. 즉.<meta>나 <script>는 포함되지 않으며 속성중 display:none; 처리 된 요소들도 포함되지 않습니다. 이 노드들에게 CSSOM 규칙을 적용합니다.
DOM Tree 와 CSSOM Tree 가 합쳐서 Render Tree가 만들어지게 됩니다.
Layout
Render Tree가 생성되고, 기기의 뷰포트 내에서 Render Tree의 노드가 정확한 위치와 크기를 계산하는 과정을 Layout(Webkit기준) , Reflow(Gecko 기준) 라고 합니다.
이 과정에서는 상대적인 측정값 (ex_width:50%) 을 절대적인 필셀(ex_width:100px)로 변환합니다.
만약 width: 50vw 상태에서 브라우저 너비가 달라진다면 이 값을 다시 계산해야되는데 렌더 트리에 변화가 있는것이 아니므로 Layout 단계부터 다시 계산합니다.
또한 , 부모에게 변화가 일어나면 자식요소도 영향을 받기 때문에 최대한 DOM구조 하위에 있는 노드에게 변화를 주는것이 좋습니다.
Painting
Layout 이 완료되면 브라우저는 'Paint Setup' 및 'Paint' 이벤트를 발생시키고 렌더링 트리를 화면의 픽셀로 변환하게 됩니다. 이 과정이 끝나게 되면 브라우저 화면에 UI가 나타나게 됩니다.
렌더링 최적화 - Reflow, Repaint 줄이기
사용자를 위한 인터렉션이나 애니메이션 등이이 들어가면서 크기와 색상이 달라지는 경우 , 브라우저는 다시 레이아웃을 그리고 칠하는 작업(Reflow,Repaint)을 해야합니다.
- Reflow
- 액션, 이벤트에 따라 HTML 요소의 크기,위치 등 레이아웃 수치를 수정하면 그 영향을 받는 자식,부모 노드들을 포함하여 Layout 과정을 다시 수행하게 되는데 이렇게 되면 Render Tree와 각 요소들의 크기,위치를 다시 계산해야됩니다. 이 과정을 Reflow라고 합니다.
- Repaint
- Reflow가 끝난후 Repaint과정이 일어나면서 Paint과정을 다시 거치게됩니다.
이 작업을 피하는 것도 CSS 최적화 중 하나인데 CPU의 레이아웃 재계산 없이 GPU만으로 처리 가능한 transform 속성을 사용한다고 합니다.
참고:
velog.io/@codenmh0822/%EB%A0%8C%EB%8D%94%EB%A7%81Rendering