목표
- 브라우저 렌더링 과정 딥하게 이해하기(크롬을 기준으로 작성)
- 브라우저 렌더링을 최적화하는 기법 알아보기
들어가며
최근 한 프로젝트에서 Blockly라는 블록 코딩 라이브러리를 사용하다가 이상한 현상을 발견했습니다. 블록을 드래그하면 순간적으로 이상한 위치로 이동하는 문제였습니다.
문제의 원인을 파악하기 위해 Blockly의 GitHub 코드를 살펴보던 중, 흥미로운 점을 발견했습니다. 바로 블록의 드래그가 시작될 때 해당 블록이 특정 레이어(drag layer)로 이동한다는 점이었습니다.
startDrag(e?: PointerEvent): void {
...
this.workspace.getLayerManager()?.moveToDragLayer(this.block);
}
왜 Blockly는 드래그되는 블록을 별도의 레이어로 분리하는지 궁금했습니다. 이 질문에 답하기 위해서는 브라우저가 어떻게 화면을 그리는지 이해할 필요가 있습니다.
브라우저는 어떻게 화면을 그릴까?
브라우저가 HTML, CSS, JavaScript, 이미지 등의 콘텐츠를 픽셀로 변환해 화면에 그리는 이 과정을 렌더링(rendering)이라고 합니다.
잠깐, 정확히 누가 화면을 그리는데? 🤔
근데 여기서 한 가지 의문이 들었습니다. 우리는 브라우저를 사용해 단순히 웹페이지를 보여는 것 외에도 여러 탭을 만들거나, 주소를 입력하기도 하고, 웹사이트의 히스토리를 살펴보는 등 정말 다양한 일을 할 수 있습니다. 그렇다면 실제로 렌더링을 담당하는 건 브라우저의 어떤 부분일지 궁금했습니다.
Chrome은 멀티프로세스 아키텍처
궁금해서 여러 글을 찾아보니 chrome은 여러 개의 프로세스로 구성되어 있다는 사실을 알게 되었습니다. The Chromium Projects 사이트에서 Multi-process Architecture 페이지를 보면 아래와 같은 이미지를 볼 수 있습니다.
주요 프로세스 종류는 크게 3가지가 있습니다.
- Browser Process: 주소창과 북마크 같은 UI 요소 관리
- Renderer Process: 웹 페이지 콘텐츠 처리 (탭별로 독립적)
- main thread: JS 실행, style 계산, layout, paint 담당
- compositor thread: layer 관리, scroll 관리, layer 합성 담당
- GPU Process: 최종 화면 출력 담당
이중 실제 웹페이지 렌더링을 담당하는 건 Renderer Process입니다. 각 탭마다 독립적인 프로세스가 할당되어, 한 탭의 문제가 다른 탭에 영향을 주지 않도록 설계되어 있습니다.
직접 살펴보기
실제로 chrome에서 Shift + Esc를 눌러 Chrome 전용 작업 관리자를 보면 브라우저 내의 프로세스를 구경할 수 있습니다.
Chrome(Chromium)의 렌더링 과정을 살펴보자
이제 렌더링을 담당하는 Renderer Process가 실제로 어떻게 웹페이지를 그리는지 자세히 살펴보겠습니다.
Chrome은 Edge, Opera 등 여러 브라우저의 근간이 되는 Chromium이라는 오픈소스를 기반으로 만들어졌기 때문에, Chromium의 렌더링 파이프라인을 이해하면 다른 브라우저의 동작 방식도 이해하는 데 도움이 될 것 같습니다.
Chromium Rendering Pipeline
Chromium의 RenderingNG이 렌더링을 하는 과정은 chrome for developers 페이지에서 확인할 수 있으며 전체적인 과정은 다음과 같습니다.
- HTML parsing: 브라우저가 HTML 문서를 파싱하면서 DOM(Document Object Model) 트리를 생성합니다. 이는 다음과 같은 과정으로 진행됩니다. 아래와 같은 HTML은 다음과 같은 DOM 트리로 변환됩니다.
- CSS parsing: CSS를 parsing해서 스타일 규칙을 계산하고 최종적으로 Render Tree 생성합니다.
- Layout: 각 Tree의 노드가 화면상에 그려질 크기와 위치를 계산합니다.
- Paint: Layout 단계에서 계산된 정보를 바탕으로 실제 픽셀로 그리는 과정입니다
- Compositing: 생성된 레이어들을 최종적으로 합성하는 과정입니다
(...다시 작성중)
레이어 분리가 가져오는 이점
이제 원래 질문으로 돌아가보면 Blockly가 드래그되는 블록을 별도 레이어로 분리하는 이유는 성능 최적화를 위해서입니다.
일반적으로 요소의 위치가 변경되면 스타일 재계산, 레이아웃 트리 재구성, 페인트 순서 결정, 레이어 업데이트가 필요합니다. 그러나 드래그 중인 블록을 별도 레이어로 분리하면 컴포지터 스레드에서 해당 레이어의 위치만 업데이트하면 됩니다. 이는 메인 스레드의 부하를 줄이고 불필요한 리렌더링을 방지할 수 있습니다.
컴포지터 스레드는 메인 스레드와 별도로 동작하므로 GPU에서 직접 처리할 수 있어 부드러운 애니메이션과 드래그 동작이 가능합니다.
마치며
결과적으로 브라우저의 렌더링 과정을 이해함으로써, 처음에 궁금했던 Blockly의 레이어 분리 전략이 단순한 구현 선택이 아닌, 성능 최적화를 위한 깊은 고민의 결과임을 알 수 있었습니다. 또한, 크롬 브라우저의 멀티 프로세스 아키텍처와 렌더링 파이프라인은 Blockly의 동작 방식과 밀접하게 연결되어 있으며, 이러한 구조를 활용하면 복잡한 DOM 연산을 피하면서 효율적으로 화면을 갱신할 수 있습니다.
참고
https://velog.io/@pakxe/Browser-rendering-pipeline
https://www.youtube.com/watch?v=_XTr2metxJg
https://www.youtube.com/watch?v=K2QHdgAKP-s&list=PL9ioqAuyl6ULp1f36EEjIN1vSBEfsb-0a&index=3
https://velog.io/@adultlee/브라우저가-그리는-법
https://levelup.gitconnected.com/how-web-browsers-use-processes-and-threads-9f8f8fa23371
https://www.chromium.org/developers/design-documents/multi-process-architecture/
https://www.youtube.com/watch?v=K2QHdgAKP-s&list=PL9ioqAuyl6ULp1f36EEjIN1vSBEfsb-0a&index=3
'Frontend' 카테고리의 다른 글
Mixpanel을 활용한 사용자 행동 패턴 분석(1) (1) | 2025.01.12 |
---|---|
SVG 파헤치기 (0) | 2024.11.17 |
div 테두리 겹침 현상 해결하기 (1) | 2024.10.13 |
offsetWidth으로 강제로 랜더링시키기 (1) | 2024.09.08 |