react typescriptreact-query recoilemotionVercelfirebaseSPA 애플리케이션 특성 상 첫 렌더링 시 모든 페이지에 필요한 정보들을 다운로드 하느라 FP 시간이 굉장히 지연되고 있는 것을 확인하였습니다. 이를 해결하기 위해 모든 페이지들을 필요할 때 다운로드 받도록 React.lazy를 이용하여 스플리팅을 진행하였으며, 이에 아래와 같은 효과를 마주하였습니다.
bundle.js 다운로드 시간 : 150 ms → 110 ms1.11초 → 0.8초애플리케이션 사이즈가 크진 않아서 드라마틱한 성능 개선을 기대하긴 어려웠으나, 당장에 불필요한 다운로드 시간을 단축시켜 최적화를 진행했습니다.
webpack-bundle-anaylyzer라이브러리를 통해 서비스에 포함되지만 당장은 사용되지 않는 무거운 라이브러리를 분리하여 번들 크기 최적화를 진행하였습니다. 이로 인해, 1.3MB 크기를 갖던 bundle.js를 1.2MB로 경량화에 성공했습니다.
병목 코드로 의심되는 함수를 브라우저 성능 탭에서 직접 찾아서, CPU 성능을 줄이고, 함수의 파라미터 값을 극한으로 늘리는 테스트를 진행하였습니다. 그 결과 해당 함수는 최적화가 필요하다고 판단을 내렸으며, 이중 for 문으로 구현한 문자열 parse 함수를 정규표현식을 사용한 replace 함수로 교체하면서 24.71ms 걸렸던 페이지 렌더링 시간을 6.38ms까지 단축하였습니다.
기본적으로 woff2, woff, ttf 세 개의 파일을 통해 브라우저 호환성에 따라 더 작은 사이즈의 폰트 파일을 사용하도록 font-face를 구현했습니다. 하지만, 여전히 woff2 폰트 파일의 크기가 2.5MB로 무시할 수 없는 크기였으며, 3G 환경으로 테스트 했을 때 10초 이상 다운로드 속도가 경과했기 때문에 추가적인 최적화가 필요했습니다.
우선, 폰트 파일 중 폰트가 적용될 문자들을 모두 살펴봤을 때, 일상생활에서 자주 사용되지 않는 한글의 자음, 모음 조합이 상당히 많이 포함된 것을 발견했습니다. 이를 해결하기 위해 Transfonter라는 사이트에서 폰트 적용이 필요한 텍스트만 포함된 새로운 폰트 파일을 다운로드하여 적용했으며, 이에 따라 467KB 수준으로 파일 크기가 감소했습니다.
추가적으로, 폰트가 다운로드 받아지는 시점을 개발자 도구의 성능 탭에서 확인해봤을 때, 당장 화면에 보이지 않는 이미지보다도 늦게 다운로드 되는 것을 확인했습니다. 하지만, 폰트는 첫 화면에서 바로 보여야 하기 때문에 Preload를 적용할 필요성을 느꼈습니다. 애플리케이션의 커스텀을 위해 기존에 craco로 감싸줬던 것을 그대로 활용하여 webpack-font-preload-plugin의 도움을 받아 폰트 다운로드에 Preload를 적용했습니다.
페이지네이션 없이 무한 스크롤을 통해 많은 양의 리스트 요소들을 렌더링하게 되면 리스트 요소들이 지속적으로 쌓이게 됩니다. 프로젝트에서 한 화면 안에 너무 많은 양의 요소들이 렌더링되면서 화면 버벅임 현상을 직면했습니다.
이를 방지하고자 react-virtuoso 라이브러리를 사용하여 윈도잉 기법을 적용하기로 채택하였으며, 사용자가 스크롤을 통해 보고 있는 화면의 요소만 렌더링되는 효과를 기대할 수 있었습니다. 이에 따라 화면의 버벅임 문제를 해결할 수 있었습니다.
CRA로 생성한 클라이언트 사이드 렌더링 프로젝트는 일부 요소들이 미리 렌더링 되어 내려오는 서버 사이드 렌더링 프로젝트보다 SEO에 취약한 문제에 대해 고민이 있었습니다. 이를 해결하기 위해 react-helmet-async 라이브러리를 통해 페이지마다 필요한 검색 엔진 최적화 요소들을 설정해 주었으며, 이에 따라 화면 공유 프리뷰를 포함하여 검색 엔진 최적화가 가능함을 경험하였습니다.
백엔드 서비스 플랫폼으로 Firebase를 채택하였습니다.
Firebase Auth를 통해 회원가입, 로그인/로그아웃 기능을 연동하였고, Firebase Store을 통해 데이터베이스 관리를 하였으며, Firebase Storage를 통해 이미지 등 여러 가지 리소스들을 저장하도록 분리하여 개발하였습니다.
Infinity Scroll Query에 대한 구현은 리스트 요소의 마지막 요소에 대한 snapshot을 넘겨주어 firebase의 query를 사용하여 원하는 조건에 맞춰 fetch 하도록 구현하였습니다.