[250221] TIL

오늘 한 일

리플로우와 리페인트를 줄일 수 있는 방법 정리

리플로우란 ?

  • 웹 브라우저가 웹 페이지의 레이아웃을 계산하고, 요소의 위치와 크기를 다시 배치하는 과정

리플로우, 언제 발생하나요?

  • DOM 요소 추가/제거
  • 요소의 크기 변경 (너비, 높이, 패딩, 마진 등 변경될 때)
  • 윈도우 크기 변경
  • 폰트 변경
  • CSS 속성 변경

리페인트란?

  • 웹 브라우저가 시각적 요소를 다시 그리는 과정

리페인트, 언제 발생하나요?

  • 색상 변경 (배경색, 테두리 등)
  • 가시성 변경 (visibility, z-index)
  • 배경 이미지 변경
  • 텍스트 스타일 변경 (색상이나 그림자)

리플로우, 리페인트는 웹 페이지의 성능에 큰 영향을 미칠 수 있기 때문에, 최소화 하는 것이 중요 !

(특히 리플로우가 리페인트보다 영향을 많이줌)


리플로우 최소화 방법

1. DOM 조작 최소화

Batch DOM Changes

  • 여러 DOM 변경을 한 번에 수행
  • 변경 사항을 모아서 한 번에 적용하는 방법

Document Fragment 사용

  • 여러 요소를 추가할 때 Document Fragment 를 사용하여 DOM에 한 번에 추가
  • JS 에서 DOM 최적화하기 위해 사용되는 가벼운 DOM 객체
  • 메모리에만 존재하고 실제 DOM 트리에는 포함되지 않는 특수한 노드
  • 최종적으로 DOM에 추가할 때 한 번의 조작으로 처리할 수 있도록 도와줌
     const fragment = document.createDocumentFragment();
     for (let i = 0; i < 100; i++) {
         const newElement = document.createElement('div');
         fragment.appendChild(newElement);
     }
     document.body.appendChild(fragment);

2. 스타일 변경 최소화

  • CSS 클래스 사용
  • 개별 스타일 속성을 하나하나 변경하기보다는 CSS 클래스를 추가하거나 제거하기
// 나쁜 예시
document.getElementById('toggleButton').addEventListener('click', function() {
    const element = document.getElementById('myElement');
    if (element.style.width === '200px') {
        element.style.width = '100px';
    } else {
        element.style.width= '200px';
    }
});
// 좋은 예시 
.highlight {
    width : 200px
}

document.getElementById('toggleButton').addEventListener('click', function() {
    const element = document.getElementById('myElement');
    element.classList.toggle('highlight');
});

3. 오프라인에서 작업

  • DOM 을 변경하기 전에, 요소를 문서에서 제거하고 변경 후 다시 추가
  • 큰 DOM 트리에서 유용
document.getElementById('updateButton').addEventListener('click', function() {
    const container = document.getElementById('container');
    const element = document.getElementById('myElement');

    // 요소를 문서에서 제거
    container.removeChild(element);

    // 요소 변경 (메모리에서만 이루어짐)
    element.textContent = 'Updated Text';
    element.style.color = 'red';
    element.style.fontSize = '20px';

    // 변경된 요소를 다시 추가
    container.appendChild(element);
});

4. 레이아웃 읽기와 쓰기 분리

// 나쁜 예시
// offsetHeight를 읽은 후 높이를 변경하면
// offsetHeight를 읽기전에 브라우저는 레이아웃을 다시 계산해야 한다고 함
element.style.height = (element.offsetHeight + 10) + 'px';
     
 // 좋은 예시
 const height = element.offsetHeight; // 읽기
 element.style.height = (height + 10) + 'px'; // 쓰기

5. CSS 속성 사용

  • transformopacity 속성을 사용하여 애니메이션 수행
  • 두 속성은 레이아웃이랑 페인팅 단계를 건너뛰고 합성 단계에서 처리되기 때문에 리플레이 리플로우를 유발하지 않음
  • 참고
    • DOM 생성 → 스타일 계산 → 레이아웃 → 페인팅 → 합성 (최종 그려질 요소를 합성)

리페인트 최소화 방법

1. 스타일 변경 최소화

  • 여러 스타일 변경을 한 번에 수행
  • 위에서 말한 것처럼 style 속성 여러 번 변경하는 대신, CSS 클래스를 사용하여 스타일 변경

2. 가시성 변경 최적화

  • visibility 대신 display: none 을 사용하여 리페인트를 피할 수 있음
  • display none은 DOM에서 아예 제거하기 때문에 리페인트가 발생하지 않음
  • 리플로우는 발생함

3. 애니메이션 최적화

  • transformopacity 속성을 사용하여 애니메이션 수행
  • 위와 마찬가지로 합성 단계에서 처리되기 때문에 리플레이, 리플로우 유발 X

4. 복잡한 CSS 선택자 피하기

  • 복잡한 CSS 선택자는 브라우저가 스타일을 적용하는데 더 많은 시간을 소모하게 만듬
  • 가능한 간단한 선택자 사용

5. CSS 속성 사용

  • will-change 속성 사용하여 브라우저에 어떤 속성이 변경될 것인지 미리 알려주어 최적화를 도움
  • 단, 남용하지 말아야함 오히려 성능에 부정적인 영향을 미칠 수 있음
.animated-box {
    width: 100px;
    height: 100px;
    background-color: lightblue;
    transition: transform 0.3s ease, opacity 0.3s ease;
    will-change: background-color;
}

Categories:

Updated:

Leave a comment