# nextjs

## Next.js란?

* `Next.js`는 react application에서 `Isomorphic Rendering`을 쉽게 해주는 React framwork입니다.
* csr(Client-Side-Rendering)을 하게되면 구글을 제외한 검색봇들이 csr application을 크롤링할 수 없어서 seo 문제가 생기며, 메타 데이터나 오픈 그래프 태그도 JS가 생성하기 때문에 SNS 공유하기에도 문제가 생깁니다.
* ssr(Server-Side-rendering)을 하게되면 서버의 리스폰스에 의존해서 페이지를 이동해야하기 때문에 페이지 이동시 fouc가 발생하며 spa에 비해 퍼포먼스가 떨어집니다.
* Isomorphic(Universal) Rendering을 하게되면 최초 리퀘스트의 응답은 ssr하고 그이후에는 csr와 동일하게 동작해서 SEO와 UX 모두를 충족할 수 있습니다.

## getInitialProps(Next.js 9.3 버전이후 부터는 권장 하지않음)

`getInitialProps` 함수는 `pages` 디렉토리 안에 있는 컴포넌트에서만 사용할 수 있는 스태틱 메소드입니다.

`Next.js`는 `pages` 디렉토리안 파일에서 `getInitialProps`나 `getServerProps` 함수를 사용하고 있으면 빌드 타임에 ssr를 적용할 수 있게 `html`이 아니라 `js`로 빌드합니다.

`getInitialProps` 함수는 해당 페이지가 초기 로딩 페이지면 서버에서 호출되며, 아닐경우 클라이언트에서 호출됩니다.

`getInitialProps` 함수의 인자로는 `context`객체가 전달되는데 해당 객체는 `pathname`, `query`, `req(ssr only)`, `res(ssr only)`, `err` 등의 프로퍼티를 갖고 있습니다.

`getInitialProps` 함수의 리턴 값은 해당 컴포넌트의 첫 번째 인자로 전달됩니다.

```javascript
import fetch from "isomorphic-unfetch";

function Page({ stars }) {
  return <div>Next stars: {stars}</div>;
}

Page.getInitialProps = async (ctx) => {
  const res = await fetch("https://api.github.com/repos/zeit/next.js");
  const json = await res.json();
  return { stars: json.stargazers_count };
};

export default Page;
```

## getStaticProps

`getInitialProps` 함수는 빌드 타임에 데이터를 가져오고 싶을 때 사용할 수 있습니다

`getInitialProps` 함수는 빌드 될 때 서버 사이드에서 한번만 호출됩니다.

`getServerSideProps, getInitialProps`와 달리 해당 함수를 사용해도 `Next.js`는 페이지를 ssr하지 않습니다.

```javascript
import fetch from "node-fetch";

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  );
}

export async function getStaticProps() {
  const res = await fetch("https://.../posts");
  const posts = await res.json();

  return {
    props: {
      posts,
    },
  };
}

export default Blog;
```

## getStaticPaths

`getStaticPaths` 함수는 다이나믹 라우팅을 하면서 `getStaticProps` 를 사용하는 페이지가 빌드 타임에 어떤 `params`에 해당하는 페이지를 프리 렌더링 할지 결정할려고 사용합니다.

`getStaticPaths` 함수를 사용하는 이유는 `Next.js` 입장에서는 해당 함수를 이용해 `paths`를 전달 해주지 않으면 `params`에 어떤 값이 들어올지 모르기 때문에 프리 렌더링을 하기위해 필수로 사용해야 합니다.

`getStaticPaths` 함수는 `paths`와 `fallback` 프로퍼티를 갖고있는 객체를 리턴해야합니다.

`fallback` 프로퍼티가 `true`면 `paths` 에 없는 경로에 방문 했을 때 `fallback` 페이지를 보여주고, 백그라운드로 `getStaticProps`와 같이 `params`에 해당 하는 페이지를 생성해서 사용자에게 보여줍니다.

해당 프로퍼티가 `false` 일 경우 `paths` 에 없는 경로에 방문 했을 때 그냥 404 페이지를 보여줍니다.

```javascript
import fetch from 'node-fetch'

const Post = () => {
  ...
}

export async function getStaticPaths() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  const paths = posts.map(post => ({
    params: { id: post.id },
  }))

  return { paths, fallback: false }
}

export async function getStaticProps({ params }) {
  ...
}

export default Post
```

## getServerSideProps

`getServerSideProps` 함수는 ssr을 할려고 할 때 사용합니다.

`getServerSideProps` 함수는 `getStaticProps` 와 달리 해당 페이지 경로로 리퀘스트가 올 때 마다 호출됩니다.

`getServerSideProps` 함수는 항상 서버 사이드에서만 호출됩니다.

```javascript
function Page({ data }) {
  ...
}

export async function getServerSideProps({ params }) {
  const res = await fetch(`https://.../${params.id}`)
  const data = await res.json()

  return { props: { data } }
}

export default Page
```

## Routing

`pages` 디렉토리 안에 파일을 생성하면 자동으로 파일명에 해당하는 주소로 접속할 수 있습니다.

* `pages/index.js` → `/`
* `pages/blog/first-post.js` → `/blog/first-post`
* `pages/blog/[id].js` → `/blog/[id]`
* `pages/blog/[...all].js` → `/blog/*`

### Dynamic Routing

만약 동적으로 라우팅을 해야할 경우 파일명을 `[id]`와 같이 대괄호로 묶어서 생성하면 다이나믹 라우팅 기능을 이용할 수 있습니다.

해당 `id` 값은 `router` 객체나 `context` 객체의 `query` 파라미터로 조회할 수 있습니다.

`Link` 컴포넌트에서 다이나믹 라우팅을 사용할려면 `href` prop에 파일명을 넣고 `as` prop에 이동할 주소를 넣어야합니다. 굳이 `href`를 넘겨주는 이유는 `Next.js`가 사전에 컴포넌트를 `preload` 하기위해 넘겨줘야 합니다.

```javascript
import Link from "next/link";

const index = () => (
  <Link href="/blog/[id]" as="/blog/test-post">
    test-post link
  </Link>
);
```

위 예시의 `pages/blog/first-post.js`와 `pages/blog/[id].js`처럼 미리 정의된 주소가 동적 라우팅 주소에도 해당 될 수 있을 경우, 미리 정의된 주소(`first-post.js`)가 우선 순위를 갖습니다.

또한 라우트 파라미터와 쿼리 파라미터가 동일하다면 라우트 파라미터가 우선 순위를 갖습니다.

```javascript
import Router from "next/router";

// query 파라미터의 프로퍼티로는 2가 아니라 "second-post"가 들어감
// push메소드는 첫 번째 인자로 href를 받고 두 번째 인자로 as 마지막 세 번째 인자로 option를 받습니다
Router.push("/blog/[id]", "/blog/second-post?id=2");
```

### Shallow Routing

동일한 `pathname`에서 리렌더링 하지않고 쿼리 스트링만 바꾸고 싶을 때는 `Router.push`메소드에 `shallow` 옵션을 `true`로 전달하면 `Shallow Routing`을 진행합니다.

```javascript
import Router from "next/router";

Router.push("/blog/first-post?id=5", null, { shallow: true });
```

`Shallow Routing`을 할 경우 `getInitialProps` 함수는 실행되지 않습니다.

## Dynamic Import

`Dynamic Import` 를 할려면 `next/dynamic` 모듈에서 `default export`된 `dynamic` 함수를 이용하면 됩니다.

```javascript
import dynamic from "next/dynamic";

// default export된 컴포넌트를 다이나믹 임포트
const DynamicComponent = dynamic(() => import("../components/hello"), {
  loading: <Loader />,
});

// Hello라는 이름으로 named default된 컴포넌트를 다이나믹 임포트
const DynamicNamedComponent = dynamic(() =>
  import("../components/hello").then((module) => module.Hello)
);

function Home() {
  return (
    <>
      <DynamicComponent />
      <DynamicNamedComponent />
    </>
  );
}

export default Home;
```

## Custom Error Page

### Customizing The 404 Page

기본적으로 `Next.js`가 제공해주는 404 에러 페이지를 커스터마이징 할려면 `/pages` 디렉토리에 `404.js` 파일을 생성하면 됩니다.

```javascript
// pages/404.js
export default function Custom404Page() {
  return <h1>페이지를 찾을 수 없습니다.</h1>;
}
```

### Customizing The Error Page

404 에러외에도 다양한 에러를 표시해주는 페이지를 커스터마이징 할려면 `/pages` 디렉토리에 `_error.js` 파일을 생성하면 됩니다.

만약 `_error.js`와 `404.js`파일이 둘다 존재하면 `404.js`파일이 우선 순위를 갖습니다.

```javascript
function Error({ statusCode }) {
  return (
    <p>
      {statusCode
        ? `An error ${statusCode} occurred on server`
        : "An error occurred on client"}
    </p>
  );
}

Error.getInitialProps = ({ res, err }) => {
  const statusCode = res?.statusCode ?? err?.statusCode ?? 404;
  return { statusCode };
};

export default Error;
```

## Custom Document

일반적인 SPA `React.js`앱과 다르게 `Next.js` 앱에서는 `index.html` 파일이 없기때문에

Custom Document는 `Next.js`가 마운트(Server-Side) 되는 지점을 커스터마이징 할려고 사용합니다.

Custom Document는 주로 애플리케이션의 `<html>`, `<body>`, `<head>` 태그를 마크업하는 용도나 서버 사이드 렌더링 환경에서 `css-in-js` 동작을 구현하기 위해 사용합니다.

Custom Document는 `pages/_document.js`경로에 파일을 생성해서 만들 수 있습니다.

주의할 점으로는 custom Document에서는 `<title>` 태그를 가질 수 없고, `Data Fetching`과 `Lifecycle`을 사용할 수 없습니다.

```javascript
import Document, { Html, Head, Main, NextScript } from "next/document";

class MyDocument extends Document {
  render() {
    return (
      <Html lang="ko">
        <Head>
          <meta
            name="viewport"
            content="initial-scale=1.0, width=device-width"
            key="viewport"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;
```

### Custom App

`Next.js`는 페이지를 초기화할 때 `App` 컴포넌트를 사용합니다.

그래서 `Document`와 달리 `App` 컴포넌트는 페이지가 이동될 때 마다 사용됩니다.

해당 `App` 컴포넌트를 커스터마이징 할려면 `pages/_app.js` 경로에 Custom App를 생성해서 커스터마이징할 수 있습니다.

Custom App는 주로 모든 페이지들의 공용 레이아웃을 정의하거나, Global CSS를 추가할 때 사용합니다.

만약 Custom App에 `getInitialProps` 함수를 사용하면 [Automatic Static Optimization](https://nextjs.org/docs/advanced-features/automatic-static-optimization) 기능이 비활성화 됩니다.

```javascript
// component: 현재 페이지 컴포넌트
// pageProps: getInitialProps, getServerSideProps등으로 preloaded된 페이지 props
function MyApp({ Component, pageProps }) {
  return (
    <div>
      hello world!
      <Component {...pageProps} />
    </div>
  );
}

export default MyApp;
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lsw0150305.gitbook.io/til/react/nextjs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
