본문 바로가기

개발 일지/React

[NextJs] react-query(prefetching)로 SSR 구현

성능 개선의 필요성 인식

진행하고 있는 프로젝트의 상세페이지는 SSR로 구현 되어있다.

프로젝트 MVP 주간에는 페이지가 렌더링 되면 클라이언트 사이드에서 데이터를 요청하는 방식이였으나

SSR의 장점을 활용하지 못한다고 판단하였고 react query의 prefetching 을 활용하는 방향으로 성능 개선을 진행하였다.

성능개선 이전 구글 개발자도구의 LightHouse 로 측정결과 TTI 는 4초가 걸렸다

react-query 의 prefetching 활용 

우리 서비스를 이용하는 사용자는 대부분 상세페이지를 클릭할 것이다. 

이럴 경우 사용자가 상세페이지에 필요한 데이터를 기다릴 필요가 없도록 미리 데이터를 가져오면 보다 좋은 사용자 경험을 제공할 것이다. 

이때 사용 할 수 있는것이 react query의 prefetching 이다.

상세페이지는 prefetching 을 사용함에 있어 적합한가?

1) 상세페이지에 들어가는 모집공고문의 데이터는 비교적 안정적이기 때문에 prefetching 에 특히 적합하다고 판단한다. 

2) 모집공고문의 데이터는 실시간으로 변동하는 주식시세와 같은 동적인 데이터를 가져오는 것이 아니기 때문에 캐시 된 데이터에 의존하더라도 문제가 되지 않을 가능성이 매우 높다고 생각했다. 

3) 유입되는 사용자의 대부분은 메인페이지에서 페이지가 로드 될 것이다.

그렇기 때문에 상세페이지에 필요한 데이터가 캐시에 미리 로드 되고,

로드된 데이터의 캐시 시간이 다 되기전에 사용자가 상세페이지로 이동하는 한 사용자는 서버호출을 할때까지 기다릴 필요가 없다.

사용자가 기다릴 필요가 없다는 것은 그만큼 더 좋은 사용자 경험을 제공하는 것이다. 

 

 

NextJs에서 prefetching 사용 하기 위해선

공식문서에 따른 두 가지 방법이 있다.

  1. initialData
  2. 서버에서 캐시를 dehydrate 후 클라이언트에서 hydrate를 하는 방식

첫 번째 방법의 사용법은 간단하지만, 클라이언트 사이드에서 해당 데이터를 사용하는 컴포넌트까지 props로 넘겨주어야 하는 비효율적인 작업이 동반된다. 

두 번째 방법은 위와 같은 비효율적인 작업은 없지만 프론트에서 셋업 해야 할 것 들이 있다.

 

우리는 두 번째 방법을 채택하였다. 

 

서버에서 캐시를 dehydrate 후 클라이언트에서 hydrate를 하는 방식

1. 공식문서에서 알려주는 대로 최상단 컴포넌트에서 보일러 플레이트 코드를 작성하였다. 

 

_app.tsx에서 pageProps에 전달된 dehydrate된 props를 가져와서 hydrate시킨다. 즉, getServerSideProps에서는 데이터를 가진 상태의 dehydrate state를 전달하고, 다시 클라이언트에서 hydrate한다.

이렇게 세팅을 마치면 useQuery에서 동일한 queryKey로 값에 접근할 수 있다.

 

2.getServerSideProps에서 새로운 QueryClient를 생성하고 해당 QueryClient에 값을 prefetch한다. 그리고 이 queryClient를 dehydrate시켜서 props로 전달한다.

처음 1회는 SSR로 값을 받아와서  캐시에 데이터가 저장 되고, stale 상태가 된 이후에는 클라이언트에서 동일하게 API호출을 하여 값을 갱신하게 된다.

 

결과

prefetch 이후 LightHouse 로 상세페이지에 대한 성능을 측정한 결과  TTI를 4초에서 0.2초로 단축시킬수 있는 엄청난 성능을 보여주었다.