(Next.js) Pre-rendering : getServerSideProps

yunsungyang-omc·2022년 5월 19일
1

Next.js

목록 보기
5/6

이 포스팅은 (Next.js) Pre-rendering : Static Generation와 이어진 글입니다.


다시 들어서며

앞서 살펴보았듯이, 정적인 요소를 담은 페이지를 렌더링하기 위해서 getStaticProps를 사용한다는 것을 알게 되었다. 여기서 정적인 요소를 담은 페이지로 마케팅 페이지, 블로그 포스트, 커머스 페이지 중 상품 상세 페이지 등을 말한다.

이어 이번 포스팅에서 알아볼 내용은 getServerSideProps이다.

언제 사용하는 것이 좋을까?

이전 포스팅을 통해 알아본 getStaticProps는 빌드 시에 데이터를 가져온다. 때문에 빌드되고 나면 그 데이터는 조작되지 않는다. 따라서, 최신화된 정보를 업데이트해 보여줘야 한다면 getStaticProps보다는 요청할때마다 데이터를 최신화할 수 있는 getServerSideProps를 사용해야 한다.

팀 프로젝트에서 상품 상세페이지를 Next.js로 컨버팅해야할 일이 있었는데 일반적으로 상품 상세 페이지는 getStaticProps를 이용할 것을 권장하고 있었다. 하지만, 해당 페이지에서 고객 리뷰, 세일 적용 가격 등 동적으로 구성해야할 요소들이 존재하고 있어서 getServerSideProps를 이용하기로 결정했다.

getServerSideProps와 React Query

팀 프로젝트는 상태관리를 위해 redux-toolkit을 사용하고 있다. getServerSideProps에 다른 상태관리 라이브러리를 시범적으로 적용해볼 수 있을 것 같아, 서버 스테이트를 다루는데 탁월한 기능을 제공하는 React Query를 사용해보기로 했다. (경우에 따라서는, vercel에서 제공하는 useSwr도 적용해볼만 할 것 같다.)

Next.js에 React Query 적용하기

리액트 앱과 마찬가지고 리액트 쿼리도 앱 자체를 Provider로 감싸준 후 context api처럼 context 객체를 앱 전체에서 접근할 수 있도록 사전 작업을 해주어야 한다.

_app.js

import { Hydrate, QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';

const App = (props) => {
  const { Component, pageProps } = props;

  const [queryClient] = useState(() => new QueryClient());
  
  return (
    <QueryClientProvider client={queryClient}>
      <Hydrate state={pageProps.dehydratedState}>
		<Provider store={store}>
			<CssBaseline />
			<Component {...pageProps} />
		</Provider>
	  </Hydrate>
	  <ReactQueryDevtools />
    </QueryClientProvider>
  );
};

export default App;

context Provider처럼 QueryClientProvider로 감싸고, 다시 Hydrate로 감싸주었다.
이를 통해 Hydrate(업데이트되거나 패칭된)된 server state를 앱 전반에서 접근할 수 있게 되었다.

여기서 컴포넌트들이 접급하게 될 상태들은 pageProps 객체의 dehyderatedState에 내장된 값들이다.

src/util/hooks/useFeedDispatch.js

import { useDispatch, useSelector } from 'react-redux';
import { useQuery } from 'react-query';

export const useFeedDispatch = () => {
	const dispatch = useDispatch();

    const fetchFeedList = async (feedOrderType, page) => {
        try {
            ...
            const { data } = await axios({
                url: `${BASE_URL}/feeds?${queryString}`,
                method: 'GET',
            });
            return data;
        } catch (err) {
            consoloe.log('err', err);
        }
    };

    const useFeedList = () => {
        return useQuery(['feedList'], () => fetchFeedList());
    };
export { useFeedList, fetchFeedList };

해당 파일에서는 서버 통신으로 데이터를 받아 useQuery를 이용해 'feedList'라는 키를 갖는 쿼리 객체 속에 리스폰스로 넘어온 데이터를 담아줄 것이다.

src/pages/item/[id].js

import { dehydrate, QueryClient, useQuery } from 'react-query';
import { fetchFeedList } from 'src/util/hooks/useFeedDispatch';


const FeedDetail = () => {

  	const { isLoading, error, isData } = useQuery('feedList', () =>
		fetchFeedList(),
	);
  
  	if (isLoading) return <div>Loading</div>;
	if (error) return 'error has occured';
  
    return (
          ...
    )
           
export default FeedDetail;

export async function getServerSideProps() {
	const queryClient = new QueryClient();

	await queryClient.prefetchQuery('feedList', () =>
		fetchFeedList('SELLS_MOST', 1),
	);

	return {
		props: {
  		  dehydrateState: dehydrate(queryClient),
     	},
    };
}

getStaticProps함수에서는 props에 데이터 객체를 할당해 jsx 컴포넌트의 매개변수로 전달해 사용하고 있지만, getServerSideProps에서 쿼리 객체를 사용할때는 굳이 props를 매개변수로 전달할 필요가 없다. 왜냐하면 이미 _app.js에서 Hydrate 프로바이더로 컴포넌트를 감싸주고 있어 접근이 가능하기 때문이다.

따라서 위처럼 getServerSideProps 함수 내에서 데이터를 받아오는 fetchFeedList함수를 실행 후 props: {dehydrateState: dehydrate(queryClient) }라고 작성만 해주면 jsx 컴포넌트 내부에서 useQuery로 받아온 값들을 사용할 수 있다.

구동 프로세스 자세히 알아보기 : Mastering data fetching with React Query and Next.js


마치며

만일, 리액트 쿼리를 이용하지 않고 상태관리를 redux로 관리할 경우에는 _app.js에 redux Wrapper로 감싸주는 등의 추가 작업이 역시 필요하다. (redux는 역시 보일러 플레이트를 좋아해..)

next-redux-wapper에 관해 알아보기

리액트 쿼리를 이용할 경우, server state와 client state로 state 관리를 위한 관심사를 분리할 수 있고 패칭된 데이터를 알아서 로컬 캐싱해주는 등의 장점이 있기 때문에 프로젝트에 적용하면 장점이 많을 것이라는 생각이 들었다.

프로젝트에 적용하기 위해 참고한 많은 글들이 타입스크립트를 기준으로 작성되어 있어, 내 경우처럼 리액트와 next.js를 결합한 프로젝트를 진행하는 누군가가 있다면 도움이 되길 바라는 마음을 담아 이 글을 맺는다.


읽어보면 좋은 글들

리액트 쿼리 공식 가이드
[React Query] Next.js + React Query로 무한 스크롤과 SSR 구현하기

profile
안녕하세요 주니어 프론트엔드 개발자 양윤성입니다.

0개의 댓글