Next
- v14를 기준으로 합니다.
- 기존 Next.js 를
page router
정도의 개념으로 생각하며 사용해왔기에, SSR 등의 규칙과 14버전의 업데이트에 맞춰 다시 정리해보는 글.
Next.js v14
React Framework
React
는 자바스크립트 라이브러리이다.
- 라이브러리와 프레임워크에 대한 개념도 알아야 하는데, 라이브러리는 우리가 주도권을 갖고 함수나 코드를 작성하고, 파일과 폴더의 이름 및 위치 등을 작성하여 실행한다.
- 프레임워크는 파일과 폴더의 이름과 위치 등을 정해진 규칙에 맞게 작성해야 하며, 우리가 작성한 함수나 코드를 Next.js에서 해석하고 실행시켜주는 역할을 하기 때문에 주도권이 프레임워크에 있다고 볼 수 있다.
- 예를 들어,
React
에서 메타 데이터를 작성하기 위해선 여러 가지 코드를 작성하여야 하지만,Next.js
에서 메타 데이터를 작성하고자 한다면 Metadata를 import 하는 것만으로 작성이 가능한데, 이는 Next.js에서 주도적으로 실행시키기 때문에 가능한 일이다.
- 예를 들어,
- 라이브러리는 우리가 사용하고, 프레임 워크는 우리의 코드를 사용한다.
주요 개념
- 이전 버전에서는
pages
폴더에서 작성하였지만, v14에선 app 폴더에서 작성한다.- 기존 버전에서 작성한대로 14버전으로 변경한다고 하더라도 동작이 가능하다.
data fetching
하는 방법인 getStaticProps, getServerSideProps, getStaticPaths는 사용하지 않는다.
/pages/index.tsx
또는/pages/movie/[id].tsx
등과 같이 페이지를 동적으로 생성하였지만, app 폴더 내에 새로운 폴더를 만들고page.tsx
를 생성하면 된다.- 생성한 폴더 이름은 페이지의
router
가 된다. - 만약
/app/movie/detail/page.tsx
와 같이 작성되어 있고detail
폴더에는page
컴포넌트를 작성하지 않았을 때, detail은 url 경로를 위해서만 사용되고 아무런 페이지가 없게 된다. - 이전 버전과는 달리, 폴더 안에서 작성되는 파일에 대해서 실제 페이지를 갖게 됐지만, page 컴포넌트로 실제 페이지가 작동하며, 다른 컴포넌트를 생성해도 실제 페이지가 되진 않는다.
/pages/movie/detail/star.tsx & reserve.tsx
=> star와 reserve 페이지가 생성된다.- v14
/app/movie/pages.tsx & star.tsx
=> movie 페이지만 생성된다.
- 생성한 폴더 이름은 페이지의
layout
컴포넌트와 같이 특별한 파일 이름인not-found
컴포넌트가 존재하며, 이는 404 페이지이다.
- 사용자가 페이지에 들어오면 interactive 하지 않은 더미 HTML이 로드되고, 사용자가 더미 HTML을 보고 있을 동안 프레임 워크를 로딩하고 빈 HTML에 자바스크립트를 입히는 과정으로 초기화시켜 리액트가 된다.
- 이렇게 HTML을 리액트로 초기화 시키는 과정을 hydration 이라고 한다.
client component vs server component
- 주요 개념의 5번 문항인 hydration을 읽고 나면, Next.js는 빈 HTML에 자바스크립트를 입혀 리액트로 초기화 시키는 과정을 거친다는 것을 알 수 있다.
- 최상단에 작성하는
use client
는 클라이언트에서 동적인 작동을 하는 컴포넌트를 명시해주기 때문에 함수를 넣는다는지, useState, useEffect 등을 사용하여 interactive한 컴포넌트로 사용된다. - 이와 달리
use client
를 작성하지 않은 컴포넌트는 server component가 되며, 로드된 더미 HTML을 끝으로 noninteractive 한 컴포넌트로 남게 되기 때문에 동적인 작동이 불가하다. - 개념적으로 사용자가 다운로드 해야 할 자바스크립트 파일을 줄여줄 수 있다.
- 서버 컴포넌트 내에서 클라이언트 컴포넌트를 사용하는 것은 가능하지만, 클라이언트 컴포넌트 내에서 서버 컴포넌트를 import 할 수 없다.
- 대신에 클라이언트 컴포넌트 내에서 서버 컴포넌트를 child 로 넘겨주는 것은 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// clientComponent.tsx => 불가능
'use client';
import ServerComponent from "./Server"
export default function ClientComponents(){
return (
<div>
<ServerComponent />
</div>
)
}
// page.tsx => 클라이언트 내 서버 컴포넌트 가능
import ClientComponent from "./Client";
import ServerComponent from "./Server";
export default function Page(){
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
)
}
// ClientComponent.tsx
"use client";
export default function ClientComponent({child}){
return (
<>{child}</>
)
}
Layout
- 이전 버전에서 사용하던
App
파일과 비슷한 개념이다. - 만약 우리가 동일하게 사용할 필요가 있는
Nav, Footer, Header
등이 있을 때, 매 컴포넌트를 페이지마다 붙여넣기 해줄 필요 없이 상위 컴포넌트인App
컴포넌트를 사용하면 됐다. - Layout은 이러한 공통 상위 컴포넌트와 같은 의미로 적용되며, 각 페이지마다 Layout을 정할 수 있어 필요한 레이아웃을 다르게 적용할 수 있다.
routes group
- 생성한 폴더는 URL 페이지를 갖게 된다.
- 하지만 괄호를 사용하여 만든 폴더는 그룹화만 해줄 뿐 실제 페이지를 생성하지 않기 때문에, 페이지와 관련된 컴포넌트를 작성하는 등의 역할로 사용하기 좋다.
Meta Data
- 리액트에서
head
태그 안에meta data
를 작성하던 것과 달리, Next.js의 MetaData를 import하여 쉽게 사용할 수 있다. - MetaData는 중첩이 가능하기 때문에 home 페이지에서 보여줄 메타 데이터와 layout으로 갖는 기본 메타 데이터를 각 각 작성할 수 있다.
- 메타 데이터는 중첩이 가능하기 떄문에 공통적으로 들어갈 metadata를 공통 컴포넌트인 layout에 작성하고, 각 페이지마다 메타 데이터를 작성할 수 있다.
- 아래의 예제처럼,
description
은 모든 페이지에서 공통적으로 갖게 되지만,home
페이지와my
페이지에서 갖는 각각의title
을 달리 설정해줄 수 있다. - 다른 컴포넌트에서는 메타 데이터를 사용할 수 없고, 오직 page 컴포넌트에서만 메타 데이터를 설정할 수 있다.
- 클라이언트 컴포넌트에서는 사용할 수 없고, 오직 서버 컴포넌트에서만 설정할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// layout
import type { Metadata } from "next";
export const metadata: Metadata = {
description: "공통적으로 보여질 설명입니다."
}
export default function RootLayouy(){
...
}
// /home/page.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "home 페이지에서만 보여질 타이틀입니다."
}
export default function Page(){
...
}
// /my/page.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "my 페이지에서만 보여질 타이틀입니다."
}
export default function Page(){
...
}
metadata option
- 메타 데이터 옵션
- 메타 데이터는 여러 가지 옵션을 사용할 수 있다.
- 템플릿을 바탕으로 다른 페이지의 타이틀을 합쳐줄 수 있고, 페이지에 따른 타이틀을 동적으로 생성해낼 수도 있다.
title
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// /layout.tsx import type { Metadata } from "next"; export const metadata: Metadata = { title: { template: "%s 템플릿입니다.", default: "Next.js", // template 사용 시 필요 }, description: "메타 데이터 만들기", }; // /home/page.tsx import type { Metadata } from "next"; export const metadata: Metadata = { title: "home 페이지", }; // /my/page.tsx import type { Metadata } from "next"; export const metadata: Metadata = { title: "my 페이지", };
- home, my 페이지에 대한 각각의 타이틀은, Metadata를 공유하는 layout의 %s 에 들어가게 된다.
- 출력, /home => home 페이지 템플릿입니다. && /my => my 페이지 입니다.
- metadata 가 작성되어 있지 않을 시, 기본값인
Next.js
가 들어가게 되어 Next.js 템플릿입니다.를 생성한다.
dynamic metadata
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
// /my/[id]/page.tsx import type { Metadata, ResolvingMetadata } from "next"; type Props = { params: { id: string }, searchParams: { [key: string]: string | string[] | undefined }, }; export async function generateMetadata( { params, searchParams }: Props, parent: ResolvingMetadata ): Promise<Metadata> { // read root params const id = params.id; // fetch data const my = await fetch(`https://나의 도메인/my/${id}`).then((res) => res.json() ); return { title: my.title, }; } export default function Page(props: Props) { console.log(props); }
- 동적으로 페이지의 params 를 확인하여 타이틀에 작성할 수도 있다.
- /my/choigirang => {id: “choigirang”}
- 클라이언트 컴포넌트가 아닌 서버 컴포넌트의 console을 터미널에서 확인할 수 있기에 개발자 도구의 콘솔에서는 아무것도 실행되지 않는다.
- 만약 우리가 choigirang 이라는 페이지로 이동 시 props를 확인해보면 터미널에는 {params: “choigirang”, searchParams: {}} 가 찍혀있는 것을 확인할 수 있다.
http://.../choigirang?page=1 => {params: "choigirang", searchParams: {page: 1}}