브라우저 렌더링 Browser rendering
브라우저는 'content (콘텐츠)' 를 'rendering (렌더링)' 해서 'pixel image(픽셀 이미지)' 즉 화면을 만듭니다.
- 브라우저 렌더링 Browser rendering - 0단계 소개
- 브라우저 렌더링 Browser rendering - 1단계 파싱 'parse'
- 브라우저 렌더링 Browser rendering - 2단계 스타일링 'style'
- 브라우저 렌더링 Browser rendering - 3단계 레이아웃 'layout'
- 브라우저 렌더링 Browser rendering - 4단계 페인팅 'paint'
- 브라우저 렌더링 Browser rendering - 5단계 변경 'change'
렌더링 파이프라인 Rendering pipeline
2단계 스타일링 'style'
이전 단계에서 HTML 텍스트 파일을 해석해서 DOM 을 만들었고 스타일링 단계에서는 CSS 텍스트 파일에 정의된 스타일을 DOM 에 적용할 수 있도록 해석하는 작업을 진행합니다. HTML 파일과 달리 CSS 파일은 해석하기 까다로운 구조로 정의되어 있어서 어떻게 해석되어 관리되는지를 자세하게 설명드리도록 하겠습니다.
Chrome 의 렌더링 엔진 Blink 는 Document
로 모든 콘텐츠를 관리합니다. Document
의 함수 중 UpdateStyleAndLayoutTree
함수는 렌더링 단계 중 스타일링과 레이아웃을 수행하는 함수이고, 위 그림처럼 UpdateStyle
함수를 호출하게 됩니다. UpdateStyle
은 스타일링만 수행하는 함수입니다.
// third_party/blink/renderer/core/dom/document.cc
void Document::UpdateStyleAndLayoutTree() {
// ...
UpdateStyleAndLayoutTreeForThisDocument();
// ...
}
void Document::UpdateStyleAndLayoutTreeForThisDocument() {
// ...
UpdateStyle();
// ...
}
void Document::UpdateStyle() {
// ...
// StyleEngine 에게 위임해서 콘텐츠의 스타일과 레이아웃 트리를 업데이트합니다.
GetStyleEngine().UpdateStyleAndLayoutTree();
// ...
위 코드를 보면 결국 스타일링 작업은 Document
가 StyleEngine
에게 위임하여 처리한다는 사실을 확인할 수 있습니다. 즉 StyleEngine
이 Document
의 스타일을 계산하고 관리하는 역할을 맡고 있습니다.
// third_party/blink/renderer/core/css/style_engine.cc
// StyleEngine 생성자 함수
StyleEngine::StyleEngine(Document& document)
: document_(&document),
// Style Engine 은 생성될 때 StyleResolver 를 초기화합니다.
resolver_(MakeGarbageCollected<StyleResolver>(document)),
}
void StyleEngine::UpdateStyleAndLayoutTree() {
// ...
// 그림에서 설명하는 Style recalc 과정을 진행합니다.
RecalcStyle();
RebuildLayoutTree();
// ...
}
StyleEngine
은 StyleResolver
를 가지고 있는데, StyleResolver
는 모든 CSS 규칙 중 DOM 트리의 각 Element 에 어떤 스타일 규칙을 적용할지 계산하는 역할을 맡고 있습니다. Chrome DevTools 에서 볼 수 있는 'Computed Style' 은 StyleResolver
가 만들어 주는 정보입니다. StyleResolver
에게 제공되는 StyleSheetContents
들은 CSSParser
가 만들어서 Document
가 보관하게 됩니다.
StyleSheetContents
들은 위 그림 순서로 만들어 집니다. 먼저 CSS 텍스트 파일을 CSSParser
에서 토큰화한 뒤, 토큰을 하나씩 처리하면서 스타일 규칙 StyleRule
을 만들고 StyleSheetContents
안에 담습니다.
// third_party/blink/renderer/core/css/parser/css_parser_impl.cc
ParseSheetResult CSSParserImpl::ParseStyleSheet(
const String& string,
const CSSParserContext* context,
StyleSheetContents* style_sheet,
// ...) {
// ...
// CSS 파일을 해석할 준비를 합니다.
CSSTokenizer tokenizer(string);
CSSParserTokenStream stream(tokenizer);
CSSParserImpl parser(context, style_sheet);
ParseSheetResult result = ParseSheetResult::kSucceeded;
// ConsumeRuleList 함수로 토큰을 하나하나 해석합니다.
bool first_rule_valid = parser.ConsumeRuleList(
stream,
// ...
// 해석을 완료한 StyleRule 을 StyleSheetContents 안에 담습니다.
style_sheet->ParserAppendRule(rule);
});
return result;
}
template <typename T>
bool CSSParserImpl::ConsumeRuleList(CSSParserTokenStream& stream,
RuleListType rule_list_type,
const T callback) {
// ...
// Token 을 하나하나 처리하여 StyleRule 을 만듭니다.
while (!stream.AtEnd()) {
StyleRuleBase* rule;
switch (stream.UncheckedPeek().GetType()) {
// ...
case kAtKeywordToken:
rule = ConsumeAtRule(stream, allowed_rules);
break;
// ...
if (rule) {
allowed_rules = ComputeNewAllowedRules(allowed_rules, rule);
callback(rule);
}
}
// ...
}
이 때 StyleRule
은 StyleResolver
가 Element
에 스타일을 적용할지 여부를 결정할 수 있도록 돕는 CSSSelectorList
와 어떤 속성에 어떤 값을 적용할지 기록한 CSSPropertyValueSet
가 기록되어 있습니다.
// third_party/blink/renderer/core/css/parser/css_parser_impl.cc
// 예시로 ViewPort 와 관련한 스타일을 해석하는 함수입니다.
StyleRuleViewport* CSSParserImpl::ConsumeViewportRule(
CSSParserTokenStream& stream) {
// ...
return MakeGarbageCollected<StyleRuleViewport>(
// CSSPropertyValueSet 을 StyleRuleViewport 를 생성할 때 전달합니다.
CreateCSSPropertyValueSet(parsed_properties_, kCSSViewportRuleMode));
}
// CSSPropertyValueSet 을 생성하는 함수입니다.
static ImmutableCSSPropertyValueSet* CreateCSSPropertyValueSet(
HeapVector<CSSPropertyValue, 256>& parsed_properties,
CSSParserMode mode) {
// ...
}
// third_party/blink/renderer/core/css/style_rule.h
// A single rule from a stylesheet. Contains a selector list (one or more
// complex selectors) and a collection of style properties to be applied where
// those selectors match. These are output by CSSParserImpl.
// StyleRule 정의 일부입니다.
class CORE_EXPORT StyleRule : public StyleRuleBase {
public:
// ...
// CSSSelectorList 를 가지고 있습니다.
const CSSSelectorList& SelectorList() const { return selector_list_; }
// CSSPropertyValueSet 를 가지고 있습니다.
const CSSPropertyValueSet& Properties() const;
순서대로 정리하자면 다음과 같은 순서로 동작한다고 볼 수 있습니다.
Document
가 만들어지고 HTML 파일이 해석되어 DOM 트리가 생성됩니다.CSSParser
로 CSS 파일을 해석하여StyleRule
을 담은StyleSheetContents
를 준비합니다.Document
가 스타일을 업데이트할 때StyleResolver
에게 준비된StyleSheetContents
들을 전달합니다.StyleResolver
는 전달받은StyleSheetContents
에 있는StyleRule
들로ComputeStyle
을 생성합니다.
참고
Life of a pixel (Chrome University 2019)
Life of a pixel
How Blink works
'Develop > Frontend 가이드' 카테고리의 다른 글
[FE] 브라우저 렌더링 Browser rendering - 4단계 페인팅 'paint' (0) | 2021.03.30 |
---|---|
[FE] 브라우저 렌더링 Browser rendering - 3단계 레이아웃 'layout' (0) | 2021.03.27 |
[FE] 브라우저 렌더링 Browser rendering - 1단계 파싱 'parse' (0) | 2021.03.27 |
[FE] 브라우저 렌더링 Browser rendering - 0단계 소개 (2) | 2021.03.26 |
[React] 조건부 렌더링 패턴 Conditional Rendering Pattern (2) | 2021.01.10 |
꾸준히 노력하는 개발자 "김예건" 입니다.