Reflow, Repaint을 알아보자!

young_pallete·2021년 6월 23일
54

Web Browser

목록 보기
1/3
post-thumbnail

1. 시작하며 👏

오랜만의 글인 것 같습니다.
최근에 제 자신에 대한 회의감도 들었고, 그래서 잠시 슬럼프를 겪었지만, 이제 어느정도 극복하고, 다시 꾸준함을 찾으려합니다.

결국 꾸준함이야 말로 제가 성장하고 있는 증거이자, 증명할 수 있는 무기니까요. 😋
우리 모두, 꾸준함을 잃지 않아봅시다!

그렇게 슬럼프를 딛고 첫 글, RepaintReflow를 톺아봅시다.

2. Repaint, Reflow란?

말 그대로 해당 요소를 다시 칠하고, 다시 플로우를 정하는 거겠죠!
처음 배울 때에는 무턱대고 쉽게 했지만, 점점 배울 수록 최적화 고민으로 인해 머리 빠지는 원인 중 하나입니다. 같이 배워나가자구요! 😂😂

물론 이것을 원해서 들어온 건 아닐 거겠지만, 그래도 확실히 이해하기 위해서는 브라우저의 렌더링 과정을 이해해야 하는 것이 선행과정입니다.

2-1. 브라우저의 작동방식

우리의 브라우저는 말이죠, 다 다르게 동작하겠지만 대다수의 브라우저는 다음과 같은 동작 방식을 거치게 된답니다.
(사진은 다음 출처에서 갖고 왔습니다 😊)

간단히 살펴보자면, HTML을 파싱해서 DOM 트리를 만들고,
CSS를 파싱해서 스타일 구조체를 만들어내는데, 이를 CSSOM 트리라고 말합니다.

정리 2-1-1.

1. HTML 파싱 -> DOM Tree
2. 그러다가 link를 만나면 CSS 파싱 -> CSSOM Tree
(사실상 DOM을 만들다 보면 CSS도 파싱되는 과정을 함께 거치기 때문에, 둘다 동시에 만들어진다~라고 표기하는 듯합니다.)

이후는 말이죠, 위 둘을 토대로 attachment라는 과정을 거치며 Render Tree라는 것을 생성해냅니다. 쉽게 말하자면... 각 노드에는 attach라는 메소드가 있으며, 해당 노드가 추가되면 메소드를 실행시킵니다. 이 attach는 노드의 스타일을 객체 형태로 리턴시키면서, 둘이 합쳐지는 과정인 겁니다.

특이한 건, DOM과 Render Tree가 항상 일치하지는 않습니다.
Render Tree는 문서의 시각적 측면에서 올바른 순서대로 내용을 그리도록 하기 위한 목적을 갖고 있어요.

예컨대, 렌더 트리는 display: none의 경우, 화면상에 나타나지 않으므로 불필요하다고 판단, 트리에서 제외시켜버립니다.
좀 더 나아가자면, head와 같은 것들도 마찬가지로 제외시켜버리구요.

정리 2-1-2.
3. attachment -> Render Tree

그 다음에 이제 오늘의 주제인 reflow라는 게 나옵니다. 사실, 레이아웃이라는 단계로 더 많이 불리기도 하죠. 레이아웃에서는 렌더 트리의 목적에 맞게, 각 요소의 구체적인 위치와 크기를 연산해냅니다.

또한, 최신 브라우저의 경우에는 이 과정 이후, update Layer Tree를 생성해낸다고 합니다.

결과적으로 이를 브라우저에 픽셀을 렌더링하는 페인팅 과정을 거치게 됩니다. 이를 위해 여기서는 각 노드를 거치면서 paint() 메소드를 호출하죠. (여기가 Repaint와 사실상 대응하겠죠?!)

이후에는 최신 브라우저의 경우 합성(Composite) 단계가 조건적으로 발생합니다. 이 단계는 생성된 Layer 들을 합성하여 단 한장의 비트맵으로 만들어버립니다!

각 Layer 별로 paint 되기 때문에 불필요한 painting 을 줄여 효율적으로 그릴 수 있습니다.

정리 2-1-3.
4. Render Tree -> Layout 산출
(최신 브라우저의 경우 Layer 단계 발생)
5. Render Tree -> Painting 처리
(최신 브라우저의 경우, 만약 Renderer Layer가 2개 이상이면 Composite 단계 발생)


2.2. Reflow, Repaint 발생!

사실상 우리의 브라우저에서 계속해서 같은 스타일로 있지는 않겠죠?
개발자의 의도에 따라 어떤 노드에 무엇을 추가하고, 어떤 요소에는 스타일이 달라지기도 합니다.

또한, 브라우저 크기를 조절하는 경우에도 스타일은 다시 계산되기도 하죠.(viewport 변경)

이럴 때 발생하는 게 ReflowRepaint입니다.
만약 스타일이나 DOM 내부를 변경하는 DOM API가 사용됐다면, 우리의 DOM

  1. 뭔가 변경됐음을 감지하고,
  2. 다시 위의 브라우저 작동 과정을 반복하고,
  3. 리렌더링을 진행하죠.

이러한 과정에서 리플로우와 리페인트가 발생하는 겁니다!

먼저, 레이아웃의 경우 다음과 같이 PaintComposite과정 모두를 하게 됩니다.

JS, CSS 파싱 → 렌더 트리 구축 → 레이아웃 → 리페인트 → 레이어 업데이트 → 합성

만약 리페인트만 한다면, 다음과 같습니다.

JS, CSS 파싱 → 렌더 트리 구축 → 리페인트 → 레이어 업데이트 → 합성

만약 둘 다 필요 없는 스타일의 변화라면, 다음과 같겠습니다. 성능을 따진다면 가장 이상적이죠!

JS, CSS 파싱 → 렌더 트리 구축 → 레이어 업데이트 → 합성

따라서, 우리는 레이아웃과 리페인트가 일어나도록 유발하는 스타일 속성이 무엇인지를 인지해야 합니다. 그냥 속성과 메서드를 사용했다는 이유만으로도, 리플로우가 발생하는 것들이 있습니다. 이는 다음 글의 표를 갖고 왔어요.


2-3. Reflow, Repaint 최소화

물론, 리플로우와 리페인트는 완전히 피할 수 없습니다. 그러나 최적화할 수 있다면 최대한 줄이는 게 현명한 선택이겠죠! 따라서 우리는 최적화에 대한 고민을 해야 합니다.

Repaint의 경우, VisibilityDOM API을 통해 조절했을 때 자식 노드들까지 다 검색하기 때문에 성능 저하를 발생시킬 수도 있다고 합니다.

특히 Reflow는 더 심각한 성능 저하를 만들기도 해요. 리플로우는 해당 요소의 자식요소와 부모/조상 요소역시 레이아웃 계산을 진행해버리니까요.

따라서 둘을 유발하는 스타일 속성 조정은, 만약 성능이 중요시되는 애플리케이션이라면 고려해볼만한 최적화 요소입니다.

대표적인 리플로우의 원인은 다음과 같습니다.

  • 윈도우 리사이징 (뷰포트 변화는 Global Layout에 영향)
  • 폰트의 변화 (height계산에 영향을 주므로 Global Layout에 영향)
  • 스타일 추가 또는 제거(레이아웃을 바꾸므로)
  • 내용 변화 (인풋박스에 텍스트 입력 등..)
  • :hover와 같은 CSS Pseudo Class
    (CSS: The Definitive Guide: The Definitive Guide 55p에서, hover할 시 나타나는 변화로 인한 우려가 생긴다는 의미인 듯 합니다.)
  • 클래스 Attribute의 동적 변화
  • JS를 통한 DOM 동적 변화
  • 엘리먼트에 대한 offsetWidth / offsetHeight (화면에서 보여지는 좌표) 계산시
  • 스타일 Attribute 동적변화

너무나 많죠. 특히 인터렉티브한 요소를 위한 제어라면, 이를 다 피할 수가 없습니다.
그렇지만 이를 인지하고 개발하는 것과, 모르고 개발한 결과물은 엄청난 차이가 존재할 것입니다.

따라서 우리는, 이를 어떻게 애플리케이션에서 활용할지를 고민해야 하는데요.
저는 이 글을 참고하며 적고 있습니다.

  1. 최대한 DOM 구조 상 말단 노드에만 클래스를 사용
    리플로우의 영향을 최소화하여 수행 비용을 줄인다네요.
  2. 인라인 스타일 자제
    인라인 스타일이 주어지면 리플로우가 수차례 발생하게 됩니다. 클래스를 사용합시다.
  3. 애니메이션은 positonabsolutefixed로 하자.
    주변 레이아웃 영향 X
  4. 퀄리티와 퍼포먼스를 타협
    애니메이션 계산, 페이지 Reflow에 대한 CPU 퍼포먼스 비용을 고려하자
  5. 테이블로 구성된 레이아웃 자제
    작은 변화도 테이블 전체 노드가 리플로우 발생
  6. CSS에서의 JS 표현식 자제
    문서중 일부가 Reflow될 때마다 표현식이 다시 계산되기 때문
  7. JS를 통한 스타일 변화는 최대한 그루핑하자
  8. CSS 하위 선택자는 필요한 만큼만 쓰자.
    CSS Recalculation할 때, CSS Rule에 따라 오른쪽 -> 좌쪽으로 매치시킬 Rule이 없거나 잘못된 Rule이 튀어나올 때까지 계속 매칭하기 때문
  9. 일부 속성과 메서드는 자주 사용할 때 캐싱하자.
    사용한다는 이유만으로도 리플로우가 발생하는 속성과 메서드가 있기 때문
  10. position: relative; 주의!
    -일반적인 경우: Box model → Normal flow
    -position:absolute or fixed: Box model → Out of flow(Positioning)
    -position:relative: Box model → Normal flow → Positioning

p.s.
만약에 내가 정말 스타일 하나하나에 어떻게 리플로우, 리페인트가 될 지가 중요하고 알고싶다면! https://csstriggers.com/ 를 가보시면, 잘 나와있다고 합니다. 참고하세요!


참고자료

브라우저의 동작 과정 1
브라우저의 동작 과정 2
Layer Model
리페인트, 리플로우 1
Reflow의 원인과 마크업 최적화
Reflow or Repaint(or ReDraw)과정 설명 및 최적화 방법

profile
People are scared of falling to the bottom but born from there. What they've lost is nth. 😉

1개의 댓글

comment-user-thumbnail
2021년 9월 14일

좋은글 감사합니다~!

답글 달기