use client
window
or state
, then that component should be treated as a client component
"use client";
// This pattern will **not** work!
// You cannot import a Server Component into a Client Component.
import ExampleServerComponent from "./example-server-component";
export default function ExampleClientComponent({
children,
}: {
children: React.ReactNode;
}) {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
<ExampleServerComponent />
</>
);
}
"use client";
import { useState } from "react";
export default function ExampleClientComponent({
children,
}: {
children: React.ReactNode;
}) {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
{children}
</>
);
}
The very same strategy of "lifting content up" has been used to avoid state changes in a parent component re-rendering an imported nested child component.
Props passed from the Server to Client Components need to be serializable
. This means that values such as functions, Dates, etc, cannot be passed directly to Client Components.
export async function getData() {
const res = await fetch("https://external-service.com/data", {
headers: {
authorization: process.env.API_KEY,
},
});
return res.json();
}
To prevent this sort of unintended client usage of server code, we can use the server-only package to give other developers a build-time error if they ever accidentally import one of these modules into a Client Component.
The corresponding package client-only can be used to mark modules that contain client-only code – for example, code that accesses the window object.
import "server-only";
export async function getData() {
const res = await fetch("https://external-service.com/data", {
headers: {
authorization: process.env.API_KEY,
},
});
return res.json();
}
Client Components
since they have the "use client" directive, but they won't work within Server Components.import { Carousel } from "acme-carousel";
export default function Page() {
return (
<div>
<p>View pictures</p>
{/* Error: `useState` can not be used within Server Components */}
<Carousel />
</div>
);
}
"use client";
import { Carousel } from "acme-carousel";
export default Carousel;
"use client";
import { createContext } from "react";
export const ThemeContext = createContext({});
export default function ThemeProvider({ children }) {
return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>;
}
import ThemeProvider from "./theme-provider";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}
이건 직접 읽어보시는 게 좋을 것 같아요 :)
https://nextjs.org/docs/app/building-your-application/routing/parallel-routes
https://nextjs.org/docs/app/building-your-application/routing/intercepting-routes
A layout is UI that is shared between multiple pages. On navigation, layouts preserve state, remain interactive, and do not re-render. Layouts can also be nested
.
You can define a layout by default exporting a React component from a layout.js file. The component should accept a children prop that will be populated with a child layout (if it exists) or a child page during rendering.
Layouts in a route are nested by default. Each parent layout wraps child layouts below it using the React children prop.
Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will automatically dedupe the requests
without affecting performance.
Layouts do not have access to the current route segment(s). To access route segments, you can use useSelectedLayoutSegment
or useSelectedLayoutSegments
in a Client Component.
In the app directory, you can modify the 'head' HTML elements such as title and meta using the built-in SEO support
.
Good to know: You should not manually add 'head' tags such as 'title' and 'meta' to root layouts. Instead, you should use the Metadata API
which automatically handles advanced requirements such as streaming and de-duplicating 'head' elements.
There are two ways to navigate between routes:
If you'd like to scroll to a specific id on navigation, you can append your URL with a # hash link or just pass a hash link to the href prop. This is possible since 'Link' renders to an 'a' element.
'Link' is a React component that extends the HTML 'a' element to provide prefetching
and client-side navigation between routes. It is the primary way to navigate between routes in Next.js.
Recommendation: Use the 'Link' component to navigate between routes unless you have a specific requirement for using useRouter.
On navigation, Next.js will use soft navigation if the route you are navigating to has been prefetched
, and either doesn't include dynamic segments
or has the same dynamic parameters as the current route.
Hard Navigation
이란 용어도 있다.
한 마디로 모두 새로 다시 그리는 페이지 이동을 뜻한다..
A Dynamic Segment can be created by wrapping a folder's name in square brackets: [folderName]
. For example, [id]
or [slug]
.
Dynamic Segments are passed as the params prop to layout
, page
, route
, and generateMetadata
functions.
Dynamic Segments can be extended to catch-all subsequent segments by adding an ellipsis inside the brackets [...folderName]
.
For example, app/shop/[...slug]
/page.js will match /shop/clothes, but also /shop/clothes/tops, /shop/clothes/tops/t-shirts, and so on.
Catch-all Segments can be made optional by including the parameter in double square brackets: [[...folderName]]
.
For example, app/shop/[[...slug]]
/page.js will also match /shop, in addition to /shop/clothes, /shop/clothes/tops, /shop/clothes/tops/t-shirts.
An error.js boundary will not handle errors thrown in a layout.js component in the same segment because the error boundary is nested inside that layouts component.
To specifically handle errors in these root components, use a variation of error.js called app/global-error.js located in the root app directory. Because of this, it is important to note that global-error.js must define its own <html>
and <body>
tags.
Route Handlers are only available inside the app directory. They are the equivalent of API Routes inside the pages directory meaning you do not need to use API Routes and Route Handlers together.
But there cannot be a route.js file at the same route segment level as page.js.
걍 바로 API도 App 폴더 안에 같이 만들 수 있다.
Middleware runs before cached content and routes are matched.
Use the file middleware.ts (or .js) in the root of your project to define Middleware. For example, at the same level as pages or app, or inside src if applicable.
다음 두 페이지에 있는 우선 순위만 한번 봐두자
And, even when a route is made publicly accessible, only the content returned by page.js or route.js is sent to the client.
This means that project files can be safely colocated inside route segments in the app directory without accidentally being routable.
Private folders can be created by prefixing a folder with an underscore: _folderName
You can create URL segments that start with an underscore by prefixing the folder name with %5F (the URL-encoded form of an underscore): %5FfolderName.
Next의 Runtime에는 세 가지가 있다. 제대로 이해하게 되면 내게도 알려주자.
기본값은 Nodejs이나 무거우니 가능하다면 Edge로 바꿔도 된다.
use is a new React function that accepts a promise conceptually similar to await.
Wrapping fetch in use is currently not recommended in Client Components and may trigger multiple re-renders. For now, if you need to fetch data in a Client Component, we recommend using a third-party library such as SWR or React Query.
Caching at the fetch level with revalidate or cache: 'force-cache' stores the data across requests in a shared cache. You should avoid using it for user-specific data (i.e. requests that derive data from cookies() or headers())
import { getArtist, getArtistAlbums, type Album } from "./api";
export default async function Page({
params: { username },
}: {
params: { username: string };
}) {
// Initiate both requests in parallel
const artistData = getArtist(username);
const albumData = getArtistAlbums(username);
// Wait for the artist's promise to resolve first
const artist = await artistData;
return (
<>
<h1>{artist.name}</h1>
{/* Send the artist information first,
and wrap albums in a suspense boundary */}
<Suspense fallback={<div>Loading...</div>}>
<Albums promise={albumData} />
</Suspense>
</>
);
}
// Albums Component
async function Albums({ promise }: { promise: Promise<Album[]> }) {
// Wait for the albums promise to resolve
const albums = await promise;
return (
<ul>
{albums.map((album) => (
<li key={album.id}>{album.name}</li>
))}
</ul>
);
}
import prisma from "./lib/prisma";
export const revalidate = 3600; // revalidate every hour
async function getPosts() {
const posts = await prisma.post.findMany();
return posts;
}
export default async function Page() {
const posts = await getPosts();
// ...
}
Requests are not cached if:
React allows you to cache()
and deduplicate requests, memoizing the result of the wrapped function call. The same function called with the same arguments will reuse a cached value instead of re-running the function.
The cache arguments must be flat and only include primitives. Deep objects won't match for deduplication.
In this new model, we recommend fetching data directly in the component that needs it, even if you're requesting the same data in multiple components, rather than passing the data between components as props.
import { cache } from "react";
export const getUser = cache(async (id: string) => {
const user = await db.user.findUnique({ id });
return user;
});
//
import { getUser } from "@utils/getUser";
export default async function UserLayout({
params: { id },
}: {
params: { id: string };
}) {
const user = await getUser(id);
// ...
}
import { getUser } from "@utils/getUser";
export const preload = (id: string) => {
// void evaluates the given expression and returns undefined
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
void getUser(id);
};
export default async function User({ id }: { id: string }) {
const result = await getUser(id);
// ...
}
//
import User, { preload } from "@components/User";
export default async function Page({
params: { id },
}: {
params: { id: string };
}) {
preload(id); // starting loading the user data now
const condition = await fetchCondition();
return condition ? <User id={id} /> : null;
}
import { cache } from "react";
import "server-only";
export const preload = (id: string) => {
void getUser(id);
};
export const getUser = cache(async (id: string) => {
// ...
});
컴포넌트 단위로 rebalidate 하려면 해당 컴포넌트 파일에서 예약어인 revalidate
를 추가하면 된다.
export const revalidate = 60; // revalidate this segment every 60 seconds
There are two types of revalidation in Next.js:
Background: Revalidates the data at a specific time interval.
fetch("https://...", { next: { revalidate: 60 } });
//
export const revalidate = 60; // revalidate this page every 60 seconds
On-demand: Revalidates the data based on an event such as an update.
Server Functions called as an action.
Server Actions can be progressively enhanced by passing them to a form element's action prop
. The form is interactive before any client-side JavaScript has loaded. This means React hydration is not required for the form to submit.