CSS will-change 프로퍼티에 관해 알아둬야 할 것을 읽고

  • 지난번에도 정리했지만 본래 브라우저 렌더링에는 CPU를 주로 사용한다. 하지만, FPS 등을 얘기하면서 나온 말이지만 이러한 브라우저 렌더링이 블로킹 되는 경우가 있다(너무 많은 리렌더링이 짧은 주기로 반복되는 등의 현상이 일어날 때). 이런 경우에는 CPU의 부하를 줄이기 위해 GPU를 사용하면 분업이 잘되는데 이를 하드웨어 가속이라고 한다.
    ** 하드웨어 가속의 원리 : 하드웨어 가속을 쓸 부분(엘리먼트)을 다른 레이어로 분리하고, 이 레이어는 GPU 가속을 통해 처리한 뒤에 나중에 합쳐서 렌더링하는 방식.
  • 이러한 하드웨어 가속은 CSS Animation, transform, transition 속성에 자동으로 쓰여지지 않는다. 즉, 아무렇게나 css property를 써준다고 GPU 가속이 활성화 되는 것이 아니다. 그래서 이전에 썼던 방식은
transform: translate3d(0, 0, 0);

이런식으로 브라우저를 속이는 방식이었다. 즉, 3d로 변형할 부분이 아닌데, 다시 말해 2차원 공간에서 애니메이션되는 요소에 대해 3D 변형을 위와 같은식으로 강제하여 렌더링을 가속(GPU 가속)을 활성화하는 방법이었다.

하지만 CSS 핵을 이용해 생성한 레이어는 성능 병목을 해소하는데 항상 도움이 되지는 않는다. 합성 레이어를 생성하는 것은 페이지 출력 속도를 빠르게 할지는 모르겠지만 그만한 비용이든다. RAM이나 GPU의 메모리 사용량이 커지며 레이어를 많이 생성하면 할수록 그만큼 악영향을 끼치고, 특히 모바일 기기에서 이 점은 더 두드러지게 나타난다.

  • 그렇다면 더 나은 방법은 없을까? : will-change
    : 먼저, will-change란 엘리먼트에 어떠한 변경을 할 것인지를 미리 브라우저에 알려주는 것이며 이것이 will-change 속성의 역할이다. 그러면 미리 브라우저에 굳이 왜 알려야할까? 아까 말한 GPU 가속에서 새로운 레이어로 엘리먼트를 분리하는 것은 비교적 비용이 필요한 작업이다. 이에 따라 새로운 레이어로 분리하는 데에 있어서 지연 현상이 발생할 수 있다. 이에 따라 화면에서 깜빡이는 현상도 생길 수 있는 것이다. 이 지연 현상을 막으려면 엘리먼트의 변경이 실제로 발생하기 전에 그 변경에 관해 브라우저에게 알려주면 된다. 그러면 브라우저는 여유를 가지고(이미 알고 있으니까) 해당 변경에 대처할 수 있게 되는 것이다.
    ** 반례로 transform 등의 속성은 처음부터 레이어를 분리하지 않고, 변경이 일어날 때 레이어를 분리하기에 지연이 일어날 수 있다(translate3d 등은 변경 전에 분리함)
    => 결론적으로 레이어 분리(GPU 가속에 따른) 자체가 일반 top, width 등의 속성을 이용할 떄보다(CPU 사용) 효율적인건 맞지만(이것도 많아지면 비효율적) 그 상태에서 먼저 레이어를 분리하느냐 vs 변경 후에 분리하느냐의 차이로 또 효율성이 갈리는 것이다.

will-change 속성을 사용해 앞으로 일어날 변경에 관해 브라우저에게 알려주고자 할때는 대상이 되는 엘리먼트에 아래와 같이 CSS 코드를 작성하면 된다.

** 3D transform, scale3d 등 은 자동으로 will-change가 해주는 역할을 수행한다. 즉, 레이어 분리를 먼저 해둔다.

will-change: transform;

추가로 1개의 엘리먼트에 여러개의 값을 변경할 생각이라면 콤마(,)로 구분하여 기술할 수 있다. 예를들어 한 엘리먼트를 애니메이션 시키는 동시에 위치를 변경하고자하는 경우는 다음과 같이 선언하면 된다.

will-change: transform, opacity;
  • _will-change 사용법:”할 것”과 “하지 말아야할 것”

. will-change의 역할을 이해하고 나면 “브라우저에 모든 것을 최적화하면 좋지 않을까”라고 생각할 수도 있을 것이다. 누구라도 모든 변경에 관한 최적화가 한번에 됐으면 좋겠다고 생각할 수 있다.
. 확실히 will-change는 파워풀하고 훌륭한 도구지만, 또다른 훌륭한 도구들과 마찬가지로 위력이 있는 만큼 책임을 가지고 다뤄야한다. will-change는 무분별하게 사용하면 성능저하가 발생하고 결과적으로 페이지의 작동이 중단될 것이다.
. will-change는 성능저하뿐 아니라 바로 감지하기 어려운 사이드 이펙트(원래 will-change는 보이지 않는 곳에서 브라우저에 명령하는 방법이므로 감지하지 못하는 건 당연하다)를 발생시키기 때문에 사용하기 까다로운 속성이다. 이 속성을 사용해 최대한 효과를 발휘하고 발생할 수 있는 문제를 피하기 위해서는 아래 몇 가지 규칙을 이해해야한다.

  • 무분별한 will-change의 사용
    : 예를 들어,
*,
*::before,
*::after {
	will-change: all;
}

이와 같이 모든 것에 will-change를 쓰면, 이롭지 않을까라고 생각할 수 있다. 하지만, will-change에 연결된 강력한 최적화가 머신의 자원을 대량으로 소비하는 결과를 낳고, 결국 페이지의 속도 지연이나 때때로 크래쉬까지 일으키는 원인이 되기 때문에 이롭지 않다.오히려 일어날지 일어나지 않을지 모르는 변경에 대비해 브라우저를 최적화 시키는 것은 현명하지 못하고 효과가 없을 뿐더러 문제를 발생시키니 하지 말자

  • will-change 는 말그대로 브라우저에게 미리 말하는 개념이기 때문에 특정 애니메이션 효과와 같이 쓰면 의미가 없다. 예를 들어,
.element:hover {
	will-change: transform;
	transition: transform 2s;
	transform: rotate(30deg) scale(1.5);
}

위와 같이 쓰면 의미가 없다. 왜? 이미 transform이 일어나도록 해놓은 부분에 미리 알려봤자,, 미리 알린게 아니기 때문(?)이다.
따라서, 위에처럼 hover 이벤트가 발생했을 때 변경이 일어날 것을 미리 알리고자 한다면 적어도 변화가 생길 아주 조금 전에 그 변화를 알려줄 방법을 생각해 will-change를 설정하는 것이다.

  • 1번 케이스
.element {
	/* 스타일 선언 */
	transition: transform 1s ease-out;
}
.element:hover {
	will-change: transform;
}
.element:active {
	transform: rotateY(180deg);
}
  • 2번 케이스
.element {
	transition: opacity .3s linear;
}
	/* 마우스 커서가 조상 엘리먼트에 오는 경우에 변경사항에 대해 선언한다 */
.ancestor:hover .element {
	will-change: opacity;
}

/* 엘리먼트에 마우스 커서가 오는 경우의 변경 사항을 적용한다 */
.element:hover {
	opacity: .5;
}

위의 두개의 케이스처럼 어떤 이벤트를 발생시킬 시점보다 한단계 전에 will-change를 세팅해주는 것이다. 이 때, will-change 자체가 최적화를 꽤나 오래 유지하기 때문에 한번 변화가 생긴 것에 대해 will-change를 적용했다면 그 이벤트(클릭, 호버 등)가 끝날 때쯤 will-change를 지워주는게 좋다.

위에서는 style sheet에서 사용하는 것을 보여줬는데, 사실 will-change는 빈번한 변화가 아니면 js로 설정했다가 지워주는 방식이 훨씬 이롭다. 내가 말한 빈번한 변화가 있는 곳이란 특정 좌표값을 requestAnimationFrame 등으로 계속해서 갱신하며 위치를 바꾸는 작업 등을 말한다. 이러한 작업에 대해서는 어차피 계속해서 transform이 일어날 것이기에 이에 대해 will-change를 그 자체 속성으로 스타일 시트에 적어줘도 된다(혹은 마우스의 움직임에 맞춰 엘리먼트를 이동시키는 등의 항상 혹은 정기적으로 변화하는 부분).

마무리 하면서 실제 will-change에 쓸 수 있는 속성들은 mdn에 정리돼있으니 참고하도록 하고, 지원 브라우저를 얘기해보면,

2016년 7월 기준으로 will-change 속성을 지원하는 브라우저는 Chrome 36+, Opera 24+, Firefox 36+, Safari 9.1이다. iOS Safari 9.3 그리고 Android Browser 50 에서도 지원한다. 자세한 내용은 caniuse#will-change에서 참고한다. Edge는 “under consideration” 상태이며 곧 모든 현대 브라우저에서 지원할 것으로 기대한다.

이다.

profile
완벽함 보다는 최선의 결과를 위해 끊임없이 노력하는 개발자

0개의 댓글