무한스크롤 성능개선 트러블슈팅
한 페이지에 최대 600개의 데이터를 가져올 수 있는 무한스크롤 페이지가 있다.
한 200개 정도 넘어가면서부터 화면이 바로바로 랜더링 되지 않고 흰 화면이 잠깐 나왔다가 뒤늦게 랜더링 되는 이슈가 있었다.
특히 모바일에서 이런현상이 나타났고, 사파리 브라우저에서 굉장히 심했다.
당연히 브라우저마다 현상이 다르니 사파리 브라우저의 메모리 문제가 가장 먼저 떠올랐다.
구글링을 해보니 사파리 브라우저의 리페인트 과정에서 소모되는 리소스 문제인 것 같다.
스크롤을 내리면서 사파리에서 성능측정을 해 보았다.
스크롤을 내릴수록 cpu사용량이 점점 많아지고 눈에 띄게 느려졌다.
트러블슈팅 1
스크롤이 길어지면 부모컴포넌트의 리랜더링이 발생하면서 다시 만들 필요 없는 Dom까지 랜더링 한다?
가장 부모컴포넌트에서 data를 가져와서 state에 넣어주고 상태를 자식에게 Props로 넘겨주었는데
부모컴포넌트에는 무한스크롤 데이터 외에도 다양한 Dom요소가 있으므로 여기서 리랜더링 시키지 않고
자식컴포넌트로 data fetch부분을 이동시켜서 데이터 추가에 따른 부모컴포넌트의 리랜더링이 발생하지 않도록 수정했다.
but 여전히 느리다
트러블슈팅 2
chatGPT가 말하길 혹시 자바스크립트 파일이 너무 큰 거 아냐?라고 해서
해당 페이지에 있는 몇 개의 모달이 떠올랐다. 모달이 실제로 유저의 눈에는 보이지 않지만 코드는 로드되므로
이 부분을 lazy loading 해서 실제 화면에 필요할 때 로드하도록 수정하였으나 변함이 없다.
트러블슈팅 3
사파리브라우저가 화면을 그리는데 cpu가 아니라 gpu를 사용할 수 있도록 하드웨어 가속화를 사용할 수 있다는 글을 보았다.
transforms로 3D효과를 주어 하드웨어 가속화로 돔을 랜더링 하면 좀 나을까 싶었지만, 레이아웃을 변경하는 상황이라기 보다는 새로운 돔을 랜더링 하는 상황이므로 애니메이션 최적화와는 관련이 없는 것 같다.
트러블슈팅 4
데이터 구조의 특성상 무한스크롤 Data fetch 후 pages 배열에서 특정 키를 구분으로 하여 총 3개의 리스트로 구분하는 로직이 필요하다.
예를 들면 100개의 데이터를 가져와서 premiumList, excellentList, regularList로 나눈다.
이 연산이 data.length가 바뀔 때마다 재실행되는데 data가 몇백 개가 되면서 이 연산이 되는 속도보다 스크롤 속도가 빨라서 흰 화면이 나올 수 있다고 생각했다.
최초에는 모든 배열 요소에 filter메서드를 사용해서 해당되는 것만 뽑아서 새로운 state에 담아주는 로직을 구현했다.
이 속도를 개선하기 위해 list를 구분할 수 있는 index를 찾고 전체 배열중에 뚝뚝 잘라서 필요한 모양으로 이어 붙여주었다.
여기서 약간의 개선이 있었지만 여전히 느리다.
트러블슈팅 5
이번에는 오늘의 집 블로그에서 참고를 했는데, 우리와 똑같은 현상을 해결하기 위해 가상리스트라는 것을 이용했다.
가상리스트에 대해 검색을 해보니 우리와 같은 무한스크롤 랜더링 문제를 해결하기 위해 사용되는 것 같았다.
무한스크롤을 하면서 Dom사이즈가 커질수록 유저의 눈에 보이지 않는 Dom까지 전부 리페인트를 하면서
어느 순간 리페인트 속도보다 스크롤을 내리는 속도가 빨라지면서 흰 화면만 보이는 현상이 생긴다.
이는 브라우저 제조사의 자바스크립트 엔진 구현방법에 따른 것인데,
부드러운 스크롤의 움직임을 구현하기 위해 페인트 하는 스레트와 스크롤하는 스레드를 따로 두어서
Dom사이즈가 커지면 어느순간 리페인트에 시간이 소요되면서 페인트는 아직 안됐는데 스크롤만 되는 일이 벌어진다.
즉, 내가 겪고있는 문제가 발생한다.
간단히 구현해 보기 위해 react-virtualized 라이브러리를 사용해서 가상리스트를 구현해 봤는데 확실한 개선이 있었다.
스크롤을 내리면 Dom을 이어붙이지 않고 실제 유저의 눈에 보이는 Dom만 랜더링한다. 즉, 항상 Dom크기가 작게 유지된다.
근데 해당 라이브러리는 커스텀이 어려워서 우리 프로젝트에 바로 적용하기는 힘들다.
가상리스트를 직접 구현해서 프로젝트에 적용하고 블로그를 작성해 볼 예정이다.