웹뷰 애니메이션 최적화 하기(FPS, Css hack, Throttle)

김규빈·2021년 10월 26일
22

요즘 정말 바쁜 3개월을 보냈다. 신규 서비스 개발이 시작되어 우리 개발팀은 각자의 롤을 맡아 본격적으로 개발을 하느라 여름이 어떻게 지나갔는지 모를 정도로 개발에 매진했다.

요번 신규 서비스 어니언은 앱을 베이스로 주린이를 위한 주식 앱 프로젝트로 기술 스택은 Flutter, vue, Node, OracleDB 로 내가 맡은 롤은 웹뷰, 랜딩페이지, 백오피스를 맡아 진행했다.

웹뷰 애니메이션

디자인팀에게 전달받은 웹뷰 디자인의 목표는 라이트하고, 아기자기하며, 애니메이션을 통해 주 앱 타겟층의 선호도를 파악하여 인상을 남기는 디자인이였다. 이를 토대로 웹뷰화면을 리스팅 하였고 주로 스팟성 페이지나, 앱 업데이트 없이 변화를 줄 수 있는 페이지를 웹뷰(웹, 패드, 앱 사이즈)로 제작했다. 웹뷰 페이지는 웹이나 패드 정도에선 무리없이(매끄럽게?) 작동했지만 앱화면에서 버벅임을 발견했고 그를 해결하기 위해 적용했던 기술들을 포스팅 하겠다.

시작하기

먼저 웹뷰를 제작하면서 애니메이션을 잘 사용하는 타 회사들의 애니메이션을 찾아봤는데 그 중 가장 먼저 떠오른곳이 애플과 토스였다. FN개발자라면 다들 머리속으로 애플 애니메이션 구현을 생각해 봤을테다. 먼저 애플은 고해상도의 사진과, canvas속성을 많이 사용하여 애니메이션을 구현했는데, 이는 디자인 컨셉과도 맞지 않을 뿐더러 앱안의 웹뷰에선 무리라는 판단을 했다. 토스 같은 경우에는 js애니메이션 보다는 대부분이 video를 이용하여 애니메이션을 구현한다는 사실을 알았다. 비디오를 이용해 구현하는 방법이 가장 깔끔하고 반응이 좋다. 하지만 그럴 시간과 여유, 인력이 없기에 iframe속성과, js, 그리고 aos프레임워크를 이용하여 작업하였다.

FPS 조절하기


레이어를 겹쳐 전환되는 애니메이션이다. 이 부분은 웹에선 매끄럽게 작동하지만 앱에선 약간의 버벅거림을 확인했다. 이를 해결하기위해 requestanimationframe를 사용하였다.
requestanimationframe은 애니메이션을 알리고 다음 리페인트가 진행되기 전에 해당 애니메이션을 업데이트하는 함수를 콜백한다. 여기서 리페인트라는 플로우가 등장하는데 repaint와 reflow의 메커니즘을 꼭 학습하길 바란다. 간락히 말하자면 웹을 그리는 렌더링 과정 중 html를 파싱하여 dom을 그리고 css를 파싱하여 ssom을 그리는 과정에서 배치와 그리기를 수행하게 되는데 여기서 repaint는 디자인요소(배경색, 배경이미지, 테두리, 투명도)등을 결정하는 과정이고 reflow는 레이아웃요소(width, heigth, position)요소들을 배치하는 작업을 수행하게 된다. 해당 애니메이션의 경우 그리기와 배치작업이 모두 일어나게 됨으로 브라우저에게 미리 애니메이션을 업데이트 하는 함수를 콜백 받아와 알려주었다. 움직임 모션의 경우 1초에 60Fps가 되어야 인간이 매끄럽다고 느낀다고 한다. 일반적으로 모션일 경우 setTimeout() 또는 setInterval()을 사용하게 되는데, 이와 같은 함수들은 주어진 시간내에 작동할 뿐 FPS를 전혀 고려하지 않기 때문에 버벅임을 느낄 수 있다. requestanimationframe 메소드는 실제 화면이 갱신되어 표시되는 주기에 따라 함수를 호출해주기 때문에 자바스크립트가 프레임 시작 시 실행되도록 보장시켜 주어 프레임을 유지 시켜 주었다.

개발자도구에서 command+shift+p / show fps 에서 확인 가능하다

will-change / translate3d 속성

흔히 말하는 하드웨어 가속화를 이용하는 방법이다. will-change는 브라우저에게 미리 변환될 속성이라는 것을 전달하여 해당 레이어를 GPU를 통해 랜더링 하는 과정이다. 브라우저는 실제 요소가 변화되기 전에 적절하게 최적화를 할 수 있게되고, 큰 비용이 드는 변화도 최적화로 인해 페이지의 반응성을 증가시킬 수 있습니다. 다만 will-change의 사용에는 제약이 있는데,

너무 많은 요소에 will-change 를 적용하지 말라. 브라우저는 모든 것을 최적화하기 위해 가능한 모든 것을 이미 시도하고 있다. will-change 와 묶인 강한 최적화의 몇은 많은 기기 자원을 소모할 것이다. 또한 이 같은 과도한 사용은 페이지 속도를 늦추거나 엄청난 자원을 소비할 수 있다.
라고 설명한다.

translate3d 속성 또한 GPU를 사용하게 되는데, GPU에 이미지가 업로드되면 이미지는 3차원 그래픽 표면에 다양한 형태로 매핑될 수 있으며, 위치 이동이나 변형 등 다양한 작업을 이미지를 매핑하는 형태로 적용할 수 있다. 그래서 더 적은 비용으로 다양한 변형과 요소의 합성을 실행할 수 있게 된다.

개발자 도구에서 Layer탭에서 확인해보면 GPU에 검정 박스 요소들이 업로드 됬음을 알 수 있다.

Throttle

스크롤에 따른 애니메이션이다. 스크롤 애니메이션의 경우 매 순간의 스크롤 값이 변하기 때문에 이벤트를 제어하는 방법은 필수적이라고 생각한다. 과도한 이벤트의 발생이 성능 저하를 초래하기 때문에 debounce와 throttle중 throttle을 선택하여 제어하였다. 스로틀은 일정 시간 내에 한 번만 함수를 호출하도록 하는 기술로, 디바운스와 가장 큰 차이점이라면 정해진 시간 간격 내에 반드시 최대 한 번 함수가 호출된다는 것 이다. 둘다 사용법은 아주 간단하니 간단한 예제 코드이다.

throttle (callback, milliseconds) {
      return function () {
        if (!throttleCheck) {
          // setTimeout을 이용하여 설정한 주기마다 콜백이 실행될 수 있도록 하였고,
          // 실행이 끝난 후에는 다시 throttleCheck를 false로 만들어 주어, 설정한 주기마다 이벤트가 한 번씩만 호출되도록 하였습니다.
          throttleCheck = setTimeout(() => {
            callback(...arguments);
            throttleCheck = false;
          }, milliseconds);
        }
      }
    },
    

milliseconds 만큼 스크롤체크 딜레이를 발생시키고 콜백으로 수행하는 예제 코드이다.
내가 사용한 스크롤 라이브러리 aos config를 통해 간단하게 적용시킬 수 있다.

끝 🎇

profile
FrontEnd Developer

1개의 댓글

comment-user-thumbnail
2021년 11월 10일

홀리~ 멋집니다 빈님 ㅎㅎ

답글 달기