이번 편에서는 다음의 주제들을 갖고 얘기를 해보도록 하겠다.
Next.js에는 다음과 같이 세개의 함수를 써서 데이터를 가져올 수 있다.
결국 이 함수는 빌드타임에 데이터를 미리 가져와서 넥스트가 페이지를 미리 만들 수 있게 도와주는 역할이다. 사용 방법은 페이지가 있는 파일에 비동기 함수인 getStaticProps를 export해주면 넥스트가 알아서 이 함수를 먼저 실행해서 값을 모두 갖고 온 이후 페이지 컴포넌트를 실행해서 렌더하는 방식이다.
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
이때 context파라미터에 들어오는 값은 다음과 같다.
getStaticProps는 다음의 값들을 가지고 있는 객체를 return해야 한다.
{ destination: string, permanent: boolean }
, 어떤 경우에서는 permanent 말고 statusCode라는 프로퍼티를 사용해야 할때도 있다. 서버쪽에서 돌려지는 코드를 getStaticProps안에 써도 된다. 그렇지만 getStaticProps 안에서 fetch()를 써서 API route를 불러오지 말고 따로 만들어놓은 API route를 사용해서 통신하는게 좋다고 문서에 나와있다.
다음의 경우에서 getStaticProps를 사용하면 좋다.
GetStaticProps
타입스크립트에서 사용하기 위해서는 next에서 GetStaticProps 타입을 추출 import해서 사용한다.
import { GetStaticProps } from 'next'
export const getStaticProps: GetStaticProps = async (context) => {
// ...
}
만약 props를 위한 type inference를 사용하고 싶다면 InferGetStaticPropsType<typeof getStaticProps>
를 사용하면 된다. 샘플코드는 다음과 같다.
import { InferGetStaticPropsType } from 'next'
type Post = {
author: string
content: string
}
export const getStaticProps = async () => {
const res = await fetch('https://.../posts')
const posts: Post[] = await res.json()
return {
props: {
posts,
},
}
}
function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
// will resolve posts to type Post[]
}
export default Blog
문서를 읽다보면 ISR이 나오는데 한국말로 어떻게 해석해야할지 좋은 단어가 생각나지 않아서...하지만 굳이 해석을 하자면 점진적 정적 재생성이 될것이다.
ISR을 통해서 Next.js는 page의 데이터가 바뀌면 전체 앱을 다시 build해서 바꾸는 것이 아니고 page단위로 재build할 수 있는 기능을 제공한다. (완전 꿀기능.. next너무 좋네...)
ISR기능을 사용하기 위해서는 getStaticProps의 revalidate를 사용하면 되는데 다음의 예시코드를 보자.
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// the path has not been generated.
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: blocking } will server-render pages
// on-demand if the path doesn't exist.
return { paths, fallback: 'blocking' }
}
export default Blog
위의 코드를 보면 getStaticProps가 있고 getStaticPaths가 있다. 이 두 비동기함수는 모두 서버쪽에서 돌아간다. 보면 revalidate
가 값 10을 가진 것을 볼 수 있다. 이렇게 설정해주면 Next.js가 request가 들어오면 매 10초 페이지가 재생성된다.
만약 page에 해당하면 path가 아직 생성되지 않았으면 Next.js는 페이지를 생성하고 갖고 있는다. 그리고 후에 path가 생성되면 그 파일로 서비스를 제공한다.
이 부분에 대해서는 ISR페이지를 더 읽어보자.
process.cwd()
사용해서 file읽기getStaticProps 안에서 직접적으로 filesystem에 접근해서 File을 읽을 수 있다. 그렇게하기 위해서는 full path을 적어야 한다.
파일의 경로를 가져올때는 process.cwd()를 사용해서 Next.js가 실행되는 주소를 가져올 수 있다.
예제코드와 설명은 다음을 보자
만약 페이지가 getStaticProps를 사용하고 동적 라우팅을 사용한다면 build타임에 path들이 필요하다. 이 동적라우팅을 구현하기 위해서는 getStaticPaths라는 비동기함수가 필요하다. Next.js는 getStaticPaths에서 반환하는 객체의 paths안에 있는 값을 가지고 render에 사용할 것이다.
getStaticPaths가 return하는 객체에는 반드시 paths
라는 key가 항상 있어야 한다.
만약 다음의 라우팅경로를 사용하는 페이지가 있다고 가정해보자
pages/posts/[id].js
이럴 경우 page에서 export하는 getStaticPaths의 return은 다음과 같다.
return {
paths: [
{ params: { id: '1' } },
{ params: { id: '2' } }
],
fallback: ...
}
params 값에 맞춰진 객체에는 페이지 동적 라우팅에 사용된 값이 동일하게 사용된다는 것을 유념해야한다.
getStaticPaths에서 return하는 객체는 이 값을 필수로 가지고 있어야하고 이 값의 형태는 boolean이다.
export async function getStaticPaths() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
만약 fallback이 true
인 경우 getStaticProps의 작동 방식이 달라진다.**
해당 페이지의 fallback 버전이란 다음을 가리킨다고 한다.
router.isFallback
의 값이 true
import { useRouter } from 'next/router'
function Post({ post }) {
const router = useRouter()
// If the page is not yet generated, this will be displayed
// initially until getStaticProps() finishes running
if (router.isFallback) {
return <div>Loading...</div>
}
// Render post...
}
위의 예제에서 보이는 것처럼 page 컴포넌트는 router.isFallback 값이 true이면 loading page를 반환한다.
fallback: true는 render해야하는 페이지가 굉장히 많고 data에 의존한 render일때 이다. 예를들면 e-commerce가 있을 것이다.
pre-render를 하면 좋지만, pre-render해야하는 페이지수가 워낙 많기 때문에 굉장히 오래 걸릴 것이다.
따라서 페이지 수가 1000페이지 까지만 있다면, 앞의 100페이지 정도만 pre-render해서 가지고 있다가 나머지는 그때 요청이 들어오면 server-side에서 next가 페이지를 생성하도록 하면 된다.
한 가지 주의할 점은 fallback: true는 이미 만들어져있는 페이지를 다시 generate하지 않는다. 이를 위해서는 상위에서 봤던 ISR을 읽어보자.
만약 fallback 값이 blocking이면 말 그대로 블락이된다. 이 뜻은 요청이 들어와서 캐싱이 되지만 서버쪽에서 Next.js가 페이지를 생성하지 않고 getStaticPaths가 HTML이 생성되길 그저 기다린다는 것이다. 이 요청은 페이지 당 한번만 요청 가능하다.
getStaticPaths는 정적으로 동적라우팅을 사용하는 페이지를 pre-render할때 굉장히 유용하다. 예를들면 블로그 포스트들에 주소 끝자리가 id이고 그것만 바뀌고 안의 컨텐츠만 바뀌면 getStaticProps와 getStaticPaths를 사용하면 된다.
타입스크립트에서는 GetStaticPaths 타입을 추출해서 함께 사용하면 된다.
import { GetStaticPaths } from 'next'
export const getStaticPaths: GetStaticPaths = async () => {
// ...
}
getStaticPaths는 getStaticProps와 함께 사용한다.
위의 getStaticProps와 getStaticPaths들은 build시 실행된다고 했다. 그렇다면 실시간으로 또는 자주 데이터가 바뀐다면 어떻게해야할까? 그럴때 사용할 수 있는 것이 바로 getServerSideProps이다.
만약 page 파일에서 getServerSideProps라고 하는 비동기함수가 export된다면 Next.js는 매 요청마다 page를 getServerSideProps를 실행해서 가지고온 데이터로 pre-render해서 제공할 것이다.
getServerSideProps의 형태는 다음과 같다.
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
}
context는 다음의 key들이 들어있다.
getServerSideProps는 다음의 값을 가지고 있는 객체를 반환한다.
유념해야할 것은 지금까지 공부한 getStaticProps, getStaticPaths, getServerSideProps 모두 서버쪽에서 실행되고 client-side bundle되지 않기 때문에 이를 유념해야하고 그래서 getServerSideProps에서 import한 모듈들은 client-side에 포함되지 않는다.
타입스크립트에서 사용할 경우 GetServerSideProps라는 타입을 추출해서 사용한다.
props를 위한 inferred typing을 하고 싶으면 InferGetServerSidePropsType을 next에서 추출하여 사용하면 된다.
import { InferGetServerSidePropsType } from 'next'
만약 getServerSideProps를 export하게 될 경우 request시 Next.js는 server로 API요청을 보내고 getServerSideProps를 실행한다. 그리고 JSON 객체를 반환 받는다. 그리고 page를 render한다.
만약 데이터가 굉장히 빨리 바뀌고 페이지를 pre-render할 필요가 없다면 client에서 data를 fetch해오면 된다. Next.js에서는 다음과 같은 방법을 권한다.
SWR이라고 하는것은 Next.js팀이 만든 데이터를 가져오는 React Hook이다. client에서 데이터를 불러올 경우 이 Hook을 사용하는 것을 권한다. caching, revalidation, focus tracking, refetching 등 많은 기능을 담당한다.