드디어 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 번들 크기에 영향을 미쳤던 큰 의존관계는 대신 서버에 남겨 성능 향상을 시킬 수 있다.
서버 컴포넌트와 클라이언트 컴포넌트의 결정 전까지는 서버 컴포넌트(기본값)를 사용하는 것이 좋다.
사례의 요약이다.
기본값으로 사용하기 때문에 클라이언트 컴포넌트를 다룰 기준만 고려하면 될 것 같다.
- 컴포넌트가 라이프 사이클과 이벤트 리스너들의 보편적인 사용으로만 구성되어있다면
- 브라우저 API만 사용한다면
- 1,2를 이용하는 hook
이 정도의 경우에만 클라이언트 컴포넌트를 사용하면 될 것 같다.
Recommendations
- Passing props from Server to Client Components (Serialization)
- 서버 구성 요소를 클라이언트 구성 요소의 자식 또는 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>