Next 24장 - 기초부터 다시 배우기 v14 (1)
포스트
취소

Next 24장 - 기초부터 다시 배우기 v14 (1)

Next

  • v14를 기준으로 합니다.
  • 기존 Next.jspage router 정도의 개념으로 생각하며 사용해왔기에, SSR 등의 규칙과 14버전의 업데이트에 맞춰 다시 정리해보는 글.

Next.js v14

React Framework

  • React는 자바스크립트 라이브러리이다.


  • 라이브러리와 프레임워크에 대한 개념도 알아야 하는데, 라이브러리는 우리가 주도권을 갖고 함수나 코드를 작성하고, 파일과 폴더의 이름 및 위치 등을 작성하여 실행한다.


  • 프레임워크는 파일과 폴더의 이름과 위치 등을 정해진 규칙에 맞게 작성해야 하며, 우리가 작성한 함수나 코드를 Next.js에서 해석하고 실행시켜주는 역할을 하기 때문에 주도권이 프레임워크에 있다고 볼 수 있다.
    • 예를 들어, React에서 메타 데이터를 작성하기 위해선 여러 가지 코드를 작성하여야 하지만, Next.js에서 메타 데이터를 작성하고자 한다면 Metadata를 import 하는 것만으로 작성이 가능한데, 이는 Next.js에서 주도적으로 실행시키기 때문에 가능한 일이다.


  • 라이브러리는 우리가 사용하고, 프레임 워크는 우리의 코드를 사용한다.

주요 개념

  1. 이전 버전에서는 pages 폴더에서 작성하였지만, v14에선 app 폴더에서 작성한다.
    • 기존 버전에서 작성한대로 14버전으로 변경한다고 하더라도 동작이 가능하다.


  1. data fetching 하는 방법인 getStaticProps, getServerSideProps, getStaticPaths는 사용하지 않는다.


  1. /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 페이지만 생성된다.


  1. layout 컴포넌트와 같이 특별한 파일 이름인 not-found 컴포넌트가 존재하며, 이는 404 페이지이다.


  1. 사용자가 페이지에 들어오면 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
  • 메타 데이터 옵션
  • 메타 데이터는 여러 가지 옵션을 사용할 수 있다.
  • 템플릿을 바탕으로 다른 페이지의 타이틀을 합쳐줄 수 있고, 페이지에 따른 타이틀을 동적으로 생성해낼 수도 있다.
  1. 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 템플릿입니다.를 생성한다.


  1. 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}}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.