inflearn logo
강의

講義

知識共有

DOMからピクセルまで、ブラウザレンダリングとCRP完全攻略 - [DOM完全攻略 Part 3]

レンダリングエンジンを自作する — 全体の流れのシミュレーション

Composition단계에서 `Layer`는 누가 만드는지

解決済みの質問

23

krak

投稿した質問数 1

0

Composition(합성)단계에서는 여러 Layer를 가지고 하나의 화면에 합성시킵니다.

그런데... 그 여러개의 Layer들은 누가 만드는건가요? Layout > Painting > Composition 단계에서 갑자기 여러개의 Layer가 나타나니까 혼란스럽네요.

요약하면...

HTML/CSS javascript react dom frontend

回答 1

0

nhcodingstudio

안녕하세요 krak님! 이 부분은 과거에도 비슷한 질문을 주신 분이 있어, 당시 브라우저의 실제 렌더링 구조와 실무에서 어디까지 알아두면 좋은지까지 상세하게 정리해 둔 자료가 있습니다. 해당 내용을 바탕으로 이번 질문에 맞게 조금 더 쉽게 풀어서 설명드리겠습니다.

질문하신 내용을 먼저 짧게 답하면, Composition 단계에서 여러 Layer가 갑자기 만들어지는 것은 아닙니다. 그리고 렌더 트리 여러 개가 각각 Layer가 되는 것도 아닙니다.

현재 Chromium 계열 브라우저를 기준으로 조금 더 정확하게 보면, Layout 이후 Pre-Paint와 Paint를 거치면서 브라우저가 화면을 그리기 위한 정보를 만들고, 그 결과를 Paint Chunk라는 단위로 정리합니다. 이후 이 Paint Chunk들을 바탕으로 어떤 내용을 별도의 composited layer로 관리할지를 결정하는 layerization 과정이 진행됩니다. 그다음 Rasterization을 거쳐 실제로 화면에 그릴 수 있는 픽셀 데이터가 준비되고, 마지막으로 여러 결과를 합성해서 화면에 출력합니다. Chromium의 현재 구현 문서에서도 Paint 과정에서 만들어진 Paint Chunk들을 PaintArtifactCompositor가 처리하여 compositor가 사용할 cc::Layer들을 만드는 구조로 설명하고 있습니다.

쉽게 전체 흐름부터 잡고 시작하면 다음과 같습니다.

HTML + CSS
    ↓
DOM / Style
    ↓
Layout
    ↓
Pre-Paint
    ↓
Paint
    ↓
Display Items
    ↓
Paint Chunks
    ↓
Layerization
    ↓
Composited Layers
    ↓
Rasterization
    ↓
Composition
    ↓
화면 출력

강의에서는 보통 이 중간 과정들을 모두 설명하면 처음 배우실 때 오히려 전체 흐름을 놓치기 쉬워서 다음처럼 많이 단순화합니다.

Layout
  ↓
Paint
  ↓
Composite

그래서 질문하신 것처럼 “Paint까지 Layer 이야기가 없다가 왜 Composite에서 갑자기 Layer가 여러 개 나오지?”라는 의문이 자연스럽게 생길 수 있습니다.

실제로는 그 사이에 몇 가지 중요한 과정이 숨어 있습니다.


1. Layout에서는 Layer를 만드는 것이 아니라 위치와 크기를 계산합니다

우선 이런 HTML이 있다고 해보겠습니다.

<body>
  <header>Header</header>

  <main>
    <h1>Hello</h1>
    <p>Content</p>
  </main>

  <div class="modal">Modal</div>
</body>

브라우저가 HTML과 CSS를 해석하고 Layout을 수행하면 각 요소가 화면의 어디에 위치하고, 얼마나 큰지를 계산합니다.

개념적으로 보면 다음과 같습니다.

body
├─ header
├─ main
│  ├─ h1
│  └─ p
└─ modal

여기서 브라우저가 계산하는 것은 대략 이런 정보입니다.

header
→ x: 0
→ y: 0
→ width: 1200px
→ height: 80px

main
→ x: 0
→ y: 80px
→ width: 1200px
→ height: 1000px

modal
→ x: 400px
→ y: 200px
→ width: 400px
→ height: 300px

물론 실제 내부 구조는 훨씬 복잡하지만, Layout을 이해할 때는 “각 요소가 어디에 있고 얼마나 큰지를 결정한다”고 생각하시면 충분합니다.

여기서 첫 번째로 꼭 구분해야 할 것이 있습니다.

DOM 요소 하나 = Layer 하나

가 아닙니다.

header가 있으니까 Header Layer가 생기고, main이 있으니까 Main Layer가 생기고, modal이 있으니까 Modal Layer가 생기는 식으로 동작하는 것이 아닙니다.

DOM 요소가 수백 개여도 compositor layer는 훨씬 적을 수 있고, 반대로 특정 상황에서는 일부 콘텐츠가 별도의 layer로 분리될 수도 있습니다.

실무에서는 이 차이를 알아두는 것이 중요합니다. 예를 들어 React로 카드 1,000개를 렌더링했다고 해서 “브라우저에 Layer가 1,000개 생겼겠구나”라고 생각하면 안 됩니다. DOM 개수와 compositor layer 개수는 일대일 관계가 아닙니다.


2. Layout이 끝났다고 바로 Paint하는 것도 아닙니다

Layout으로 위치와 크기를 계산했다고 해도 실제 화면을 그리기 위해서는 추가 정보가 필요합니다.

예를 들어 modal에 다음 CSS가 있다고 해보겠습니다.

.modal {
  position: fixed;
  opacity: 0.9;
  transform: translateX(20px);
  overflow: hidden;
}

Layout만 알고 있다고 해서 이 요소를 어떻게 그릴지 완전히 결정할 수 있는 것은 아닙니다.

브라우저 입장에서는 다음과 같은 정보도 필요합니다.

이 요소는 transform되어 있는가?

어떤 영역에서 잘려야 하는가?

opacity가 적용되는가?

다른 요소보다 앞에 그려야 하는가?

어떤 좌표계를 기준으로 그릴 것인가?

이와 관련된 준비 작업이 Layout과 Paint 사이의 Pre-Paint 단계에서 이루어집니다.

현재 Chromium에서는 Pre-Paint 과정에서 paint invalidation을 처리하고 transform, clip, effect 등의 상태를 표현하기 위한 paint property tree를 준비합니다.

처음 공부하실 때는 내부 자료구조 이름까지 외우실 필요는 없습니다.

다만

Layout
→ 위치와 크기 결정

Pre-Paint
→ 실제 Paint를 하기 위해 필요한 상태 정리

정도로만 구분해두시면 이후 내용이 훨씬 이해하기 쉬워집니다.

실무 예시를 하나 들어보겠습니다.

사용자가 버튼을 눌러서 어떤 요소의 width를 변경했다고 해보겠습니다.

box.style.width = "500px";

width가 달라지면 주변 요소의 위치에도 영향을 줄 수 있으므로 Layout을 다시 계산해야 할 수 있습니다.

반대로 단순히 어떤 요소의 색상만 바꾼다면

box.style.backgroundColor = "red";

위치와 크기가 바뀐 것이 아니기 때문에 Layout 자체는 필요하지 않을 가능성이 높고 Paint 쪽 변경이 중심이 됩니다.

이런 식으로 “내가 변경한 CSS 속성이 렌더링 파이프라인의 어디까지 영향을 주는가?”를 생각하는 것이 실무 성능 분석에서 상당히 중요합니다.


3. Paint는 실제 픽셀을 바로 칠하는 단계라고 생각하면 조금 부정확합니다

Paint라는 단어 때문에 가장 많이 생기는 오해 중 하나입니다.

처음 배우면 보통 다음처럼 생각하기 쉽습니다.

Paint
=
모니터에 픽셀을 직접 칠한다

하지만 현대 Chromium의 렌더링 구조를 이해할 때는 Paint를

무엇을
어떤 순서로
어떻게 그릴 것인지 기록하는 과정

이라고 생각하는 편이 좋습니다.

예를 들어 앞의 HTML이 있다면 개념적으로 다음과 같은 그리기 작업들이 필요할 수 있습니다.

header의 배경을 그린다

header의 글자를 그린다

main의 배경을 그린다

h1의 글자를 그린다

p의 글자를 그린다

modal의 배경을 그린다

modal의 글자를 그린다

Chromium에서는 이런 그리기 작업을 표현하는 단위 중 하나가 Display Item입니다.

아주 단순화하면 다음과 같습니다.

Display Item 1
→ Header 배경

Display Item 2
→ Header 텍스트

Display Item 3
→ Main 배경

Display Item 4
→ h1 텍스트

Display Item 5
→ p 텍스트

Display Item 6
→ Modal 배경

Display Item 7
→ Modal 텍스트

현재 Chromium의 Paint 구현도 Layout 결과를 순회하면서 display item list를 만들고 이를 이후 compositor에서 사용할 수 있는 형태로 변환합니다.

이 부분은 실무에서도 알아두면 좋습니다.

예를 들어 버튼의 색상 하나만 바꿨다고 해보겠습니다.

button:hover {
  background-color: tomato;
}

버튼의 크기나 위치는 그대로입니다.

따라서 일반적으로 Layout을 다시 할 이유는 없지만, 버튼의 모양이 바뀌었기 때문에 Paint 결과는 변경될 수 있습니다.

즉,

width 변경
→ Layout까지 영향을 줄 수 있음

background-color 변경
→ 주로 Paint와 관련

transform 변경
→ 조건이 맞으면 compositor에서 처리 가능

처럼 어떤 CSS 속성을 변경했느냐에 따라 필요한 작업이 달라질 수 있습니다.


4. Display Item들이 Paint Chunk로 묶입니다

그렇다면 Display Item 하나가 Layer 하나일까요?

이것도 아닙니다.

Display Item 하나
=
Layer 하나

라고 생각하시면 안 됩니다.

Display Item은 너무 세부적인 단위이기 때문입니다.

브라우저는 동일한 paint property 상태를 공유하는 인접한 Display Item들을 Paint Chunk라는 단위로 묶습니다.

예를 들어 개념적으로 다음과 같이 될 수 있습니다.

Paint Chunk A
├─ Header 배경
└─ Header 텍스트


Paint Chunk B
├─ Main 배경
├─ h1 텍스트
└─ p 텍스트


Paint Chunk C
├─ Modal 배경
└─ Modal 텍스트

현재 Chromium 문서에서도 같은 property tree state를 공유하는 인접한 Display Item들을 Paint Chunk로 그룹화한다고 설명합니다.

여기서 또 중요한 오해가 하나 있습니다.

Paint Chunk 하나
=
Layer 하나

도 아닙니다.

Paint Chunk는 이후 layerization에서 판단에 사용되는 중요한 단위이지만, 여러 Paint Chunk가 하나의 compositor layer로 합쳐질 수도 있습니다.


5. 이제 여기서 실제로 Layerization이 이루어집니다

질문하신 “그 여러 Layer는 누가 만드는 건가요?”의 핵심이 바로 이 부분입니다.

Paint 결과로 Paint Chunk들이 만들어지면 Chromium의 PaintArtifactCompositor가 그 결과를 받아 compositor가 사용할 layer 구조로 변환합니다.

그리고 Chromium에서는 이 과정을 layerization이라고 부릅니다. 현재 구현에서는 Paint Chunk들을 바탕으로 우선 PendingLayer를 구성하고, 서로 합칠 수 있는 것들은 합친 뒤 최종 cc::Layer들을 만듭니다. 이 과정에서는 GPU 메모리 사용량과 콘텐츠 변경 시 발생하는 비용 등의 trade-off도 고려됩니다.

앞의 예제로 돌아가보겠습니다.

Paint 결과가 이렇게 만들어졌다고 가정해보겠습니다.

Paint Chunk A
→ Header

Paint Chunk B
→ Main

Paint Chunk C
→ Modal

브라우저가 Header와 Main은 하나로 관리해도 괜찮다고 판단하고 Modal은 독립적으로 관리할 이유가 있다고 판단했다면 개념적으로는 다음처럼 될 수 있습니다.

Compositor Layer 1
├─ Paint Chunk A
└─ Paint Chunk B


Compositor Layer 2
└─ Paint Chunk C

즉 Paint Chunk가 세 개라고 Layer도 반드시 세 개가 되는 것이 아닙니다.

브라우저가 필요에 따라 묶거나 나눕니다.

전체 관계를 아주 간단하게 표현하면 이렇게 생각하시면 좋습니다.

여러 DOM 요소
      ↓
여러 LayoutObject
      ↓
여러 Display Item
      ↓
여러 Paint Chunk
      ↓
Layerization
      ↓
하나 또는 여러 Compositor Layer

이것이 질문하신 “Layer가 갑자기 어디서 나오는 것인가?”에 대한 가장 중요한 답입니다.

Composition이 시작된 순간 아무것도 없는 상태에서 갑자기 Layer가 생성되는 것이 아니라, 그 이전 Paint 결과를 가지고 layerization을 수행하면서 compositor가 사용할 Layer들이 결정됩니다.


6. 그러면 왜 굳이 Layer를 여러 개로 나눌까요?

여기서 “그냥 화면 전체를 하나의 큰 이미지로 만들면 안 되나요?”라는 의문이 생길 수 있습니다.

가능은 하지만 모든 것을 하나로 처리하면 화면의 일부만 움직여도 더 많은 작업을 다시 해야 할 수 있습니다.

예를 들어 페이지에 다음 두 영역이 있다고 해보겠습니다.

─────────────────────────────
      일반적인 본문
─────────────────────────────

           Modal
─────────────────────────────

Modal에 다음 애니메이션이 있다고 가정해보겠습니다.

.modal {
  transform: translateX(100px);
}

만약 Modal이 적절하게 독립적인 composited content로 관리되고 있다면, 이미 만들어진 Modal의 결과물을 다시 처음부터 그리지 않고 compositor에서 위치만 변경할 수 있는 경우가 있습니다.

개념적으로는 이런 방식입니다.

본문 Layer
→ 그대로 유지


Modal Layer
→ 기존 그림 그대로 유지
→ 위치만 이동


마지막에 두 결과를 다시 합성

Photoshop을 생각하시면 이해가 조금 쉽습니다.

배경과 사람을 각각 다른 Layer로 만들어 놓았다면 사람의 위치를 옮길 때 배경 그림 전체를 다시 그릴 필요는 없습니다.

브라우저의 compositor layer도 완전히 동일한 구조는 아니지만, 처음 개념을 잡기에는 비슷한 비유가 도움이 됩니다.


7. 여기서 Rasterization이 등장합니다

Paint에서 “무엇을 그릴 것인가”를 기록했다면 실제 픽셀로 만들어야 합니다.

이 과정이 Rasterization입니다.

아주 쉽게 표현하면 다음과 같습니다.

Paint

"여기에 사각형을 그려라"
"여기에 Hello라는 글자를 그려라"
"여기에 이미지를 그려라"

        ↓

Rasterization

실제로 화면에 표시할 수 있는
픽셀 데이터로 변환

따라서 Paint와 Raster는 구분해서 알아두시는 것이 좋습니다.

Paint
→ 무엇을 어떻게 그릴지 기록

Raster
→ 그 정보를 실제 픽셀 데이터로 변환

그리고 여기서도 자주 생기는 오해가 있습니다.

Layer 하나
=
거대한 Bitmap 이미지 하나

라고 생각하면 정확하지 않습니다.

실제로 큰 layer의 콘텐츠는 여러 tile로 나뉘어서 rasterize될 수 있습니다.

개념적으로는 다음과 같습니다.

하나의 큰 콘텐츠

┌─────────────┬─────────────┐
│   Tile 1    │   Tile 2    │
├─────────────┼─────────────┤
│   Tile 3    │   Tile 4    │
├─────────────┼─────────────┤
│   Tile 5    │   Tile 6    │
└─────────────┴─────────────┘

이런 구조 덕분에 매우 긴 페이지 전체를 항상 한꺼번에 거대한 이미지로 만드는 식으로 처리할 필요가 없습니다.


8. 마지막 Composition에서는 무엇을 하나요?

이제 드디어 질문에서 말씀하신 Composition 단계입니다.

이 시점에서는 브라우저가 가지고 있는 여러 렌더링 결과를 조합해서 최종 화면을 만듭니다.

개념적으로 보면 다음과 같습니다.

Layer A
→ 일반 페이지 콘텐츠

Layer B
→ Modal

Layer C
→ 별도의 효과가 적용된 콘텐츠

        ↓

Composition

        ↓

최종 화면

예를 들어 Modal Layer가

transform: translateX(100px);
opacity: 0.8;

를 가지고 있다면 compositor는 준비된 결과를 적절한 위치로 이동시키고 opacity를 반영해서 다른 결과와 합성할 수 있습니다.

그래서 Layerization과 Composition은 서로 다른 개념입니다.

Layerization은

무엇을 어떤 Layer로 나눌 것인가?

를 결정하는 과정이고,

Rasterization은

그 Layer가 그릴 내용을 실제 픽셀로 어떻게 만들 것인가?

와 관련된 과정이며,

Composition은

준비된 여러 결과를 어떻게 조합해서
최종 화면을 만들 것인가?

와 관련된 과정입니다.


9. 그러면 “렌더 트리 여러 개 = Layer 여러 개”인가요?

두 번째 질문에 대해서는 명확하게 아니라고 보시면 됩니다.

Render Tree라는 개념과 compositor layer는 역할 자체가 다릅니다.

입문 자료에서 이야기하는 Render Tree는 쉽게 말하면

페이지에서 실제로 렌더링될 요소들이
어떤 구조를 가지고 있는가?

와 관련된 개념입니다.

현대 Chromium 내부에서는 이를 단순히 하나의 “Render Tree”라는 표현으로만 설명하기보다는 LayoutObject tree, fragment 구조, PaintLayer, paint property tree, PaintArtifact 등 여러 자료구조가 렌더링 단계별로 사용됩니다. 현재 Chromium의 core rendering 역시 DOM, Style, Layout, Paint를 주요 단계로 두고, core rendering의 결과인 PaintArtifact를 compositor의 layer 목록으로 변환하는 구조입니다.

반면 Compositor Layer는

최종 렌더링 결과를
어떤 단위로 독립적으로 관리하고 합성할 것인가?

와 관련된 구조입니다.

예를 들어 페이지 구조가 다음과 같다고 해보겠습니다.

body
├─ header
├─ main
│  ├─ article
│  └─ aside
└─ modal

Layout 관점에서는 하나의 연결된 페이지 구조입니다.

그런데 compositing 결과는 상황에 따라 다음처럼 될 수 있습니다.

Layer A
├─ header
└─ main


Layer B
└─ modal

따라서

Render Tree가 두 개이기 때문에
Layer도 두 개가 된 것

이 아닙니다.

하나의 문서를 렌더링한 결과 중 일부를 독립적으로 관리하는 것이 유리하다고 브라우저가 판단했기 때문에 여러 compositor layer로 나뉠 수 있는 것입니다.


10. PaintLayer와 Compositor Layer도 같은 Layer가 아닙니다

여기까지 공부하면 검색하다가 또 다른 Layer라는 단어들을 만나게 됩니다.

대표적으로 다음과 같습니다.

PaintLayer

Compositor Layer

cc::Layer

GPU Layer

Stacking Context

이 부분에서 많이 헷갈립니다.

특히 Chromium 내부에는 실제로 PaintLayer라는 클래스가 있습니다.

그렇다고

PaintLayer
=
Compositor Layer

라고 이해하시면 안 됩니다.

현재 Chromium 문서에서도 PaintLayer는 Blink의 오래된 내부 구현 세부사항이라고 설명하고 있으며, LayoutObject/PaintLayer 쪽의 Paint 결과를 이후 PaintArtifactCompositor가 처리해 별도의 cc::Layer 목록으로 만드는 구조를 명확히 구분합니다.

프론트엔드 개발자 입장에서는 PaintLayer 내부 구현을 세부적으로 외우실 필요는 없습니다.

대신

브라우저 내부에서 Layer라는 단어가 나왔다고 해서
모두 compositor layer를 의미하는 것은 아니다.

이것만 확실하게 기억하시면 됩니다.


11. Stacking Context도 Compositor Layer와 다릅니다

이 둘도 정말 많이 혼동합니다.

예를 들어 다음 CSS가 있다고 해보겠습니다.

.modal {
  position: relative;
  z-index: 9999;
}

z-index와 stacking context는 주로

무엇을 먼저 그릴 것인가?

어떤 요소가 어떤 요소 위에 나타날 것인가?

와 관련된 개념입니다.

반면 compositor layer는

어떤 렌더링 결과를
독립적으로 rasterize하거나
composite할 것인가?

와 관련됩니다.

따라서

Stacking Context
≠
Compositor Layer

입니다.

이 부분은 실무에서 특히 중요합니다.

예를 들어

z-index: 999999999;

를 줬는데도 Modal이 다른 요소 밑에 있다면, 단순히 “GPU Layer가 이상하다”고 볼 문제가 아닙니다.

부모 요소가 새로운 stacking context를 만들고 있는지부터 살펴봐야 하는 경우가 많습니다.

반대로 애니메이션이 버벅거린다면 stacking context보다는 Layout, Paint, Rasterization, compositing 비용을 확인하는 것이 더 중요할 수 있습니다.


12. 실무에서 가장 많이 연결되는 것이 transform과 opacity입니다

Layer와 Composition을 배우는 가장 큰 이유 중 하나가 프론트엔드 성능과 연결되기 때문입니다.

예를 들어 어떤 상자를 오른쪽으로 이동시키고 싶다고 해보겠습니다.

다음처럼 구현할 수도 있습니다.

.box {
  position: relative;
  left: 300px;
}

left 값의 변경은 요소의 기하학적 위치에 영향을 주므로 상황에 따라 Layout이 다시 필요할 수 있습니다.

그러면 이후 과정까지 영향을 받을 수 있습니다.

Style
  ↓
Layout
  ↓
Pre-Paint
  ↓
Paint
  ↓
Raster
  ↓
Composite

반면 다음처럼 만들 수도 있습니다.

.box {
  transform: translateX(300px);
}

해당 콘텐츠가 적절하게 composited되어 있고 필요한 조건이 충족된다면, 이미 rasterize되어 있는 콘텐츠를 다시 Layout하고 Paint하지 않고 compositor에서 transform 정보만 변경하여 새로운 위치에 합성할 수 있습니다.

개념적으로는 다음과 같습니다.

이미 만들어져 있는 Box의 결과
           ↓
transform 정보 변경
           ↓
Composition
           ↓
새 위치에 표시

이 때문에 실무에서 애니메이션을 만들 때 transform과 opacity가 자주 권장되는 것입니다.

하지만 여기서도 하나는 꼭 조심하셔야 합니다.

transform 사용
=
무조건 새로운 GPU Layer 생성
=
무조건 빠름

이라는 의미는 아닙니다.

실제 layerization은 브라우저가 여러 조건을 보고 결정합니다. Chromium의 현재 layerization 알고리즘 역시 각 Paint Chunk를 무조건 독립 Layer로 만드는 것이 아니라, 가능한 경우 PendingLayer들을 합치고 직접적인 compositing reason, overlap, sparsity 등의 조건을 고려해 분리 여부를 판단합니다.

따라서 실무에서는 “transform은 무조건 빠르다”보다

transform이나 opacity는
상황에 따라 Layout과 Paint를 건너뛰고
compositor 단계에서 처리할 가능성이 높기 때문에
애니메이션에 유리하다.

정도로 이해하시는 것이 더 정확합니다.


13. will-change도 Layer와 관련해서 꼭 알아둘 만합니다

다음과 같은 코드를 본 적이 있으실 수 있습니다.

.card {
  will-change: transform;
}

이는 브라우저에게

이 요소의 transform이
곧 변경될 가능성이 높습니다.

라는 힌트를 주는 속성입니다.

현재 Chromium 구현에서도 will-change: transform은 직접적인 compositing reason으로 사용될 수 있고, layerization 결과에 영향을 줄 수 있습니다.

그렇다고 다음처럼 사용하는 것은 좋지 않습니다.

* {
  will-change: transform;
}

또는 카드가 2,000개 있는데

.card {
  will-change: transform;
}

을 전부 적용하는 것도 일반적으로 좋은 최적화 방식은 아닙니다.

Layer와 관련된 리소스는 공짜가 아닙니다.

별도의 compositing 처리가 늘어나면 메모리 사용량이나 관리 비용 또한 증가할 수 있습니다.

그래서 실무에서는 먼저 성능을 측정하고, 실제 애니메이션이 자주 발생하며 효과가 있는 요소에 필요한 경우 제한적으로 적용하는 것이 좋습니다.


14. 실무에서는 “몇 개의 Layer가 있는가?”보다 “어느 단계가 다시 실행되는가?”가 더 중요합니다

실제로 프론트엔드 개발을 하다 보면 “이 요소가 Layer인가 아닌가?”만 외우는 것보다 다음 사고방식이 훨씬 도움이 됩니다.

예를 들어 애니메이션이 끊긴다고 해보겠습니다.

그때는 먼저 이 CSS 변경이 Layout을 일으키는지를 생각합니다.

Layout이 발생한다면 얼마나 많은 요소가 영향을 받는지 확인합니다.

그다음 Paint가 매 프레임 발생하고 있는지 확인합니다.

Paint가 발생한다면 화면에서 어느 정도의 영역이 다시 그려지고 있는지 확인합니다.

그리고 Rasterization 비용이 너무 크지는 않은지, compositor에서 처리 가능한 움직임인데 불필요하게 Paint가 반복되고 있지는 않은지도 살펴봅니다.

즉 실무에서는 이런 식으로 생각하시면 됩니다.

CSS / JS 변경
      ↓
Layout까지 필요한가?
      ↓
Paint가 필요한가?
      ↓
Raster를 다시 해야 하는가?
      ↓
Composite만으로 처리 가능한가?

예를 들어 마우스를 따라다니는 tooltip을 매 프레임 left, top으로 움직였는데 성능 문제가 있다면 transform 기반 이동을 검토해볼 수 있습니다.

대형 사이드 메뉴가 열릴 때 매 프레임 width를 변경하고 있다면 Layout을 계속 발생시키고 있지는 않은지 확인할 수 있습니다.

스크롤 시 특정 요소의 배경 효과 때문에 넓은 영역이 계속 repaint된다면 Paint 비용을 살펴볼 수 있습니다.

이렇게 실제 성능 문제를 “Layout 문제인지, Paint 문제인지, compositing 문제인지”로 나누어 보는 것이 Layer 개수를 암기하는 것보다 훨씬 중요합니다.


15. 오래된 자료와 최신 자료의 설명이 다른 이유도 알아두시면 좋습니다

검색하다 보면 어떤 자료에서는

Layout
↓
Layer 생성
↓
Paint
↓
Composite

라고 설명하고,

다른 자료에서는

Layout
↓
Paint
↓
Layerization
↓
Composite

라고 설명하는 것을 볼 수 있습니다.

이 부분에서 “둘 중 하나가 틀린 건가?”라고 생각하기 쉽습니다.

그런데 Chrome의 렌더링 아키텍처가 실제로 변경되어 왔기 때문에 오래된 자료와 현재 구현을 그대로 비교하면 차이가 생길 수 있습니다.

과거 Chromium의 Slimming Paint V1 계열에서는 layerization이 Pre-Paint와 Paint보다 앞에서 이루어지는 구조가 사용됐습니다. 이후 Slimming Paint V2와 Composite After Paint 계열로 발전하면서 Paint Chunk를 만든 이후 PaintArtifactCompositor가 layerization하는 구조로 변경되었습니다. 현재 Chromium의 Paint 문서는 Layout 결과를 Pre-Paint와 Paint를 거쳐 Paint Chunk로 만들고, 이를 PaintArtifactCompositor가 처리하여 cc::Layer 목록으로 변환하는 방식을 설명하고 있습니다.

따라서 현재 Chromium을 기준으로 공부한다면 다음 흐름을 기준으로 이해하는 것이 좋습니다.

Layout
   ↓
Pre-Paint
   ↓
Paint
   ↓
Display Items
   ↓
Paint Chunks
   ↓
Layerization
   ↓
Compositor Layers
   ↓
Rasterization
   ↓
Composition

강의에서 Layout → Paint → Composite라고 설명하는 것은 이 모든 내부 단계를 초보자에게 처음부터 보여주기보다는 핵심적인 큰 흐름만 추려서 표현한 것이라고 생각하시면 됩니다.


마지막으로 정리해보겠습니다

첫 번째 질문인 “Layer 여러 개는 어떤 단계에서 생성되는 건가요?”에 대해서는, 현재 Chromium 기준으로 Paint 과정에서 Display Item과 Paint Chunk 같은 그리기 정보가 만들어지고, 이후 layerization 과정에서 이 Paint Chunk들을 바탕으로 compositor가 사용할 여러 cc::Layer가 결정된다고 이해하시면 됩니다.

따라서 Composition 단계에서 갑자기 Layer가 만들어지는 것은 아닙니다. Composition은 이미 준비된 렌더링 결과들을 최종 화면으로 조합하는 쪽에 더 가깝습니다.

두 번째 질문인 “렌더 트리 여러 개가 Layer 여러 개인가요?”에 대해서는 아닙니다.

Render/Layout 계층은 페이지의 구조와 위치, 크기 등을 계산하기 위한 구조이고, compositor layer는 렌더링 결과를 어떤 단위로 독립적으로 관리하고 rasterize하고 합성할지를 결정하기 위한 별도의 개념입니다.

가장 중요한 흐름만 다시 보면 다음과 같습니다.

Layout
→ 어디에 얼마나 크게 배치할 것인가?


Pre-Paint
→ Paint에 필요한 transform, clip, effect 등의 상태 준비


Paint
→ 무엇을 어떤 순서로 그릴 것인가?


Display Item / Paint Chunk
→ Paint 결과를 브라우저 내부에서 관리하기 좋은 단위로 정리


Layerization
→ 어떤 Paint 결과들을 같은 Layer로 묶고,
   어떤 것은 별도의 Layer로 관리할 것인가?


Rasterization
→ 그리기 정보를 실제 픽셀 데이터로 변환


Composition
→ 준비된 여러 결과를 위치, transform, opacity 등을 반영하여
   하나의 최종 화면으로 합성

그리고 이 부분을 공부하실 때는 다음 관계를 특히 구분해두시면 좋습니다.

DOM Element = Layer가 아니고, Render Tree = Layer Tree도 아니며, PaintLayer = Compositor Layer도 아닙니다. Stacking Context = Compositor Layer도 아니고, Paint Chunk 하나 = Layer 하나도 아닙니다. 마찬가지로 transform을 사용한다고 항상 새로운 Layer가 만들어지는 것도 아니며, will-change를 많이 사용한다고 항상 성능이 좋아지는 것도 아닙니다.

프론트엔드 실무에서는 Chromium 내부 클래스명까지 전부 외우실 필요는 없습니다. 오히려 “내가 변경한 코드가 Layout까지 다시 발생시키는지, Paint가 필요한지, 아니면 기존 결과를 활용해서 Composite만으로 처리할 수 있는지”를 구분할 수 있는 것이 훨씬 중요합니다.

이 흐름을 기준으로 이해하시면 이후에 reflow, repaint, composite, transform, opacity, will-change, stacking context, z-index, GPU 가속 같은 개념을 공부하실 때도 각각이 어느 지점에서 연결되는지 훨씬 수월하게 이해하실 수 있습니다.

질문 감사합니다.

음성불량

0

11

1

1강 동영상 재생 오류

1

14

2

generateStaticParams() 응용 질문 드립니다.

0

13

1

Mac intel 에서 Homebrew 설치

0

19

0

🔥 [필수] 강의교안 & 소스코드 🔥 링크오류

0

21

2

Context7 버전 관련

0

16

1

깃 초기화?

0

18

1

13강 [로그인 단일 토큰] 에서 프로젝트 실행 시 에러

0

15

1

헤더에서 empty column이 삭제가 안되고, 헤더 레이아웃을 동영상처럼 선택하는 옵션이 안나오는데 어떻게 하나요?

0

14

1

서브에이전트

0

31

2

서브에이전트의 유효성 질문

0

38

2

쉬림프 태스트 매니저 관련 질문

0

35

2

c:가 아니라 D:로 이동 할려면 어떻게 해야되나요?

0

40

2

ultrathink 관련 질문

0

47

2

Strict Mode 예시 질문!

0

47

2

수업이 안보여요

0

53

1

강의교안이 어디에 있는지 알 수 있을까요

0

59

2

저자 블로그에 있는 강의는 어떤 강의인가요?

1

51

1

해당 플러그인을 설치했는데 추가 지원 안될 수 있다고 다른 플러그인을 깔아보라는데 어떻게 해야하나여?

0

49

2

클로드 코드 시작하기: 설치부터 첫 앱 만들기 맛보기 중간 내용 질문입니다.

0

65

2

렌더링 차단 리소스 javascript 실행에 관련해서 질문 있습니다.

0

102

2

렌더링 차단 리소스 관련해서 CSS 질문이 있습니다.

0

97

2

지금 이 화면에서 뭘로 fps를 알 수 있나요?

0

266

2

CSS까지만 지연에 영향을 주는건가요?

0

175

2