트렌비 세일스캐너 페이지가 상품 이미지 증가로 모바일에서 앱이 뻗을 정도로 느려지자 Intersection Observer 기반 지연 로딩을 적용한 기록이다. 원인을 좁히기 위해 시도한 두 가지 검증과 검토했다 접은 대안들이 앞에 나온다. 최초 로딩 리소스가 120MB에서 평균 9.6MB로 줄었다.
핵심 포인트- MD가 엄선한 카테고리와 상품을 모두 진열하면서 이미지가 급증했다.
- 카테고리 수를 제한해보니 안정적으로 로딩되는 한계가 30개였다. 한 번에 많은 콘텐츠를 부르는 것이 문제였다.
- 데스크톱에서 속도를 제한해보니 느리긴 해도 로딩은 됐으므로 네트워크 속도는 근본 원인이 아니었다.
- 이미지 컴포넌트에만 지연 로딩을 걸면 빈 공간이 잠깐 보이므로 목록 컴포넌트 자체를 지연시켰다.
- 최초 호출 리소스가 120MB에서 평균 9.6MB로, 첫 페이지 로딩이 약 30초에서 평균 5.5초로 줄었다.
상세 정리- 문제 상황: 데스크톱에서는 기다리면 로딩이 됐지만 모바일에서는 오래 걸리는 로딩과 과도한 리소스 요청으로 앱이 아예 뻗었다.
- 검증 1: API 응답의 카테고리 개수를 제한해봤다. 카테고리별로 상품을 6~12개 보여주는데 안정적으로 감당되는 카테고리가 30개였다.
- 검증 1의 결론: 한 번에 너무 많은 콘텐츠를 로딩하는 것이 원인이라는 방향을 잡았다.
- 검증 2: 모바일에서만 문제라면 네트워크 속도 탓인지 확인하려 데스크톱 속도를 제한해봤다.
- 검증 2의 결론: 오래 걸렸을 뿐 로딩은 완료됐으므로 속도는 근본 원인이 아니었다.
- 대안 1 검토: 리소스를 다 가져온 뒤 20개만 보여주고 더 보기를 붙이는 방법은, 애초에 많은 이미지를 최초에 부르는 것이 문제라 증상이 나아지지 않는다.
- 대안 2 검토: API에 페이지네이션을 걸어 스크롤 위치마다 요청하는 방법은 백엔드 공수가 필요해 최후의 선택지로 뒀다.
- 대안 3 선택: 최초에는 필요한 만큼만 이미지를 부르고 나머지는 필요한 시점에 부르는 방식이 공수가 가장 적고 리소스 문제까지 줄일 수 있었다.
- 개념 정리: 지연 로딩은 첫 화면에 필요한 섹션만 로드하고 나머지는 사용자가 필요로 할 때까지 로딩을 미루는 것이다.
- 적용 시 고민 1: 이미지에만 걸어도 되는지 따져봤다.
- 적용 시 고민 2: 이미지만 늦게 채워지면 그 사이 빈 공간이 보이는데 이를 어떻게 없앨지 고민했다.
- 결정: 이미지 컴포넌트가 아니라 목록을 그리는 컴포넌트 자체를 지연시키기로 했다.
- 사용 기술: 대상 요소와 뷰포트의 교차 변화를 비동기로 관찰하는 Intersection Observer를 썼다.
- 동작 원리: 목록 컴포넌트마다 관찰자를 붙여 화면에 보이는 것만 실제 목록을 렌더링하고 나머지 자리에는 공용 로딩 컴포넌트를 놓는다.
- 구현 1 순수 자바스크립트: ref로 대상을 잡고 IntersectionObserver를 만들어 교차 여부를 확인해 상태를 켠 뒤, 정리 함수에서 관찰을 해제한다.
- 구현 2 리액트 훅: react-intersection-observer의 useInView를 쓰면 ref와 교차 여부를 받아 같은 동작을 짧게 쓸 수 있고, 임계값과 한 번만 실행 옵션을 지정할 수 있다.
- 결과 1: 최초 로딩 시 호출되는 리소스가 이미지 전체 120MB에서 필요한 만큼인 평균 9.6MB로 줄었다.
- 결과 2: 첫 페이지 로딩 완료가 약 30초에서 평균 약 5.5초가 되어 시간 기준 약 500% 개선됐다.
- 저자의 단서: 이미지 호출이 극단적으로 많은 페이지라 효과가 더 두드러진 면이 있다고 스스로 밝힌다.
왜 읽나이미지가 많은 목록 페이지가 모바일에서 죽는다면, 원인을 좁히는 두 가지 검증과 컴포넌트 단위로 지연시키는 선택의 근거를 그대로 참고할 수 있다.