들어가며
배달의민족 앱은 일부 지면을 웹뷰로 구성하는 하이브리드 앱입니다. 대표적인 커머스 서비스인 B마트의 가게홈 지면도 웹뷰로 구성됩니다. 웹뷰는 한 번의 개발로 iOS와 Android에 동시에 대응할 수 있어 개발 공수가 적고, 스토어 심사를 거치지 않아 필요할 때 배포할 수 있는 장점이 있습니다. 다만 화면을 그리기 위한 에셋(HTML, CSS, JavaScript)을 매번 다운로드해야 해서, 코드가 이미 앱 번들에 포함되어 있는 네이티브에 비해 로딩 속도가 느린 단점이 있기도 합니다.
로딩 속도가 느리면 어떤 문제가 있을까요? 사용자는 답답함을 느끼고 페이지를 이탈할 수 있습니다. 이탈률이 증가하면 주문 전환율의 감소로 이어집니다. 반대로 로딩 속도를 개선하면 이탈률을 줄이고 주문 전환율을 높일 수 있습니다. 실제로 영국의 통신 기업 Vodafone은 2021년에 LCP를 개선한 페이지와 기존 페이지를 A/B 테스트했습니다. 그 결과 LCP가 31% 개선된 페이지에서 매출이 약 8%, 장바구니 진입 비율이 11% 증가했습니다. (web.dev에서 소개된 사례)
LCP(Largest Contentful Paint)는 페이지 로딩을 시작한 뒤 화면에 보이는 요소 중 가장 큰 이미지나 텍스트가 표시되는 데 소요된 시간입니다. 로딩 성능을 평가하는 대표적인 지표로 쓰입니다.
이상적인 목표는 웹뷰의 로딩 시간을 네이티브 앱 수준까지 단축하는 것입니다. 그래서 이 글에서는 사용자가 웹뷰도 쾌적하게 사용할 수 있도록 API 요청 시점을 앞당겨 웹뷰 로딩 시간을 개선한 과정을 소개합니다.
문제 분석하기
커머스 서비스(B마트 같은 장보기 서비스)의 웹뷰 중 사용자가 가장 많은 웹뷰는 첫 진입점에 있는 가게홈(가게에 들어가면 처음 보이는 화면) 웹뷰입니다. 사용자가 가장 많은 웹뷰의 로딩 속도는 어땠을까요? 좋은 네트워크 환경과 좋은 성능의 스마트폰에서 열어 봤는데도 네이티브로 구현된 페이지에 비하면 느리다고 체감되었습니다. 네트워크 상태가 나쁘거나 저사양 기기를 쓰는 사용자라면 로딩이 훨씬 더 느릴 것입니다.
로딩 속도가 느린 이유를 알기 위해 먼저 Chrome DevTools의 Performance 탭으로 로딩 과정을 측정했습니다.
화면이 그려지기까지 다음 일이 차례대로 일어났습니다.
브라우저가 index.html 을 다운로드하고 파싱합니다.
<script> 태그에 적힌 JavaScript 번들을 다운로드합니다.
번들을 실행해서 React 컴포넌트 트리를 만듭니다.
클라이언트에서 서버로 API를 요청합니다.
서버가 요청을 처리해서 응답을 돌려줍니다.
응답을 받은 React가 리렌더링하여 화면을 그립니다.
커머스 웹뷰는 빌드된 에셋이 정적 파일로 배포되기 때문에 SSR(Server-Side Rendering)처럼 클라이언트에 전송하기 전에 서버에서 HTML을 렌더링하는 과정은 없습니다.
모든 일이 순차적으로 일어나고 있었습니다. 각 단계는 앞 단계가 끝나야 시작되기 때문에 LCP는 각 단계에서 소요된 시간을 모두 더한 값이 됩니다.
접근법
로딩 성능을 개선하는 방법으로는 다음 세 가지가 있습니다.
API 응답에 소요되는 시간을 개선합니다.
번들 크기를 줄입니다.
<link rel="preconnect"> 로 API 서버와 미리 연결해 둡니다.
다만 이미 충분히 최적화되어 있어 개선할 수 있는 부분이 많지 않았습니다. 물론 최적화를 더 진행하면 지표상으로는 조금 나아질 수 있지만, 사용자 입장에서 체감할 수 있을 정도의 유의미한 차이를 만들긴 어렵습니다. 당장 개선하더라도 향후 비즈니스 로직을 추가하다 보면 절감했던 로딩 시간이 금세 사라질 수도 있습니다.
로딩 과정을 크게 바꿀 수 있는 부분이 없을지 곰곰이 생각해 보다가, 한 가지 아이디어를 떠올렸습니다.
"HTML에서 인라인 스크립트를 사용하면, HT


