
웹페이지에서 스타일을 바꿨을 때, 어떤 건 부드럽게 바뀌고 어떤 건 화면이 튕기듯 바뀌던 경험이 있다면 그 배경에는 브라우저의 렌더링 과정 중 하나인 Repaint와 Reflow가 있다.
이번 글에서는 이 두 개념이 무엇인지, 어떻게 다르고 어떤 성능 이슈를 유발할 수 있는지에 대해 알아보자.
브라우저는 HTML과 CSS를 받아 다음과 같은 과정을 거쳐 화면에 표시한다
1. HTML 파싱 → DOM 생성
2. CSS 파싱 → CSSOM 생성
3. DOM + CSSOM → Render Tree 구성
4. Layout (= Reflow) - 위치/크기 계산
5. Paint (= Repaint) - 픽셀 채우기
6. Composite - 레이어 합성
여기서 중요한 것은 Layout과 Paint 단계다. 이 둘이 바로 Reflow와 Repaint에 해당하기 때문이다.
Reflow는 레이아웃 계산이라고도 불리는데, 요소의 위치와 크기를 계산하는 작업이다.
width, height, padding, border, margin 변경 등)Repaint는 요소의 스타일(시각적 표현)이 변경되어서 다시 그리는 작업이다.
위치는 바뀌지 않지만 색상, 배경, 그림자 등 겉모습이 바뀌었을 때 일어난다.
color, background-color, box-shadow 변경visibility 토글| 항목 | Reflow | Repaint |
|---|---|---|
| 정의 | 레이아웃 재계산 | 시각적 스타일 재적용 |
| 트리 영향 | 부모 → 자식까지 영향 | 주로 해당 요소를 중심으로 영향 |
| 예시 속성 | width, height, display, position 등 | color, background, border-color 등 |
| 성능 비용 | 크다 | 비교적 작다 |
Reflow는 자주 발생할수록 앱의 성능에 큰 영향을 준다. 특히 다음과 같은 코드는 주의해야 한다.
// ❌ 비효율적인 방법 (매번 Reflow 발생)
for (let i = 0; i < 100; i++){
const el = document.getElementById('box');
el.style.width = `${i}px`;
}
이렇게 DOM에 반복 접근하며 레이아웃 속성을 계속 바꾸면, 매번 Reflow가 발생하게 된다.
이런 경우는 다음과 같이 최적화하는 것이 좋다.
const el = document.getElementById('box');
// ✅ 개선 방법 1: 한 번에 모든 스타일 적용
el.style.cssText = 'width: 100px; height: 100px;';
// ✅ 개선 방법 2: 클래스 활용
el.className = 'optimized-box';
// ✅ 개선 방법 3: DocumentFragment 활용 (DOM 조작 시)
const fragment = document.createDocumentFragment();
// fragment에 요소들 추가 후 한 번에 DOM에 추가
// ✅ 개선 방법 4: CSS Transform 활용 (Composite만 발생)
el.style.transform = 'translateX(100px)
필자는 퍼블리셔로 재직할 당시 주로 클래스를 활용하는 방법을 사용했다. 또한 사용자 인터랙션에 따른 요소 이동 시에는 top이나 left 대신 transform을 활용해 성능을 최적화했는데, transform은 Composite-Only 속성이어서 더 부드러운 애니메이션을 구현할 수 있기 때문이다.
transform 이외에도 Composite-Only 속성에는 opacity와 filter가 있다.
이런 속성들은 GPU 가속을 받을 수 있어 성능 최적화에 도움이 되니 참고하면 좋겠다! 🤓