웹 브라우저의 렌더링 성능은 프론트엔드 개발에서 절대 가볍게 여겨질 수 없는 요소입니다. 특히 DOM 조작이나 스타일 변경이 빈번한 UI 구현에서는 페이지의 응답 속도와 사용자의 체감 속도에 큰 영향을 미칩니다. 이러한 렌더링 과정에서 핵심이 되는 두 가지 개념이 바로 Reflow와 Repaint입니다. 이 둘은 브라우저가 화면을 다시 그리는 방식에 따라 달라지며, 각각의 동작은 성능에 매우 다른 영향을 끼칩니다. 본문에서는 브라우저의 렌더링 원리를 바탕으로 Reflow와 Repaint가 발생하는 조건, 차이점, 그리고 프론트엔드 개발자로서 실무 및 면접에 대비하기 위한 최적화 전략까지 단계적으로 설명합니다.
브라우저 렌더링 단계와 Reflow
Reflow는 브라우저 렌더링 과정에서 레이아웃이 다시 계산되는 것을 의미합니다. 웹 브라우저는 HTML을 파싱하여 DOM(Document Object Model)을 만들고, CSS를 파싱하여 CSSOM(CSS Object Model)을 생성한 후, 이 둘을 결합하여 렌더 트리(Render Tree)를 구성합니다. 이후 렌더 트리를 바탕으로 각 요소의 크기와 위치를 계산하는 레이아웃 단계가 실행되는데, 이때 발생하는 것이 바로 Reflow입니다.
Reflow는 페이지의 구조 또는 위치가 변경될 때 발생합니다. 대표적인 예로는 요소의 크기, 위치, 폰트 크기, 창 크기 변경 등이 있습니다. 예를 들어 아래와 같은 코드에서 버튼의 너비가 변경되면 전체 레이아웃을 다시 계산하게 됩니다:
const btn = document.getElementById("btn");
btn.style.width = "300px"; // Reflow 발생
Reflow는 DOM 트리의 모든 자식 요소에 영향을 줄 수 있습니다. 부모 요소가 변경되면 그 하위 요소들의 레이아웃도 다시 계산되기 때문입니다. 이러한 특성 때문에 Reflow는 비용이 크며, 반복적으로 발생할 경우 페이지 성능이 급격히 저하됩니다.
브라우저는 최적화를 위해 여러 Reflow 요청을 모아 한 번에 처리하려고 하지만, 스크립트에서 layout 정보를 즉시 요청하는 경우 강제로 계산이 발생합니다. 예:
element.style.height = "200px";
console.log(element.offsetHeight); // 강제 Reflow 발생
이처럼 Reflow는 필수적이지만 자주 발생해서는 안 되는 렌더링 비용입니다. 이를 줄이기 위한 개발자의 전략이 매우 중요합니다.
Repaint 발생 조건과 차이점
Repaint는 Reflow보다 가벼운 작업입니다. 요소의 위치나 레이아웃이 아닌 시각적 속성(색상, 배경, 테두리 등)이 변경될 때 발생하며, 요소의 크기나 위치에는 영향을 주지 않습니다. 예를 들어 다음 코드에서는 Repaint만 발생합니다:
const box = document.querySelector(".box");
box.style.backgroundColor = "red"; // Repaint 발생
Repaint는 픽셀을 다시 칠하는 과정만 포함되므로 Reflow보다는 성능 부담이 적습니다. 하지만 무시할 수 있는 수준은 아닙니다. 특히 애니메이션, transition, scroll 이벤트처럼 자주 발생하는 상황에서 반복적인 Repaint가 성능 저하의 주범이 되기도 합니다.
Reflow와 Repaint의 가장 큰 차이점은 다음과 같습니다:
- Reflow: 레이아웃 재계산 → 모든 하위 요소까지 영향 → 비용 큼
- Repaint: 시각적 스타일만 변경 → 레이아웃은 동일 → 비용 적음
예를 들어, border, color, visibility 등의 속성을 변경하면 Repaint는 발생하지만 Reflow는 일어나지 않습니다. 반면 height, padding, position 속성 변경은 Reflow와 그에 따른 Repaint까지 유발합니다.
중요한 점은 두 작업 모두 렌더링 경로를 방해할 수 있다는 것입니다. 따라서 최소화하는 전략이 필요하며, 이를 이해하고 있는 개발자는 사용자 경험에 큰 차이를 만들어낼 수 있습니다. 실무에서는 이러한 차이를 이해하고 작업 순서를 조정하거나, layout-thrashing(레이아웃 연산 남용)을 피하는 등의 전략이 필요합니다.
최적화를 위한 실전 전략
Reflow와 Repaint를 줄이기 위한 전략은 프론트엔드 성능 최적화의 핵심 중 하나입니다. 가장 기본적인 전략은 DOM 조작 횟수 자체를 줄이는 것입니다. 예를 들어 다음과 같이 DOM 요소를 여러 번 직접 수정하는 방식은 피해야 합니다:
el.style.width = "100px";
el.style.height = "200px";
el.style.margin = "10px";
이보다 더 나은 방식은 클래스를 미리 정의해두고 한 번에 교체하는 것입니다:
el.classList.add("active");
이렇게 하면 스타일 변경이 한 번에 일어나고 브라우저가 이를 한 사이클에서 처리할 수 있게 됩니다.
또 다른 전략은 DocumentFragment를 사용하여 오프라인에서 DOM을 구성한 뒤, 최종적으로 한 번에 삽입하는 방식입니다. 이는 Reflow를 최소화하는 매우 효과적인 방법입니다. 또한 requestAnimationFrame을 활용하면 렌더링 타이밍을 브라우저에 위임할 수 있어 애니메이션 성능도 향상됩니다.
React, Vue 같은 현대 프레임워크는 가상 DOM(Virtual DOM)을 통해 Reflow와 Repaint를 최소화하도록 설계되어 있습니다. React에서는 state 변경이 실제 DOM에 반영되기 전, 가상 DOM에서 비교(diff)와 배치(batch) 작업을 거치기 때문에 불필요한 Reflow/Repaint가 줄어듭니다.
면접에서도 자주 묻는 질문이 있습니다:
- "Reflow를 줄이기 위해 어떤 방법을 사용해봤나요?"
- "React에서 어떻게 렌더링 최적화를 할 수 있나요?"
이러한 질문에는 실제 경험이나 예제를 곁들여 대답하면 효과적입니다. 예: styled-components에서 조건부 렌더링으로 DOM 변경을 제한한 사례 등.
마지막 팁으로, CSS 속성 중 GPU 가속이 가능한 속성(transform, opacity 등)을 활용하면 Reflow/Repaint 없이 화면 변경이 가능하므로, 애니메이션 성능 개선에도 큰 도움이 됩니다.
Reflow와 Repaint는 프론트엔드 개발자가 반드시 이해해야 하는 브라우저 렌더링의 핵심 개념입니다. 이 둘의 차이를 알고 나면 단순한 스타일 수정이나 DOM 조작도 훨씬 신중하게 다룰 수 있게 됩니다. 성능 최적화는 단지 속도를 빠르게 만드는 것이 아니라, 사용자에게 부드럽고 안정적인 경험을 제공하기 위한 기본기입니다. 면접에서도 단순한 정의에 머무르지 말고, 발생 조건, 예제, 실전 대응 전략까지 말할 수 있어야 좋은 평가를 받을 수 있습니다. 이 글을 통해 브라우저의 동작 원리를 기반으로 성능을 통제할 수 있는 개발자로 한 단계 성장하시길 바랍니다.