Next.js Basics(3)

mintConeflower·2021년 6월 27일
1

Next.js

목록 보기
3/3
post-thumbnail
post-custom-banner

이번 편에서는 다음의 주제들을 갖고 얘기를 해보도록 하겠다.

  • 데이터 가져오기
  • 이미지 최적화
  • 폰트 최적화
  • 정적 파일 다루기

데이터 가져오기(Data fetching)

Next.js에는 다음과 같이 세개의 함수를 써서 데이터를 가져올 수 있다.

  • getStaticProps(SSG): 데이터를 build할때 가져온다.
  • getStaticPaths(SSG): Specify dynamic routes to pre-render pages based on data.
  • getServerSideProps(SSR): Fetch data on each request.(매 요청때마다 데이터를 가져온다.)

getStaticProps

결국 이 함수는 빌드타임에 데이터를 미리 가져와서 넥스트가 페이지를 미리 만들 수 있게 도와주는 역할이다. 사용 방법은 페이지가 있는 파일에 비동기 함수인 getStaticProps를 export해주면 넥스트가 알아서 이 함수를 먼저 실행해서 값을 모두 갖고 온 이후 페이지 컴포넌트를 실행해서 렌더하는 방식이다.

export async function getStaticProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}

이때 context파라미터에 들어오는 값은 다음과 같다.

  • params : contains the route parameters for pages using dynamic routes. 예를 들면 page의 이름이 [id].js라면 params는 { id: ... } 이다.
  • preview : 페이지에 preview 모드이면 true이고 이외의 경우 undefined이다.
  • previewData : setPreviewData에 의해서 결정된 preview데이터셋이 들어온다. 이건 PreviewMode 문서를 더 읽어보자.
  • locale : (사실 영어문서를 읽는데 locale의 영어 단어 뜻인 사건이 발생하는 장소를 말한다.) 따라서 이것은 현재 페이지를 가리키는게 아닐까싶은데.. 확실하지 않다. location같은 느낌?
  • locales : 지원된 locales들을 갖고 있다.
  • defaultLocale : 설정된 locale의 정보를 갖고 있다.

getStaticProps는 다음의 값들을 가지고 있는 객체를 return해야 한다.

  • props : page 컴포넌트로 전달되는 props를 담은 선택적인 객체
  • revalidate : default값은 false이고 선택적이다. 페이지가 얼마만큼의 시간(초 단위) 뒤에 re-generation이 일어날 수 있는지 그 값을 결정한다.
  • notFound : boolean형 값이고 true를 주면 404 page 컴포넌트를 return 한다.
  • redirect : 이 값은 다음의 두 값을 지닌 객체 형태여야 한다. { destination: string, permanent: boolean }, 어떤 경우에서는 permanent 말고 statusCode라는 프로퍼티를 사용해야 할때도 있다.

서버쪽에서 돌려지는 코드를 getStaticProps안에 써도 된다. 그렇지만 getStaticProps 안에서 fetch()를 써서 API route를 불러오지 말고 따로 만들어놓은 API route를 사용해서 통신하는게 좋다고 문서에 나와있다.

그렇다면 getStaticProps는 언제 사용하는게 좋은가?

다음의 경우에서 getStaticProps를 사용하면 좋다.

  • page를 구성하기 위한 data가 유저가 요청해서 페이지를 보기 전에 이미 사용가능하고 build시간에 만들 수 있는 데이터이면 사용가능하다.
  • data가 headless CMS에서 오면 사용가능하다.
  • 데이터가 공적으로 캐싱이 가능하면 사용가능하다.
  • 페이지의 render가 빨라야하고 SEO를 위해서 미리 render가 되어 있어야하면 사용하는게 좋다. getStaticProps는 HTML와 JSON파일을 둘 다 만든다. 이것들은 CDN에서 퍼포먼스를 위해서 캐싱될 수 있는 것들이다.

Typescript에서 사용하는 방법 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(Incremental Static Regeneration)

문서를 읽다보면 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초 페이지가 재생성된다.

  • 만약 처음에 페이지가 만들어지고 build시간에 pre-render가 된다면 초기에 보여지는 페이지는 build 시간에 만들어진 페이지이다.
  • 그리고 10초 전까지 들어오는 request는 캐싱되고 원래있던 페이지가 보여진다.
  • 10초가 지나면 사용자에게는 계속 초기에 만들어졌던 page가 보이고 Next.js는 이 페이지에 대해서 재생성을 시작한다.
  • Page 재생성이 성공적으로 마무리되면 지금까지 들어왔던 캐싱됬던 요청들을 invalidate하고 페이지를 교체한다. 만약 page 재생성이 실패하면 이전 페이지가 보여지게 된다. (API가 바꼈거나 데이터를 가져올 수 없으면 실패하게 될 것이다.)

만약 page에 해당하면 path가 아직 생성되지 않았으면 Next.js는 페이지를 생성하고 갖고 있는다. 그리고 후에 path가 생성되면 그 파일로 서비스를 제공한다.

이 부분에 대해서는 ISR페이지를 더 읽어보자.

process.cwd() 사용해서 file읽기

getStaticProps 안에서 직접적으로 filesystem에 접근해서 File을 읽을 수 있다. 그렇게하기 위해서는 full path을 적어야 한다.

파일의 경로를 가져올때는 process.cwd()를 사용해서 Next.js가 실행되는 주소를 가져올 수 있다.

예제코드와 설명은 다음을 보자

getStaticPaths

만약 페이지가 getStaticProps를 사용하고 동적 라우팅을 사용한다면 build타임에 path들이 필요하다. 이 동적라우팅을 구현하기 위해서는 getStaticPaths라는 비동기함수가 필요하다. Next.js는 getStaticPaths에서 반환하는 객체의 paths안에 있는 값을 가지고 render에 사용할 것이다.

paths

getStaticPaths가 return하는 객체에는 반드시 paths라는 key가 항상 있어야 한다.

만약 다음의 라우팅경로를 사용하는 페이지가 있다고 가정해보자
pages/posts/[id].js
이럴 경우 page에서 export하는 getStaticPaths의 return은 다음과 같다.

return {
  paths: [
    { params: { id: '1' } },
    { params: { id: '2' } }
  ],
  fallback: ...
}

params 값에 맞춰진 객체에는 페이지 동적 라우팅에 사용된 값이 동일하게 사용된다는 것을 유념해야한다.

fallback

getStaticPaths에서 return하는 객체는 이 값을 필수로 가지고 있어야하고 이 값의 형태는 boolean이다.

fallback 값이 false인 경우

  • fallback 값이 false인 경우 getStaticPaths로부터 return되지 않은 경로들은 404page가 나타난다.
  • 따라서 페이지가 자주 추가되지 않으면 false로 놔두면 되고, build만 해주는 것으로 next 앱을 업데이트할 수 있다.
    다음은 예제 코드이다.
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인 경우

만약 fallback이 true인 경우 getStaticProps의 작동 방식이 달라진다.**

  • getStaticPaths로부터 return된 paths들이 HTML에 build할때 render된다.
  • 만약 page가 build때 만들어져 있지 않다면 404로 이어지는 것이 아니라, Next.js는 해당 페이지의 fallback 버전을 제공한다.
  • 이때 request가 들어오면, Next.js는 정적으로 HTML과 JSON을 만들고 getStaticProps도 실행한다.
  • 이것이 완료되면 브라우저는 해당 경로에 필요한 JSON을 전달받게되고 이전 render된 페이지와 새롭게 날라온 정보를 교체한다. 따라서 유저에게는 fallback page에서 full page로 바뀌는 것으로 보인다.
  • 그와 동시에 Next.js능 이 경로(path)를 pre-rendered pages 목록에 추가한다.
  • 그리고 이후 request에는 이렇게 render된 페이지를 제공한다.
  • fallback: true는 next export를 사용할때 지원되지 않는다.

Fallback 페이지란 무엇인가?

해당 페이지의 fallback 버전이란 다음을 가리킨다고 한다.

  • page의 props들이 비어있다.
  • router를 이용해서 fallback이 render되고 있는지 확인할 수 있다.
    router.isFallback의 값이 true
    따라서.. 이 Fallback을 쓸 수 있는 좋은 예시코드는 다음과 같다.
    페이지가 로딩 되는 시나리오를 가정해보자.
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가 유용할까?

fallback: true는 render해야하는 페이지가 굉장히 많고 data에 의존한 render일때 이다. 예를들면 e-commerce가 있을 것이다.

pre-render를 하면 좋지만, pre-render해야하는 페이지수가 워낙 많기 때문에 굉장히 오래 걸릴 것이다.
따라서 페이지 수가 1000페이지 까지만 있다면, 앞의 100페이지 정도만 pre-render해서 가지고 있다가 나머지는 그때 요청이 들어오면 server-side에서 next가 페이지를 생성하도록 하면 된다.

한 가지 주의할 점은 fallback: true는 이미 만들어져있는 페이지를 다시 generate하지 않는다. 이를 위해서는 상위에서 봤던 ISR을 읽어보자.

fallback: blocking

만약 fallback 값이 blocking이면 말 그대로 블락이된다. 이 뜻은 요청이 들어와서 캐싱이 되지만 서버쪽에서 Next.js가 페이지를 생성하지 않고 getStaticPaths가 HTML이 생성되길 그저 기다린다는 것이다. 이 요청은 페이지 당 한번만 요청 가능하다.

getStaticPaths는 언제 사용해야 하는가?

getStaticPaths는 정적으로 동적라우팅을 사용하는 페이지를 pre-render할때 굉장히 유용하다. 예를들면 블로그 포스트들에 주소 끝자리가 id이고 그것만 바뀌고 안의 컨텐츠만 바뀌면 getStaticProps와 getStaticPaths를 사용하면 된다.

Typescript에서 GetStaticPaths사용하기

타입스크립트에서는 GetStaticPaths 타입을 추출해서 함께 사용하면 된다.

import { GetStaticPaths } from 'next'

export const getStaticPaths: GetStaticPaths = async () => {
  // ...
}

getStaticPaths는 getStaticProps와 함께 사용한다.

  • getStaticPaths는 build시간에 server쪽에서 실행된다.
  • page안에서만 export되어서 사용할 수 있다.

getServerSideProps

위의 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들이 들어있다.

  • params: 페이지가 동적 라우팅을 사용한다면 params는 route parameter를 담고 있다.
  • req: HTTP incomingMessage 객체
  • res: HTTP response 객체
  • query: query string 객체
  • preview: boolean값, preview mode가 참이면 true, 이외의 경우 false로 들어온다.
  • previewData: setPreviewData로 정해진 preview data set
  • resolveUrl: 정규화된 request URL
  • locale: 일어나는 지점
  • locales: 일어나는 지점들을 담은 값
  • defaultLocale: 초기 설정된 지점

getServerSideProps는 다음의 값을 가지고 있는 객체를 반환한다.

  • props(선택)
  • notFound(선택) -> boolean값이다.
  • redirect: getStaticProps와 같은 설명이다. getStaticProps설명을 읽자

유념해야할 것은 지금까지 공부한 getStaticProps, getStaticPaths, getServerSideProps 모두 서버쪽에서 실행되고 client-side bundle되지 않기 때문에 이를 유념해야하고 그래서 getServerSideProps에서 import한 모듈들은 client-side에 포함되지 않는다.

getServerSideProps는 언제 사용하는게 좋은가?

  • page를 request시 pre-render를 해야하는 경우 사용한다.
  • 당연한 얘기겠지만 TTFB는 getStaticProps보다 느리다.
  • 또한 이럴경우 결과는 특정한 설정없이는 CDN에 캐싱되지 않는다.

GetServerSideProps, Typescript

타입스크립트에서 사용할 경우 GetServerSideProps라는 타입을 추출해서 사용한다.
props를 위한 inferred typing을 하고 싶으면 InferGetServerSidePropsType을 next에서 추출하여 사용하면 된다.

import { InferGetServerSidePropsType } from 'next'

만약 getServerSideProps를 export하게 될 경우 request시 Next.js는 server로 API요청을 보내고 getServerSideProps를 실행한다. 그리고 JSON 객체를 반환 받는다. 그리고 page를 render한다.

client side에서 data 가져오기

만약 데이터가 굉장히 빨리 바뀌고 페이지를 pre-render할 필요가 없다면 client에서 data를 fetch해오면 된다. Next.js에서는 다음과 같은 방법을 권한다.

  • 먼저 페이지를 빠르게 데이터가 없이 보여준다. page의 몇몇 부분들은 정적 생성으로 pre-render할 수 있다. 데이터가 없는 곳에는 loading 상태를 보여줄 수 있을 것이다.
  • 그리고나서 client단에서 데이터를 가져와서 display한다.

SWR

SWR이라고 하는것은 Next.js팀이 만든 데이터를 가져오는 React Hook이다. client에서 데이터를 불러올 경우 이 Hook을 사용하는 것을 권한다. caching, revalidation, focus tracking, refetching 등 많은 기능을 담당한다.

profile
Let your codes speak
post-custom-banner

0개의 댓글