서버 액션(alpha)
서버 액션은 API를 작성하지 않고 함수 수준에서 서버에 직접 접근해 데이터 요청 등을 수행할 수 있는 기능이다. 서버 액션을 사용하기 위해서는 함수 내부 혹은 파일 상단에 'use server' 지시자를 선언해야 하며, 해당 함수는 반드시 async여야 한다.
form의 action
export default function Page() {
async function handleSubmit() {
**'use server'**
console.log('해당 작업은 서버에서 수행합니다. 따라서 CORS 이슈가 없습니다.')
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'post',
body: JSON.stringify({
title: 'foo',
body: 'bar',
userId: 1,
}),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
})
const result = await response.json()
console.log(result)
}
return (
<div className="space-y-4">
<form **action={handleSubmit}**>
<button type="submit">form 요청 보내보기</button>
</form>
</div>
)
}
input의 submit과 image의 formAction
input type="submit"과 input type="image"에 fromAction prop으로 서버 액션을 추가할 수 있다.
startTransition과의 연동
startTransition을 사용해 서버 액션을 실행할 수도 있으며, isPending을 활용해 컴포넌트 단위의 로딩 처리도 가능하다.
export default async function Page({ params }: { params: { id: string } }) {
const key = `test:${params.id}`;
const data = await kv.get<Data>(key);
return (
<div className="space-y-4">
<h1 className="text-xl font-medium text-gray-400/80">form with data</h1>
<h2 className="text-l font-medium text-gray-400/80">
서버에 저장된 정보: {data?.name} {data?.age}
</h2>
<div className="space-y-4">
<ul className="list-disc space-y-2 pl-4 text-sm text-gray-300">
<li>아래 버튼을 누르면 서버에서 직접 form 요청을 보냅니다.</li>
<li>이 작업은 useTransition을 기반으로 실행됩니다.</li>
<li>
<ClientButtonComponent id={params.id} />
</li>
</ul>
</div>
</div>
);
}
"use client";
import { useCallback, useTransition } from "react";
import { updateData } from "#server-action";
import { SkeletonBtn } from "#components/components";
export function ClientButtonComponent({ id }: { id: string }) {
const [isPending, startTransition] = useTransition();
const handleClick = useCallback(() => {
startTransition(() => updateData(id, { name: "기본값", age: 0 }));
}, []);
return isPending ? (
<SkeletonBtn />
) : (
<button onClick={handleClick}>기본값으로 돌리기</button>
);
}
"use server";
import kv from "@vercel/kv";
import { revalidatePath } from "next/cache";
import { cookies } from "next/headers";
export async function updateData(
id: string,
data: { name: string; age: number }
) {
const key = `test:${id}`;
await kv.set(key, {
name: data.name,
age: data.age,
});
revalidatePath(`/server-action/form/${id}`);
}
server mutation이 없는 작업
server mutation이 필요하다면 서버 액션을 useTransition으로 감싸서 써야하지만, 별도의 server mutation이 없다면 생략해도 된다.
서버 액션 사용 시 주의할 점
서버 액션은 클라이언트 컴포넌트 내에 정의 될 수 없다. "use server"가 선언된 파일에 서버 액션을 작성하고 이를 import해야 한다. 서버 액션을 props 형태로 클라이언트 컴포넌트에 넘길 수도 있다.
Reference
모던 리액트 딥다이브
https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#