
브라우저 렌더링 원리(Browser Rendering): 주소창에 엔터를 치면 일어나는 일
HTML 파싱부터 DOM, CSSOM 생성, 렌더 트리, 레이아웃(Reflow), 페인트(Repaint), 그리고 합성(Composite)까지. 브라우저가 화면을 그리는 6단계 과정과 치명적인 렌더링 성능 최적화(CRP) 가이드.

HTML 파싱부터 DOM, CSSOM 생성, 렌더 트리, 레이아웃(Reflow), 페인트(Repaint), 그리고 합성(Composite)까지. 브라우저가 화면을 그리는 6단계 과정과 치명적인 렌더링 성능 최적화(CRP) 가이드.
프론트엔드 개발자가 알아야 할 4가지 저장소의 차이점과 보안 이슈(XSS, CSRF), 그리고 언제 무엇을 써야 하는지에 대한 명확한 기준.

매번 3-Way Handshake 하느라 지쳤나요? 한 번 맺은 인연(TCP 연결)을 소중히 유지하는 법. HTTP 최적화의 기본.

클래스 이름 짓기 지치셨나요? HTML 안에 CSS를 직접 쓰는 기괴한 방식이 왜 전 세계 프론트엔드 표준이 되었는지 파헤쳐봤습니다.

HTTP는 무전기(오버) 방식이지만, 웹소켓은 전화기(여보세요)입니다. 채팅과 주식 차트가 실시간으로 움직이는 기술적 비밀.

프론트엔드 개발자라면 누구나 한 번쯤 겪는 문제입니다. 버튼을 눌렀는데 화면이 버벅거리거나(Jank), 애니메이션이 뚝뚝 끊기는 현상. 이걸 해결하려면 리액트(React)나 뷰(Vue)를 잘하는 것도 중요하지만, 더 근본적으로 브라우저가 어떻게 화면을 그리는지를 알아야 합니다.
우리가 크롬 주소창에 google.com을 치고 엔터를 누르는 순간부터, 눈앞에 검색창이 뜨기까지 브라우저 내부에서는 엄청난 일이 벌어집니다. 이 일련의 과정을 CRP (Critical Rendering Path, 중요 렌더링 경로)라고 부릅니다.
이 경로를 최적화하는 것이 웹 성능 최적화의 핵심이며, 구글이 웹사이트의 성능을 측정하는 지표(Core Web Vitals)의 근간이 됩니다.
오늘은 브라우저의 렌더링 엔진(Blink, WebKit, Gecko 등)이 어떻게 단순한 HTML 텍스트를 아름다운 픽셀로 바꾸는지, 그 6단계 과정을 아주 상세하게 뜯어봤습니다.
서버로부터 HTML 파일을 받으면(Response), 이는 단순한 0과 1로 된 바이트(Bytes) 스트림일 뿐입니다. 브라우저는 이를 해석 과정을 거쳐 이해 가능한 구조로 바꿉니다.
<, h, t, m, l, >)StartTag: html, StartTag: body)html -> body -> div)DOM은 자바스크립트가 HTML 구조에 접근하고 조작할 수 있도록 만든 API 인터페이스입니다.
원칙적으로 DOM 파싱은 동기적(Synchronous)으로 일어납니다. 즉, HTML을 읽다가 <script> 태그를 만나면, 스크립트 실행이 끝날 때까지 파싱을 멈춥니다.
하지만 브라우저는 생각보다 똑똑합니다. 메인 파서가 멈춰있는 동안, 백그라운드에서 프리로드 스캐너라는 녀석이 HTML을 미리 훑어봅니다.
"어, 저 뒤에 <img>, <link>, <script>가 있네?" 하고 미리 네트워크 요청을 보냅니다.
덕분에 파싱이 끝나기도 전에 리소스 다운로드가 병렬로 진행되어 전체 로딩 속도가 빨라집니다.
HTML을 읽다가 <link rel="stylesheet">나 <style> 태그를 만나면 CSS 파싱을 시작합니다.
DOM과 비슷하게 CSS도 CSSOM 트리로 변환됩니다.
여기에는 "상속(Cascading)" 규칙이 적용됩니다. body { font-size: 16px }라고 지정하면, 자식인 div도 16px를 상속받는 계산이 이 단계에서 모두 끝납니다.
중요: CSSOM이 완성될 때까지 렌더링은 차단됩니다(Render Blocking Resource). CSS가 없으면 화면이 깨져 보이기(FOUC) 때문입니다.
DOM과 CSSOM이 합쳐져서 렌더 트리(Render Tree)가 됩니다. 여기서 중요한 점은 "화면에 실제로 보이는(Visible) 요소만 포함한다"는 것입니다.
opacity: 0 -> 눈에는 투명해서 안 보이지만, 자리는 차지하므로 렌더 트리에 포함됩니다. (클릭 이벤트도 받음)visibility: hidden -> 역시 자리는 차지하므로 렌더 트리에 포함됩니다.display: none -> 아예 공간조차 차지하지 않으므로 렌더 트리에 포함되지 않습니다. (트리에서 제외됨)그래서 성능 최적화를 할 때, 요소를 잠깐 숨기려면 display: none을 쓰는 것이 렌더링 비용을 아끼는 방법이고, 애니메이션(페이드 아웃)을 주려면 opacity를 써야 합니다.
렌더 트리가 만들어지면, 이제 각 요소가 화면의 어느 위치에, 어떤 크기로 배치될지 계산합니다. 이 과정을 레이아웃(Layout) 또는 리플로우(Reflow)라고 합니다.
"Header는 가로 100%, 높이 50px. 그 밑에 Content는 가로 80%..."
브라우저는 뷰포트(Viewport) 크기에 맞춰 기하학적 계산을 수행합니다. % 나 vh 같은 상대 단위가 절대 단위인 px로 변환되는 시점이 바로 이때입니다.
레이아웃 단계는 계산 비용이 매우 비쌉니다. 다음과 같은 경우에 다시 발생합니다.
width, height, margin, padding, top, left)자바스크립트로 스타일을 읽고 쓸 때 주의해야 합니다.
// 나쁜 예: 읽고(Read) -> 쓰고(Write) -> 읽고 -> 쓰고
const list = document.getElementById('list');
const width = list.offsetWidth; // 브라우저는 정확한 값을 주기 위해 강제로 레이아웃을 다시 계산함!
list.style.width = (width + 10) + 'px';
브라우저는 offsetWidth를 정확히 계산하기 위해 큐에 쌓인 변경 사항을 즉시 처리하고 레이아웃을 다시 돌립니다. 이걸 반복문 안에서 하면 성능이 바닥을 칩니다.
해결책은 값을 읽는 것과 쓰는 것을 분리하거나, requestAnimationFrame을 사용하는 것입니다.
위치가 잡혔으면 이제 픽셀을 채울 차례입니다. 텍스트의 색상, 이미지, 그림자, 테두리, 배경색 등을 그립니다. 이 과정을 페인트(Paint) 또는 시스템 래스터화(Rasterizing)라고 합니다.
페인트도 그냥 한 도화지에 그리는 게 아니라, 포토샵처럼 여러 레이어(Layer)로 나눠서 그립니다.
position: fixed, transform: translate3d, video, canvas 태그 등을 사용하면 브라우저는 해당 요소를 별도의 레이어로 분리(Promotion)합니다. 레이어가 분리되면 나중에 그 부분만 다시 그리면 되기 때문에 성능상 이점이 있습니다.
마지막으로, 나뉘어진 레이어들을 합쳐서 최적으로 화면에 표시합니다. 이 과정은 주로 GPU가 담당합니다.
우리가 애니메이션을 만들 때 left, top 대신 transform: translate()를 쓰라고 하는 이유가 여기에 있습니다.
left 변경 -> Layout부터 다시 수행 (CPU 부하 큼, 주변 요소도 다 다시 계산). 이를 리플로우라고 합니다.transform 변경 -> Layout과 Paint를 건너뛰고 Composite 단계만 수행 (GPU 사용, 매우 빠름).GPU는 텍스처(이미지)를 이동시키거나 회전시키는 데 최적화되어 있습니다. 레이아웃 변화 없이 레이어만 움직이는 것이기 때문에 60fps(초당 60프레임)를 유지하기 훨씬 쉽습니다.
리액트가 빠른 이유는 돔 조작을 최소화하기 때문입니다. 데이터가 바뀌면 가상 DOM을 먼저 수정하고, 실제 DOM과 비교(Diffing)해서 바뀐 부분만 실제 DOM에 적용(Reconciliation)합니다. 이는 불필요한 레이아웃(Reflow) 횟수를 획기적으로 줄여줍니다.
styled-components나 Emotion 같은 CSS-in-JS 라이브러리는 편리하지만 "런타임 비용"이 있습니다.
자바스크립트가 실행되면서 스타일을 문자열로 생성하고, 해시 클래스 이름을 만들고, <style> 태그를 동적으로 헤더에 삽입합니다.
이 과정에서 JS 실행 시간과 CSSOM 트리 재구성 시간이 소요됩니다.
이 때문에 최근에는 Zero-runtime CSS (Tailwind CSS, Vanilla Extract)가 각광받고 있습니다. 빌드 타임에 이미 CSS 파일이 완성되어 나오기 때문에, 브라우저 입장에서는 렌더링 부하가 훨씬 적습니다.
웹 성능 최적화의 핵심은 "가능한 뒤쪽 단계(Composite)만 실행되도록 만드는 것"입니다. 앞 단계(Layout)로 돌아갈수록 브라우저는 괴로워하고 배터리 소모는 심해집니다.
transform, opacity만 사용: CPU 대신 GPU를 일하게 하세요.display: none 적극 활용: 안 보이는 건 렌더 트리에서 빼버리세요.offsetWidth 등)는 최소화하세요.requestAnimationFrame 사용: setTimeout 대신 이걸 써야 프레임 드랍 없이 부드럽게 움직입니다.브라우저가 어떻게 픽셀을 그리는지 이해하는 것, 그것이 "코더"를 넘어 "엔지니어"로 가는 첫걸음입니다.