반응형

Next.js로 만든 홈페이지의 배너를 제작하면서 슬라이더를 구현하고 테스트 중 버벅거리는 현상을 발견하였습니다. 사실 이미지의 크기가 작으면 애니메이션이 버벅거림 문제는 발생하지 않지만 큰 사이즈의 이미지를 사용하자마자 이러한 문제가 발생해서  UX적으로 굉장히 좋지 않다고 판단하였고 이를 해결하기 위해 이미지를 최적화할 수 있는 방법에 대해서 알아보았습니다.

 

배너 이미지 크기

배너 이미지 크기가 꽤 큰편
슬라이더 버튼 클릭 시 버벅거림 발생

 

작은 사이즈의 이미지(24KB)의 경우

큰 사이즈의 이미지 애니메이션보다 부드러움

 

사실 사용할 이미지를 미리 최대한 압축해서 사용하여 성능을 끌어올릴 수 있지만 저는 개발자이기 때문에(?) 최악의 케이스를 염두에 두고 코드로서 최적화할 수 있는 방법이 있는지 찾아봤습니다.

 

🧐 스타일 적용 순서

  left: ${(props) => `${props.left}px`};
  transition: left 0.2s ease;

위의 배너  슬라이더 애니메이션을 구현하면서 transtion 속성을 사용하였는데 먼저 웹브라우저에 스타일이 어떤 순서로 적용되는지 알아보겠습니다.

1. Style

브라우저가 객체에 적용할 스타일의 값을 계산 및 재계산

 

2. Layout

브라우저가 객체의 모양과 위치 생성

<적용되는 속성>

position display overflow overflow-y
width height min-width min-height
padding margin border border-width
top bottom left right
font font-family font-size font-weight
white-space line-height vertical-align float
clear      

 

3. Paint

브라우저가 객체 영역의 픽셀들을 채움

<적용되는 속성>

color background visibility text-deocration
background-image background-position background-repeat background-size
outline outline-color outline-style outline-width
border-radius border-style box-shadow  

 

4. Composite

브라우저가 레이어(z-index) 순으로 객체를 화면에 그림

transform opacity

 

 

🚀 스타일 성능 최적화

위의 순서에 따라 Layout 속성을 바꾸면 Paint, Composite 순서를 거치기 때문에 성능 저하가 발생하고 이를 Reflow라 합니다. 또한 Paint 속성을 바꾸면 Composite 순서를 거치기 때문에 성능 저하가 발생하고 이를 Repaint라 합니다.

따라서 최대 성능을 위해 최대한 적은 순서를 거치도록 Composite 속성만 바꿔야 합니다.

 

.banner{
    left: -100px;
    transition: left 0.2s ease;
}

.banner-move .banner{
    left: 0px;
}
.banner{
    transform: translateX(-100px);
    transition: transform 0.2s ease;
}

.banner-move .banner{
    transition: none;
}

첫 번째 코드에서는 left가 변할 시 Layout->Paint->Composite 순서를 거치게 되고
두 번째 코드에서는 transform이 변할 시 Composite 순서만 거치게 됩니다.

 

  left: ${(props) => `${props.left}px`};
  transition: left 0.2s ease;

 

하지만 저의 경우 Next.js에서 Styled-components를 사용 중이고 배너의 개수가 고정된 것이 아닌 가변적인 여러 개의 배너를 띄울 수 도 있고 적용되는 스타일의 값이 변하므로 항상 Style->Layout->Patin->Composite의 모든 순서를 거치게 됩니다. 즉, 다른 방법을 모색해야 합니다.

 

 

🔥 will-change 속성

will-change: 속성명;

will-change 속성은 브라우저에게 값이 변경될 속성에 대한 힌트를 미리 지정해주어 앞으로 동적으로 변화할 값을 알고 부드러운 이벤트를 구사할 수 있게 해주는 속성입니다.

GPU 가속을 이용하여 애니메이션의 프레임을 최대로 끌어올릴 수 있습니다.

그러나,

MDN web docs에 있는 글에 따르면 이 속성을 너무 남발해서는 안된다고 합니다. 이미 페이지가 잘 돌아간다면 사용하지 말고 당장의 성능 문제를 빠르게 해결하기 위한 마지막 수단으로 사용되기 위해 만들어진 속성이며, 너무나도 많은 will-change의 사용은 과도한 메모리 사용과 복잡한 렌더링을 초래할 수 있다고 합니다.

또한 will-change 속성은 애니메이션이 종료되었을 때 속성을 제거해주는 것을 권장하고 있습니다.

will-change: auto;

 

 

👀 비교샷

(아래의 코드에서 props는 리액트 컴포넌트에 전달하기 위한 값으로 해당 styled-component에 left값을 변수로 내려주는 역할을 한다고 보시면 될 것 같습니다.)

bannerNum에 따라 다른 left값을 props로 전달

 

transition : left

 left: ${(props) => `${props.left}px`};
 transition: left 0.2s ease;

 

transition : transform

 transform: ${(props) => `translateX(${props.left}px)`};
 transition: transform 0.2s ease;

 

transition : left + whill-change

left: ${(props) => `${props.left}px`};
transition: left 0.2s ease;
will-change: transform;

 

transition : transform + whill-change

transform: ${(props) => `translateX(${props.left}px)`};
transition: transform 0.2s ease;
will-change: transform;

 

will-change를 사용 후 애니메이션 속도가 확실히 빨라진 것을 체감하였습니다. 그러나 너무 남발하면 안 될 것 같은 속성이고 급한 불을 끄기 위한 속성 같아 보입니다. 따라서 사이트의 속도를 최대로 끌어올리기 위해서는 근본적인 문제(이미지의 크기, 압축, 최적화)를 해결해야 하지 않을까 싶습니다.

 

 

 

📚 참고

https://aproid.github.io/2019/03/21/transition-optimize/

https://developer.mozilla.org/ko/docs/Web/CSS/will-change

반응형

+ Recent posts