브라우저 렌더링 reflow와 css
프론트엔드 개발에서 HTML 태그와 CSS 속성의 역할을 명확히 이해하는 것은 필수적이다. 브라우저의 렌더링 과정에 영향을 미치기 때문이다. HTML, CSS는 기본적인 지식처럼 보이지만, 이를 간과하면 예기치 못한 버그가 발생할 수 있다. 이번에 특정 버그를 해결하면서 이 점을 다시금 실감하게 되었다.
버그
회사 프로젝트에서 React에서 input의 value가 변경되면 그 값을 span 태그에 반영하는 UI가 있었다. 그리고 이 span 태그의 크기를 감지하기 위해 @react-hook/size 라이브러리의 useSize 훅을 사용하고 있었다.
그 컴포넌트를 간단하게 요약한 코드
import { useState, useRef } from "react";
import useSize from "@react-hook/size";
const DynamicSizeComponent = () => {
const [value, setValue] = useState("");
const spanRef = useRef(null);
const [width, height] = useSize(spanRef);
return (
<div>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<span ref={spanRef}>{value}</span>
<p>Width: {width}px, Height: {height}px</p>
</div>
);
};
export default DynamicSizeComponent;
TypeScript
복사
그러나 input 값이 변경될 때 span 태그의 내용은 업데이트되지만, useSize에서 감지하는 크기는 변경되지 않는 문제가 발생하고 있었다.
원인
브라우저 렌더링 과정과 Reflow
브라우저가 HTML을 렌더링할 때, 요소의 크기와 위치를 계산하는 과정이 필요하다. 이 과정은 크게 Reflow와 Repaint로 나뉜다.
•
Reflow: 레이아웃이 변경될 때 발생하며, 요소의 크기와 위치를 재계산한다.
•
Repaint: 스타일이 변경될 때 발생하며, 화면에 다시 그린다. (예: color 변경)
이때, display: inline 요소는 고유한 크기(width, height)를 가지지 않으며, 콘텐츠에 따라 유동적으로 크기가 조정된다. 그러나 브라우저는 inline 요소의 크기를 레이아웃 계산 과정에서 별도로 업데이트하지 않을 수도 있다.
즉, span의 내용이 바뀌어도 useSize가 감지하는 크기가 업데이트되지 않는 이유는 브라우저가 Reflow를 트리거하지 않기 때문이다.
해결
Reflow를 유발하는 CSS 속성 적용
보통 성능 최적화를 위해 Reflow를 최소화하는 것이 좋지만, 특정 요소의 크기 변경을 감지해야 할 때는 Reflow가 필요할 수도 있다. 대표적인 방법은 display 속성을 inline-block 또는 block으로 변경하는 것이다.
<span ref={spanRef} style={{ display: "inline-block" }}>{value}</span>
TypeScript
복사
위와 같이 display: inline-block을 적용하면 span 요소가 고유한 width와 height 값을 가지게 되어, 콘텐츠가 변경될 때 브라우저가 Reflow를 실행한다. 그 결과 useSize가 크기 변화를 감지할 수 있게 된다.
결론
HTML과 CSS의 기본 속성이 브라우저의 렌더링 과정에 미치는 영향을 이해하는 것은 중요하다. 단순한 스타일 변경처럼 보일 수 있지만, inline 요소의 특성으로 인해 크기 감지가 되지 않는 문제를 경험하면서, 렌더링 메커니즘을 깊이 이해하는 것이 실무에서도 필수적이라는 점을 다시금 깨닫게 되었다.
이러한 문제를 해결하기 위해서는 Reflow를 유도할 수 있는 CSS 속성을 적절히 적용하거나 성능을 위해 사용하지 않는 것이 중요하다. 이번 경험을 통해, 사소해 보이는 HTML과 CSS의 속성 하나가 예상치 못한 문제를 일으킬 수도 있으며, 그 해결책은 아주 간단할 수 있다는 점을 다시 한번 확인했다.