==🎬 애니메이션 최적화: 브라우저 렌더링 과정 분석 및 기법==

1. 🖥️ 브라우저 렌더링 과정 이해

  • 브라우저는 렌더링을 위해 일련의 과정을 거칩니다.

  • 렌더링 과정을 축소하여 5단계로 설명합니다.

    1.1. 🧩 Parsing 단계

    • HTML 파싱 → DOM 트리 생성
    • CSS 파싱 → CSSOM 트리 생성
    • DOM 트리와 CSSOM 트리를 결합하여 렌더 트리 생성
    • DOM 또는 CSSOM 트리 변경 시 Parsing 단계 재수행

    1.2. 📏 Layout 단계

    • 렌더 트리를 기반으로 요소의 크기와 위치 계산
    • 크기/위치 변경 시 Layout 단계 재수행 (리플로우)
    • 리플로우는 렌더링 모든 단계를 재수행하므로 비용이 큼

    1.3. 🎨 Paint 단계

    • 레이아웃 기반으로 시각적 스타일 정보 페인트 레코드로 생성
    • Paint Layer 분리를 통해 리렌더링 영향 최소화
    • 시각적 스타일 변경 시 Paint 단계 재수행 (리페인트)
    • 리페인트는 리플로우보다 저렴하지만 자원 소모 큼

    1.4. 🧱 Layerize 단계

    • 페인트 결과물을 사용하여 Composited Layer List 생성
    • 렌더링 최적화를 위해 그래픽 레이어 사용
      • 컴포지팅 트리거 만족 & 스크롤 가능 컨텐츠 → 그래픽 레이어로 분리
      • GPU 가속을 통해 병렬 처리 및 빠른 렌더링 가능

    1.5. 🎭 Composite 단계

    • 독립적으로 렌더링된 레이어들을 합성하여 최종 웹페이지 출력
    • 필요한 레이어만 렌더링하여 빠른 렌더링 수행

2. 🎯 애니메이션 최적화 목표 및 핵심 키워드

  • ==목표==: 리플로우와 리페인트 최소화, 레이어 분리 전략 수립

    2.1. 🚫 리플로우(Reflow) & 리페인트(Repaint) 최소화

    • 브라우저 렌더링 과정 전체를 재수행하므로 최소화 필요

    2.2. layers (페인트) 분리

    • 레이어 간 독립적 렌더링 가능
    • 특히, 그래픽 레이어는 GPU 가속으로 성능 향상
    • 레이어 분리의 리소스/성능 트레이드오프 고려

3. 🎨 CSS 속성 활용 전략

  • CSS 속성을 Layout, Paint, Composite 속성으로 분류

    3.1. 📐 Layout 속성

    • 요소의 크기/위치 결정 (width, height, margin 등)
    • 변경 시 리플로우 발생

    3.2. 🖌️ Paint 속성

    • 시각적 스타일 변경 (background, border, outline 등)
    • 변경 시 리페인트 발생

    3.3. 🧩 Composite 속성

    • 별도 레이어 생성 및 업데이트 (transform, opacity 등)
    • ==애니메이션 최적화==: Layout/Paint 속성 대신 Composite 속성 활용

4. 🧪 코드 개선 예시

  • ==문제==: ==left 속성 사용 시 리플로우 발생==

  • ==해결==: ==left 대신 transform 속성 사용 (페인트 레이어로 분리)==

    4.1. 💫 opacity 속성 주의

    • opacity 기본값(1)이 아닌 경우 페인트 레이어 생성 규칙에 따라 리플로우 발생 가능
    • 해결: opacity 값을 ==0.99 등으로 설정 (Stacking Context 활용)==

    4.2. 층 쌓임 맥락 활용하기

      - z-index 속성을 통해 페인트 레이어로 분리, 리플로우 방지
      - 부모 요소와 자식 요소에 z-index 사용

5. ⚙️ will-change 속성 활용

  • 브라우저에게 변경될 요소에 대한 힌트 제공 → 사전 최적화 유도

    5.1. ✨ will-change 동작 방식

    • GPU 가속 활용 (2D Transform은 CPU 처리, will-change 사용 시 GPU 처리)
    • 다양한 최적화 수행 (브라우저 내부 동작은 비공개)
    /*
     * will-change
     *  - 브라우저에게 어떤 속성이 변경될 지 미리 알려주는 속성
     *  - 애니메이션 성능을 최적화
     */

    5.2. ⚠️ will-change 사용 시 주의사항

    • 성능 문제 해결을 위한 최후의 수단으로 사용

    • 섣부른 최적화는 지양

    • ==애니메이션 시작 전 미리 설정==, ==애니메이션 종료 후 스타일 제거==:

      .element:hover {
          will-change: transform; /* hover 시 transform 속성 변경을 알림 */
          transition: transform 0.3s ease-in-out; /* transform 애니메이션 */
      }
       
      .element {
          transform: scale(1.0);  /* Transform 속성 초기화 */
      }
       
      .element:not(:hover) {
          will-change: auto; /* hover 끝나면 will-change 제거 */
      }
    • 애니메이션 최적화 동작을 메모리에서 계속 저장 시 성능 저하 가능성

6. 📚 결론

  • 애니메이션 최적화를 위해 브라우저 렌더링 과정 이해 필수
  • 리플로우, 리페인트 최소화 및 레이어 분리 전략 수립 중요
  • CSS 속성 (Layout, Paint, Composite) 적절히 활용
  • will-change 속성은 신중하게 사용

대본

애니메이션 최적화를 주제로 발표하게 된6기 프론트엔드 월하입니다오늘 발표에서는 브라우저의 렌더링 과정을 통해서애니메이션 최적화의 힌트를 찾아보고애니메이션 최적화 기법에 대해서이야기를 나눠보려고 합니다브라우저는 렌더링 될 때다음과 같은 일련의 과정을 통해서 렌더링이 되는데요이제 다음은 브라우저 렌더링 과정을다섯 단계로 축소해 놓은 것입니다이제 브라우저의 렌더링 과정을 한 단계씩 살펴보면서애니메이션 최적화 힌트를 찾아보겠습니다첫 번째 단계는 Parsing 단계입니다Parsing 단계는 HTML 파일을 파싱하여DOM 트리를 생성하고CSS 파일을 파싱하여CSSOM 트리를 생성합니다DOM 트리는 HTML 구조에 대한 정보를 담고 있고CSSOM 트리는 각 요소의 스타일 정보를 담고 있습니다이후 이 두 가지 트리를 결합하여 렌더 트리를 생성합니다따라서 DOM 트리나 CSSOM 트리가 변경되면즉 레이아웃 또는 스타일 요소가 변경되면렌더 트리를 다시 생성해야 되기 때문에Parsing 단계가 다시 수행되게 됩니다두 번째 단계는 Layout 단계입니다랜드 트리가 생성되면각 요소의 크기와 위치를 다시 계산하게 되는데요이 과정에서 크기와 위치가 변경되면Layout 단계를 다시 수행하게 되는데이를 리플로우라고 합니다리플로우가 발생하면 브라우저는 Parsing 단계부터브라우저가 렌더링되는모든 단계를 다시 수행해야 되기 때문에상당히 큰 작업이라고 할 수 있습니다세 번째 단계는 Paint 단계입니다Paint 단계에서는 레이아웃이 완성되면각 요소에 시각적 스타일 정보를화면에 어떻게 그릴지에 대한 페인트 레코드를 생성하는 작업을 수행합니다이제 이때 Layout 단계에서는페인트 작업을 효율적으로 처리하기 위하여Paint Layer라는 것을 분리하는데요Paint Layer는 독립적으로 렌더링 되기 때문에상호 간의 리렌더링에 대한 영향을 최소화할 수 있습니다그리고 요소의 시각적 스타일이 변경되면리렌더링과 마찬가지로Paint 단계가 다시 수행되는데, 이를 리페인트라고 합니다페인트에서는 레이아웃 단계를 건너뛴브라우저의 렌더링 과정을 수행하게 되는데Layout 단계를 수행하지 않는다는 점에서리플로우보다는 더 저렴한 작업이라고 할 수 있겠지만레이아웃 렌더링 과정을 거의 모두 수행한다는 점에서많은 자원을 소모하는 작업이라고 할 수 있습니다4번째 단계는 Layerize 단계입니다Layerize 단계는 페인트 과정에 결과물을 사용해서Composited Layer List라는 데이터를 생성하는 단계입니다이제 간단히 말하면페이지를 몇 개의 레이어로 쪼개는 단계인데요이때 브라우저는 렌더링 과정을 더욱 최적화하기 위하여그래픽 레이어라는 것을 사용합니다그래픽 레이어는 페인트 레이어 중에서 컴포지팅 트리거를 만족하면서스크롤링이 가능한 컨텐츠가 있다면그 페인트 레이어를 그래픽 레이어로 분리합니다그래픽 레이어의 특징은 GPU 가속을 활용하여병렬적으로 처리할 수 있고병렬적으로 렌더링된 레이어를 최종적으로 합성함으로써빠르게 렌더링 할 수 있다는 장점이 있습니다마지막으로 Composite 단계입니다Composite 단계에서는이렇게 독립적으로 렌더링 된 모든 레이어들을 합성하여최종적으로 웹페이지를 출력하는 작업을 수행합니다각 레이어는 서로 독립적으로 렌더링 되기 때문에이렇게 Layerize 단계에서 말했던 것처럼필요한 레이어만 렌더링 하여서빠르게 렌더링을 수행할 수 있습니다지금까지 브라우저의 렌더링 과정을 살펴보면서중요했던 키워드가 몇 가지 있었습니다첫 번째는 리플로우와 리페인트가 있었는데요리플로우와 리페인트는 브라우저의 렌더링 과정을 전부다시 수행하게 한다는 점에서 최소화해야 되는저희의 첫 번째 목표라고 할 수 있습니다두 번째 키워드로는 레이어 분리가 있는데요각 레이어는 서로 독립적으로 렌더링 될 수 있고특히 그래픽 레이어의 경우에는GPU 과속을 통해서 빠르게병렬적으로 처리할 수 있다는 점에서레이어를 분리했을 때의 리소스와성능 최적화의 트레이드 오프를 잘 고려하여서레이어를 적절히 분리하는 것이 저희의 두 번째 목표가 되겠습니다이를 위해서 저희는 CSS 속성을 잘 활용해야 되는데요CSS 속성은 크게 세 가지로 분류할 수 있습니다Layout 속성, Paint 속성, Composite 속성인데요첫 번째로 Layout 속성은요소의 크기나 위치 등을 결정하는 속성으로width, height, margin 같은 속성이 여기에 해당합니다Layout 속성이 변경되면 리플로우 과정이 다시 수행되게 됩니다두 번째로 Paint 속성은브라우저 시각적 스타일을 변경하는 속성인데요대표적으로 background, border, outline 등이 여기에 해당됩니다이제 Paint 속성이 변경되면브라우저는 리페인트 작업을 다시 수행하게 됩니다마지막으로 Composite 속성은 별도의 레이어를 생성하고업데이트하는 속성인데요대표적으로 transform, opacity와 같은 속성들이 있습니다저희가 애니메이션을 최적화하기 위해서는리플로우와 리페인트를 유발하는 레이아웃 속성과 리페인트 속성 대신Composite 속성을 잘 활용해야 됩니다다음은 간단한 애니메이션 소스 코드에 대해서CPU 쓰로틀링을 건 다음에 성능을 측정한 결과인데요보시면 리플로우 과정이 반복해서일어나는 것을 확인할 수 있고또 Frame Drop 또한 계속해서 발생하고 있습니다이 소스코드에서 어떤 점을 개선할 수 있을까요정답은 바로 Layout 속성인 left 대신transform 속성을 사용하는 것입니다transform 속성을 사용하면텍스트 클래스를 가진 요소가 별도의 페인트 레이어로 분리되어서독립적으로 렌더링 되기 때문에리플로우를 피할 수 있습니다그런데 이대로 성능 측정을 다시 해보면아직도 여전히 리플로우가 발생하고 있을 텐데요그 원인은 바로 opacity 속성 때문입니다opacity 속성은 분명 컴포지트 속성인데리플로우를 유발하고 있는 것이 이상해 보이는데요여기에 대한 힌트는 Paint Layer 생성 규칙에 있습니다Paint Layer의 생성 규칙 중에Stacking Context를 생성하는 속성의 값이기본 값이 아닌 경우Paint Layer를 생성하도록 하고 있는데요컴포지트 속성인 opacity 속성의 기본 값은 1이기 때문에opacity 속성의 값을 1이 아닌0.99와 같이 설정해 주어야 합니다이후 다시 성능을 측정해보면리플로우가 없어진 것을 확인할 수 있고Frame Drop 또한 해결된 것을 확인할 수 있습니다0.99란 값이개발자 사이에서는 좀 불편한 값이라고 생각되어서차라리 리플로우를 한 번도 수행하고 말지라고생각하시는 분들이 있을 수도 있겠는데요리플로우는 애니메이션이 시작되는 순간딱 한 번만 실행되는 것이 아니라이 모든 프레임마다계속해서 리플로우가 반복적으로 실행되기 때문에반드시 리플로우를 최소화하기 위한 노력을 해야 된다고 말씀드리고 싶습니다또한 이 방법 말고도개발자 분들이 이제 잘 사용하지 않는 z-index를 사용한 방법도 있는데요z-index 또한Stacking Context를 생성하는 속성이기 때문에부모 요소와 자식 요소에 z-index를 사용함으로써자식 요소를 별도의 페인트 레이어로 분리하여리플로우를 없애는 효과를 볼 수 있습니다이렇게 컴포지트 속성을 사용하여애니메이션을 최적화해 보았는데요페인트 레이어를 분리하는 것도분명히 CPU 입장에서는리소스를 소모하는 작업일 겁니다따라서 아무리 최적화를 열심히 하더라도동시에 트리거된 애니메이션이 너무나 많아지면이제 메인 스레드에는 부하가 걸리게 되고애니메이션은 딜레이가 생길 수밖에 없습니다일을 해결하기 위한 도구로 will-change 속성이 있습니다will-change 속성은 브라우저에게이 애니메이션이 변경될 것이라는 사실을 미리 알려주어서요소의 변화에 미리 대응하고또 요소의 속성에 따른 최적화를 알아서 수행해주는 속성입니다이 과정에서 각 속성에 따른 최적화 작업을 수행하는데대표적으로 2D Transform 작업을 GPU 가속을 활용하여GPU를 처리하는 작업을 많이 사용합니다이 transform은 아까 컴포지트 속성이라고 소개드렸는데사실은 이제 transform 중에서도3D 작업만 GPU에서 처리하고2D transform 작업은 CPU에서 처리하도록브라우저가 동작하고 있는데요will-change 속성을 사용하여 transform 작업을 최적화하면transform 작업이 그래픽 단에서 동작하게 되어서더 빠른 스크롤링과 애니메이션을 가능하게 해줍니다이외에도 아래 보시는 것처럼will-change 속성은 다양한 최적화를 수행하는데요사실 이거는 이제 최적화의 극히 일부이고사실 will-change가 어떠한 속성을 최적화 했을 때정확히 브라우저 내부적으로 어떠한 동작을 하는지는 아무도 모릅니다이게 어떠한 말이냐 하면will-change 속성은will-change 속성의 값으로최적화하고 싶은 속성을 입력하면그 속성에 대한 최적화를브라우저에게 알아서 잘 최적화하라는 그런 속성입니다따라서 이제 공식 문서에서도 이렇게 경고가 있는데요성능 문제를 해결하기 위한 최후의 수단으로 사용하되성능 문제를 예상하여서섣부른 최적화를 수행하지 말라고 경고하고 있습니다이제 경고문과 아래의 사용 방법과 주의사항을오른쪽 소스코드와 함께 살펴보겠습니다will-change 속성의 사용 시 주의 사항은 딱 두 가지가 있는데요애니메이션이 발생하기 전에 미리그리고 애니메이션이 끝날 때까지입니다첫 번째로 애니메이션이 끝날 때까지라는 말은애니메이션이 끝난 다음에동적으로 CSS 스타일을 제거해 주어야 한다는 말인데요애플리케이션이 실행되는 동안애니메이션의 최적화 동작을 메모리에서 계속 저장하고 있으면메모리에 저장하고 있을 때는오히려 성능이 저하되는 현상이 발생할 수 있기 때문입니다두 번째로 애니메이션이 시작하기 전에미리라는 말은 애니메이션이 발생하는 직후에will-change 속성을 통해서브라우저에게 이 속성에 대한 최적화를 준비하라고 해도브라우저가 충분한 시간을 확보하지 못하기 때문입니다따라서 여기 보시는 것처럼이벤트 버튼이 트리거 되기 전에마우스 커서가 올라가는 hover 이벤트에서will-change를 설정하여서브라우저가 애니메이션을 최적화할 시간을 미리 확보해 주어야 한다는 말입니다이렇게 사용하면will-change 속성을 안전하게 사용할 수 있습니다여기까지 애니메이션 최적화에 대한 이야기를 나눠 보았는데요이외에도 되게 다양하고 깊은 최적화 기법들과브라우저의 동작 원리들이 있는데ppt에 다 담을 수가 없어 가지고여기까지 발표를 준비했는데이후에 더 관심 있으신 분은찾아보시면 되게 재미있을 것 같습니다지금까지 발표 들어 주셔서 감사합니다