'use server' 로 폼 제출을 서버에서 처리한다.
서버 액션과 폼
서버 액션은 클라이언트에서 서버 함수를 직접 호출하게 해 주는 기능입니다. 별도 API 엔드포인트를 만들지 않고도 폼 제출, DB 쓰기 같은 변경(mutation)을 처리할 수 있습니다. 'use server' 한 줄이면 시작됩니다.
학습 목표
'use server'로 서버 액션을 정의한다.<form action={...}>에 서버 액션을 연결한다.useFormStatus,useActionState로 제출 상태를 다룬다.revalidatePath로 캐시를 갱신하고 폼을 검증한다.
서버 액션 정의
'use server' 를 함수 안 첫 줄에 두거나, 파일 맨 위에 두면 그 함수(들)는 서버에서 실행되는 액션이 됩니다. 폼의 action 에 직접 연결할 수 있습니다.
// app/new/page.tsx — 서버 컴포넌트
import { revalidatePath } from 'next/cache';
async function createTodo(formData: FormData) {
'use server';
const title = formData.get('title') as string;
await db.todo.create({ data: { title } });
revalidatePath('/'); // 목록 페이지 캐시 갱신
}
export default function NewTodo() {
return (
<form action={createTodo}>
<input name="title" placeholder="할 일" />
<button type="submit">추가</button>
</form>
);
}
JavaScript가 로드되기 전에도 폼이 동작합니다(점진적 향상). formData.get(name) 으로 입력값을 읽습니다.
revalidatePath / revalidateTag
데이터를 바꾼 뒤에는 화면에 반영되도록 캐시를 무효화해야 합니다.
import { revalidatePath, revalidateTag } from 'next/cache';
revalidatePath('/todos'); // 특정 경로의 캐시 갱신
revalidateTag('todos'); // 태그가 붙은 fetch 캐시 갱신
💡 TIP —
redirect('/todos')를 액션 끝에서 호출하면 작업 후 다른 페이지로 이동시킬 수도 있습니다.redirect는next/navigation에서 가져옵니다.
제출 상태 — useFormStatus
제출 중 버튼을 비활성화하려면 useFormStatus 를 씁니다. 이 훅은 폼 안쪽의 클라이언트 컴포넌트에서만 동작합니다.
'use client';
import { useFormStatus } from 'react-dom';
export function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? '저장 중...' : '저장'}
</button>
);
}
결과·검증 — useActionState
서버 액션의 반환값(검증 메시지 등)을 화면에 보여주려면 useActionState 를 씁니다. 액션의 첫 인자로 이전 상태가 들어옵니다.
// actions.ts
'use server';
export async function submit(prev: unknown, formData: FormData) {
const email = formData.get('email') as string;
if (!email.includes('@')) {
return { error: '이메일 형식이 올바르지 않습니다.' };
}
await save(email);
return { error: null, ok: true };
}
'use client';
import { useActionState } from 'react';
import { submit } from './actions';
export default function Form() {
const [state, action] = useActionState(submit, { error: null });
return (
<form action={action}>
<input name="email" />
{state.error && <p style={{ color: 'red' }}>{state.error}</p>}
<button type="submit">구독</button>
</form>
);
}
⚠️ 주의 — 서버 액션은 누구나 호출할 수 있는 엔드포인트와 같습니다. 입력값 검증과 권한 확인을 반드시 서버 쪽에서 하세요. 클라이언트 검증만 믿으면 안 됩니다.
요약
'use server'로 정의한 함수를<form action={...}>에 직접 연결한다.formData.get()으로 입력값을 읽고,revalidatePath/revalidateTag로 캐시를 갱신한다.useFormStatus로 제출 중 상태를,useActionState로 결과·검증 메시지를 다룬다.- 검증과 권한 확인은 반드시 서버에서 한다.
연습문제
- 입력한 제목을 목록에 추가하는 서버 액션을 만들고, 추가 후
revalidatePath로 목록을 갱신하세요. useFormStatus로 제출 중 버튼을 "저장 중..."으로 바꾸고 비활성화하세요.- 이메일에
@가 없으면 에러 메시지를 반환하는 검증을useActionState로 화면에 표시하세요. - 액션 성공 후
redirect로 목록 페이지로 이동시켜 보세요.
힌트 —
useFormStatus는 폼 내부 의 클라이언트 컴포넌트에서만pending을 읽을 수 있습니다.useActionState(action, 초기상태)의 반환값[state, action]에서action을 폼에 연결하세요.
💡 연습문제 풀이
불러오는 중…
댓글 0
“Next.js” 강좌에 대한 댓글입니다.