가볍게 테스팅할거라 express로 만들지 않고 firebase의 realtime DB를 임시 endPoint를 만들어줬다.
(요즘 Sanity.io도 그렇고 이렇게 가볍게 테스팅할 임시 DB가 인기가 많은거같다)
index.d.ts를 살펴보니, 멋대로 추론하여 any를 리턴하는 케이스가 있어서 이 내용은 조금 타이트하게 타입을 지정할 겸 설정해줬다.
참고로, Next.js에서 제공하는 기본 타입중에 문맥이 비슷한게 상당히 많으므로 확인하면서 사용 요망
그 비슷하다는 것 중에 InferGetStaticPropsType vs GetStaticPropsResult가 있는데, getStaticProps가 Promise라 단순히 리턴타입으로는 추론을 못한다. 그래서, Infer을 통해 추론을 시키는 방안으로 가야한다.
위의 내용에서, InferGetStaticPropsType의 T 두번째 조건인,
T extends (context?: GetStaticPropsContext<any>) => Promise<GetStaticPropsResult<infer P>>
에 getStaticProps가 리턴하는 타입과 매칭이 되기 때문에 이를 추론하여 타입을 정의해주고 있다.
getStaticProps의 본질은 미리 html과 거기에 패칭해야 할 json 파일을 pre-rendering 시점에 미리 만들어 두는 것이다.
(여기에 revalidate 옵션이 있으면 매번 revalidate 타이밍이 지날 때마다 새롭게 pre-rendering이 되는 것)
위와 같이 db 엔드포인트로 get요청을 통해 데이터를 받아온 후, featured 데이터만 필터링하는 함수를 구현하고
이것을 getStaticProps 내에서 호출한 후, props 객체에 데이터를 담은 상태이다.
그렇다면 HomePage 컴포넌트는 props로 해당 내용을 받게 된다. 그렇다면, 과연 build를 했을 때에 데이터 구조는 어떻게 되어있을까.
빌드 내용을 보면 (SSG) 라는 말에서 JSON을 미리 만들어 두었음! 이란 정보를 알 수 있다.
실제 페이지를 보면 이렇게 정적으로 만들어진 json을 script 안으로 넣어서 가져오는 것을 알 수 있다.
재밌는 점은, 현재 데이터 경로에 보면 이미지의 출처가 "/imgaes/img3.jpg" 로 되어있는데, 이말인 즉슨 static하게 해당 파일을 가져오겠다는 소리고,
실제로 index.js 페이지에서 첫 정적 페이지가 업데이트 될 때 이 경로를 확인하고 img 역시 첨부해서 보내주는 것을 알 수 있다.
항상 이 메서드를 이용할때에는 어떤 상황에서 써야 할 지를 염두해두고 써야한다.
이 함수가 호출되면 결과적으로 html과 json을 미리 저장해두고 있다가, 요청이 들어오면 해당 내용을 섞어서 보내주는 구조로 Next.js는 되어있다.
그리고 이런 절차를 통해 web crawler에 노출되기 쉬운 환경을 만들 수 있다.
더욱, dynamic page의 경우 상세 데이터가 더 많이 들어있으므로 더욱 crawlable한 페이지를 만드는 것에 도움이 된다.
또한, 정적인 페이지를 언제까지고 계속 re-build 전까지 똑같이 보여주는 것도 flexible 하지 않으므로
이렇게 30분마다 revalidate하여 정적인 페이지를 재생성하도록 요청하면, 매번 요청마다 static 페이지를 다시 만들 필요는 없어진다.
더 추가적으로, 보통 일반적으로 상세 페이지 데이터와 같은 것은 useEffect를 통하여 서버에서 매번 요청하는 것이 일반적인 react에서의 데이터 패칭이었다. 하지만, 상세페이지의 일반적인 경우 재고량과 같은 변동성이 심한 내용을 제외하고는 모든 데이터들이 정적인 경우가 많다. 즉, 굳이 매번 요청할 필요가 없이 정적으로 가지고 있는것이 나을것이다. 다만, 데이터가 늘 fresh해야 할 필요는 있으므로 revalidate옵션을 여기서도 줘서 30초정도로 매번 요청마다 새롭게 페이지를 pre-rendering 한 후, 이것을 클라이언트한테 건네주는 것이 좋을 것이다.
동적 페이지에서는, getStaticProps만으로는 이 동적 페이지에 렌더링 되야 할 params가 정확하게 뭔지 알지 못하기 때문에 pre-rendering을 할 수 없어 에러를 내므로, getStaticPaths를 호출해서 미리 설정해두어야 한다고 했었다.
이때, path배열에 들어가는 것은 객체로, {params: { dynamicFilename: "pathString"}} 형태로 되어있어야 pre-rendering을 할 수 있다.
하지만, 현실적으로 상세페이지가 수십만개가 넘어갈때 그 수십만개를 미리 pre-rendering하는 것은 비효율에 가깝기 때문에,
가장 노출도가 높은 정보들을 위주로 paths 배열을 구성하고, 나머지는 들어올 때마다 pre-rendering을 하도록 하게 만드는 것이 효율적이다.
따라서, fallback 옵션에 true를 주도록 한다.
추가적으로, 클라이언트단 코드인 함수 컴포넌트 입장에선 아직 prop에 json 데이터가 들어오지 않은 시점이기 때문에 이를 처리하기 위한 추가적인 if문이 필요하다.
여기서 재미난 점이 있다. 그렇다면 과연 Next.js에서 아직 pre-rendering을 하지 않은 동적 루트들에 대해 어떤식으로 pre-rendering을 하고있을까
첫 빌드 당시에는, 아직 해당 동적 라우트에 대한 정보가 있지 않으므로, 아직 pre-rendering이 진행되지 않은 상황이다.
하지만, 첫 파일을 pre-rendering 하면서 추적해나갔을 때 해당 동적 페이지로 접근할 수 있는 Link 컴포넌트와 데이터가 존재하거나, 아니면 실제로 해당 동적페이지에 접근했을 경우,
Next.js는 해당 파일을 미리 준비해야한다고 생각하고 pre-rendering하여 파일을 만든다.
위 사진은, 첫 index 페이지의 랜더링 시에 import 되는 컴포넌트 내부에 Link 의 href로 동적 라우팅에 관련한 내용들이 들어가 있기 때문에 자동으로 pre-rendering의 대상이 된다.
따라서, Next.js는 이렇게 파일을 준비해두고 만약 Link를 눌러서 이동하면 CSR을 하겠다는 의미이므로 압축된 js파일과 json 데이터를 보내준다.
즉, 다시금 말하자면 getStaticPaths에 fallback을 true로 설정해두고 나서 해당 동적 라우팅과 관련한 접근 (ex, Link 컴포넌트) 나 직접 url을 통한 접근이 있었을 경우, Next.js는 pre-rendering을 실시하게 된다.
위의 내용을 더욱 확실하게 하기 위해, 직접 없는 데이터에 대한 조회를 url을 통해 접근하게 된다면
이처럼 데이터가 없다고 컴포넌트적으로 표시가 되고
파일상으로도 해당 경로에 대한 파일이 자동으로 pre-rendering되어 생성된 것을 볼 수 있다.
참고로, 해당 경로로 파일이 생성이 되기는 했지만 그 뒤에 getStaticProps가 호출되었을 때 props로 전달될 데이터는 여전히 존재하지 않으므로, 전달되는 json 데이터는 비어있는 상태로 전달되게 된다.
추가적으로, revalidate 옵션이 30초로 되어있기 때문에 만약 그 이후에 데이터가 추가가 되었을 경우라면 getStaticProps 호출 시 결과값이 달라져있을 것이다.
이렇게 추가 데이터를 입력한 후, 서버에 새로운 요청이 전달될 경우
자동으로 json을 새롭게 제작한 이후, 클라이언트측에 데이터를 전송하는것을 볼 수 있다.
더욱이 놀라운점은, Next.js는 캐싱과 관련된 행위를 자동으로 해주고 있었다.
위에서 볼 수 있듯, 데이터 구조형성을 위해 필요한 json 데이터나 이미지들이 304, 즉 not modified 형태로 응답되어 캐시를 계속 사용하도록 만드는것을 볼 수 있다! (etag도 알아서 만들어준다... 너무대단하다)