이 글은 Tali Garsiel의 "How Browsers Work: Behind the scenes of modern web browsers"를 번역한 글입니다.
Renderer
가 생성되어 트리에 추가될 때는 위치나 크기에 대한 정보를 가지고 있지 않습니다. 위치, 크기 값을 계산하는 과정을 Layout
혹은 Reflow
라고 부릅니다.
HTML은 위치, 크기 값을 단 하나의 경로를 통해 계산할 수 있는 Flow based layout model
을 사용합니다. 나중에 배치되는 요소가 flow
안에 있다면, 이미 배치된 요소에 영향을 미치지 않기 때문에, 글을 읽는 것과 마찬가지로 단 하나의 경로를 통해 계산할 수 있습니다. 그러나, table
과 같은 요소는 하나 이상의 경로가 필요합니다.
좌표계는 좌측 상단을 기준으로 하여 좌표를 계산합니다.
Layout
은 <html>
에 해당하는 Root renderer
부터 시작해서, 프레임에서 renderer
가 필요한 좌표를 재귀적으로 계산합니다.
Root renderere
의 좌표는 (0, 0) 이고, 크기는 viewport
입니다. viewport
는 브라우저에서 보이는 부분의 크기를 의미합니다.
모든 renderer
는 layout
혹은 refelow
메소드를 가지고 있고, 배치해야 할 자식의 layout
메소드를 호출합니다.
변화가 생길 때마다 모든 element
의 layout
을 방지하기 위해서, 브라우저는 dirty bit
를 사용합니다. 추가되거나 변화가 있는 renderer
와 자식들은 dirty
로 표시합니다.
dirty
, 그리고 renderer
의 layout
은 필요하지 않지만 자식의 layout
필요한 children are dirty
플래그가 있습니다.
Render tree
전체에 layout
이 발생하는 것을 Global layout
이라고 부릅니다. 이 현상은 아래의 이유로 발생합니다.
Renderer
에 영향을 주는 스타일의 변화resize
이벤트Incremental layout
은 renderer
가 dirty
할 때 비동기적으로 발생합니다. 예를 들어, 새로운 element
가 네트워크로부터 로드되어 DOM 트리에 추가된 후, renderer
가 render tree
에 추가 될 때 발생합니다.모
Incremental layout
Incremental layout
은 비동기적으로 일어납니다. Firefox는 reflow commands
를 모으고, 스케쥴러 이를 한 번에 실행합니다. Webkit은 incremental layout
을 실행시키는 타이머가 존재합니다. 타이머가 종료되면, 트리를 순회하면서 dirty renderer
를 layout
합니다.
"offsetHeight" 와 같은 스타일 정보가 필요한 스크립트는 incremental layout
을 동기적으로 실행시킬 수 있습니다.
Global layout
은 대부분 동기적으로 실행됩니다.
layout
은 스크롤 위치와 같은 속성의 변화로 인해서 첫 layout
이후 콜백으로 다시 실행될 수 있습니다.
resize
나 renderer
의 위치 변화에 의해 layout
이 발생하면, renderer
의 크기는 캐싱된 값을 사용합니다.
텍스트 필드에 텍스트가 추가된 상황처러머 변화가 주변에 영향을 미치지 않는 경우에는 하위 트리만 수정되고 루트로부터 layout
이 발생하지 않습니다.
layout
은 아래와 같은 형태로 진행합니다.
renderer
는 자신의 width
를 결정합니다.renderer
의 좌표를 계산합니다.dirty
하거나 Global layout
인 경우, 자식의 layout
메소드를 호출합니다.height
, margin
그리고 padding
값을 합쳐 자신의 height
로 사용합니다.dirty bit
플래그를 제거합니다.Firefox는 layout
의 파라미터로 state object(nsHTMLReflowState)
를 사용합니다. 이 객체는 부모의 width
값을 가지고 있습니다. Firefox에서 layout
메소도의 리턴값은 metrics object(nsHTMLReflowMetrics)
입니다. 이 객체는 renderer
의 height
를 가지고 있습니다.
Renderer
의 width
는 width
프로퍼티, 그리고 margin
, border
갑을 사용해서 계산합니다.
아래와 같은 div
의 width
를 계산해 보겠습니다.
<div style="width: 30%"/>
Webkit은 아래의 과정으로 계산합니다. RenderBox
클래스의 calcWidth
메소드를 사용합니다.
width
는 max(availableWidth, 0)
입니다. availableWidth
는 아래와 같이 계산합니다. clientWidth() - paddingLeft() - paddingRight()
clientWidth
와 clientHeight
는 border
와 scrollbar
를 제외한 값을 의미합니다.
width
는 width
속성이 결정합니다. 컨테이너 width
의 백분율 값을 절대 값으로 변환합니다.border
와 padding
값을 추가합니다.위의 과정을 통해서 preferred width
를 계산했습니다. 이제 width
최대값과 최소값과 비교해야 합니다.
preferred width
가 최대값보다 크면 최대값을 사용합니다. 마찬가지로, 최소값보다 작으면 최소값을 사용합니다.
width
가 고정된 경우 캐싱해서 사용합니다.
Renderer
가 layout
중간에 줄을 바꿔야한다면, 이를 부모에 전달합니다. 부모는 renderer
를 추가적으로 만들어 layout
을 호출합니다.
참고 자료
How Browsers Work: Behind the scenes of modern web browsers
브라우저는 어떻게 동작하는가?