빠른 참조

Layout Thrashing 및 강제 리플로우(Forced Reflow) 치트 시트

레이아웃 스래싱(Layout Thrashing)과 강제 리플로우 종합 참고 가이드: 렌더링 성능을 저하시키는 DOM 요소 속성, 마우스 이벤트, window 메서드 및 jQuery 트리거 목록.

· 게시일

레이아웃 스래싱(Layout Thrashing, 또는 강제 동기 레이아웃 / 강제 리플로우)은 JavaScript가 DOM 쓰기와 레이아웃 속성 읽기를 반복적으로 번갈아 수행해 브라우저가 페이지 레이아웃을 동기적으로 다시 계산하도록 강제할 때 발생합니다.

레이아웃 무효화(Layout Invalidation) 트리거

이전 DOM 변경으로 레이아웃이 무효화된 상태에서 다음 DOM 속성 및 메서드를 호출하면 계산된 기하 정보의 동기 재계산이 강제됩니다.

요소 속성 및 메서드

박스 기하 정보 및 치수

  • clientHeight, clientWidth
  • clientLeft, clientTop
  • offsetHeight, offsetWidth
  • offsetLeft, offsetTop
  • offsetParent
  • scrollHeight, scrollWidth
  • scrollLeft, scrollTop

경계 상자(Bounding Box) 조회

  • getBoundingClientRect()
  • getClientRects()

스크롤 동작

  • scrollIntoView()
  • scrollIntoViewIfNeeded()
  • scrollByLines()
  • scrollByPages()

텍스트 및 포커스

  • innerText
  • outerText
  • focus() (포커스 링 및 스크롤 조정을 강제 실행)

마우스 이벤트 속성

MouseEvent 인스턴스의 특정 좌표를 조회하면 일부 브라우저 엔진에서 레이아웃 계산을 유발할 수 있습니다:

  • layerX, layerY
  • offsetX, offsetY

Window 메서드 및 속성

  • window.getComputedStyle() (width, height, margin 또는 transform 같은 레이아웃 종속 값을 조회할 때)
  • window.scrollBy()
  • window.scrollTo()
  • window.scrollX, window.scrollY

Frame, Document 및 이미지

  • 레이아웃이 안정화되기 전에 이미지의 width 또는 height 조회
  • document.body.scrollHeight 접근

jQuery 작업

여러 jQuery 헬퍼 메서드는 내부적으로 계산된 레이아웃 값을 읽어옵니다:

// 리플로우(Reflow) 유발
$(elem).offset();
$(elem).offsetParent();
$(elem).position();
$(elem).scrollLeft();
$(elem).scrollTop();
$(elem).css('width');  // 모든 레이아웃 의존 스타일 계산
$(elem).text();

// 기하학적 가시성을 검사하는 가상 선택자
$(':hidden');
$(':visible');
$(':contains');

Layout Thrashing 방지를 위한 모범 사례

1. 읽기 작업과 쓰기 작업 일괄 처리(Batch)

항상 모든 DOM 측정을 먼저 수행한 다음, 모든 DOM 수정을 한꺼번에 처리하세요.

// 나쁜 예: 읽기와 쓰기가 번갈아 일어나 매 반복마다 강제 레이아웃 발생
elements.forEach((el) => {
  const width = el.clientWidth; // 읽기 (레이아웃 강제)
  el.style.width = width * 2 + 'px'; // 쓰기 (레이아웃 무효화)
});

// 좋은 예: 읽기를 일괄 수행한 뒤 쓰기를 일괄 수행
const widths = elements.map((el) => el.clientWidth); // 일괄 읽기
elements.forEach((el, index) => {
  el.style.width = widths[index] * 2 + 'px'; // 일괄 쓰기
});

2. requestAnimationFrame 사용

스타일 변경을 다음 애니메이션 프레임으로 예약하여 브라우저가 렌더링 주기를 최적화하도록 하세요:

const height = element.clientHeight; // 현재 단계에서 읽기

window.requestAnimationFrame(() => {
  element.style.height = height + 50 + 'px'; // 다음 프레임에서 쓰기
});

3. CSS Transform 및 Opacity 사용

레이아웃 속성(top, left, width, height) 대신 컴포지터 스레드에서 처리되는 CSS 속성(transform, opacity)을 애니메이션하세요.


참고 자료

관련 주제