Android 온스타일 홈탭 스크롤 속도 개선 Mission Possible

신규찬
CJ 온스타일 기술 블로그
11 min readAug 20, 2024

안녕하세요.
CJ ENM 커머스 부문에서 Android 앱을 개발하는 신규찬입니다.

온스타일 앱은 4월 홈탭 개편 기점으로 많은 변화가 있었습니다. 첫 홈탭 개편 기획 리뷰 들어갔을 때, 홈 탭 개편은 많은 영상과 다양한 UI 로 구성될 예정이라고 하였습니다.

개발 리뷰 후 들었던 생각은 홈탭 개선 개발 완료 후 운영 시점에 홈탭의 메모리 부하로 고생할 수도 있을거 같다는 걱정이 있었습니다. 저는 이런 걱정을 할 때마다 항상 이슈로 다가 왔었는데 이번 프로젝트에서도 저의 걱정은 홈탭 스크롤시 약간씩 버벅인다는 이슈로 나타났습니다.

홈탭 스크롤 시 버벅임을 어떻게 해결하였는지 공유하겠습니다.

어디서 느려진 것일까?

홈탭 스크롤 하다보면 일부 구간에서 미세하게 덜컹(?) 되는 부분을 발견할 수 있었는데요. 이 영역 위주로 체크하니 크게 세 부분을 발견할 수 있었습니다. 하나는 영상 Prepare, Release 시점, 두번째는 Cookie의 데이터를 가져와 성인 상품 구좌 이미지를 그리는 시점, 세번째 비동기 모듈 호출 후 중간에 다량의 영역에 모듈구좌가 들어가는 시점 이었습니다.

동영상 영역에서의 느림

저희는 영상을 Exoplayer 라이브러리를 활용해서 사용하고 있습니다. 이 ExoPlayer는 준비(prepare)할 때, 플레이어는 버퍼나 디코더와 같은 자원을 할당하기 시작하면서 메모리 사용량이 증가하게 됩니다. 준비에서 메모리 사용하듯이 종료(release) 할 때도 디코더, 렌더러, 버퍼 등 다양한 리소스를 제거하면서 메모리를 사용하게 됩니다.

홈탭은 다양한 동영상을 사용하고 있는 구조로 리스트가 빠르게 스크롤 하다보면 많은 동영상을 Prepare와 Release 하는 경우가 생깁니다. 한 두 개의 동영상은 영향을 줄 수 없겠지만, 홈탭은 상단 헤더 영상, 가로 스크롤 영상, 배너에서 펼쳐지는 영상 등 다양한 동영상 요소가 존재하기 때문에, 이를 효과적으로 처리하기 위한 설계가 필요합니다.

홈탭에 존재하는 영상 예시

이미지 로드의 느림

커머스 앱에서 전시되는 상품 중 성인 상품이 등록되는 경우도 있습니다. 앱에서는 성인 상품을 표시하기 앞써 해당 사용자가 성인인지 확인을 하는데 성인 여부는 Cookie 데이터를 가져와서 판단합니다. 적은양의 Cookie 데이터를 가져오는 것은 메모리 사용에 크게 무리를 줄 수 없지만 디바이스 내 Cookie가 많은 경우 Cookie 가져오는 것으로도 메모리를 어느정도 사용하게 됩니다.

홈탭은 다수의 상품이 꽂혀있는 상품 구좌가 존재하는데 이 탭에서 빠르게 스크롤을 내리게 되면 많은 상품의 Cookie를 한번에 가져오면서 메모리에 영향을 줄 수 있게 됩니다.

홈탭에 사용하는 이미지가 성인 상품인지 확인하는 로직 예시

비동기 화면 증가 로직

탭의 모든 상품구좌의 데이터를 다 가져오면 좋지만 개인화 데이터, 랭킹 데이터, 라이브 방송 데이터와 같은 준 실시간 데이터에 대해서는 비동기로 제공하는게 일반적으로 사용하는 비즈니스 로직 입니다.

온스타일도 비동기 통신 시 로딩뷰를 보여주고 비동기 API 완료 후 로딩뷰를 제거하며 해당 구좌를 표시하게 됩니다. 탭내 스크롤로 하단에 있는 상품 구좌로 이동 중 비동기 통신하는 상품 구좌를 만날 경우 로딩뷰를 보여주게 될 것이고, 비동기 통신 완료 후에 상품구좌를 보여줄 것입니다.

만약 표시할 상품 구좌가 로딩뷰보다 긴 경우 상품 구좌가 스크롤의 위치를 조정하면서 스크롤에 영향을 주게 됩니다. 그리고 로딩뷰와 상품 구좌의 배경색이 반전으로 구성되어 있으면 깜박임 또는 버벅임처럼 느껴질 수 있습니다.

어떻게 해결할 것인가?

“문제를 정확하게 정의하면 해결의 50%는 이미 달성된 것”
(“The ability to ask the right question is more than half the battle of finding the answer.”)
Thomas J. Watso(IBM의 창립자)

Thomas J.Watso 의 말을 인용하자면 모든 문제를 해결하는데 문제에 대한 정확하게 정의하면 50% 이상은 해결할 수 있다고 합니다.

50% 이상의 성공 확률이라는 믿음을 갖고 스크롤 버벅임 문제를 해결하기 위해 분석 하였고, 그 결과 여러 가지 해결 시나리오를 도출했습니다. 이제, 각 문제 해결 과정을 하나씩 살펴보겠습니다.

비디오 영역 해결방안

Lazy Prepare Video

동영상 영역에서의 느림은 Exoplyer 의 Prepare 하는 과정 중 메모리를 사용하는데 영향이 있음을 확인 하였습니다. 빠른 스크롤 하는 사용자는 탭의 존재하는 영상을 보는 것 보다 원하는 상품이 있는 구좌로 이동하는 목적을 갖고 행동합니다. 그렇기에 빠른 스크롤 중 지나치는 동영상의 prepare는 ‘SKIP’ 해도 될거라고 생각하였습니다.

간단히 비즈니스 로직을 설명 드리면, 스크롤이 이뤄질 경우 Recycler View의 Scroll Listener 가 호출되는데요. 이 때 호출되는 Scroll Lister로 전달되는 이동 픽셀값을 기반으로 Scroll Speed 을 저장하여 평균을 구합니다. 스크롤 도중 Video Prepare 을 시도 시 저장된 Scroll Speed의 평균 값이 임계치를 초과한 상태인지 확인 하고 임계치를 초과할 경우 Video Prepare는 ‘SKIP’하는 로직을 구성하였습니다.

Lazy Prepare Video 예시

Lazy Video Release

Lazy Prepare Video의 비즈니스 로직과 비슷한 방식으로 Video Release 의 비즈니스 로직을 구성하였습니다. 이전에는 Exoplayer Release를 View 가onDetachedFromWindow() 가 호출 시점에 처리하였습니다. 이렇게 구성된 탭에서는 동영상 영역을 빠르게 지나갈 때마다 동영상 Release를 처리하게 됩니다. 이런 비즈니스 로직은 동영상 해제를 통한 메모리 사용으로 빠르게 상품 구좌로 이동하려는 사용자의 행동에 방해를 줍니다. 그래서 고안한 방식이 지연 비디오 해제(Lazy Video Release) 입니다.

지연 비디오 해제(Lazy Video Release) 비즈니스 로직을 간단히 공유드리면, 동영상 해제 시도 시 Video Release Pool Manager 에 보관하고 있다가 스크롤이 정지되었을 경우 한번에 Video Release 처리합니다. 만약 사용자의 스크롤이 연속으로 이어져 많은 동영상이 Video Release Pool에 저장될 경우, Video Release Pool 에서 저장할 최대 갯수를 지정하여 최대치를 넘어갈 경우 선입선출(FIFO:First In First Out) 방식으로 가장 먼저 Video Release Pool 에 저장한Video 를 Release 처리합니다.

Lazy release Video 예시

이미지 영역 해결방안

이미지 로드 시 쿠키 사용 방지

이미지 표시 하는 방식 중 성인 상품에 대한 체크로직을 담당하는 이미지 뷰를 별도로 존재합니다. 여기서 성인 여부에 대한 값을 Cookie 값을 바라 본 것을 SharedPreference 의 저장된 값을 기준으로 성인 여부를 체크하였습니다.

Cookie의 데이터를 가져온 다는 것은 Cookie 의 양이 비대해 질 경우 속도 저하가 되는 부분을 가져올 수 있습니다. 그래서 Cookie 의 데이터를 가져오는 로직에서 SharedPreference 을 통해서 성인 여부 값을 가져오도록 수정하여 메모리 사용을 최소화 할 수 있습니다. 만약 SharedPreference 도 변경 하였지만 여전히 느리다면 성인 여부 값을 Application 의 Global Value로 구성하여 해당 데이터를 메모리에 올려 놓고 성인 이미지 표시 여부를 판단할 때 사용할 예정입니다.

현재는 SharedPreference 을 활용하는 것으로도 속도 향상을 경험하여 이 방식을 유지하고 있습니다.

비동기 화면 해결 방안

Preload Async API

비동기 통신을 한번에 호출하는 것은 API 비용 문제에 영향을 줄 수 있습니다. 하지만 이런 API 호출 문제는 비동기 통신에 대한 캐시 정책으로 해결할 수 있는 방안입니다. 이런 비용 문제를 떠나서 홈 화면의 쾌적한 스크롤을 우선순위를 두고 대응한다면 비동기 API를 상품 구좌가 보였을 때 호출하는 것이 아니라 홈 화면이 노출 시점에 비동기 API를 사전에 호출 하여 UI 구성 한다면, 스크롤 중간에 UI 튀는 현상을 최소화 할 수 있습니다.

한 탭에서 한번에 많은 비동기 API 을 호출하다보면 모듈의 INSERT, DELETE가 동시에 이뤄질 수 있습니다. 이렇게 동시에 INSERT, DELETE가 발생하면 INDEX 가 꼬이는 현상이 발생하여 예상과 다른 결과를 맞이할 수 있습니다. 이런 현상을 방어하기 위해서 모든 동작을 TASK로 구분하여 Queue 에 담았습니다. 이렇게 담은 TASK는 Queue에서 하나씩 꺼내서 UI 처리하도록 구성하여 동시에 요청하는 INSERT, DELETE 에 대한 index 틀어짐에 대한 대비할 수 있었습니다.

동시에 Api 호출로 인한 Insert, Delete Task Queue 활용 예시

속도 개선 결과는?

홈 탭 속도 개선을 위해서 여러 시나리오는 갖고 진행하였습니다.

속도 개선을 위한 시나리오 리스트

여러 시나리오를 넣고 테스트 한 결과 앞써 소개한 해결방안인 Lazy Prepare Video, Lazy Release Video, Preload Dynaic API 을 적용했을 때 이전 버전에 비해 홈 탭 스크롤 시 UI 렌더딩 지연 현상(버벅임)이 확연히 줄여들었던 것을 확인하였습니다.

이 해결 방안이 맞는지 검증하기 위해 온스타일 탭 중 홈 탭이 아닌 다른 탭인 ‘특가' 탭에도 적용 하였는데, 홈 탭과 같이 특가 탭도 이전에 비해 비해 빨려졌다는 긍정적인 평가를 받았습니다.

홈탭 스크롤 속도 개선에 대한 객관적인 증표로 수치를 뽑으려고 하였으나, 아쉽게도 스크롤 빨라졌다는 것에 대한 기록에 대한 아이디어 부족으로 실패하였습니다. 홈탭 스크롤 속도 변화에 대한 수치화 실패한 아쉬운 마음을 AS-IS 와 TO-BE 영상 비교 영상을 준비했습니다.

AS-IS 영상에는 스크롤 도중 ‘틱.. 틱' 끊기는 현상을 발견할 수 있으며, TO-BE 에서는 AS-IS 영상의 버벅임이 개선됨을 확인할 수 있습니다.

마무리

이 속도 개선 프로젝트는 메모리의 활용을 줄이는 방식을 초점을 두고 문제해결에 접근 하였습니다. 이런 스크롤 버벅임은 모든 개발자가 맞이 할 수 있는 문제이고 RecyclerView의 스크롤에 대한 UI 처리에 대해서는 많은 고민이 필요합니다.

“나는 내가 아무것도 모른다는 것을 안다. “
(“I know that I know nothing.”)

소크라테스 (Socrates)

소크라테스의 명언처럼 사람은 완벽한 결과물을 만들어 낼 수 없듯이 홈탭 스크롤 개선에 대해서 현재 해결 방식이 완벽한 방식이 아닐 수 있습니다. 하지만 해당 방식을 공유 드린 이유는 이 해결한 방식에서 INSIGHT를 얻어 더 좋은 해결방안이 나올 수 있기 때문에 저와 같은 스크롤 이슈를 맞이한 개발자 분들에게 도움이 되었으면 좋겠습니다.

개발자는 개발의 완성이 없다는 생각을 갖고 개발에 임해야한다고 생각합니다. 이런 점에서 홈 탭 속도 개선은 앞으로 다른 버전으로 계속 이뤄질 것이고, 앞으로 개발되는 모든 탭에서 속도 개선을 위한 고뇌가 이어질 것입니다.

--

--