새 블로그 개발일지 - 8: getServerSide vs getStaticProps

Next.js getStaticProps 를 이용한 속도 개선하기: 블로그같은 정적인 페이지를 제공하지만 CMS를 사용하여 페이지를 업데이트 해야 할 경우에는 어떻게 해야할까?

getServerSideProps vs getStaticProps

getServerSideProps 는 페이지를 호출시마다 서버에서 페이지를 구성한다. 따라서 실시간으로 갱신되어야 하는 페이지에 유리하다. 하지만 페이지를 호출시마다 구성하는만큼 시간이 필요하다는 뜻이다.
반면에 getStaticProps 는 유저의 호출시 정적 페이지를 빌드하여 캐싱한다. 한번 캐싱된 페이지는 다른 유저들에게 제공될때도 유지되므로 속도가 매우매우 빠르다는 장점이 있다.
 
블로그같은 정적인 페이지를 제공하지만 CMS를 사용하여 페이지를 업데이트 해야 할 경우에는 어떻게 해야할까?
정답은 getStaticProps 를 사용하면서 동시에 적절한 revalidate 시간을 적용하여 유효한 페이지를 만들어 주는 것이다.
 

getStaticProps 를 이용한 속도 최적화

// pages/index.tsx // import ... export default function ({ data }: { data: Response<PageObjectResponse[]> }) { return <Home initialPosts={data} />; } export const getStaticProps = async () => { try { const res = await postApis.getPosts(); return { props: { data: res }, revalidate: 3600 }; } catch (e) { console.error(e); return { notFound: true }; } };
기존에 getServerSidePropsgetStaticProps 로 교체해주었다. 그리고 revalidate 를 3600초로 두면 1시간에 한번 페이지를 최신화한다. 생성된 정적페이지를 매우 빠른속도로 유저들에게 제공하게 된다.
 

동적라우팅과 getStaticProps

getStaticProps 는 말 그대로 페이지를 미리 생성한다. 하지만 동적라우팅의 경우 어떤 경로로 페이지를 미리 생성할지 알 수가 없다. 이 말은 즉 기존 getServerSideProps 를 이용해야한다.
그렇다면 동적라우팅에서는 속도를 최적화 할 수 없는걸까…? 생각이 들었지만 역시나 갓버셀은 그렇지 않다. getStaticPaths 를 이용하여 미리 생성할 라우트들을 전달해주면 된다.
 
// pages/posts/[id].tsx // import ... export default function ({ id, data }: IPostPageProps) { // logic ... return ( <> /* render */ </> ); } export const getStaticPaths: GetStaticPaths = async () => { try { const res = await postApis.getPosts(); return { paths: res.data.map((post) => `/post/@${post.id}`), fallback: true }; } catch (e) { return { fallback: true, paths: [] }; } }; export const getStaticProps: GetStaticProps = async (context) => { const id = context.params?.id?.slice(1) as string; if (!id) { return { notFound: true }; } try { const res = await postApis.getPost(id); return { props: { id, data: res }, revalidate: 3600 }; } catch (e) { return { notFound: true }; } };
기존과 달리 두개의 함수를 사용하게 된다. 먼저 getStaticPaths 에서 보면
export const getStaticPaths: GetStaticPaths = async () => { try { const res = await postApis.getPosts(); return { paths: res.data.map((post) => `/post/@${post.id}`), fallback: true }; } catch (e) { return { fallback: true, paths: [] }; } };
정적페이지를 만들어야 할 path들을 전달한다. 이건 메인화면에 보여질 포스트 목록과 같으므로 동일한 API를 이용해서 ID만 추출해준다. 그리고 getStaticProps 를 사용한다.
export const getStaticProps: GetStaticProps = async (context) => { const id = context.params?.id?.slice(1) as string; if (!id) { return { notFound: true }; } try { const res = await postApis.getPost(id); return { props: { id, data: res }, revalidate: 3600 }; } catch (e) { return { notFound: true }; } };
저 위에서 적었던 코드와 유사하다. 마찬가지로 revalidate 를 1시간으로 설정했다.
이렇게 하면 모든 SSR을 getServerSideProps 로 진행하는것 보다 훨씬 빠르게 작동한다.
 

마무리

처음에는 아무것도 모른채로 getServerSideProps 만 주구장창 썼는데 왜 getStaticPaths 가 필요한지 직접 체감한 순간이다. 그냥 개빠름.