Next.js : 정적생성 SSG 과 fetch! + ISR (1)
Next.js를 통해 사전렌더링을 구현하는 두가지 양식.
정적생성과 서버사이드렌더링이다.
Static Generation 과 Server-Side-Rendering.
SSG (Static Site Generation) / SSR (Server Side Rendering)
권장양식은 정적 생성이라고 한다.
둘의 가장 큰 차이점은 페이지의 생성 시점이다.
SSG의 경우 빌드되는 동안 페이지가 사전 생성된다.
SSR의 경우 배포 이후에 클라이언트 요청이 발생했을 때 페이지가 생성된다.

getStaticProps 함수와 getServerSideProps 함수를 통해 정적 생성과 서버 사이드 렌더링을 구현할 수 있다.
하지만 두 함수를 사용하지 않더라도 next.js는 기본적으로 자동 정적 최적화(Automatic Static Optimization) 를 제공한다.
다시 말하면 두 함수를 사용하지 않으면 next.js는 자동 정적 최적화(Automatic Static Optimization) 를 제공한다.
(동적 생성 제외)
자동 정적 최적화란 html페이지를 만드는데 데이터가 따로 필요하지 않다면, 페이지를 HTML로 자동으로 미리 렌더링 해두는 것.
- 사전렌더링의 과정
데이터를 포함한 html페이지를 사전에 렌더링해서 만들어 두고, 그 페이지를 클라이언트에게 전달 한다.
다음에 클라이언트는 상호작용하는 로직이 포함된 js 파일을 다운로드, 리액트에서 hydrate (기존 전송했던 html파일과 합치는 것) 한다.
js파일을 나중에 다운로드 하게 되는 것과 별개로,
처음 전송한 html페이지에 주요 정보들이 다 있기 때문에 크롤러는 모든 콘텐츠를 살펴볼 수 있게 된다.
1. 정적생성 (SSG) 과 getStaticProps
간단히 말해 필요한 페이지를 빌드할 때 먼저 만들어 버린다는 것. 요청이 왔을 때 그냥 만들어 뒀던 것을 보내주면 되는 것.
한 번 생성되면 CDN에 캐시될 수 있기 때문에 말로만 들어도 매우 빠르겠거니 하고 예상하게 된다.
어떻게 사전생성할까?
이것을 위해 사용할 수 있는 함수가 getStaticProps이다.
Next.js는 이 함수를 만나면 '사전 생성해야 하는 페이지다 !' , '빌드하는 과정에서 생성해야 하는 녀석이다 !' 라고 인식한다.
사전 생성이 필요한 페이지에서 사용할 수 있고,
이 함수가 있으면 컴포넌트 코드를 읽기 전에 먼저 이 함수가 실행된다. export 해주어야 한다.
- 넥스트js 는 동적데이터가 없는 모든 페이지를 ‘기본적으로’ 사전 생성한다고 말했다.
(자동 정적 최적화(Automatic Static Optimization)
그렇다면 왜 getStaticProps함수를 굳이 써야 할 까?
프리페칭(pre-fetching) 과 동적생성.
우리는 getStaticProps함수를 통해 원하는 데이터를 통신해서 가져와서 페이지 컴포넌트에 넘겨줄 수 있다.
export const getStaticProps(context) = async () => { return { props: } }
물론 기존처럼 클라이언트 사이드에서 통신을 통해 데이터를 받아올 수 도 있다.
그렇게 하면 js코드를 통해 페이지를 로드 하는 과정에서 fetch하는 http통신 코드를 만나고
그 때 서야 데이터를 받아오는 작업이 수행되는데.
getStaticProps를 이용한다면 해당 컴포넌트를 생성하기 전에,
즉 이 컴포넌트를 사전 렌더링 하기 전에 데이터를 먼처 페치해서 전달할 수 있다 !
getStaticProps 함수에서는 항상 props 키가 있는 객체를 반환해야 한다.
이 함수가 하는 일은 컴포넌트에 대한 프롭스! 를 준비 하는 것.
const HomePage = (props) => { return <div> hi </div> }
export const getStaticProps(context) = async () => { return { props: x } }
위의 코드를 예로 들어보면, 아래 getStaticProps에서 return 값으로 넘겨준 props : x <- x값이,
위의 HomePage 컴포넌트의 props값으로 전달되는 것. 멋지다!
- getStaticProps 내부에서는 서버사이드에서만 실행이 가능했던 코드들을 사용할 수 있다.
ex) fs, 와 같이 파일시스템과 상호작용하는 코드.
- 마찬가지로 페이지가 표시된 이후에 react를 통해 hydrate 과정을 거친다.
- 이 함수 내부에서 씌여진 코드들은 클라이언트로 재전송되는 코드로 포함되지 않는다.
이 함수 내부의 코드들을 클라이언트(브라우저)환경에서 볼 수 없다.
- 크레덴셜과 같은 데이터베이스를 안에 작성할 수 있다.
구체적으로 내부적으로 어떤 일이 알고 싶다면 빌드를 해보면 된다.
yarn build
(람다기호)ㅅ - Server SSR 나중에 보게될 서버사이드 전용 페이지를 나타낸 것.
⚈ SSG - server side generation - 배포전에 서버측에서 다 생성해 놓은 페이지를 나타낸 것.
O Static - 모든 페이지가 생성되어있으나 데이터는 따로 필요하지 않아 get Static props와 같은것을 쓰지 않은 경우.
build생성된 next폴더의 server폴더의 page에 들어가보면 실제 만들어진 html파일을 직접 볼 수 도 있다.
2. ISR (Incremental Static Regeneration)
그런데 문제가 있다. 정적 생성인데 자주 바뀌는 데이터의 경우 어떻게 해야 할까.
데이터가 변경될 때마다 수정하고 빌드하고 배포하고, 비효율적이다.
두가지 해결책이 있다.
첫번째 해결책은 CSR과의 결합이다. 처음 로딩의 경우 SSG를 통해서 특정 데이터와 함께 화면이 빠르게 나오게 하고,
이후부터는 클라이언트 사이드에서 useEffect와 dependency를 사용한 통신코드를 넣어서 바뀐 데이터들로 업데이트 되게 하는 것이다.
두번째는 ISR (Incremental Static Regeneration) 을 활용하는 것.
앞서서 우리는 getStaticProps함수에서 return 값으로 props하나를 넘겼다. 그런데 여기서 revalidate 이라는 키를 추가해서 값을 초 단위로 지정해 주면, 해당 시간 이후 부터는 업데이트가 진행된다.
10초로 지정해 두었다면 10초 안에 들어오는 내용에 대해서는 기존 값이 표시되고,
10초가 지난 이후에는 업데이트 된 값이 컴포넌트로 전달 되는 것이다.
배포 후에도 배포 없이 값이 계속 업데이트 되는 것.
결론은. 지정해 둔 시간이 지나지 않은 페이지는 기존 페이지를 그대로 보여주고
그 시간이 지났다면 새롭게 재생성해서 업데이트 시켜주는 것.
새로운 페이지는 기존의 오래된 페이지를 대체, 캐시되며 향후 방문자는 재생성된 페이지를 보게된다.
3. 동적 매개 변수에 대한 페이지 생성. getStaticPaths 함수
페이지를 사전생성 하는데, 동적으로 생성하려면 어떻게 해야할까.
문제는 몇개의 페이지를 사전생성 해야 하는지 모른다는 것이다.
getStaticProps 를 통해 이 페이지를 사전 생성 해줘! 라고 코드를 작성 했는데,
작성하려는 페이지가 동적 페이지이다 보니 '어떤 페이지?' 라고 되 묻는 것이다.
예를들어 [pid] 폴더의 index.js 에서 상품 상세 페이지를 사전 생성 한다고 했을 때,
getStaticProps 값만 가지고는 생성이 되지 않는다.
어떤 페이지를 사전 생성 할 것인지! 어떤 pid값에 대한 페이지를 사전생성할 것인지에 대한 내용이 담긴 getStaticPaths 함수가 필요하다.
export const getStaticPaths = async () => {
return {
paths: [
{ params : { pid : ‘p1’ } } ,
{ params : { pid : ‘p2’ } } ,
{ params : { pid : ‘p3’ } } ,
],
fallback : false
},
}
위의 예시는 pid가 p1, p2, p3 인 페이지 3개를 사전생성 하라라는 의미이다.
domain.com/products/p1
domain.com/products/p2
domain.com/products/p3
세 페이지가 사전 생성되게 된다. 그러면 해당 페이지로 이동하는 버튼을 클릭했을 때, 새로운 요청없이 기존 생성 해두었던 그 페이지로 이동하게 되는 것. SPA처럼.
그리고 리턴값에 뒤이어 나오는 fallback 키 값이 중요하다.
fallback은 '그럼 지정한 페이지 외의 다른 페이지들은?' 이라고 묻는 키 라고 생각하면 된다.
false라고 할 경우, 지정된 페이지 외의 페이지로 요청시 404에러 페이지를 반환한다.
true하면 요청이 온 값에 대해 새로 페이지를 만들어 반환한다. 즉 위의 p1,p2,p3페이지는 사전생성하고 그 외의 p4, p5의 경우 사전에 생성은 하지 않고 요청이 오면 그때 서버에서 만들어서 보내주는 것.
즉 방문률이 높은 특정 페이지만 사전 생성해두고 나머지는 fallback을 true로 해둠으로써 요청이 올때만 생성하게 할 수 있다 !
다만 fallback상태를 반환할 수 있어야 한다.
fallback 상태일 경우 보여질 페이지가 필요하다는 의미이다. 새롭게 요청하면 그 페이지를 생성하는데 시간이 걸리기 때문.
isLoading 상태의 로딩화면과 같은 설정이 필요하다.
그걸 원하지 않는다면 'blocking'이 있다. true와 비슷하지만 loading되는 시간을 기다린 이후에 화면이 표시된다.
데이터 페칭으로 페이지 로딩에 오랜 시간이 걸리는 경우. fallback :true + isLoading화면을 켜주는게 좋고,
방문자에게 불완전한 페이지를 보여주고 싶지 않은 경우 시간이 조금 더 걸리더라도 fallback : ‘blocking’ 해주면 기다렸다 실행된다.