캐싱은 데이터를 저장하므로 요청이 있을때마다 데이터를 다시 받아올 필요가 없습니다.
Next는 기본적으로 반환된 값을 자동으로 데이터 캐시에 캐시합니다.
-> 그 말은 빌드 또는 요청 시 캐시 값을 재사용 할 수 있음을 의미합니다.
다음과 같은 경우는 fetch 요청이 캐시되지 않습니다.
데이터 캐시를 제거하고 최신 데이터를 다시 받아오는 프로세스 입니다.
이는 데이터가 변경되어 최신 정보를 표시하려는 경우에 유용합니다.
시간 기반 재검증
일정 시간마다 재검증 합니다. 이는 자주 변경되지 않고 최신값 변경이 중요하지 않은 데이터에 유용합니다.
온디맨드 재검증
이벤트 기반으로 재검증합니다. 이는 태그기반 또는 경로기반 접근방식을 이용하여 재검증 할 수 있습니다. 가능한 빨리 최신값을 표시하려는 경우에 유용합니다.
시간 간격을 두고 데이터를 재검증하려면 fetch의 next.revalidate 옵션을 사용하여 리소스의 캐시 시간을 설정할 수 있습니다.
fetch('https://...', { next: { revalidate: 3600 } })
또는 라우트 세그먼트의 모든 response를 재검증하기 위해 세그먼트 구성 옵션을 사용할 수 있습니다.
export const revalidate = 3600 // revalidate at most every hour
정적으로 렌더링된 경로에 여러 fetch response가 있고 각각의 재검증 빈도가 다른 경우, 모든 요청에 가장 낮은 시간이 사용됩니다. 동적으로 랜더링되는 라우트의 경우 각 fetch 요청은 독립적으로 재검증됩니다.
라우트 핸들러 또는 서버 액션 내부의 라우트 또는 캐시 태그로 데이터를 온디맨드 방식으로 재검증할 수 있습니다.
Next.js에는 경로 전반에 걸쳐 fetch요청을 무효화하기 위한 캐시 태깅 시스템이 있습니다.
예를 들어 다음 fetch 요청은 collection 캐시 태그를 추가합니다.
export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}
그런 다음 서버 작업에서 revalidateTag를 호출하여 collection 태그가 지정된 이 fetch 호출을 다시 검증할 수 있습니다.
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() {
revalidateTag('collection')
}
데이터 재검증을 시도하는 동안 오류가 발생하면 마지막으로 성공적으로 생성된 데이터가 캐시에서 계속 제공됩니다. 다음 후속 요청에서 Next.js는 데이터 재검증을 다시 시도합니다.
다음과 같은 경우 fetch 요청이 캐시되지 않습니다.
개별 fetch 요청에 대한 캐싱을 선택 해제하려면 cache에서 'no-store' 옵션을 설정하면 됩니다. 그러면 fetch 요청이 있을 때마다 데이터를 동적으로 가져옵니다.
fetch('https://...', { cache: 'no-store' })
경로 세그먼트(예: 레이아웃 또는 페이지)에 여러 fetch 요청이 있는 경우 세그먼트 구성 옵션을 사용하여 세그먼트에 있는 모든 데이터 요청의 캐싱 동작을 구성할 수 있습니다.
그러나 각 fetch 요청의 캐싱 동작을 개별적으로 구성하는 것이 좋습니다. 이를 통해 캐싱 동작을 보다 세부적으로 제어할 수 있습니다.
지원하지 않거나 대중화되지 않은 다른 라이브러리(예: 데이터베이스, CMS 또는 ORM 클라이언트)를 사용하는 경우 Route 세그먼트 구성 옵션을 사용하여 해당 fetch 요청의 캐싱 및 재검증 동작을 구성할 수 있습니다.
데이터가 캐시되는지의 여부는 경로 세그먼트가 정적으로 렌더링되는지 아니면 동적으로 렌더링되는지에 따라 달라집니다. 세그먼트가 정적(기본값)인 경우 요청 출력은 경로 세그먼트의 일부로 캐시되고 재검증됩니다. 세그먼트가 동적인 경우 요청 출력은 캐시되지 않으며 세그먼트가 렌더링될 때 모든 요청에서 다시 가져옵니다.
unstable_cache 실험용 API를 사용할 수도 있습니다.
클라이언트 구성 요소에서 데이터를 가져와야 하는 경우 클라이언트에서 Route Handler를 호출할 수 있습니다. 경로 처리기는 서버에서 실행되고 데이터를 클라이언트에 반환합니다. 이는 API 토큰과 같은 민감한 정보를 클라이언트에 노출하고 싶지 않을 때 유용합니다.
SWR이나 TanStack Query 같은 서드파티 라이브러리를 사용하여 클라이언트에서 데이터를 가져올 수도 있습니다. 이러한 라이브러리는 요청 메모, 캐싱, 재검증 및 데이터 변형을 위한 자체 API를 제공합니다.
서버액션은 서버에서 실행되는 비동기 함수 입니다. 서버 및 클라이언트 구성 요소에서 Next.js 애플리케이션의 제출 폼 및 데이터 뮤테이션을 처리하는 데 사용할 수 있습니다.
서버 액션은 React로 정의될 수 있습니다. 함수의 상단에 async를 배치하여 해당 기능을 서버 작업으로 표시하거나 별도의 파일 상단에 배치하여 해당 파일의 모든 내보내기를 서버 작업으로 표시할 수 있습니다.
서버 컴포넌트는 인라인 함수 또는 모듈에 "use server"를 사용할 수 있습니다.
서버 작업을 인라인하려면 "use server"함수 본문 상단에 다음과 같은 코드를 추가하면 됩니다.
// Server Component
export default function Page() {
// Server Action
async function create() {
'use server'
// ...
}
return (
// ...
)
}
클라이언트 컴포넌트는 "use server"을 사용하는 모듈만 불러올 수 있습니다.
클라이언트 컴포넌트에서 서버작업을 호출하려면 새 파일을 만들고 코드 상단에 "use server"를 추가하면 됩니다. 그러면 파일 내의 모든 기능은 클라이언트 및 서버 컴포넌트에서 재사용할 수 있는 서버 작업으로 표시됩니다.
'use server'
export async function create() {
// ...
}
서버액션을 prop으로 클라이언트 컴포넌트에 전달할 수 있습니다.
'use client'
export default function ClientComponent({ updateItem }) {
return <form action={updateItem}>{/* ... */}</form>
}
React는 HTML form prop을 사용하여 서버 작업을 호출할 수 있도록 하는 요소를 확장합니다.
When invoked in a form, the action automatically receives the FormData object.
만약 form을 호출한다면 동작은 자동으로 FormData 객체를 반환합니다. 필드를 관리하기 위해 useState를 쓸 필요는 없으며 대신에 기본 메서드인 FormData를 사용하여 데이터를 추출할 수 있습니다.
export default function Page() {
async function createInvoice(formData: FormData) {
'use server'
const rawFormData = {
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
}
// mutate data
// revalidate cache
}
return <form action={createInvoice}>...</form>
}
JavaScript bind 메소드를 사용하여 서버 작업에 추가 인수를 전달할 수 있습니다
'use client'
import { updateUser } from './actions'
export function UserProfile({ userId }: { userId: string }) {
const updateUserWithId = updateUser.bind(null, userId)
return (
<form action={updateUserWithId}>
<input type="text" name="name" />
<button type="submit">Update User Name</button>
</form>
)
}
userId 서버액션은 FormData 외에도 인수를 받습니다.
'use server'
export async function updateUser(userId, formData) {
// ...
}
React useFormStatusf 를 사용할 수 있습니다. 이것은 Form이 제출되는 동안 보류상태를 표시하는 hook 입니다.
'use client'
import { useFormStatus } from 'react-dom'
export function SubmitButton() {
const { pending } = useFormStatus()
return (
<button type="submit" disabled={pending}>
Add
</button>
)
}
그러고 SubmitButton은 어떤 형태로 중첩될 수 있습니다.
import { SubmitButton } from '@/app/submit-button'
import { createItem } from '@/app/actions'
// Server Component
export default async function Home() {
return (
<form action={createItem}>
<input type="text" name="field-name" />
<SubmitButton />
</form>
)
}
기본 클라이언트 측 양식 유효성 검사 type="email",required와 같은 HTML 유효성 검사를 사용하는 것이 좋습니다.
React useOptimistic 를 사용할 수 있습니다. 응답을 기다리지 않고 서버 작업이 완료되기 전에 UI를 낙관적으로 업데이트하려면 hook를 사용하세요.
button, input type="submit" 및 input type="image" 와 같은 요소는 form 내부에 중첩된 요소에서 서버 작업을 호출할 수 있습니다. 이러한 요소는 formAction prop 또는 이벤트 핸들러를 허용합니다.
이는 양식 내에서 여러 서버 작업을 호출하려는 경우에 유용합니다. 예를 들어 게시물 초안을 게시하는 것 외에도 저장하기 위한 특정 button 요소를 만들 수 있습니다.
requestSubmit() 메소드를 사용하여 양식 제출을 트리거할 수 있습니다. 예를 들어 사용자가 ⌘ + Enter를 누르면 onKeyDown 이벤트를 수신할 수 있습니다.
'use client'
export function Entry() {
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (
(e.ctrlKey || e.metaKey) &&
(e.key === 'Enter' || e.key === 'NumpadEnter')
) {
e.preventDefault()
e.currentTarget.form?.requestSubmit()
}
}
return (
<div>
<textarea name="entry" rows={20} required onKeyDown={handleKeyDown} />
</div>
)
}
그러면 가장 가까운 form 상위 항목의 제출이 트리거되어 서버 작업이 호출됩니다.
form 요소 내에서 서버 작업을 사용하는 것이 일반적이지만 이벤트 핸들러 및 useEffect와 같은 코드의 다른 부분에서도 호출할 수 있습니다.
onClick과 같은 이벤트 핸들러에서 서버 작업을 호출할 수 있습니다.
UX를 개선하려면 useOptimistic 및 useTransition과 같은 다른 React API를 사용하여 서버 작업이 서버에서 실행을 마치기 전에 UI를 업데이트하거나 보류 중인 상태를 표시하는 것이 좋습니다.
예를 들어 form 필드를 저장하기 위해 form 요소에 이벤트 핸들러를 추가할 수도 있습니다.
'use client'
import { publishPost, saveDraft } from './actions'
export default function EditPost() {
return (
<form action={publishPost}>
<textarea
name="content"
onChange={async (e) => {
await saveDraft(e.target.value)
}}
/>
<button type="submit">Publish</button>
</form>
)
}
이와 같이 여러 이벤트가 연속적으로 빠르게 실행될 수 있는 경우 불필요한 서버 작업 호출을 방지하기 위해 디바운싱을 권장합니다.
React useEffect hook을 사용하면 구성 요소가 마운트되거나 종속성이 변경될 때 서버 작업을 호출할 수 있습니다. 이는 전역 이벤트에 의존하거나 자동으로 트리거되어야 하는 변형에 유용합니다. 예를 들어 앱 바로가기를 위한 onKeyDown, 무한 스크롤을 위한 교차 관찰자 후크 또는 보기 수를 업데이트하기 위해 구성요소가 마운트되는 경우:
'use client'
import { incrementViews } from './actions'
import { useState, useEffect } from 'react'
export default function ViewCount({ initialViews }: { initialViews: number }) {
const [views, setViews] = useState(initialViews)
useEffect(() => {
const updateViews = async () => {
const updatedViews = await incrementViews()
setViews(updatedViews)
}
updateViews()
}, [])
return <p>Total Views: {views}</p>
}
useEffect의 동작과 주의사항을 고려해야 합니다.
오류가 발생하면 클라이언트에서 가장 가까운 error.js 또는 Suspense 경계에 포착됩니다. UI에서 처리할 오류를 반환하려면 try/catch를 사용하는 것이 좋습니다.
예를 들어, 서버 작업은 다음 메시지를 반환하여 새 항목을 생성할 때 발생하는 오류를 처리할 수 있습니다.
'use server'
export async function createTodo(prevState: any, formData: FormData) {
try {
// Mutate data
} catch (e) {
throw new Error('생성에 실패하였습니다')
}
}
revalidatePath API를 사용하여 서버 작업 내에서 Next.js 캐시의 유효성을 다시 검사할 수 있습니다.
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
try {
// ...
} catch (error) {
// ...
}
revalidatePath('/posts')
}
또는 revalidateTag를 사용하여 캐시 태그로 특정 데이터 가져오기를 무효화합니다.
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost() {
try {
// ...
} catch (error) {
// ...
}
revalidateTag('posts')
}
서버 작업 완료 후 사용자를 다른 경로로 리디렉션하려면 리디렉션 API를 사용할 수 있습니다. 리디렉션은 try/catch 블록 외부에서 호출되어야 합니다.
'use server'
import { redirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export async function createPost(id: string) {
try {
// ...
} catch (error) {
// ...
}
revalidateTag('posts') // Update cached posts
redirect(`/post/${id}`) // Navigate to the new post page
}
cookies API를 사용하여 서버 작업 내부에서 쿠키를 가져오고, 설정하고, 삭제할 수 있습니다.
'use server'
import { cookies } from 'next/headers'
export async function exampleAction() {
// Get cookie
const value = cookies().get('name')?.value
// Set cookie
cookies().set('name', 'Delba')
// Delete cookie
cookies().delete('name')
}
공개 API 엔드포인트와 마찬가지로 서버 작업을 처리해야 하며 사용자에게 작업을 수행할 권한이 있는지 확인해야 합니다.
'use server'
import { auth } from './lib'
export function addItem() {
const { user } = auth()
if (!user) {
throw new Error('You must be signed in to perform this action')
}
// ...
}
구성 요소 내부에 서버 작업을 정의하면 해당 작업이 외부 함수 범위에 액세스할 수 있는 클로저가 생성됩니다. 예를 들어 게시 작업은 publishVersion 변수에 액세스할 수 있습니다.
'use client'
export default function Page() {
const publishVersion = await getLatestVersion();
async function publish(formData: FormData) {
"use server";
if (publishVersion !== await getLatestVersion()) {
throw new Error('The version has changed since pressing publish');
}
...
}
return <button onClick={publish}>Publish</button>;
}
클로저는 렌더링 시점의 데이터를 캡처하여 나중에 사용할 수 있게 해주는데, 이를 보안상의 이유로 Next.js가 자동으로 암호화합니다. 새로운 개인 키가 각 액션마다 생성되며, 이는 특정 빌드에서만 액션을 호출할 수 있도록 합니다.
여러 서버에 걸쳐 Next.js 애플리케이션을 자체 호스팅하는 경우 각 서버 인스턴스는 서로 다른 암호화 키로 종료되어 잠재적인 불일치가 발생할 수 있습니다.
이를 완화하려면 process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY 환경 변수를 사용하여 암호화 키를 덮어쓸 수 있습니다. 이 변수를 지정하면 암호화 키가 빌드 전반에 걸쳐 지속되고 모든 서버 인스턴스가 동일한 키를 사용하게 됩니다.
이는 여러 배포에서 일관된 암호화 동작이 애플리케이션에 중요한 고급 사용 사례입니다. 키 순환 및 서명과 같은 표준 보안 관행을 고려해야 합니다.
서버 작업은 CSRF 공격에 노출될 수 있으므로 POST 메서드만 허용됩니다. 이는 SameSite 쿠키와 함께 사용되어 대부분의 CSRF 취약성을 방지합니다. Next.js의 서버 작업은 Origin 헤더를 Host 헤더와 비교하여 요청을 거부합니다. 역방향 프록시를 사용하는 애플리케이션의 경우 serverActions.allowedOrigins 옵션을 사용하여 안전한 원본을 명시적으로 지정할 수 있습니다.
/** @type {import('next').NextConfig} */
module.exports = {
experimental: {
serverActions: {
allowedOrigins: ['my-proxy.com', '*.my-proxy.com'],
},
},
}
React 및 Next.js에서 데이터를 가져오기 위한 몇 가지 권장 패턴과 모범 사례가 있습니다. 아래에서는 이에 대한 내용을 다루어보겠습니다.
가능하다면 서버 구성요소를 사용하여 서버에서 데이터를 가져오는 것이 좋습니다. 이를 통해 다음을 수행할 수 있습니다.
백엔드 데이터 리소스(예: 데이터베이스)에 직접 액세스할 수 있습니다.
액세스 토큰, API 키 등 민감한 정보가 클라이언트에 노출되는 것을 방지하여 애플리케이션을 더욱 안전하게 유지할 할 수 있습니다.
동일한 환경에서 데이터를 가져오고 렌더링합니다. 이렇게 하면 클라이언트와 서버 간의 앞뒤 통신은 물론 클라이언트의 기본 스레드 작업도 줄어듭니다.
클라이언트에서 여러 개별 요청을 수행하는 대신 단일 왕복으로 여러 데이터 가져오기를 수행합니다.
클라이언트-서버 Waterfall을 줄입니다.
여기서 Waterfall 이란 순차적으로 물흐르듯 네트워크 상의 흐름이 발생하는 것을 뜻합니다.
지역에 따라 데이터 가져오기가 데이터 소스에 더 가까운 곳에서 발생하여 대기 시간이 줄어들고 성능이 향상될 수도 있습니다.
그런 다음 서버 작업을 사용하여 데이터를 변경하거나 업데이트할 수 있습니다.
트리의 여러 구성 요소에서 동일한 데이터(예: current user)를 사용해야 하는 경우 전역적으로 데이터를 가져오거나 구성 요소 간에 소품을 전달할 필요가 없습니다. 대신 동일한 데이터에 대해 여러 번 요청하는 경우 성능에 미치는 영향을 걱정하지 않고 데이터가 필요한 구성 요소에서 fetch 또는 React 캐시를 사용할 수 있습니다.
이는 가져오기 요청이 자동으로 메모되기 때문에 가능합니다.
스트리밍 및 서스펜스는 클라이언트에 UI의 렌더링된 단위를 점진적으로 렌더링하고 증분적으로 스트리밍할 수 있게 해주는 React 기능입니다.
서버 구성 요소 및 중첩 레이아웃을 사용하면 특별히 데이터가 필요하지 않은 페이지 부분을 즉시 렌더링하고 데이터를 가져오는 페이지 부분에 대한 로딩 상태를 표시할 수 있습니다. 즉, 사용자는 전체 페이지가 로드되어 상호 작용을 시작할 때까지 기다릴 필요가 없습니다.

React 구성 요소 내에서 데이터를 가져올 때 병렬 및 순차라는 두 가지 데이터 가져오기 패턴을 알아야 합니다.

중첩된 구성 요소가 있고 각 구성 요소가 자체 데이터를 가져오는 경우 해당 데이터 요청이 다르면 데이터 가져오기가 순차적으로 발생합니다(자동으로 메모되므로 동일한 데이터에 대한 요청에는 적용되지 않습니다).
예를 들어, Playlists 구성 요소는 Artist 구성 요소가 데이터 가져오기를 완료한 후에만 데이터 가져오기를 시작합니다. 왜냐하면 재생 목록은 ArtistID prop에 의존하기 때문입니다.
// ...
async function Playlists({ artistID }: { artistID: string }) {
// Wait for the playlists
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 },
}: {
params: { username: string }
}) {
// Wait for the artist
const artist = await getArtist(username)
return (
<>
<h1>{artist.name}</h1>
<Suspense fallback={<div>Loading...</div>}>
<Playlists artistID={artist.id} />
</Suspense>
</>
)
}
이와 같은 경우에는 loading.js(route 세그먼트의 경우) 또는 React Suspense (중첩된 구성 요소의 경우)를 사용하여 React가 결과를 스트리밍하는 동안 즉각적인 로딩 상태를 표시할 수 있습니다.
이렇게 하면 데이터 가져오기로 인해 전체 경로가 차단되는 것을 방지할 수 있으며 사용자는 차단되지 않은 페이지 부분과 상호 작용할 수 있습니다.
데이터 요청 차단:
Waterfall을 방지하는 또 다른 접근 방식은 애플리케이션의 루트에서 전역적으로 데이터를 가져오는 것입니다. 하지만 이렇게 하면 데이터 로드가 완료될 때까지 그 아래의 모든 경로 세그먼트에 대한 렌더링이 차단됩니다.
await이 포함된 가져오기 요청은 Suspense 경계에 래핑되거나 loading.js가 사용되지 않는 한 그 아래에 있는 전체 트리에 대한 렌더링 및 데이터 가져오기를 차단합니다. 또 다른 대안은 병렬 데이터 가져오기 또는 사전 로드 패턴을 사용하는 것입니다.
데이터를 병렬로 가져오려면 데이터를 사용하는 구성 요소 외부에서 요청을 정의한 다음 구성 요소 내부에서 호출하여 요청을 적극적으로 시작할 수 있습니다. 이렇게 하면 두 요청을 병렬로 시작하여 시간이 절약되지만 두 약속이 모두 해결될 때까지 사용자는 렌더링된 결과를 볼 수 없습니다.
아래 예에서 getArtist 및 getArtistAlbums 함수는 페이지 구성 요소 외부에서 정의된 다음 구성 요소 내부에서 호출되며 두 약속이 모두 해결될 때까지 기다립니다.
import Albums from './albums'
async function getArtist(username: string) {
const res = await fetch(`https://api.example.com/artist/${username}`)
return res.json()
}
async function getArtistAlbums(username: string) {
const res = await fetch(`https://api.example.com/artist/${username}/albums`)
return res.json()
}
export default async function Page({
params: { username },
}: {
params: { username: string }
}) {
// Initiate both requests in parallel
const artistData = getArtist(username)
const albumsData = getArtistAlbums(username)
// Wait for the promises to resolve
const [artist, albums] = await Promise.all([artistData, albumsData])
return (
<>
<h1>{artist.name}</h1>
<Albums list={albums}></Albums>
</>
)
}
UX을 향상시키기 위해 Suspense Boundary를 추가하여 렌더링 작업을 분할하고 결과의 일부를 최대한 빨리 표시할 수 있습니다.
Waterfall 방지를 위한 다른 방법으로는 preload 패턴을 사용하는 것이 있습니다. preload 함수를 만들어 병렬 데이터 가져오기를 더욱 최적화할 수 있습니다. 이 패턴을 사용하면 프로미스를 props로 전달할 필요가 없습니다. preload 함수의 이름은 API가 아니라 패턴이기 때문에 자유롭게 지정할 수 있습니다.
import { getItem } from '@/utils/get-item'
export const preload = (id: string) => {
// void evaluates the given expression and returns undefined
// https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
void getItem(id)
}
export default async function Item({ id }: { id: string }) {
const result = await getItem(id)
// ...
}
import Item, { preload, checkIsAvailable } from '@/components/Item'
export default async function Page({
params: { id },
}: {
params: { id: string }
}) {
// starting loading item data
preload(id)
// perform another asynchronous task
const isAvailable = await checkIsAvailable()
return isAvailable ? <Item id={id} /> : null
}
캐시 기능, Preload 패턴 및 server-only 패키지를 결합하여 앱 전체에서 사용할 수 있는 데이터 가져오기 유틸리티를 만들 수 있습니다.
import { cache } from 'react'
import 'server-only'
export const preload = (id: string) => {
void getItem(id)
}
export const getItem = cache(async (id: string) => {
// ...
})
이 접근 방식을 사용하면 데이터를 적극적으로 가져오고 응답을 캐싱하며 이 데이터 가져오기가 서버에서만 발생하도록 보장할 수 있습니다.
utils/get-item 내보내기는 레이아웃, 페이지 또는 기타 구성 요소에서 항목의 데이터를 가져오는 때를 제어하는 데 사용할 수 있습니다.
tip!
서버 데이터 가져오기 기능이 클라이언트에서 사용되지 않도록 하려면 서버 전용 패키지를 사용하는 것이 좋습니다.
전체 객체 인스턴스나 민감한 값이 클라이언트에 전달되는 것을 방지하려면 React의 taint API인 taintObjectReference 및 taintUniqueValue를 사용하는 것이 좋습니다.
애플리케이션에서 오염을 활성화하려면 Next.js Config Experiment.taint 옵션을 true로 설정하세요.
module.exports = {
experimental: {
taint: true,
},
}
그런 다음 오염시키려는 객체 또는 값을 Experiment_taintObjectReference 또는 Experiment_taintUniqueValue 함수에 전달합니다.
//util.ts
import { queryDataFromDB } from './api'
import {
experimental_taintObjectReference,
experimental_taintUniqueValue,
} from 'react'
export async function getUserData() {
const data = await queryDataFromDB()
experimental_taintObjectReference(
'Do not pass the whole user object to the client',
data
)
experimental_taintUniqueValue(
"Do not pass the user's address to the client",
data,
data.address
)
return data
}
// page.tsx
import { getUserData } from './data'
export async function Page() {
const userData = getUserData()
return (
<ClientComponent
user={userData} // this will cause an error because of taintObjectReference
address={userData.address} // this will cause an error because of taintUniqueValue
/>
)
}