반응형
- 프로젝트: 공우 홈페이지
- 키워드: Next.js, SSG, getStaticPaths, getStaticProps
- 상황
- 공지사항 또는 뉴스(이하 포스트로 통일) 페이지(https://www.gongwoo.snu.ac.kr/notice, https://www.gongwoo.snu.ac.kr/news)에서 포스트 목록을 보여준다.
- 포스트를 클릭하면 news/123 같은 상세 페이지로 이동한다.
- 동아리 홈페이지이므로 SEO 최적화를 해야 한다. 따라서, 포스트의 상세 페이지들도 static 하게 build 해야 한다.
- 포스트 내용이 정적이기 때문에, 매 요청마다 html을 만드는 SSR 대신 getStaticProps를 사용하여 SSG로 구현한다.
- 배포 후 새로운 포스트가 만들어질 경우에도 해당 포스트에 대한 html이 자동으로 만들어져야하고, 포스트가 없다면 404페이지를 보여줘야한다.
- 기존의 포스트 내용이 수정되는 경우, 이미 만들어져있던 html을 그대로 쓰면 안되고 수정된 내용으로 업데이트 해야한다.
- 해결방법
- 최초로 프로젝트를 빌드하는 시점에 이미 DB에 존재하는 포스트들은 미리 pre-render 해둔다.
이 때, Next.js가 지원하는 getStaticPaths(https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation)를 사용하여 pre-render 할 페이지의 목록을 알려준다.
뉴스의 id 속성을 paths 에 넘기면 해당 id들은 build time에 pre-render 된다.export async function getStaticPaths() { // 뉴스(type === 1) 목록을 불러오는 graphql 쿼리 const { data } = await client.query({ query: gql` query { getPostsPage(input: { type: 1 }) { result { id } } } `, }); const paths = data.getPostsPage.result.map((news: News) => ({ params: { id: String(news.id) } })); return { paths, fallback: 'blocking' }; } /** Next.js 공식문서 export async function getStaticPaths() { return { paths: [ { params: { ... } } // See the "paths" section below ], fallback: true, false, or 'blocking' // See the "fallback" section below }; } */
fallback: 'blocking' 은 build time에 만들어지지 않은 페이지에 대한 요청이 들어왔을 때, 새로운 html을 만들기 전까지 페이지 이동을 blocking 하겠다는 뜻이다. (예를 들어 news/124 라는 새로운 페이지(build time에 pre-render하지 않은)에 접근했을 때, news/124 에 해당하는 html이 만들어지기 전까지는 뉴스 목록페이지에서 로딩 상태로 blocking 되어있다) - path(url)가 만들어졌으므로 각 url마다 필요한 포스트 내용을 GET 한 뒤 pre-render 해야한다. Next.js가 지원하는 getStaticProps 를 사용하여 페이지에서 사용할 'props'를 return한다.(https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation)
이 때, 포스트가 존재하지 않는다면(data.getPost === null) notFound를 return하여 404페이지로 이동시킨다.
export async function getStaticProps({ params }: { params: { id: string } }) { const id = Number(params.id); const { data } = await client.query({ errorPolicy: 'ignore', query: gql` query { getPost(id: ${id}) { id type title body createdAt } getPrevPost(input: { type: 1, id: ${id} }) { id title } getNextPost(input: { type: 1, id: ${id} }) { id title } } `, }); if (data.getPost === null) { return { notFound: true, }; } return { props: { news: data.getPost, prev: data.getPrevPost, next: data.getNextPost, }, }; } /** Next.js 공식문서 export async function getStaticProps(context) { return { props: {}, // will be passed to the page component as props } } */
- 일단 html이 만들어지면 SSG 특성상 다음 요청부터는 만들어진 html을 그대로 응답한다. 하지만 배포 후 DB에서 포스트 내용이 수정될 경우, html을 갱신해야 올바른 페이지를 볼 수 있다.
이때 revalidate 속성을 추가하면 html을 regenerate 할 수 있다.return { props: { news: data.getPost, prev: data.getPrevPost, next: data.getNextPost, }, revalidate: 10, // seconds };
html이 만들어지고 10초 동안은 사용자에게 같은 html파일을 그대로 응답한다. 10초가 지나고 GET 요청이 오면, 기존 html을 응답하면서 동시에 새로운 html을 regenerate 한다. 그리고 다음 GET 요청부터는 갱신된 html 문서를 응답한다. - 포스트 상세페이지에 revalidate 옵션을 추가하여, 어드민 사이트에서 포스트 내용을 수정해도 홈페이지의 html이 자동 갱신되도록 했다!
- 최초로 프로젝트를 빌드하는 시점에 이미 DB에 존재하는 포스트들은 미리 pre-render 해둔다.
- Known issues
- 랜딩페이지는 3종류(뉴스, 공지사항, 멤버)의 큰 데이터를 getStaticProps의 응답값으로 반환한다. 이 때 위에서 기술한 내용과 똑같은 방식으로 revalidate 옵션을 적용해도 정상 작동하지 않는 버그가 있다.
반응형
'프론트엔드 > Next.js' 카테고리의 다른 글
getServerSideProps로 query string 가져오기 (0) | 2022.09.19 |
---|---|
Next.js에 PWA 적용하기 (0) | 2022.07.03 |
Next.js SEO plugin 이용하기(feat. next-seo, next-sitemap) (0) | 2022.07.03 |
Next.js API routes CORS 대응하기 + hook 이중 호출 방지하기 (0) | 2022.04.22 |
Next.js를 통해 prefetch 최적화 기법 이해하기(feat.next/link) (0) | 2022.02.12 |