avatarBloKoo
About Me
드디어 Next.js 13 Update
드디어 Next.js 13 Update
2022-10-26 16:25:00

드디어 Next.js 13 업데이트(Blog - Next.js 13 | Next.js)가 되었다. Next 블로그의 업데이트 내용을 볼 때 마다 재밌는 이슈들이 항상 등장한다. 12 업데이트 쯤 Blog - Layouts RFC | Next.js 에 대한 내용이 언급되었는데 /app 디렉토리의 베타 버전을 확인 할 수 있게 되었다.

이제 야무지게 업데이트를 해서 적용을 해보자.

준비하기

yarn add next@latest react@latest react-dom@latest eslint-config-next@latest

app 폴더를 만든다.

실험적 기능(베타버전)이라 따로 config에서 설정을 해줘야 사용이 가능하다.

설정을 하고 재시작을 하니 tsconfig.json이 야무지게 변경된걸 확인 가능하다.

13 때 업데이트 된 내용이 있다.

Starting with Next.js 13, <Link> renders as <a>, so attempting to use <a> as a child is invalid.

Link 하위의 a를 지우도록 하자.

이제 정상적으로 시작되었다. 본격적으로 /app 을 적용해보자!

app 디렉토리 적용하기

우선 page.js 가 필요하다.

// app/page.tsx

import React from 'react';

const Page = () => {
    return <h1>Hello, Next.js!</h1>;
};

export default Page;

만들고 나면 레이아웃이 필요하다는 콘솔 로그와 함께 layout.tsx 파일이 만들어진다.

Your page app/page.tsx did not have a root layout, we created app/layout.tsx for you.

여기에서 root의 layout은 _app_document를 대체한다고 보면 된다.

layout이 만들어지면서

error - TypeError: React.createContext is not a function

에러가 발생했다. emotion과의 이슈가 같은데 우선 임시 방편으로 next.config.js의 emotion 컴파일러를 비활성화하고 실행하니 해결은 되었다. 이 부분은 다시 해결해봐야할 것 같다.

// app/layout.tsx

import React from 'react';

export default function RootLayout({
    children,
}: {
    children: React.ReactNode;
}) {
    return (
        <html>
            <head></head>
            <body>{children}</body>
        </html>
    );
}

정상적으로 실행되었다.

Server Components 알아보기

app 디렉토리 내의 컴포넌트는 기본적으로 서버 컴포넌트이다. 서버 컴포넌트(RFC)를 통해 개발자는 서버 인프라를 보다 효율적으로 활용할 수 있다. 예를 들어 이전에 클라이언트의 JavaScript 번들 크기에 영향을 미쳤던 큰 의존관계는 대신 서버에 남겨 성능 향상을 시킬 수 있다.

서버 컴포넌트와 클라이언트 컴포넌트의 결정 전까지는 서버 컴포넌트(기본값)를 사용하는 것이 좋다.

사례의 요약이다.

기본값으로 사용하기 때문에 클라이언트 컴포넌트를 다룰 기준만 고려하면 될 것 같다.

  1. 컴포넌트가 라이프 사이클과 이벤트 리스너들의 보편적인 사용으로만 구성되어있다면
  2. 브라우저 API만 사용한다면
  3. 1,2를 이용하는 hook

이 정도의 경우에만 클라이언트 컴포넌트를 사용하면 될 것 같다.

Recommendations

  1. Passing props from Server to Client Components (Serialization)
  2. 서버 구성 요소를 클라이언트 구성 요소의 자식 또는 Props으로 전달할 수 있고 두 구성 요소를 다른 서버 구성 요소에 래핑하여 이를 수행할 수 있다.

스트리밍

클라이언트에 UI를 점진적으로 스트리밍하는 기능이 도입되었다. Next.js의 서버 구성 요소 및 중첩된 레이아웃을 사용하면 데이터가 필요하지 않은 페이지의 일부를 즉시 렌더링하고 데이터를 가져오는 페이지의 일부에 대한 로드 상태를 표시할 수 있다.

Data Fetching

새로운 use hook은 이전 getStaticProps 그리고 getServerSidePropsReact을 대체한다.

Static Site Generation(SSG), Server-Side Rendering(SSR), Incremental Static Regeneration(ISR)의 모든 이점을 이제 React의 use후크와 Web fetchAPI 확장을 통해 사용할 수 있다.

// This request should be cached until manually invalidated.
// Similar to `getStaticProps`.
// `force-cache` is the default and can be omitted.
fetch(URL, { cache: 'force-cache' });

// This request should be refetched on every request.
// Similar to `getServerSideProps`.
fetch(URL, { cache: 'no-store' });

// This request should be cached with a lifetime of 10 seconds.
// Similar to `getStaticProps` with the `revalidate` option.
fetch(URL, { next: { revalidate: 10 } });

// app/data/page.tsx

import React from 'react';
import { use } from 'react';
import { cookies, headers } from 'next/headers';
interface Data {
    userId: number;
    id: number;
    title: string;
    completed: boolean;
}
async function getData() {
    const res = await fetch('https://jsonplaceholder.typicode.com/todos/1', {
        cache: 'force-cache',
    });
    const data: Data = await res.json();
    console.log(data, headers(), cookies());
    return data;
}
const Page = () => {
    const data = use(getData());

    return <h1>Hello, Next.js! {JSON.stringify(data)}</h1>;
};

export default Page;

오호 … 편해졌다. 클라이언트 컴포넌트로 use client 첫 줄에 명시하고 사용하니 예전 13버전 아래의 컴포넌트와 같아졌다. 확실히 분리해서 서버, 클라이언트 컴포넌트를 나눠 작업하면 편할 것 같다는 생각이 든다.

그리고 서버 컴포넌트의 함수 중 cookies, headers도 활용 할 수 있다.

Turbopack

Turbopack 알파를 사용하면 다음과 같은 결과가 나온다고 설명 되어있다.

  • 웹 팩보다 700배 빠른 업데이트
  • Vite보다 10배 빠른 업데이트
  • 웹 팩보다 콜드 스타트 속도가 4배 빠르다

@next/font

새로운 글꼴 시스템 을 도입

성능과 개인 정보 보호를 염두에 두고 모든 Google 글꼴을 편리하게 사용할 수 있고 사용자 정의 글꼴도 지원한다.

//
// Goole 글꼴
import { Inter } from '@next/font/google';

const inter = Inter();

<html className={inter.className}>


//
// 사용자 정의 글꼴
import localFont from '@next/font/local';

const myFont = localFont({ src: './my-font.woff2' });

<html className={myFont.className}>


OG 이미지 생성

yarn add @vercel/og
// pages/api/og.tsx

import { ImageResponse } from '@vercel/og';

export const config = {
    runtime: 'experimental-edge',
};

const OG = () => {
    return new ImageResponse(
        (
            <div
                style={{
                    display: 'flex',
                    fontSize: 128,
                    background: 'white',
                    width: '100%',
                    height: '100%',
                }}>
                Hello, World!
            </div>
        )
    );
};

export default OG;

http://localhost:3000/api/og 으로 들어가보면 바로 생성…. 이거 귀찮았는데 상당히 사용성이 높을 것으로 보인다.

예를 들어 메타태그의 og 이미지를 아래처럼 이용해서 적용할 수 있다. 파라미터 등을 이용하여 텍스트를 추가하고 삭제하고 다양한 작업이 가능하다.

<head>
  <title>Hello world</title>
  <meta
    property="og:image"
    content="http://localhost:3000/api/og"
  />
</head>