NextJS 공식 문서 정리 (12버전 기준)

공식 문서: Next.js by Vercel - The React Framework

[렌더링]

Learn | Next.js

렌더링: 리액트에서 작성한 코드를 HTML 로 변환하는 것

1 Pre-Rendering

SSR 과 정적 사이트는 Pre-Rendering 이라고도 부른다.

외부 데이터를 fetch해서 리액트의 컴포넌트를 HTML로 변환하기 때문이다.

2 CSR vs. Pre-Rendering

2.1 CSR

초기 렌더링 작업이 사용자 장치에서 발생한다.

브라우저는 UI 를 구성하기 위한 JS 와 비어있는 HTML 을 받는다.

참고: 리액트의 useEffect 를 사용해서 NextJS에서도 CSR를 선택적으로 수행할 수 있다.

2.2 Pre-Rendering

NextJS는 기본적으로 모든 페이지를 미리 렌더링한다.

즉 초기 렌더링이 서버에서 JS를 이용해 이루어진다.

SSR, SSG가 이에 해당한다.

  • Hydrate HTML 과 React 코드들을 연결시켜주는 과정. 처음의 HTML은 React와 연결되어 있지 않아 이벤트 핸들러들이 적용되어 있지 않은 상태이다. HTML DOM 요소 위에 한번 더 JS를 통해 렌더링 하면서, 각자 자기 자리를 찾아가며 매칭된다. HTML DOM 요소에 뒤늦게 자바스크립트가 동작하게 되는 과정이 Hydrate이다. Next.js의 Hydrate란?

3 SSR

HTML 이 각 페이지 요청에 대해서 서버에서 생성된다.

페이지를 인터랙티브(대화형) 으로 만들기 위해 HTML, JSON, JS 가 클라이언트로 전송된다.

클라이언트에서 React를 사용해서 JSON 데이터와 JS 명령을 사용해 컴포넌트를 인터랙티브하게 만든다. (이벤트 핸들러 연결) 이 과정을 Hydration 이라고 부른다.

getServerSideProps 를 사용해 SSR 를 선택적으로 적용할 수 있다.

4 Static Site Generation

애플리케이션이 배포될 때 빌드 시 콘텐츠가 한 번 생성되고 HTML이 CDN에 저장되고 각 요청에 재사용된다.

getStaticProps 를 사용해 페이지를 정적으로 생성할 수 있다.


[CDN 과 Edge]

Learn | Next.js

서버는 애플리케이션 코드가 저장되고 실행되는 메인 컴퓨터이다.

NextJS는 Origin Server, CDN, Edge에 배포할 수 있다.

1 Origin Server

1.2 Origin

CDN 서버와 Edge 서버와 같이 애플리케이션 코드가 배포될 수 있는 다른 장소와 구별하기 위해서 Origin(출처) 라는 용어를 사용한다.

Origin 서버는 요청을 받으면 응답을 보내기 전 계산을 수행하고, 그 계산 결과가 CDN으로 이동할 수 있다.

2 CDN (Content Delivery Network)

CDN은 전 세계 여러 위치에 정적 콘텐츠(HTML, img) 를 저장하고 클라이언트와 Origin 서버 사이에 배치된다.

새 요청이 들어오면 사용자와 가장 가까운 CDN에서 캐시된 결과로 응답한다.

2.1 장점

  • Origin 서버로 모든 요청이 들어오는 것이 아니라, CDN에서 각 요청에 대한 계산을 수행하기 때문에 Origin의 부하가 줄어든다.
  • 응답이 지리적으로 더 가까운 위치에서 제공되기 때문에 사용자가 더 빠르게 작업할 수 있다.
  • NextJS는 Pre-Rendering이 미리 수행될 수 있어서, CDN은 작업의 정적 결과를 저장하는데 매우 적합하다.

3 The Edge

Edge는 사용자에게 가장 가까운 네트워크의 일반화된 개념이다.

CDN은 네트워크의 가장자리(Edge)에 정적 콘텐츠를 저장하기 때문에, Edge의 일부로 간주된다.

CDN과 유사하게 Edge 서버는 전 세계 여러 위치에 배포된다.

하지만 정적 콘텐츠를 저장하는 CDN과 달리 Edge서버는 코드를 실행할 수 있다.

즉, 사용자에게 더 가까운 Edge에서 캐싱과 코드 실행을 모두 수행할 수 있다.

Edge에서 코드를 실행하면 전통적으로 클라이언트나 서버에서 수행하던 작업을 Edge에서 수행할 수 있다.

→ 클라이언트로 전송되는 코드양은 감소하고, 사용자 요청이 Origin 서버에서 계산될 필요가 없으므로 애플리케이션 성능이 향상되어 대기 시간이 줄어든다.


[React의 문제점과 NextJS의 도입]

https://nextjs.org/learn/basics/create-nextjs-app

1 React의 번거로운 부분

리액트는 애플리케이션을 처음부터 빌드하려면 신경써야하는 세부사항이 많다.

  1. Webpack으로 번들링 & Babel 컴파일러로 JSX → JS 로 변환
  2. 최적화 (Code Splitting 등)를 해야함
  3. Static Pre-Rendering (성능과 SEO를 위해) / SSR 또는 CSR 를 해야함
  4. 리액트 앱과 데이터 저장소를 연결하기 위한 서버 측 코드 작성

2 NextJS: React 프레임워크

NextJS는 React 의 번거로운 점을 간편하게 해결하였다.

  1. 직관적인 페이지 기반 라우팅 (Dynamic routes 도 지원함)
  2. Pre-rendering, static generation (SSG), server-side rendering (SSR) 이 페이지 단위로 제공됨
  3. 자동 Code Splitting
  4. prefetching을 통한 최적화된 CSR
  5. Fast Refresh
  6. 서버리스 함수로 API 엔드포인트를 빌드하기 위한 API routes
  7. 확장성

[Pages]

1 Link

  • 페이지간의 탐색을 CSR으로 진행할 수 있음.
  • Code Splitting이 페이지 별로 진행 되어 각 페이지에서 필요한 것만 로드할 수 있도록 함
  • 특정 페이지에서 오류가 발생하더라도 나머지 앱은 정상 작동 함
  • 백그라운드에서 Link로 연결된 페이지에 대한 코드를 prefetch 하여 페이지 전환이 빠름
  • 라우팅 라이브러리가 필요하지 않음.

[Assets, Metadata, CSS]

1 Assets

1.1 public 디렉토리

  • 정적 데이터를 제공하는 경우 최상위 디렉토리의 public 디렉토리를 이용하면 됨
  • robots.txt (Goggle Site Verification) 에도 유용함
  • 이미지 경로는 public 폴터가 / 로 매칭됨.
    <img src="/images/profile.jpg" alt="Your Name" />

1.2 최적화 되지 않은 이미지

일반 HTML 의 img 를 사용하면 다음을 수동으로 처리해야한다.

  • 다양한 화면 크기에 이미지가 반응형으로 적용되는지
  • 다른 툴 또는 라이브러리로 이미지 최적화
  • 뷰포트에 들어갈 때만 이미지 로드
  • 등등

1.3 Image Component - 이미지 자동 최적화

next/image 는 HTML의 img 의 확장

  1. 이미지 최적화가 자동으로 이뤄지며, CMS 같이 외부 데이터인 경우에도 최적화가 가능하다.

    • 이미지 최적화
      • 이미지 크기 조정, 최적화와 최신 이미지 포멧 (WebP) 를 제공한다.
      • 작은 viewport 의 기기에서 큰 이미지를 불러오는 것을 방지한다.
      • 자동으로 이미지 포맷을 선택하고 브라우저가 지원 가능한 포맷으로 이미지를 제공한다.

    최적화는 사용자가 요청할 때 on-demand 로 적용된다. 빌드 시 적용되지 않기 때문에 몇 개의 이미지를 제공하던 빌드 시간이 증가하지 않는다.

  2. 이미지는 기본적으로 Lazy Loading된다.

    즉, 뷰포트 밖의 이미지는 페이지 속도에 영향을 주지 않는다. 이미지는 뷰포트로 스크롤 될 때 로드된다.

import Image from 'next/image'

const YourComponent = () => (
  <Image
    src="/images/profile.jpg" // Route of the image file
    height={144} // Desired size with correct aspect ratio
    width={144} // Desired size with correct aspect ratio
    alt="Your Name"
  />
)

2 Head Component - Meta data

2.1 페이지별 메타 데이터

HTML의 title 태그와 같은 메타 데이터를 수정하기 위해서 Head 컴포넌트를 제공한다.

  • og:image : 공유 했을 때 썸네일
import Head from 'next/head'

export default function FirstPost() {
  return (
    <>
      <Head>
        <title>First Post</title>
      </Head>
      <h1>First Post</h1>
      <h2>
        <Link href="/">
          <a>Back to home</a>
        </Link>
      </h2>
    </>
  )
}

3 타사 JavaScript

NextJS 에서 타사 스크립트를 추가하기

3.1 일반 HTML의 script 를 사용한 경우

  • 이 방법은 동작은 하지만, 동일한 페이지에서 가져온 다른 JS 코드와 관련하여 로드될 시기를 명확하게 알 수 없다.
  • 그리고 특정 스크립트가 렌더링을 차단하여 콘텐츠 로드를 지연시키는 경우 성능에 영향을 미칠 수 있다.
<Head>
  <title>First Post</title>
  <script src="https://connect.facebook.net/en_US/sdk.js" />
</Head>

3.2 Script Component - 최적화된 가져오기

next/script 는 HTML 의 script 요소의 확장이다.

추가 스크립트를 가져와 실행할 때 최적화가 적용된다.

  • Script 를 적용한 예제
import Script from 'next/script'

export default function FirstPost() {
  return (
    <>
      <Head>
        <title>First Post</title>
      </Head>
      <Script
        src="https://connect.facebook.net/en_US/sdk.js"
        strategy="lazyOnload"
        onLoad={() =>
          console.log(`script loaded correctly, window.FB has been populated`)
        }
      />
      <h1>First Post</h1>
      <h2>
        <Link href="/">
          <a>Back to home</a>
        </Link>
      </h2>
    </>
  )
}
  • Script 의 propperties
    1. strategy
      • 타사 스크립트를 로드해야 하는 시기를 제어
      • value로 layzoOnload 는 특정 스크립트를 느리게 로드하도록 지정하는 것
    2. onLoad
      • 스크립트 로드가 완료된 직후 실행할 함수

4 CSS

  • NextJS에는 CSS, Sass 가 내장되어 있음

4.1 styled-jsx

NextJS 에 내장되어 있는 CSS-in-JS 라이브러리

<style jsx>{`
  …
`}</style>
  • 리액트 컴포넌트 내에서 CSS를 작성할 수 있게 해줌
  • CSS 스타일의 스코프가 해당 컴포넌트 내로 한정됨

4.2 CSS 작성 및 import

  • NextJS 는 CSS와 Sass 가 내장되어 있어서, .css.scss 를 import 할 수 있다.
  • 모듈로 스타일링 파일을 관리할 수 있다. 즉, 고유한 클래스 이름으로 자동 생성한다.
  • Code Splitting 는 CSS 모듈에서도 적용된다.
  • CSS 모듈은 빌드시 JS 번들에서 추출되어 자동으로 로드되는 .css 파일을 만든다.

components/layout.module.css

.container {
  max-width: 36rem;
  padding: 0 1rem;
  margin: 3rem auto 6rem;
}

components/layoust.js

import styles from './layout.module.css'

export default function Layout({ children }) {
  return <div className={styles.container}>{children}</div>
}

4.3 _app.js - 전역 스타일

  • _app.js
    • pages/_app.js 파일에서 전역 스타일링을 할 수 있다.
    • 다른 모든 페이지에서 공통적으로 사용되는 최상위 컴포넌트이다.
    • 전역 CSS 코드는 _app.js 에서만 import 할 수 있다.
// pages/_app.js 
import '../styles/global.css'

export default function App({ Component, pageProps }) {
  return <Component {...pageProps} />
}

[Pre-rendering & Date Fetching]

1 Pre-rendering

NextJS는 각 페이지에 대해서 미리 HTML 을 생성한다.

이는 더 나은 성능과 SEO를 제공할 수 있다.

생성된 각 HTML은 해당 페이지에 필요한 최소한의 JS 와 연결된다.

브라우저에서 페이지를 로드하면 해당 JS 코드가 실행되고 페이지가 대화식으로 만들어진다. (hydration)

1.1 Pre-rendering 인지 확인하는 법

2 SSR, SSG

Pre-rendering 안에 SSR, SSG 가 포함된다.

2.1 SSG (Static-site-generate)

  • Static Generation is the pre-rendering method that generates the HTML at build time. The pre-rendered HTML is then reused on each request.
  • SSG는 build 타임에 HTML 이 생성된다. 매 요청마다 이미 만들어진 HTML을 재사용한다.
  • getStaticProps

2.2 SSR (Server-side-rendering)

  • Server-side Rendering is the pre-rendering method that generates the HTML on each request.
  • SSR은 서버에서 요청이 들어올 때마다 HTML을 생성한다.
  • getServerSideProps

2.3 SSG vs. SSR

SSG가 SSR 보다 빠르기 때문에 가능하다면 SSG를 사용하는 것이 좋다.

하지만 SSG는 요청에 따른 최신화된 데이터를 사용하지 못하기 때문에 그런 경우에는 SSR를 사용해야한다.

2.4 Page 단위로 SSG, SSR 적용

NextJS는 페이지 단위로 SSG와 SSR을 적용할 수 있다.

필요에 따라서 선택해서 사용하자.

3 SSG (Static-site-generate)

3.1 getStaticProps

  • getStaticProps는 빌드시에만 실행되는 함수이다.
  • 비동기함수로 데이터를 불러와서 props로 컴포넌트들에게 전달할 수 있다.
export default function Home(props) { ... }

export async function getStaticProps() {
  // Get external data from the file system, API, DB, etc.
  const data = ...

  // The value of the `props` key will be
  //  passed to the `Home` component
  return {
    props: ...
  }
}

3.2 유의사항

  1. getStaticProps 에서 외부 API 나 Query database를 Fetch할 수 있다.
  2. Development vs. Production
    • development: getStaticProps 는 모든 요청에 대해서 실행됨
    • production: getStaticProps 는 빌드타임에만 실행됨. 하지만 getStaticPaths 가 반환한fallback key를 사용해서 향상될 수 있음.
  3. 페이지 안에서만 사용 가능
  4. 매 요청마다 pre-rendering은 할 수 없다. 이 때에는 SSR를 사용하자.

4 SSR (Server-side Rendering)

매 요청마다 HTML을 만들어서 제공하고 싶다면 SSR를 사용해야한다.

4.1 getServerSideProps

  • 매 요청마다 호출되므로, context 에는 요청별 매개변수가 포함된다.
  • 요청시 페이지가 pre-rendering 되어야 하는 경우에 사용한다.
    • getStaticProps 보다는 TTFB(Time to first byte) 가 느리다 매 요청마다 서버는 계산하고, 그 결과는 추가 구성 없이는 CDN에 캐싱될 수 없기 때문.
export async function getServerSideProps(context) {
  return {
    props: {
      // props for your component
    }
  }
}

5 CSR (Client-side Rendering)

pre-rendering 할 필요가 없는 경우에, CSR 방법을 사용할 수 있다.

  • 외부 데이터가 필요하지 않는 페이지 부분을 SSG한다.
  • 페이지가 로드 되면 JS를 사용해서 외부 데이터를 가져오고 나머지 부분을 채운다.

SSG + CSR 의 조합

5.1 사용 용도

  • user dashboard 페이지
    • 사용자별 비공개 페이지라서 SEO와 상관 없다.
    • 따라서 페이지를 미리 렌더링 할 필요가 없다.
    • 데이터가 자주 업데이트 된다.

6 SWR

  • SWR 은 data fetching React hook 이다.
  • 클라이언트 측에서 데이터를 fetching 하는데 사용하는걸 권장한다.
  • Caching, Revalidation, Focus tracking, Refetching on interval, and more...
import useSWR from 'swr'

function Profile() {
  const { data, error } = useSWR('/api/user', fetch)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

[Dynamic Routes]

https://nextjs.org/learn/basics/dynamic-routes

URL 이 페이지의 데이터에 따라 달라지는 것을 원하면, 동적 경로(Dynamic Routes) 를 사용해야한다.

1 Page Path

페이지 경로가 외부 데이터에 따라 달라지는 경우

즉, 외부 데이터에 의존적인 경로를 가진 페이지를 pre-render 할 수 있다.

1.1 Dynamic Routes로 페이지를 정적으로 생성하기

  1. pages/posts/[id].js 파일을 생성한다.

  2. 다른 페이지와 마찬가지로 페이지를 렌더링하는 코드를 작성한다.

  3. getStaticPaths 함수를 내보낸다. 이 함수는 id 로 가능한 값을 반환한다.

    비동기 함수라서 async 를 꼭 작성해준다.

    https://nextjs.org/docs/basic-features/data-fetching/get-static-paths

    import Layout from '../../components/layout'
    
    export default function Post() {
      return <Layout>...</Layout>
    }
    
    export async function getStaticPaths() {
      return {
        paths: [
          { params: { ... } }
        ],
        fallback: true // false or 'blocking'
      };
    }
  4. getStaticProps 함수를 내보내며 외부 데이터를 빌드시 fetch한다.

1.2 getStaticPaths 구현

  • pathsfallback 이 담긴 객체를 반환한다.
    • paths
      • 반드시 객체를 담은 배열이어야 한다.
      • 각 객체는 params key를 가져야 하며, id(파일이름) 키를 가진 객체를 포함해야 한다.
      • 만약 id 의 값이 배열이면, 포괄 경로로 생성된다. /posts/a/b/c
    • fallback
      • false : getStaticPaths 로 반환되는 path가 아닌 경우엔 404 페이지를 보여준다
      • true: 빌드타임에 만들어진 path가 아니더라도 404 페이지를 보여주지 않는다. 대신 fallback 버전 페이지를 처음 요청때 제공한다. getStaticProps가 background 에서 실행된다. 즉 요청된 path에 대해서 정적으로 페이지를 생성한다. 이후에 동일한 경로로 요청이 들어온 경우에 페이지가 빌드타임때 pre-rendering 된 페이지와 마찬가지로 생성된 페이지를 제공한다.
      • blocking: getStaticProps가 초기 렌더하기 전에 호출된다. 새로운 path가 getStaticProps 와 함께 SSR 으로 생성되고, 향후 요청을 위해서 캐싱한다.
export function getAllPostIds() {
  const fileNames = fs.readdirSync(postsDirectory)

  // Returns an array that looks like this:
  // [
  //   {
  //     params: {
  //       id: 'ssg-ssr'
  //     }
  //   },
  //   {
  //     params: {
  //       id: 'pre-rendering'
  //     }
  //   }
  // ]
  return fileNames.map(fileName => {
    return {
      params: {
        id: fileName.replace(/\.md$/, '')
      }
    }
  })
}

export async function getStaticPaths() {
  const paths = getAllPostIds()
  return {
    paths,
    fallback: false
  }
}

1.3 getStaticPaths 주의점

  • 반드시 getStaticProps 와 함께 사용해야한다. 즉, SSG 인 경우에 사용한다. getServerSideProps 와 사용하면 안된다.

1.4 언제 사용하는가

  • 데이터를 headless CMS 에서 가져오는 경우
  • 데이터가 database에서 가져오는 경우
  • 데이터가 파일시스템에서 가져오는 경우
  • 데이터가 캐싱될 수 있고, user-specific하지 않은 경우
  • 페이지가 SEO를 위해서 pre-rendered 되야 하는 경우, 또는 매우 빨라야 하는 경우

1.5 Router

useRouter hook 으로 라우터 정보를 가져올 수 있다.

1.6 404 페이지

404 이름의 페이지를 만들면 된다. 이 파일은 반드시 정적으로 생성된다.

// pages/404.js
export default function Custom404() {
  return <h1>404 - Page Not Found</h1>
}

[API Routes]

https://nextjs.org/learn/basics/api-routes/creating-api-routes

https://nextjs.org/docs/api-routes/introduction

1 API Routes란

https://ppsu.tistory.com/67

API Routes를 사용해 NextJS 앱 내에서 API 엔드포인트를 생성할 수 있다.

이는 서버리스 함수(람다)로 배포할 수 있다.

pages/api 디렉토리에 함수를 생성하여 이를 수행할 수 있다.

API Routes 코드는 클라이언트 번들에 포함되지 않아서 서버 측 코드를 안전하게 작성할 수 있다.

// req = HTTP incoming message, res = HTTP server response
export default function handler(req, res) {
  // ...
}
  • req : http.IncommingMessage 의 인스턴스

  • res : http.ServerResponse 의 인스턴스

  • 예시 pages/api/hello.js

    export default function handler(req, res) {
      res.status(200).json({ text: 'Hello' })
    }

    http://locallhost:3000/api/hello 로 접근하면 아래의 json을 응답한다.

    {"text":"Hello"}

2 API Routes 디테일

getStaticProps , getStaticPaths 에서 API 라우트를 fetch하지 마시오

  • API route를 getStaticProps, getStaticPaths 에서 fetch하면 안된다.
  • 대신에 getStaticProps, getStaticPaths 안에 직접 Server-side 코드 또는 헬퍼함수 호출을 작성해야한다.
  • getStaticProps, getStaticPaths 는 빌드타임 때만 실행되고 클라이언트 측에서 실행되지 않는다. 또한 이 함수들은 브라우저를 위한 JS 번들에 포함되지도 않는다. 즉, 직접 DB 쿼리와 같은 코드를 브라우저에 보내지 않고 작성할 수 있다는 뜻이다.

3 API Routes의 좋은 사례

  • 들어오는 데이터를 데이터베이스에 저장할 때
  • 타사 API와 안전하게 통신할 때
  • CMS에서 초안 콘텐츠를 미리 볼 때

좋은 웹페이지 즐겨찾기