Airbnb Case Study
Airbnb처럼 UI가 복잡한 플랫폼에서는 최적의 웹 성능을 갖추는 것이 중요하다. Airbnb에는 이미지나 지도 같이 페이지 속도에 부정적인 영향을 미치기 쉬운 큰 컴포넌트들이 사용된다.
Airbnb는 최대한 빠르고 효율적으로 컨텐츠가 로드되도록 점진적 hydration 및 Lazyloading 등을 사용한다.
- 컴포넌트와 섹션을 비동기적으로 로드하여 클라이언트 네비게이션에서 Time To Interactive (TTI)를 66%를 감소시키고 서버 네비게이션에선 TTI 속도를 20-40% 개선했다.
- 점진적 Hydration과 Loading on Interaction은 “불쾌한 골짜기”를 방지한다.
Airbnb는 페이지슬롯 컴포넌트를 사용하여 페이지의 가장 중요한 부분을 초기에 렌더링하고 상호작용 가능하도록 만드는 데 필요한 최소한의 자바스크립트만을 전달한다. 페이지슬롯 컴포넌트를 사용하면 각 페이지를 여러 섹션으로 나눌 수 있고 뷰포트에 없는 경우 코드 다운로드와 렌더링, hydration 작업을 지연시킬 수 있다.
React 앱은 자체 express.js 기반의 동형 서버를 사용하여 서버 렌더링되며, 그 후 각 섹션의 컴포넌트는 페이지슬롯에 의해 점진적으로 Hydrate된다. 섹션을 점진적으로 Hydrating하면 페이지의 가장 중요한 컴포넌트는 완전히 로드되어 상호작용이 가능하게 되고 동시에 현재 뷰포트에 없는 섹션은 로드 및 렌더링을 지연시킨다.
스케쥴러는 현재 뷰포트의 위치에 따라 섹션의 우선순위를 결정한다
- 가장 높은 우선순위: 뷰포트에서 높이가 0인 섹션
- 높은 우선순위: 뷰포트에서 높이가 0이 아닌 섹션
- 낮은 우선순위(기본 우선순위): 뷰포트 밖의 섹션
사용자가 페이지의 스크롤을 내려 뷰포트에 컴포넌트가 보여지면 컴포넌트의 우선순위가 높아질 수 있다.
또한 같은 시스템 내에서 클라이언트 측 네비게이션을 처리하고. 페이지를 순서대로 렌더링하여 서버에서 스트리밍 컨텐츠를 시뮬레이션하고. 응답된 페이지로의 전환을 보장한다.
비동기 페이지슬롯 컴포넌트를 사용한 페이지는 로딩될 때 하나의 작업이 여러 개의 작은 작업으로 분할되므로 초기 렌더링 시간을 크게 단축시킬 수 있다.
Airbnb의 서버는 이런 접근 방식으로 사용자에게 상호작용 가능한 컨텐츠를 20% 더 빨리 제공하고, 추가로 js번들의 캐시를 적용하면 최대 40%더 빠르게 제공할 수 있다.
코드 분할
라우터 기반 코드 분할
Airbnb는 페이지 성능을 더욱 향상시키기 위해 라우터 기반 코드 분할과 컴포넌트 기반 코드 분할을 모두 사용한다.
라우터 기반 코드 분할은 현재 경로에 필요한 컨텐츠만 가져온다. 초기 요청 시 불필요한 데이터와 코드가 로드되지 않아 적은 데이터만 처리되므로 초기 로드 및 TTI 속도가 향상된다.
섹션 기반 코드 분할
경로 기반 코드 분할도 요청된 데이터와 코드의 양을 감소시키지만 컴포넌트 기반 코드 분할으로 로딩 경험을 더 향상시킬 수 있다. 초기 렌더링에 필요하지 않은 페이지의 아래쪽에 있는 컴포넌트나 유저 인터랙션으로만 볼 수 있는 컴포넌트를 지연시킬 수 있다.
컴포넌트 레벨의 비동기화를 지원하기 위해 아래 사진과 같이 페이지 슬롯 렌더링에 사용되는 섹션과 동일한 경계로 코드를 분할한다. 페이지슬롯의 뷰포트 기반 스케쥴러는 코드가 비동기식으로 로드되더라도 콘텐츠가 순차적으로 렌더링되어 레이아웃이 대규모로 이동되어 버리거나 콘텐츠가 예기치 않게 이동하는 것을 방지한다.
또한 섹션 내에서 Hydration 후 사진 캐러셀의 하트버튼 같이 중요하지 않은 콘텐츠를 점진적으로 보여주기 위해 React.lazy를 추가적으로 사용한다.
모달
모달, 패널과 같은 초기에 보이지 않는 상호작용 요소에 대한 코드는 사용자 인터랙션에 의해 로드된다. 예를 들어 비동기식 컴포넌트는 사용자가 모달을 띄웠을 때 모달의 내용을 로드한다. 일반적인 모달의 유휴 작업은 모달을 열었을 때 지연을 줄이기 위해 더 높은 우선 순위 콘텐츠를 렌더링한 후 코드를 미리 로드하는데 사용된다.
플로우 기반 Prefetch
페이지의 콘텐츠가 로드되는 즉시 서비스 워커를 실행하여 현재 경로를 기반으로 사용자가 선택할 가능성이 가장 높은 다음 단계에 필요한 리소스를 미리 가져온다.
이미지 최적화
레이지로딩
컴포넌트를 레이지로딩하는 것 외에도 사용자에게 초기에 보이지 않는 이미지를 레이지로딩한다. 현재 슬라이드 기반으로 캐러셀의 이미지를 미리 가져와서 이미지 전환시 원활하게 보이게 한다.
WebP
가능한 경우 최신 이미지 형식인 WebP 이미지를 제공하여 JPEG에 비해 이미지 크기를 25-34% 줄인다. (자료.
반응형 이미지
클라이언트 특성 기반의 적응형 품질 압축으로 반응형 이미지를 제공하여 유선을 통해 전송되는 총 바이트 수를 줄이고 성능을 최대화한다.
마무리
Airbnb는 최신 성능 패턴을 구현하여 제품 세부정보 페이지의 성능을 개선하기로 결정했다. SSR에도 실수할 수 있는 여지들이 있지만 각 페이지를 여러 섹션으로 나누고 우선 순위에 따라 비동기적으로 렌더링하여 “불쾌한 골짜기”를 해결했다. 각 섹션의 즉각적인 Hydration을 통해 컴포넌트와 바로 상호작용 할 수 있으며 브라우저가 유휴 상태일 때 우선 순위가 낮은 컴포넌트를 로드하고 렌더링할 수 있다. Airbnb는 비동기 섹션 렌더링을 사용해 필요하지 않은 리소스를 가져오지 않도록 하여 훌륭한 사용자 경험을 제공하고 있다.
이 연구에 참여해 준 Airbnb의 Elliott Sprehn, Aditya, Callie에게 감사의 인사를 전한다.