4 minute read

[nest-next] 같이 참고하시면 좋을 nest.js + next.js기반 프로젝트 실습 포스트입니다.

1. Next.js

Next.js 리액트 기반의 프레임워크입니다. Next.js를 사용하면, CSRSSR을 혼합하여 빠른 성능을 가진 애플리케이션을 개발할 수 있습니다. 주요 특징은 다음과 같은데요, 렌더링과 관련해서는 제일 아래에 자세하게 정리해보겠습니다.

1-1. typescript 지원

npm이나 yarn으로 간단하게 typescript를 설치하는 것만으로도 타입스크립트 코딩이 가능합니다.

$ yarn add typescript @types/node @types/react

1-2. Hot Code Reloading

개발과정에서 코드 수정 후 저장하면 애플리케이션을 자동으로 로드합니다.

1-3. Routing

Next.js에는 페이지를 기반으로한 라우터 시스템이 있습니다. 파일이 pages폴더에 추가되면, 자동으로 경로로 사용할 수 있습니다. 예를 들어서 다음과 같은 라우팅이 가능합니다.

  • pages/index.js → localhost:3000
  • pages/blog/index.js → localhost:3000/blog
  • pages/blog/first-post.js → localhost:3000/blog/first-post
  • pages/blog/[slug].js→ localhost:3000/blog/:slug(ex. localhost:3000/blog/hello-world)
  • pages/[username]/settings.js→ localhost:3000/:username/settings(ex. localhost:3000/foo/settings)

1-4. Pre-rendering

기본적으로 Next.js는 모든 페이지를 pre-rendering합니다. 즉, 각 페이지에 대해 미리 HTML을 생성합니다. 이것의 장점은,

  • 더 나은 성능
  • SEO (검색 엔진 최적화) - 서버에서 컴파일되어 넘어오기에 검색엔진에서 쉽게 페이지를 크롤링할 수 있다는 것

입니다. 페이지가 브라우저에 의해 로딩되고, 자바스크립트 코드가 동작하면 페이지는 완전히 사용자와 상호작용 할수 있게 됩니다.

Next.js는 두가지 형태의 pre-rendering을 제공하고 있습니다.

1. Static Generation과,

2. Server-side Rendering인데,

지금부터 하나씩 살펴보겠습니다.

The HTML is generated at build time and will be reused on each request.

Next.js는 Static Generation(정적 생성) 방식을 추천하고 있는데, 이것은 성능상의 이유입니다. 정적으로 생성된 페이지는 성능 향상을 위한 추가 구성 없이 CDN에서 캐시할 수 있습니다. 이 방식을 사용하는 경우 페이지 HTML은 빌드 -프로덕션에서 next build을 실행-시에 생성됩니다.

사용자의 요청에 앞서 렌더링할 수 있는 페이지(변화가 적고, 반복적으로 동일 데이터를 사용하며, 사용자 개별적이지 않은 페이지겠지요?)라면, Static Generation이 추천되고, 그 예는 다음과 같습니다.

  • 마케팅 페이지
  • 블로그 게시물 및 포트폴리오
  • 전자상거래 제품 목록
  • 도움말 및 문서

2-1. Static Generation without data

미리 렌더링할 외부 데이터를 가져올 필요가 없는 경우입니다. 이와 같은 경우 Next.js는 빌드 시 페이지당 하나의 HTML 파일을 생성합니다.

function About() {
  return <div>About</div>
}

export default About


2-2. Static Generation with data

렌더링을 위해 외부 데이터를 가져와야 하는 경우입니다. 두 가지 시나리오가 있으며 하나 또는 둘 다 적용될 수 있습니다.

1) getStaticProps

  • 페이지에 들어갈 콘텐츠가 외부 데이터에 의존하는 경우

getStaticProps 함수는 빌드 시 호출되며 가져온 데이터를 pre-rendering시에 props로 페이지에 전달할 수 있습니다.

function Blog({ posts }) {
  // Render posts...
}

// This function gets called at build time
export async function getStaticProps() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts,
    },
  }
}

export default Blog


2) getStaticPaths

  • 페이지의 경로가 외부 데이터에 의존하는 경우

1-3에서 라우팅을 짧게 살펴봤었는데요, Next에서는 동적 경로가 있는 페이지를 만들 수 있습니다.

무슨 말인가 하면, pages폴더에 posts폴더를 만들고, 그 안에 [id].js라는 파일을 만듭니다. pages/posts/[id].js 이렇게요. 그러면 페이지기반의 라우팅이 가능하므로, localhost:3000/posts/1 을 입력하면 1번 아이디를 가진 포스트의 내용이 나오고, localhost:3000/posts/2를 입력하면 2번 아이디를 가진 포스트의 내용이 나오게 됩니다.

이렇게 외부 데이터에 따라서 페이지의 경로가 바뀌는 경우 getStaticPaths를 사용합니다. 이 함수는 빌드 시 호출되며 pre-rendering할 경로를 지정할 수 있습니다.

또한 pages/posts/[id].js에서, getStaticProps를 export하여 해당 id의 포스트와 관련된 데이터를 fetch 하여 렌더링에 사용합니다. 다음은 예시 코드이며, 출처는 [NEXT공식 문서-페이지(클릭)] 입니다.

function Post({ post }) {
  // Render post...
}

// This function gets called at build time
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 }
}

// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  // If the route is like /posts/1, then params.id is 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // Pass post data to the page via props
  return { props: { post } }
}

export default Post


3. Server-side Rendering

The HTML is generated on each request.

서버사이드렌더링은 짧게 SSR이라고 하거나, 동적렌더링(Dynamic Rendering)이라고도 불립니다. 페이지가 Server-side Rendering을 사용하는 경우, 페이지 HTML은 각 요청마다 생성됩니다 .

참고! Next.js에서 제공하는 SSR기능을 보기전에 SSR과 그에 대비되는 개념인 CSR에 대해서도 알고 넘어가면 좋을 것 같은데요, 블로그 두 군데를 추천합니다. 1. 우비블로그(클릭), 2. tunggary(클릭)

1) getServerSideProps

SSR에서는 async함수인 getServerSideProps을 사용합니다. 이 함수는 페이지에서 요청이 일어날 때마다 서버에 의해 호출됩니다.

예를 들어 페이지에서 자주 업데이트되는 데이터를 미리 렌더링해야 한다고 가정합니다. getServerSideProps이 데이터를 가져와 Page에 아래와 같이 전달합니다.

function Page({ data }) {
  // Render data...
}

// This gets called on every request
export async function getServerSideProps() {
  // Fetch data from external API
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  // Pass data to the page via props
  return { props: { data } }
}

export default Page

4. Summary

여기까지 Nest.js에서 제공하는 두가지 형태의 pre-rendering을 알아보았습니다. 아래와 같이 요약할 수 있을 것 같습니다.

  1. Static Generation (Recommended)
    • HTML 파일이 빌드시에 생성
    • getStaticProps사용 (필요시 getStaticPaths도 함께 사용)
    • 사용자의 요청에 앞서서 렌더링 될 수 있는 페이지 생성에 유리함
    • 추가적인 데이터를 가져오기 위해 CSR(클라이언트 쪽 자바스크립트로 렌더링)을 함께 사용할 수 있음
  2. Server-side Rendering
    • HTML 파일이 요청마다 생성
    • getServerSideProps사용.
    • Static Generation보다 느린 성능을 보이므로, (데이터의 업데이트가 자주있는 경우 등 )필요한 경우에만 사용

어떤 페이지에는 정적 생성 방식을 사용하고, 다른 페이지에는 SSR(혹은 CSR)을 사용하여 hybrid Next.js앱을 만들 수도 있습니다. 즉, 한가지 방식을 고집하기 보다는 Static Generation과 SSR, CSR을 적재 적소에 사용하는 것이 중요합니다.

감사합니다^^

참고

[NEXT공식 문서-라우팅]

[NEXT공식 문서-페이지]


광고


Comments