서버 액션은 React 액션을 기반으로 구축한 Next.js의 알파 기능입니다. 서버 측 데이터 변경, 클라이언트 측 자바스크립트 감소, 점진적으로 개선된 폼을 지원합니다. 서버 컴포넌트 내부에서 정의하거나 클라이언트 컴포넌트에서 호출할 수 있습니다.
서버 컴포넌트에서
import { cookies } from 'next/headers'
// Server action defined inside a Server Component
export default function AddToCart({ productId }) {
async function addItem(data) {
'use server'
const cartId = cookies().get('cartId')?.value
await saveToDb({ cartId, data })
}
return (
<form action={addItem}>
<button type="submit">Add to Cart</button>
</form>
)
}
클라이언트 컴포넌트에서
// app/action.js
'use server'
export async function addItem(data) {
const cartId = cookies().get('cartId')?.value
await saveToDb({ cartId, data })
}
// app/add-to-cart.js
'use client'
import { addItem } from './actions.js'
// Server Action being called inside a Client Component
export default function AddToCart({ productId }) {
return (
<form action={addItem}>
<button type="submit">Add to Cart</button>
</form>
)
}
Good to Know
- 서버 액션을 사용하면 React
experimental
채널이 실행됩니다.- React Actions,
useOptimistic
,useFormStatus
는 Next.js 또는 React Server 컴포넌트 전용 기능이 아닙니다.- Next.js는
revalidateTag
와revalidatePath
와 같은 데이터 변이 API를 추가하는 것을 포함해 React 액션을 Next.js 라우터, 번들러 및 캐싱 시스템에 통합합니다.
실험용 serverActions
플래그를 활성화아여 Next.js 프로젝트에서 서버 액션을 활성화할 수 있습니다.
module.exports = {
experimental: {
serverActions: true,
},
}
서버 액션을 두 곳에서 정의할 수 있습니다.
함수 본문 상단에 'use server'
지시문을 사용하여 비동기 함수를 정의하여 서버 액션을 생성합니다. 이 함수는 직렬화 가능한 인자와 React 서버 컴포넌트 프로토콜에 기반한 직렬화 가능한 반환값을 가져야 합니다.
export default function ServerComponent() {
async function myAction() {
'use server'
// ...
}
}
클라이언트 컴포넌트 내에서 서버 액션을 사용하는 경우, 파일 상단에 'use server'
지시문을 사용하는 별도의 파일에 액션을 생성합니다. 그리고 서버 액션을 클라이언트 파일로 가져옵니다.
// app/actions.js
'use server'
export async function myAction() {
// ...
}
// app/client-component.js
'use client'
import { myAction } from './actions'
export default function ClientComponent() {
return (
<form action={myAction}>
<button type="submit">Add to Cart</button>
</form>
)
}
다음 방법을 사용하여 서버 액션을 호출할 수 있습니다.
action
사용 : React의 action
프로퍼티를 사용하면 <form>
엘리먼트에서 서버 액션을 호출할 수 있습니다.formAction
사용 : React의 formAction
프로퍼티를 사용하면 <form>
에서 <button>
, <input type='submit'>
, <input type='image'>
요소를 처리할 수 있습니다.startTransition
을 사용한 사용자 정의 호출 : startTransition
을 사용하여 action
또는 formAction
을 사용하지 않고 서버 액션을 호출합니다. 이 방법은 프로그래시브 향상을 비활성화합니다.React action
프로퍼티를 사용해 form
앨리먼트에서 서버 액션을 호출할 수 있습니다. 액션 프로퍼티와 함께 전달된 서버 액션은 사용자 상호작용에 대한 응답으로 비동기 부수 효과로 작동합니다.
export default function AddToCart({ productId }) {
async function addItem(data) {
'use server'
const cartId = cookies().get('cartId')?.value
await saveToDb({ cartId, data })
}
return (
<form action={addItem}>
<button type="submit">Add to Cart</button>
</form>
)
}
formAction
프로퍼티를 사용하여 button
또는, input type='submit'
, input type='image'
와 같은 요소에 대한 폼 액션을 처리할 수 있습니다. formAction
프로퍼티는 form
요소의 action
보다 우선 시 됩니다.
export default function Form() {
async function handleSubmit() {
'use server'
// ...
}
async function submitImage() {
'use server'
// ...
}
return (
<form action={handleSubmit}>
<input type="text" name="name" />
<input type="image" formAction={submitImage} />
<button type="submit">Submit</button>
</form>
)
}
action
또는 formAction
을 사용하지 않고 서버 액션을 호출할 수도 있습니다. 이 작업은 useTransition
훅에서 제공하는 startTransition
을 사용하여 수행할 수 있으며, 이는 form
, button
또는 inputs
에서 서버 액션을 사용하려는 경우에 유용할 수 있습니다.
'use client'
import { useTransition } from 'react'
import { addItem } from '../actions'
function ExampleClientComponent({ id }) {
let [isPending, startTransition] = useTransition()
return (
<button onClick={() => startTransition(() => addItem(id))}>
Add To Cart
</button>
)
}
'use server'
export async function addItem(id) {
await addItemToDb(id)
// Marks all product pages for revalidating
revalidatePath('/product/[id]')
}
Server Mutations을 수행하지 않을 경우 다른 함수처럼 함수를 직접 프로퍼티로 전달할 수 있습니다.
import kv from '@vercel/kv'
import LikeButton from './like-button'
export default function Page({ params }: { params: { id: string } }) {
async function increment() {
'use server'
await kv.incr(`post:id:${params.id}`)
}
return <LikeButton increment={increment} />
}
'use client'
export default function LikeButton({
increment,
}: {
increment: () => Promise<void>
}) {
return (
<button
onClick={async () => {
await increment()
}}
>
Like
</button>
)
}
실험적 useOptimistic
훅은 애플리케이션에서 낙관적 업데이트를 구현하는 방법을 제공합니다. 낙관적 업데이트는 앱의 반응성을 높여 사용자 경험을 향상시키는 기술입니다.
서버 액션이 호출되면 응답을 기다리지 예상 결과를 반환하여 UI가 즉시 업데이트 됩니다.
'use client'
import { experimental_useOptimistic as useOptimistic } from 'react'
import { send } from './actions.js'
export function Thread({ messages }) {
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [...state, { message: newMessage, sending: true }]
)
const formRef = useRef()
return (
<div>
{optimisticMessages.map((m) => (
<div>
{m.message}
{m.sending ? 'Sending...' : ''}
</div>
))}
<form
action={async (formData) => {
const message = formData.get('message')
formRef.current.reset()
addOptimisticMessage(message)
await send(message)
}}
ref={formRef}
>
<input type="text" name="message" />
</form>
</div>
)
}
실험적 useFormStatus
훅을 Form Action
내에서 사용할 수 있으며 pending
프로퍼티를 제공합니다.
'use client'
import { experimental_useFormStatus as useFormStatus } from 'react-dom'
function Submit() {
const { pending } = useFormStatus()
return (
<input
type="submit"
className={pending ? 'button-pending' : 'button-normal'}
disabled={pending}
>
Submit
</input>
)
}
점진적 향상 기능을 사용하면 자바스크립트가 없거나 자바스크립트가 비활성된 상태에서도 <form>
이 제대로 작동할 수 있습니다. 이를 통해 사용자는 form
의 JavaScript가 아직 로드되지 않았거나 로드에 실패하더라도 양식과 상호 작용하고 데이터를 제출할 수 있습니다.
서버 Form Action
과 클라이언트 Form Action
모두 두 가지 전략 중 하나를 사용하여 점진적 향상을 지원합니다.
서버 액션이 <form>
에 직접 전달되는 경우, 자바스크립트가 비활성화되어 있어도 form
은 대화형입니다.
클라이언트 액션이 <form>
으로 전달되는 경우 form
은 여전히 대화형이지만 양식이 하이드레이트될 때까지 액션이 대기열에 배치됩니다. <form>
은 Selective Hydration으로 우선 순위가 지정되므로 빠르게 처리됩니다.
'use client'
import { useState } from 'react'
import { handleSubmit } from './actions.js'
export default function ExampleClientComponent({ myAction }) {
const [input, setInput] = useState()
return (
<form action={handleSubmit} onChange={(e) => setInput(e.target.value)}>
{/* ... */}
</form>
)
}
두 경우 모두 form
은 하이드레이션이 발생하기 전에 대화형입니다. 서버 액션은 클라이언트 자바스크립트에 의존하지 않는다는 추가적인 이점이 있지만, 원하는 경우 상호 작용을 유지하면서 클라이언트 액션으로 추가 동작을 구성할 수 있습니다.
기본적으로 서버 액션으로 전송되는 요청 본문의 최대 크기는 1MB입니다. 이렇게 하면 대량의 데이터가 서버로 전송되어 구문 분석에 많은 리소스를 소모하는 것을 방지할 수 있습니다.
그러나 실험적인 serverActionsBodySizeLimit
옵션을 사용하여 이 제한을 구성할 수 있습니다. 이 옵션은 바이트 수 또는 바이트에서 지원하는 모든 문자열 형식을 할 수 있습니다
module.exports = {
experimental: {
serverActions: true,
serverActionsBodySizeLimit: '2mb',
},
}