[Next.js] getStaticPaths 관련

getStaticPaths

이전에 동적라우팅에 대해서 리퀘스트가 일어날 때 마다 생성하는 Page를 테스트 해봤었는데 빌드타임에 Static Page를 만들 수 있는 방식도 존재했다.

api path 경로에 따라 데이터는 달라지지만 내용이 자주 바뀌지 않는 페이지에 유용할 것 같다. /post/2

getStaticPathsgetStaticProps를 활용하면 되는데 build시에 외부 데이터로 받아올 동적 path의 목록들을 getStaticPaths를 활용해서 받아오고 getStaticProps에게 전달하여 Static Page를 만드는 방식이다.

코드는 다음과 같다.

// pages/post/[id].tsx

export async function getStaticPaths() {
  const res = await fetch(`${JSONPLACEHOLDER_URL}/posts`);
  const allPostsData = await res.json();

  const paths = allPostsData.map((post: PostProps) => ({
    params: {
      id: post.id.toString(),
    },
  }));

  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps({ params }: any) {
  const res = await fetch(`${JSONPLACEHOLDER_URL}/posts/${params.id}`);
  const postData = await res.json();

  return {
    props: {
      postData,
    },
  };
}

처음에는 아래와 같이 paths를 넘겨주었는데 id는 string 형식으로 제공해야하는 것 같다.

const paths = allPostsData.map((post: PostProps) => ({
  params: {
    id: post.id,
  },
}));

(여기서 id는 동적 라우팅하는 filename에 따라 다르며 나의 경우에는 pages/post/[id].tsx이여서 id로 작성해주었다.)

Build time에 Static Page 생성

build시에 데이터를 가져와 Static Page를 미리 생성하는것을 볼 수 있었다.

(여기서 100개의 page가 생긴이유는 getStaticPaths에서 100개의 id를 가져왔기 때문이다.)

fallback의 역할

위의 코드를 보면 getStaticPaths에서 fallback을 볼 수 있으며 역할은 다음과 같다.

우선 express로 간단하게 아래와 같이 데이터를 반환하는 api를 만들어두었다.

const data = [
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  {
    "userId": 1,
    "id": 3,
    "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
    "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
  }
];  


app.get('/posts', (req, res) => {
  return res.json(data);
});

app.get('/posts/:id', function(req, res, next) {
  const { id } = req.params;
  let post = data.filter(u => u.id == id )[0];
  
  if(!post) post = {};
  
  return res.json(post);
});

app.listen(port, function () {
  console.log(`🔥Server on! http://localhost:${port}`);
});

이제 build시에 3개의 Static Page가 만들어지게 되고 url을 변경해보며 실행해보면 3개의 포스트를 잘 불러오는 것을 볼 수 있다.

fallback: false

여기서 만약 백엔드에서 데이터 변경이 있어 4번째 포스트가 생긴 상태이지만 만들어두지 않았던 4번째 포스트로 url로 이동하게 되면 어떻게 될까?

이때의 역할을 해주는 것이 fallback이다.
fallback이 false일 때 결과는 다음과 같이 404 페이지를 보여주게 된다.

fallback: true

이제 fallback을 true로 변경해보았다.
그리고 빌드를 하려니까 에러가 발생한다.

잘 읽어보면 typeError로 아래의 코드에서 undefined로 넘어오게 되어서 오류가 발생하는것을 알 수 있는데

const { userId, title, body } = postData;

해당 문제를 해결하기위해 render하는 부분에서 isCallback값을 체크했다.

function PostDetail({ postData }: Props) {
  const { isFallback } = useRouter();


  // If the page is not yet generated, this will be displayed
  // initially until getStaticProps() finishes running
  if (isFallback) {
    return <div>Loading...</div>;
  }

  const { userId, title, body } = postData;

  return (
    <ul>
      <Post userId={userId} title={title} body={body} />
    </ul>
  );

이제는 잘 build 되었고 처음에는 3개가 생성되었지만 여기서 4번째 포스트로 url이동을 하게 되면 그 때 새롭게 생긴 데이터를 fetch 후 page를 만들면서 페이지를 보여주는 것을 확인할 수 있었다.

http://localhost:3000/post/4 을 친 시점에 Static page가 만들어졌다.

그리고 처음에 페이지가 먼저 렌더되어 (isCallback: true) Loading...이 보여진 후 fetch가 끝난 시점에 (isCallback: false)로 변하면서 새로운 데이터를 보여주었다. 그래서 위에서 빌드했을 때 오류가 발생하는듯 하다.

여기서 만약 isFallback을 사용하여 새로운 동적 라우팅에 대해 Static Page를 만드는 동안 Loading...이라는 페이지를 보여주고 싶지 않다면 fallback: 'blocking' 옵션을 사용하면 된다. (SSR처럼 동작한다.)


Reference

좋은 웹페이지 즐겨찾기