> For the complete documentation index, see [llms.txt](https://taewoongs-organization.gitbook.io/jtwjs-dev-wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://taewoongs-organization.gitbook.io/jtwjs-dev-wiki/dev_note/next.js/next-13.md).

# Next 13

## Next 13

버전업이 되면서 기존 프로젝트를 마이그레이션하는 경우 `Breaking Changes`로 인해 전반적으로 변경사항에 맞춰 업그레이드를 해줘야 한다.

Next는 이것을 방지하고자 새로 추가된 기능과 규칙들을 `app/` 에 적용하여 기존 `page/` 구조의  앱이 망가지지 않고 점진적으로 마이그레이션할 수 있도록 제공한다.

{% hint style="info" %}
**Braking Changes**

소프트웨어 시스템의 한 부분이 변경되어 다른 구성 요소에 잠재적으로 장애를 일으키는 것
{% endhint %}

## Routing

```
app
 ├── api
 │   └── hello
 │       └── route.ts
 ├── layout.tsx
 ├── page.tsx
 └── product
     ├── [slug]
     │   └── page.tsx
     ├── layout.tsx
     └── page.tsx
```

매칭할 경로를 폴더 이름으로 두고 하위에 `page` 컴포넌트 파일을 두면 자동으로 라우팅 된다.\
폴더(경로)별로 범위를 좁혀 재사용되는 `레이아웃`, `에러 상태`, `로딩 UI` 등을 공유하여 해당 라우트 안에서 처리해주기 위함

* `page/`  →  `app/`&#x20;
* `Folder & File based routing`

### Dynamic Routing

```tsx
// app/[slug]/page.tsx
interface DynamicPageProps {
  params: {
    slug: string;
  }
}

export default function DynamicPage({ params }: DynamicPageProps) {
  
  return `slug: ${params.slug}`;
}

```

#### `generateStaticParams()`

이전 버전의 `getStaticPaths()` 를 대체하는 함수

미리 생성할 페이지의 동적 경로(slug)를 지정하고 { slug: value } 구조의 배열을 반환하기만 하면 된다.

```tsx
export function generateStaticParams() {
  const slugs = ['a', 'b'];
  return slugs.map((slug) => {
    slug,
  }) 
}
```

## Layout

* 레이아웃은 기본적으로 **서버 컴포넌트**(변경 가능)
* 같은 경로 및 하위 경로의 페이지에 공유되며 래핑된다. → 레이아웃(컴포넌트)
* 경로 별로 레이아웃을 지정 가능하며 상위에 있는 레이아웃에 중첩되어 적용된다.&#x20;
* 데이터 패칭이 가능하며 레이아웃간 전달이 불가능하지만 경로가 같은 경우 각 레이아웃이 중복으로 데이터 패칭시 React가 알아서 중복된 요청을 제거한다.

### Root Layout (필수)

`app/` 내에서 가장 상위에 있는 `layout.tsx` 를 루트 레이아웃이라 칭하며 모든 페이지에 래핑된다.

* 이전 버전의`_app.tsx`, `_document.ts 를 대체한다.`
* 서버에서 반환된 초기 HTML을 수정할 수 있다
* Next.js가 자동으로 `<html>`, `<body>`를  생성하지 않으므로 루트 레이아웃에 해당 태그를 정의해야 한다.

## Not Found Page

커스텀 404 page를 만드려면 파일이름을 `not-found`로 지정해야 한다.

폴더(경로)내에 가장 가까운 `not-found` 파일이 사용되어 페이지 별로 에러 페이지를 설정 가능

**현재 시점에는 `notFound()` 호출에 의해 트리거될 때만 렌더링된다.**

### Global Not Found Page

현 시점 `13.2.4` 버전에서는 **Global Not Found Page** 기능이 `app/` 라우팅에서는 아직 제공되지 않고 있다.

**Global Not Found Page를 구현하려면?**

* 기존 방식대로 `page/` 하위의 `404.tsx` 컴포넌트를 추가해 사용
* `app/ 내에서` 동적 라우팅을 활용하여 구현&#x20;

#### `우회해서 app/` 라우팅으로 Global Not Found Page 구현하기

```
├── [...not-found]
│   └── page.tsx
├── not-found.tsx
```

```tsx
// app/[...not-found]/page.tsx
import {notFound} from 'next/navigation';

export default function NotFoundCatchAll() {
  notFound();
}
```

## Metadata

어떤 페이지(or 레이아웃)에서든  객체 형태로 된 `metadata`  변수를 `export`  해주기만 하면 된다.

```tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: '타이틀',
}
```

### Dynamic Metadata

#### `generateMetadata()` 함수를 통해 비동기 데이터를 패칭 후 메타데이터 객체를 반환한다.

사용자에게 페이지를 주기 전 `generateMetadata()` 호출되고 메타데이터 정보가 완성 되어야지만 페이지를 전송한다.

```tsx
export async function generateMetadata({ params, searchParams }) {
  const data = await getData(params); // 비동기 데이터 패칭
  return { title: data.title };
}
```

### Default Metadata

> 따로 추가하지 않아도 기본적으로 설정되는 메타태그

```html
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
```

## Rendering

> 렌더링 방식이 (page/) 페이지 단위 -> (app/) 컴포넌트 단위로 변경되었다.

### 기존 page 단위 렌더링 방식의 문제점

페이지 단위의 렌더링 방식은 앱의 규모가 커질수록 클라이언트가 받는 번들 자바스크립트 코드가 커지는 문제가 있었다.

이를 해결하기 위해 React 18 버전에 맞춰 **서버 컴포넌트, 클라이언트 컴포넌트**로 구분하여 렌더링 하는 방식이 도입되었다.

### 컴포넌트 단위 렌더링 방식의 장점

하나의 페이지 안에서 여러 컴포넌트를 각기 다른 렌더링 방식을 규정해 조합하여 사용이 가능하다.

서버 컴포넌트는 서버에서만 작업이 이루어지고 서버에서 렌더링되어 클라이언트에게 보낼 코드가 없기 때문에 **클라이언트가 받을 번들 자바스크립트 코드가 현저히 줄어들게 된다.**&#x20;

즉, **사용자의 인터렉션이 필요한 코드만 클라이언트에게 전달되어 초기 로딩 속도가 더욱 빨라지게 된다.**

### [Server Component](https://yceffort.kr/2022/01/how-react-server-components-work)

> React v18에서 새롭게 추가된 기능
>
> 서버상에서 리액트 코드를 작성할 수 있게 되었다.

* **서버상에서만 동작하는 컴포넌트**
* **SSR !== RSC,** 서로 대체제가 아니며 사용자 경험을 향상시키기 위해 서로 보완하여 사용되는 것
  * 프리 렌더링(SSR) + 번들사이즈 최소화(RSC)
* `app/` 내의 모든 컴포넌트는 기본적으로 서버 컴포넌트이다.&#x20;
* 서버에서 빌드 될 때 실행되는 컴포넌트, 기본적으로 SSG
* 정적인 HTML 형태로 클라이언트에게 전달되며 **클라이언트에게 전달되는 자바스크립트 코드는 Zero**
* **브라우저에서 제공하는 API, Hooks를 사용할 수 없지만 node.js(서버)에서 제공하는 API는 사용이 가능하다.**
* 서버 컴포넌트에서 데이터를 공유할 떄 전역 싱글톤과 같은 네이티브 자바스크립트 패턴을 활용
* 서버 컴포넌트 간에 서버(DB)에서 패칭한 데이터 자원을 공유하고 싶을 때는 데이터를 불필요하게 전달할 필요 없이 그 데이터를 필요로 하는 곳에서 패칭하면 된다. 가져온 데이터는 캐싱 되어 넥스트에서 중복 요청을 제거하는 식으로 최적화가 자동으로 이루어진다.

### Client Component

> 이전 버전에서 사용했던 프리렌더링 + 하이드레이션 방식

* 디폴트가 서버컴포넌트이기 때문에 클라이언트 컴포넌트로 사용하기 위해서는 상단에 **`use client`** 를 명시해주어야 한다.
* 클라이언트 컴포넌트라고 해서 무조건 `csr` 로 동작하는 것이 아니다.
* 서버에서 컴포넌트를 읽고 정적인 부분을 분리하여 프리렌더링에 포함시키고 하이드레이션 과정이 일어난다. (기존 방식과 동일)
* 사용자의 이벤트가 있거나 브라우저에서 실행되어야 하는 코드들이 있다면 클라이언트 컴포넌트이다.
* 즉, 사용자(브라우저)에게 보내져야 하는 코드들이 담긴 컴포넌트
* 상태를 쓰거나 hooks를 사용한다면 클라이언트 컴포넌트로 사용해야 한다.
* 서버 컴포넌트에서 클라이언트 컴포넌트로 전달하는 props는 serialization(직렬화)가 가능해야 한다.
* 클라이언트 컴포넌트 내부에서 사용되는 모듈, 하위 컴포넌트들은 모두 클라이언트 컴포넌트가 된다.&#x20;
  * 클라이언트 컴포넌트 내부에서 서버 컴포넌트를 임포트해서 사용할 수 없다.
  * 따라서 가능하다면 클라이언트 컴포넌트는 컴포넌트 Tree에서 가장 끝단에 위치하는 leaf로 이동시키는 것이 좋다.
  * 불가피할 경우 넥스트에서 컴포지션 패턴(자식 컴포넌트를 props로 전달하여 자식 컴포넌트를 조합하는 패턴)을 활용할것을 권고한다.

{% hint style="info" %}
**serialization(직렬화)란** 객체를 네트워크 상에서 전송하거나 파일에 저장하기 위해 객체를 일련의 바이트 스트림으로 변환하는 과정

자바스크립트에서는 `JSON.stingify()`, `JSON.prase()`함수를 통해 객체를 JSON 문자열로 직렬화 & 역직렬화 기능을 제공한다.
{% endhint %}

{% hint style="info" %}
**Serialization(직렬화) 가능 여부 구분**\
\- 가능: 객체, 배열, 숫자, 문자열, 불리언, null,\
\- 불가능: 함수, Date, undefined, Map, Set, Class Instance, Symbol
{% endhint %}

### 서버 컴포넌트 클라이언트 컴포넌트 구분하기

> 사용자에게 전달 되어야 하는 코드가 포함된 경우만 클라이언트 컴포넌트로 만들어 성능을 높이자.

| case                                              | 서버 컴포넌트 | 클라이언트 컴포넌트                                                 |
| ------------------------------------------------- | ------- | ---------------------------------------------------------- |
| 데이터 패칭                                            | ✅       | 데이터 패칭이 가능하지만 특별한 이유가 없다면 서버에서 데이터 패칭을 하는 것이 좋다. (성능, 사용성) |
| 백엔드 리소스 직접 접근                                     | ✅       | ❌                                                          |
| 노출되면 안되는 민감한 데이터(엑세스 토큰, API Keys)                | ✅       | ❌                                                          |
| <p>사용자와 상호작용하는 코드<br>이벤트 핸들러 사용</p>               | ❌       | ✅                                                          |
| <p>React Hooks 사용 <br>(useState, useEffect 등)</p> | ❌       | ✅                                                          |
| 브라우저 API 사용                                       | ❌       | ✅                                                          |

## Data Fetching

> `getStaticProps()` , `gstServerSideProps()` 를 대체하는 여러가지 방식이 추가 되었다.

어느 시점에 페이지(or 레이아웃)를 재생성 할지 지정

### revalidate

> 브라우저에서 페이지(or 레이아웃)의 재검증하는 기본 시간을 지정

```tsx
export const revalidate = false; // default
// false(SSG) | 'force-cache'(SSG) | 0(SSR) | number(ISR)
```

`fetch()` 의 설정된 캐시 옵션을 override 하지 않는다.\
-> `revalidate < fetch()`

### fetch(url, options)

> `async/await` 을 사용하여 데이터 패칭

Next.js는 Web fetch API를 확장하여 사용

캐시 유무에 따라 `SSG`, `ISR`, `SSR` 로 나뉘게 된다.

```tsx
export default async function Page() {
  const data = await fetch(url, {
    cache: 'force-cache'; // default (SSG)
    // no-store(SSR), number(ISR)
  });
}
```

### Dynamic Rendering

**동적 함수** 를 사용하거나 `fetch()` 요청의 캐시 옵션이 **no-store** 일 때 **동적 렌더링(SSR)으로 전환**된다.

#### 동적 함수(Dynamic fuction)

사용자의 요청 시점에 알 수 있는 정보를 사용한 함수를 동적 함수라 한다.&#x20;

ex) 요청 시점에 알 수 있는 정보 -> 쿠키, HTTP 요청 헤더, URL params

## API Route

> Next가 Full Stack으로 개발할 수 있는 이유

요청 별(Get, Post)로 나누어 구조적으로 구현 가능하도록 바뀌었다.

```tsx
// app/api/[endpoint]/route.ts
export async function GET(req: Request) {
  const data = await ...
  return NextResponse.json(data);
}
```

## 라우트당 폴더 하나

> 라우트가 되는 폴더 즉, 페이지 별로 다양하고 유용한 아이템들을 설정할 수 있다.

### 폴더 별로 설정할 수 있는 항목들

* **page.tsx**: 해당 폴더를 경로로 하는 페이지 컴포넌트
* l**ayout.tsx**: 페이지를 감싸는 골격을 설정 (상위 레이아웃에 중첩됨)
* **template.tsx**: 레이아웃과 비슷하나. 페이지 전환시 컴포넌트의 새 인스턴스가 마운트되어 실행되는 차이가 있다.
* **loading.tsx**: 해당 라우트에서 페이지 UI가 준비되기 전 사용자에게 보여주는 UI 컴포넌트 (React.Suspense)
* **error.tsx**: 런타임 에러시 대체 UI 컴포넌트 (React.ErrorBoundary)
* **not-found.tsx**: 해당 라우트의 404 UI 컴포넌트

### loading.tsx

> 해당 라우트의 페이지가 준비되는 동안 loading.tsx에 선언한 컴포넌트가 보여지게 된다. \* layout을 제외한 page 부분만 적용됨

이 컴포넌트를 사용하면 내부적으로 **React.Suspense** 를 사용한다.&#x20;

```tsx
<Layout>
  <Suspense fallback={<Loading />}>
    <Page />
  </Suspense>
</Layout>
```

로딩은 동적인 SSR을 할 때 빠르게 로딩 UI를 먼저 보여주고 실제 컴포넌트를 병렬적으로 백그라운드에서 실행후 완료되면 전송하는 식으로 사용자에게 페이지가 서빙중이라는 것을 알린다.

{% hint style="info" %}
SSG는 이미 정적인 HTML을 서버에서 전송되기 때문에 로딩의 의미가 필요없다.
{% endhint %}

#### 한계점

해당 컴포넌트는 페이지 전체를 묶기 때문에 병렬 스트리밍을 위해서는 내부 요소들에 서스펜스를 따로 설정 해줘야 한다.

#### Suspense 사용 이점

> UI를 렌더링하기 전에 모든 데이터가 로드될 떄까지 기다릴 필요 없이 페이지의 일부를 더 빨리 표시할 수 있다.

* **Streaming Server Rendering:**  스트리밍을 사용하면 페이지의 HTML을 더 작은 청크로 나누고 해당 청크를 서버에서 클라이언트로 점진적으로 전송할 수 있습니다
* **Selective Hydration:**  사용자 인터렉션을 통해 어떤 컴포넌트를 먼저 수화할지 선택적으로 결정

{% hint style="info" %}
Next.js에서 loading.tsx를 사용하거나 Suspense를 사용하면 스트리밍을 구현할 수 있다.\
스트리밍은 각 컴포넌트를 하나의 청크로 간주할 수 있다
{% endhint %}

### error.tsx

> 에러 컴포넌트는 어떤 에러가 발생했는지와 페이지를 리셋할 수 있는 콜백함수를 props로 제공한다.

내부적으로 `ErrorBoundary` 를 사용한다.

```tsx
<Layout>
  <ErrorBoundary fallback={<Error />}>
    <Page />
  </ErrorBoundary>
</Layout>
```

에러 컴포넌트는 런타임에 필요한 사용자 코드와 `useEffect` 같은 훅들을 사용해야 하니 무조건 **Client 컴포넌트**가 된다.

`loading.tsx` 와 마찬가지로 페이지 내부 작은 단위로 에러 UI를 보여주고 싶다면 해당 컴포넌트를 `ErrorBoundary`로 감싸면 된다.

가장 근접한 부모의 `error.tsx` 를 보여준다. (**버블링** 됨)
