계속 헷갈렸던 getStaticProps와 getServerSideProps의 차이를 확실히 알게 되었다.
내가 이 차이점을 확실히 해야 했던 이유는, 하고 있는 프로젝트에서 가장 중요한 부분이 SEO이기 때문이다.
모든 페이지가 SEO가 잘 되어야 했고, 동적 라우팅을 사용하고 있고, 데이터 변화가 자주 일어날 것 같은 페이지와
어느정도 정적일 것 같은 페이지들이 혼합되어 있는 구조였기에 Next.js 공식문서를 꼼꼼하게 읽으며 어떻게 개발
할 지 구상해보았다.
getStaticProps와 getServerSideProps의 가장 큰 차이는
getStaticProps는 SSG 방식, getServerSideProps는 SSR 방식이다.
SSG와 SSR은 모두
Next js 에서 pre-render는 각각의 페이지들에 대해서 사전에 HTML파일을 만드는 것을 말한다.
순수 React 에서 클라이언트 사이드에서 js 가 구동되면서 페이지를 렌더링 하는 방식과는 다르다.
브라우저는 이미 다 렌더링이 된 HTML를 받기 때문에 SEO가 잘 되는 것이다.
그리고 이 Pre-render에서도 SSG 방식과 SSR 방식으로 나뉘어지는 것이다.
서버로부터 데이터 패칭이 필요 없을 경우, 즉 컨텐츠 변화가 없을 경우에는 getStaticProps를 사용할 필요도 없다. npm run build 를 하면, test.html 파일이 생겨나 있을 것이다.
서버로부터 데이터를 받아와서 이를 html 파일에 실어 보내고 싶을 경우이다. getStaticProps 메소드를 사용해야 하고, 이 메소드는 빌드 타임에 실행이 될 것이다.
그리고 패칭한 데이터를 해당 페이지 컴포넌트에 props로 return한다.
만약 /pages/post/[id] 로 동적 라우팅을 하고 있을 때, 1번 게시물에 대해서는 1.html, 2번 게시물에 대해서는 2.html을 각각 생성해야 하는 경우라면 path가 될 수 있는 모든 경우에 대해 각각 static한 html파일을 생성해 줄 수 있어야 할 것이다.
이 경우에는 getStaticPaths 와 getStaticProps 메소드를 함께 사용하여야 한다.
getStaticPaths에서는 현존하는 모든 post들의 id값을 받아 path로 설정해주어야 한다. 모든 post 목록을 리턴해주는 api 를 호출하면 될 것이다. 그리고 그 id 값을 params 에 넣어 return 해준다.
fallback 이라는 값도 설정해줄 수 있다. fallback은 빌드 타임에 생성해두지 않은 path에 대해서 어떻게 처리할 것인지에 대한 설정값이라고 생각하면 된다.
getStaticPaths로 설정해둔 path가 아니라면, 404페이지를 보여준다.
getStaticPaths로 설정해둔 path가 아니라면, 404페이지를 리턴하지 않고 fallback version page를 return한다.
여기서 fallback version 이란, page의 props가 empty이고, 이는 useRouter를 사용해 fallback 값이 true임을 감지할 수 있다. 그리고 페이지 컴포넌트 내에서 fallback 이 true일 때 로딩 컴포넌트를 띄워주는 등의 조치를 취할 수 있다.
즉, 생성되지 않은 static path에 대해 새로운 path를 요청한다면, 404페이지나 빈 페이지를 보여주지 않고, 로딩 페이지를 보여줄 수 있는 것이다.
그럼 매번 사용자는 로딩 페이지를 보게 되는 걸까 ? 아니다.
동시에 background 에서 Next.js 는 새로운 path에 대한 밑작업을 시작한다. 해당 path에 대한 HTML파일과 JSON 파일을 생성한다. 그리고 getStaticProps를 실행하여 Full Page를 만든다.
유저 입장에서는 Fallback page에서 Full page 로 전환되는 것을 보게 된다.
그리고 이 새롭게 생성된 Full page 를 프리렌더링된 페이지 리스트에 추가한다.
이 path에 대한 2번째 request부터는 이 프리렌더링 페이지 리스트의 Fullpage 를 반환하는 것이다.
fallback: true일 때와는 다르게 로딩 페이지를 보여주지 않고 새 페이지를 만들 때 까지 기다리게 한다.
getStaticProps를 사용한다고 절대 페이지가 변경되지 않는 것은 아니다. revalidate 속성을 통해 언제 페이지를 다시 re-generate 할 지 결정할 수 있다. 이를 ISR (Incremental Static Regeneration) 이라고 하는데, 전체 site를 재생성하는 것이 아니라, 페이지 단위로 재생성할 수 있게 도와준다.
만약 revalidate: 60으로 설정해둔다면, 첫 프리 렌더링이 이루어지고, 60초간의 request들에 대해서는 캐시해둔 페이지를 보여줄 것이다. 그러나 60초 이후 부터의 request에 대해서는 새로 regenerate한 페이지를 보여주어 data update 를 가능하게 하는 것이다. 만약 regenerate에 실패한다면, cached page 를 반환한다.
fallback 시 Full page 반환하는 과정과 동일하다.
Next 12.2 update 이후, on-demand revalidate (ODR) 가 가능하게 되었다. ODR은 API 로 부터 revalidate을 수동으로 커스텀 할 수 있도록 한다. 어떤 상황에서 필요할까 ?
예를 들어 revalidate 를 10초로 잡았을 때, 컨텐츠가 10초안에 100개가 새로 등록되었는데도 10초동안은 업데이트가 되지 않을 것이다. 따라서 API 요청을 통해 게시물 개수의 변화를 알고 revalidate를 수동으로 할 수 있어야 한다. 이 경우에 ODR 을 통해 해결 할 수 있다.
기존에 fallback: true와 fallback: blocking 의 차이점은 로딩 페이지를 보여주냐 아니냐의 차이였다.
유저 입장에서는 당연히 흰 화면보다는 로딩 페이지를 보는 것이 적절하겠지만
SEO 의 관점에서 바라보았을 때, Web Crawler 가 로딩 페이지를 보다가 페이지가 Fullpage 로 변환된다면
SEO에는 문제가 있을 것이다. 따라서 SEO를 위해서는 fallback: blocking 모드를 사용하여 Full page 가 되었을 때 크롤링 하도록 했어야 했다.
그러나, Next 12 업데이트와 함께 SEO에 조금 더 최적화 되도록 User가 일반 유저인지, WebCrawler 인지를 판별하여 Crawler일 경우에는 로딩 페이지를 보지 않고 컨텐트를 바로 보도록 하고, 일반 유저의 경우 로딩 페이지를 보도록 업데이트 되었다.
빌드 시 html 파일을 한 번 만드는 SSG 와 달리 ( ISR 고려 안함 ) SSR 방식은 매 요청마다 HTML 파일을 만든다. 따라서 변화가 시시각각 많은 페이지의 경우 SSR 방식을 사용하는 것이 적절하다.
Next.js 공식 문서에서도 SEO가 중요하지 않고, 변화가 없는 페이지에서는 SSG 와 CSR 사용을 권장하고 있다.
SSR 방식은 페이지를 생성할 때, CDN에 캐시하지 않고, 매번 새로운 html 을 만들어내기 때문에 부하가 크다.
cache-control header 를 추가해 캐싱할 수 있다고도 하는데, 이 부분은 좀 더 찾아봐야 할 것 같다.
나는 일단 리뷰를 작성하고, 불러오고, pagination을 처리해야 하는 리뷰 목록 페이지의 경우 SSR을 하려고 한다.
빌드 시 SSG 로 pagination을 일부 페이지 몇 개만 미리 받아온 뒤, 리뷰가 등록될 때마다 혹은 그 이상 페이지 넘버를 접속할 때마다 ISR 로 처리하는 방식도 있는데 어떤 방식이 더 좋을 지는 안 해봐서 잘 모르겠다.