일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- nextjs사용법
- SCSS extend
- 리액트
- flex
- intersection opserver
- 프로그래머스 프론트엔드 데브코스
- Spacer
- 리스트 렌더링
- 폼 입력 바인딩
- netlify redirect
- vue mixin
- Vue
- 프로그래머스 데브코스
- 이벤트 수식어
- KDT 프로그래머스
- 프로그래머스 데브코스 프론트엔드
- 다른컴퓨터에서 git사용
- git 같은계정 다른 컴퓨터
- SCSS use
- vuex map
- vue 이벤트 수신
- 고양이 사진 검색기
- SCSS forward
- 프로그래머스 K_Digital Training
- react next
- SCSS import
- 쌓임맥락
- KDT 프로그래머스 데브코스 프론트엔드
- vue 지역 컴포넌트
- postcss
- Today
- Total
혼자 적어보는 노트
[Next.js] SSR + ReactQuery 적용하기 본문
Next.js를 사용하여 프로젝트를 진행중이었는데 SSR로 작성된 페이지로 진입 시 csr보다 1초정도 느리게 페이지가 이동되는 현상이 있었다. (이전 프로젝트에서도 마찬가지)
이 부분을 개선하고 싶어서 여러 방면으로 알아보았지만 마땅한 해결 방법을 찾지 못했고 계속 신경쓰이던 부분이었는데,이번에 reactQuery를 테스트삼아 도입을 하며 조금은 개선을 하게 되었다. (아직 이후 발견하는 문제를 찾지는 못함)
📝 과정
1. 질문 목록을 클릭하면 해당 질문의 카테고리 정보와 함께 상세 페이지([id].tsx)로 이동한다.
2. 상세 페이지에서는 상세정보를 요청을 하고(SSR) 응답받은 데이터를 페이지에 내려준다.
3. 2번에서 받은 상세 정보에는 이전,다음 질문에 대한 (prevId, nextId)가 존재하며 해당 값이 담긴 버튼을 통해 페이지를 이동한다.
✅ 원하는 결과
1. 한번 들어갔던 상세 페이지를 다시 들어갈 경우에 캐싱된 데이터로 바로 보여주기
2. 다음 질문에 대한 데이터를 prefetch하여 다음 페이지 이동 시 바로 보여주기
Dehydrate
SSR에서 reactquery를 사용할 때 initialData를 사용하는 방법도 있었지만 dehydrate가 조금 더 효율적이라고 해서
dehydrate방식을 사용하기로 했다.
[_app.tsx]
import type { AppProps } from 'next/app';
import { Hydrate, QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from '~/react-query/queryClient';
import type { DehydratedState } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
function MyApp({ Component, pageProps }: AppProps<{ dehydratedState: DehydratedState }>) {
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} />
</Hydrate>
</QueryClientProvider>
);
}
export default MyApp;
[[id].tsx]
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60,
},
},
});
export const getServerSideProps = async (context: NextPageContext) => {
const { id, mainCategory, subCategory } = context.query;
const questionId = Number(id);
//.. 생략
try {
const query = { mainCategory, subCategory };
await queryClient.prefetchQuery([QUERY_KEY.questionDetail, questionId, query], () =>
questionApi.getDetail(questionId, query),
);
return {
props: {
questionId,
query,
dehydratedState: dehydrate(queryClient),
},
};
} catch (e) {
return {
notFound: true,
};
}
};
const QuestionDetail = ({ questionId, query }: QuestionDetailProps) => {
const { detailData } = useQuestionDetail(questionId, query);
// 생략
}
여기서 캐시된 데이터를 사용하려면 queryClient를 getServerSideProps 함수의 바깥에 작성해주어야 한다.
❗ 내부에 작성하게되면 다시 해당 페이지에 접근 시 캐시된 데이터를 사용하지 않음!
사실 이 부분에서 조금 쩔쩔맸는데 새벽에 프로젝트 팀원분이 찾아주셨다! 👍🙇♀️
[참고 링크: React-Query를 Next.js와 함께 사용해보자]
이렇게 하면 질문 목록페이지에서 상세정보페이지(ssr)에 접근 시 처음에는 서버에 응답을 받는 시간이 소요되지만
다른 페이지로 이동했다가 다시 접근 시 서버 요청/응답을 기다리지 않고 캐시된 데이터를 불러온다.
(지정한 staletime까지)
Prefetch 적용하기
위처럼 캐시된 데이터를 사용하는 것을 이전/다음페이지(SSR)로 이동할 때도 응용할 수 있을거라 생각해서 미리 다음 페이지를 받아오도록 prefetch를 해보았지만 페이지가 넘어가는 순간! 이상하게도 동일한 데이터를 server에서 다시 받으면서 딜레이가 생기길래 CSR로 처리해보기로 했다.
[참고 링크: React Query v4 + SSR in Next JS]
[이전/다음페이지 버튼]
const QuestionMoveButtons = ({ categoryQuery, nextId, prevId }: QuestionMoveButtonProps) => {
const router = useRouter();
const onMovePrev = () => {
router.push({ pathname: `/question/${prevId}`, query: { ...categoryQuery } }, undefined, {
shallow: true,
});
};
const onMoveNext = () => {
router.push({ pathname: `/question/${nextId}`, query: { ...categoryQuery } }, undefined, {
shallow: true,
});
};
새로고침 or URL 변경 시에는 SSR로 데이터를 받아오고 이후 페이지를 이동할 때는 serverSideProps를 거치지 않도록 shallow 옵션을 사용했다.
URL은 변경되지만 데이터는 그대로이기 때문에 페이지가 변경됨에 따라 데이터도 변경될 수 있도록 처리를 해주어야했다.
[[id].tsx]
const QuestionDetail = ({ questionId: defaultId, query }: QuestionDetailProps) => {
const [questionId, setQuestionId] = useState(defaultId);
const { detailData } = useQuestionDetail(questionId, query);
const router = useRouter();
const queryClient = useQueryClient();
useEffect(() => {
const { id } = router.query;
const numberId = Number(id);
/* CSR 이동 체크 */
if (numberId !== questionId) {
setQuestionId(numberId);
}
}, [router.query.id]);
useEffect(() => {
/* 다음 페이지 데이터 미리 받아오기 */
if (detailData && detailData.nextId) {
queryClient.prefetchQuery([QUERY_KEY.questionDetail, detailData.nextId, query], () =>
questionApi.getDetail(detailData.nextId, query),
);
}
}, [detailData?.nextId]);
// 생략
}
페이지가 이동된다면 url의 id는 변경되지만 questionId는 변경되지 않기에 두 값을 비교하여 페이지가 이동된 것을 체크하고, 서버에서 받은 detailData의 nextId가 변경될 때 다음페이지의 데이터를 미리 받아오도록 했다.
+ 댓글 또한 위와 비슷한 방식으로 questionId의 값에 따라 새로 업데이트 해주면 된다!
✅결과
'NextJS' 카테고리의 다른 글
[Next.js] Storybook next/image 설정 (0) | 2022.11.30 |
---|---|
[Next.js] Rewrites (0) | 2022.11.06 |
[Next.js] MSW로 API 모킹하기 (0) | 2022.10.13 |
[Next.js] SVGR 으로 SVG 파일 다루기 (0) | 2022.10.05 |
[Next.js] Shallow: true 뒤로가기 시 데이터 패칭 (0) | 2022.08.31 |