
// next.config.js
const nextConfig = {
experimental: { appDir: true },
};
// app/page.tsx
// This file maps to the index route (/)
export default function Page() {
return <h2>Hello, I am Page!</h2>;
}
// app/layout.tsx
import "./global.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode,
}) {
return (
<html>
<head></head>
<body>{children}</body>
</html>
);
}
"use client";
import { useState } from "react";
export default function Avatar() {
const [person, setPerson] = useState(0);
return (
...
);
}
// app/avatar/loading.tsx
export default function Loading() {
return <p>Loading Avatars...</p>;
}
async function getData() {
// 기존의 API 호출 방식은 아래와 비슷 할 것이다.
// const res = await fetch("https://api.github.com/");
// 여기서 3초정도 기다려 준다.
await new Promise((res) => setTimeout(res, 3000));
// 기존의 data return 방식은 아래와 비슷 할 것이다.
// return res.json();
return "Avatar data";
}
export default async function Page() {
const name = await getData();
return <p>🤩 Hello there, {name}!</p>;
}
"use client";
import { use } from "react";
async function getData() {
await new Promise((res) => setTimeout(res, 3000));
return "Avatar data in Client Components";
}
export default function Page() {
const name = use(getData());
return <p>🤩 Hello there, {name}!</p>;
}
// 수동으로 invalidate 될때까지 cache 가 된다.
// `getStaticProps` 와 비슷하다.
// `force-cache` 가 기본설정이며, 간결성을 위해 생략 될 수 있다.
// Static data fetching 을 의미한다.
fetch(URL, { cache: "force-cache" });
// 매 request 마다 refetch 가 이루어진다.
// `getServerSideProps` 와 비슷하다.
// Dynamic data fetching 을 의미한다
fetch(URL, { cache: "no-store" });
// 이 요청은 10초의 캐시 유효기간을 가진다.
// 'revalidate'옵션을 가진 `getStaticProps` 와 비슷하다.
fetch(URL, { next: { revalidate: 10 } });
import Albums from "./albums";
async function getArtist(username) {
const res = await fetch(`https://api.github.com/artist/${username}`);
return res.json();
}
async function getArtistAlbums(username) {
const res = await fetch(`https://api.github.com/artist/${username}/type`);
return res.json();
}
export default async function Page({ params: { username } }) {
// 두개의 request 를 동시에 실행한다.
const artistData = getAvatar(username);
const albumData = getAvatarType(username);
// promise 가 resolve 될때까지 기다린다.
const [artist, album] = await Promise.all([artistData, albumData]);
return (
<div>
<h1>{artist.name}</h1>
<Albums list={albumData} />
</div>
);
}
// parallel 로 initiate 를 하지만,
const artistData = getArtist(username);
const albumData = getArtistAlbums(username);
// artistData 의 promise 가 먼저 resolve 되도록 기다린다.
const artist = await artistData;
return (
<div>
{/* 아티스트 이름 정보를 먼저 렌더 시키고, 앨범 정보는 Suspense 안에 wrap 하여 나중에 보여지도록 한다*/}
<h1>{artist.name}</h1>
<React.Suspense fallback={<h1>Loading...</h1>}>
<Album promise={albumData} />
</React.Suspense>
</div>
);
// 그리고 Album 컴포넌트 내에서 albums 의 promise 가 resolve 될때까지 기다린다.
async function Album({ promise }) {
const albums = await promise;
return <p>{album.name}</p>;
}
// app/artist/page.tsx
// ...
async function Playlists({ artistID }) {
// playlist 를 기다린다
const playlists = await getArtistPlaylists(artistID);
return (
<ul>
{playlists.map((playlist) => (
<li key={playlist.id}>{playlist.name}</li>
))}
</ul>
);
}
export default async function Page({ params: { username } }) {
// artist 를 기다린다
const artist = await getArtist(username);
// "await" 을 사용하여 request 를 더 추가하면 평행 fetch 가 된다
return (
<>
<h1>{artist.name}</h1>
<Suspense fallback={<div>Loading...</div>}>
<Playlists artistID={artist.id} />
</Suspense>
</>
);
}
import { Montserrat } from "@next/font/google";
const montserrat = Montserrat();
<p> className={montserrat.className}> Hello World </p>;
import localFont from "@next/font/local";
const myFont = localFont({src: './my-font.woff2});
<p className={myFont.className}> Hello World </p>;
//아래와 같은 사용에서
<Link href="/about">
<a>About</a>
</Link>
//이렇게 보다 간단하게 변경되었다.
<Link href="/about">
About
</Link>
// pages/api/og.jsx
import { ImageResponse } from "@vercel/og";
export const config = {
runtime: "experimental-edge",
};
export default function () {
return new ImageResponse(
(
<div
style={{
display: "flex",
fontSize: 128,
background: "white",
width: "100%",
height: "100%",
}}
>
Hello, World!
</div>
)
);
}
Vercel 의 middleware 란 사이트에서 요청이 처리되기 이전에 실행되는 코드를 말한다.
middleware 를 통해 incoming request 를 수정하거나, 요청이 처리되기 전에 로직을 추가할 수도 있다.
이제는 middleware 의 header 를 세팅하는것이 더 쉬워졌으며, rewrite 나 redirect 없이 middleware 에서 직접 response 를 반환 할 수도 있다.
next.config.js 안의 experimental.allowMiddlewareResponseBody 를 enable 해야 사용 가능.
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
// request header 를 복사하여 새로운 header를 생성, 이 새로운 헤더는 `header-for-the-server` 서버로 보내진다.
const requestHeaders = new Headers(request.headers);
requestHeaders.set("header-for-the-server", "hello server");
// NextResponse.rewrite 을 통해 request header 를 설정하는 것도 가능하다.
const response = NextResponse.next({
request: {
// 새로운 request headers
headers: requestHeaders,
},
});
// 브라우저에서 inspect 가 가능한 새로운 response header 설정
// 브라우저의 네트워크 탭에서 "header-for-the-client" 를 확인해보자.
response.headers.set("header-for-the-client", "hello client");
return response;
}
React 의 최소버전이 17.0.2 에서 18.2.0 으로 상향 되었다.
Node.js 의 최소 버전이 12.22.0 에서 14.6.0 으로 상향 되었다.