브라우저의 동작 방식(2)

렌더 트리 구축

DOM 트리가 구축되는 동안에 브라우저는 렌더 트리를 구축한다. 구성요소를 올바른 순서대로 내용을 그려내기 위함이다. 이 구성 요소를 렌더러(renderer) 또는 렌더 객체(render object), 형상(frames)라고 부른다.

DOM 트리와 렌더 트리의 관계

렌더러는 DOM 요소에 부합하긴 하지만 일대일 대응 관계는 아니다. head와 같은 비 시각적 요소는 렌더 트리에 추가 되지 않고, display:none항목도 마찬가지로 렌더 트리에 나타나지 않는다. (visibility:hidden은 나타난다.) 그리고 select와 같은 태그는 DOM 트리에서는 하나의 항목이지만 렌더트리에서는 표시 영역, 드롭다운 목록, 버튼으로 나뉜다.

렌더 트리를 구축하려면 각 렌더 객체의 스타일 계산이 필요한데, 스타일을 계산하는 일은 몇가지 어려움이 있다.

  1. 스타일 데이터 구성이 너무 광범위하여 스타일 속성들을 수용하는데 메모리 문제가 야기될 수 있다.
  2. 각 요소에 할당된 규칙 목록을 전체 목록에서 찾는일이 비효율적이다.
  3. 규칙을 적용하는 것은 계층 구조를 파악해야 하는 복잡한 다단계 규칙을 수반한다.

이러한 문제를 브라우저는 여러가지 방법으로 처리한다. 그 중 하나가 아래와 같은 스타일 정보 공유이다.

스타일 정보 공유

웹킷 노드는 스타일 객체를 참조하는데, 이 객체는 일정 조건아래서 공유가 가능하다.

  1. 동일한 마우스 반응 상태를 가진 요소
  2. 아이디가 없는 요소
  3. 태그 이름이 일치하는 경우
  4. 링크 상태가 일치하는 경우
  5. 지정된 속성이 일치하는 경우
  6. 클래스 속성이 일치하는 경우
  7. 초점 상태가 일치하는 경우
  8. 문서 전체에서 속성 선택자의 영향을 받는 요소가 없는 경우.(속성 선택자 예 input[type=text])
  9. 요소에 인라인 스타일 속성이 없는 경우 (인라인 스타일 예

    )
  10. 문서 전체에서 형제 선택자를 사용하지 않는 경우

배치

렌더러가 생성되어 트리에 추가될 때 크기와 위치 정보를 계산하는 것을 배치라고 부른다. HTML은 흐름 기반의 배치모델을 사용하는데, 일반적으로 흐름 속에서 나중에 등장하는 요소는 앞서 등장한 요소의 위치와 크기에 영향을 미치지 않기 때문에 배치는 왼쪽에서 오른쪽으로, 위에서 아래로 흐른다. 단 표는 예외이다.

배치는 계속 반복되고 HTML문서의 요소인 최상위 렌더러에서 시작한다. 최상위 렌더러의 위치는 0.0이고, 브라우저 창의 보이는 영역에 해당하는 뷰포트 만큼의 면적을 갖는다.

더티 비트 체제

소소한 변경 때문에 전체를 다시 배치하지 않기 위해 브라우저에서는 더티 비트를 사용하고, 렌더러는 다시 배치해야할 요소들을 더티라고하는데, 더티 상태와 자식이 더티 상태라는 플래그가 있는데, 자식이 더티한 것은 자식가운데 하나를 다시 배치할 필요가 있다는 뜻이다.

전역 배치와 점증 배치

모든 요소의 배치가 일어나는 경우를 전역 배치라고 하는데 아래의 경우 발생한다.

  1. 글꼴 크기 변경과 같이 모든 렌더러에 영향을 주는 변경
  2. 화면 크기 변경에 의한 결과

점증 배치는 렌더러가 더티일 때 비동기적으로 일어난다. 예를 들어서 네트워크를 통해 추가 내용을 받아 DOM트리에 더해진 다음 새로운 렌더러가 렌더 트리에 붙을 때 일어난다.

비동기 배치와 동기 배치

점증 배치는 비동기로 실행되고, 전역 배치는 보통 동기적으로 실행된다. 때때로 배치는 스크롤 위치 변화같은 속성으로 인해 초기 배치 이후 콜백으로 실행되기도 한다.

최적화

배치가 크기 변경이나 렌더러 위치 변화 때문에 실행되는 경우, 렌더러 크기는 다시 계산하지 않고 캐시에서 가져온다. 배치는 기본적으로 최상위 부터 실행되지만 변화 범위가 한정적인 경우 하위트리만 수정되기도 한다.

배치 과정

배치 과정은 아래와 같다.

  1. 부모 렌더러가 자신의 너비 결정
  2. 부모가 자식을 검토 2-1. 자식 렌더러를 배치 2-2. 부모와 자식이 더티하거나 전역 배치 상태인 경우, 필요하면 자식 배치를 호출하여 자식의 높이를 계산한다.
  3. 부모는 자식의 누적된 높이와 여백, 패딩을 사용해서 자신의 높이를 설정한다. 이 값은 부모 렌더러의 부모가 사용한다.
  4. 더티 플래그를 제거한다.

줄 바꿈

렌더러가 배치되는 동안 줄을 바꿀 필요가 있을 때 배치는 중단되고 줄 바꿀 필요가 있다고 부모에게 전달한다. 부모는 추가 렌더러를 생성하고 배치를 호출한다.

그리기

그리기 단계에서는 화면의 내용을 표시하려고 렌더 트리를 탐색하고 렌더러의 paint 메서드를 호출한다. UI기반 구성요소를 사용한다.

전역과 점증

배치와 마찬가지로 전역 또는 점증 방식으로 그린다. 점증 그리기는 일부 렌더러만 그리는 것이다. 변경된 렌더러는 화면 위 사각형을 무효화 하고, OS는 이것을 더티 영역으로 보고 paint 이벤트를 발생시켜 처리한다.

그리기 순서

블록 렌더러가 쌓이는 순서는 다음과 같다.

  1. 배경색
  2. 배경 이미지
  3. 테두리
  4. 자식
  5. 아웃라인

웹킷 사각형 저장소

리페인팅 전에 웹킷은 기존 사각형을 비트맵으로 저장해서 새로운 사각형과 비교해서 차이있는 부분만 그린다.


참고

  1. 브라우저는 어떻게 동작하는가?’

Written by@[Ykss]
고이게 두지 않고 흘려보내는 개발자가 되자.

GitHubInstagramLinkedIn