Server Actions는 Next.js 애플리케이션에서 폼 제출 및 데이터 변조를 처리하기 위해 서버에서 실행되는 비동기 함수이다. 이러한 함수는 서버 및 클라이언트 컴포넌트 모두에서 호출될 수 있다.
Server Actions를 사용하면 클라이언트와 서버 간의 복잡한 데이터 요청 흐름을 단순화할 수 있다. 이를 통해 서버에서 처리해야할 작업들을 클라이언트 코드에서 분리하고, 클라이언트에서 서버 함수를 간편하게 호출할 수 있다.
Server Actions는 Next.js 15에서 도입된 기능으로, 서버에서 실행되는 비동기 함수이다. React에서 API 호출을 통해 서버와 데이터를 주고받던 기존 방식과 비교하면, Server Actions는 데이터 요청 및 변조 흐름을 보다 단순하게 만들 수 있다. 이를 통해 개발자가 서버와 클라이언트 간의 데이터 전달 방식에서 불필요한 복잡성을 줄이고, 보안과 성능을 개선할 수 있다.
1. 불필요한 네트워크 요청
useEffect를 사용할 경우, 클라이언트에서 초기 렌더링 시 불필요한 네트워크 요청이 발생할 수 있음.2. 상태 관리의 복잡성 증가
useState에 저장하고, 이를 기반으로 UI를 갱신해야 함.react-query와 같은 별도의 라이브러리를 도입해야 할 수도 있음.3. 보안 취약점
4. 폼 제출 후 리디렉션 처리의 어려움
router.push 등을 이용해 다른 페이지로 이동해야 함.Server Actions를 활용하면 위와 같은 문제점을 해결하고, 보다 직관적인 데이터 요청 흐름을 구현할 수 있다.
1. 네트워크 요청 최소화
2. 상태 관리 간소화
useState나 useEffect 없이 Server Actions에서 데이터를 직접 처리할 수 있음.react-query 같은 상태 관리 라이브러리 없이도 비즈니스 로직을 서버에서 직접 실행할 수 있음.3. 보안 강화
4. 직관적인 데이터 요청 흐름
use server 지시어를 사용하여 함수 내에서 서버에서 실행됨.5. 폼 제출과 리디렉션을 간편하게 처리
action 속성에 직접 사용할 수 있어, onSubmit 이벤트 핸들러를 별도로 정의할 필요 없음.async function submitForm(formData: FormData) {
'use server';
// 데이터 저장 및 리디렉션
redirect('/success');
}
return <form action={submitForm}>...</form>;
Server Actions는 강력한 기능이지만, 모든 경우에 필요한 것은 아니다. 다음과 같은 경우에는 기존 방식이 더 적절할 수 있다.
1. 실시간 데이터가 필요한 경우
react-query 같은 클라이언트 캐싱 솔루션이 적절한 경우.2. 브라우저에서 직접 처리해야 하는 작업
3. 다른 API 호출이 필요한 경우
Server Action은 React의 "use server" 지시어를 사용하여 정의할 수 있다. async 함수의 상단에 이 지시어를 추가하여 해당 함수를 Server Action으로 표시하거나, 파일 상단에 지시어를 추가하여 해당 파일의 모든 내보내기를 Server Action으로 지정할 수 있다.
서버 컴포넌트에서는 인라인 함수 레벨 또는 모듈 레벨에서 "use server" 지시어를 사용할 수 있다. 인라인으로 Server Action을 추가하려면, 함수 본문 상단에 "use server"를 추가하면 된다.
// app/page.tsx
export default function Page() {
// Server Action
async function create() {
'use server';
// 데이터 변조 로직
}
return '...';
}
클라이언트 컴포넌트에서 Server Action을 호출하려면, 새로운 파일을 생성하고 파일 상단에 "use server" 지시어를 추가한다. 이 파일 내의 모든 함수는 서버 및 클라이언트 컴포넌트에서 재사용할 수 있는 Server Actions로 표시된다.
// app/actions.ts
'use server';
export async function create() {
// 데이터 변조 로직
}
// app/ui/button.tsx
'use client';
import { create } from '@/app/actions';
export function Button() {
return <button onClick={create}>Create</button>;
}
Server Action을 클라이언트 컴포넌트에 prop으로 전달할 수 있다.
<ClientComponent updateItemAction={updateItem} />
// app/client-component.tsx
'use client';
export default function ClientComponent({
updateItemAction,
}: {
updateItemAction: (formData: FormData) => void;
}) {
return <form action={updateItemAction}>{/* ... */}</form>;
}
서버 액션을 사용하여 폼 데이터를 처리하는 예시이다.
// app/invoices/page.tsx
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'),
};
// 데이터 변조 로직
// 캐시 재검증 로직
}
return (
<form action={createInvoice}>
<input name="customerId" type="text" placeholder="Customer ID" />
<input name="amount" type="number" placeholder="Amount" />
<input name="status" type="text" placeholder="Status" />
<button type="submit">Create Invoice</button>
</form>
);
}
requestSubmit() 메서드를 사용하여 Programmatic하게 폼 제출을 트리거하는 예시이다.
// app/entry.tsx
'use client';
import { useRef } from 'react';
export default function EntryForm() {
const formRef = useRef<HTMLFormElement>(null);
async function saveEntry(formData: FormData) {
'use server';
// 데이터 저장 로직
}
function handleKeyDown(event: React.KeyboardEvent) {
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
formRef.current?.requestSubmit();
}
}
return (
<form ref={formRef} action={saveEntry} onKeyDown={handleKeyDown}>
<textarea name="content" placeholder="Write your entry..." />
<button type="submit">Save Entry</button>
</form>
);
}
리액트 18버전에서 추가 된 useFormStatus 훅을 사용하여 폼의 로딩 상태를 표시하는 예시이다.
// components/SubmitButton.tsx
'use client';
import { experimental_useFormStatus as useFormStatus } from 'react-dom';
export default function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
);
}
// app/form/page.tsx
import SubmitButton from '@/components/SubmitButton';
export default function FormPage() {
async function handleSubmit(formData: FormData) {
'use server';
// 폼 데이터 처리 로직
}
return (
<form action={handleSubmit}>
<input name="username" type="text" placeholder="Username" />
<SubmitButton />
</form>
);
}
Server Actions는 기존의 클라이언트 API 호출 방식이 가지는 복잡성과 보안 문제를 해결할 수 있는 강력한 기능이다.
이를 사용하면 네트워크 요청을 최소화하고, 상태 관리를 단순화하며, 보안을 강화할 수 있다. 또한, 폼 제출 및 데이터 변조와 같은 기능을 간편하게 구현할 수 있어 Next.js 기반 애플리케이션의 개발 생산성을 크게 향상시킨다.
React에서 API 호출을 직접 수행하던 방식과 비교했을 때, Server Actions는 더욱 효율적이고 보안성이 높은 대안이 될 수 있다. 이를 적절히 활용하면 서버와 클라이언트 간 데이터 흐름을 단순화하면서도 성능과 유지보수성을 개선할 수 있다.