Server Action(서버 액션)이란 브라우저에서 호출할 수 있는 서버에서 실행되는 비동기 함수로서, 폼 제출 및 데이터 변조를 처리하기 위해 서버에서 실행된다. html <form> 태그의 action 속성에 함수를 지정하고, 해당 함수에 'use server' 지시자가 있으면 함수는 Next 서버에서만 실행된다. 이를 통해, 오직 자바스크립트 함수를 통해서 브라우저와 서버 간의 통신을 가능하게 한다.
서버 액션을 만들면, 'use server' 하위 코드에 대한 api가 자동으로 생성되며 브라우저에서 <form> 태그를 제출하면 자동으로 호출된다.
'use server' 지시어를 추가한다.'use server' 지시어를 추가한다.function ReviewEditor() {
async function createReviewAction(formData: FormData) {
"use server";
// File 타입은 받지 않으므로 null이 아니면 문자열로 변환
const content = formData.get("content")?.toString();
const author = formData.get("author")?.toString();
}
return (
<section>
<form action={createReviewAction}>
<input name="content" placeholder="리뷰 내용" />
<input name="author" placeholder="작성자" />
<button type="submit">작성하기</button>
</form>
</section>
);
}
bind 메서드 사용JavaScript bind 메서드를 사용하여 Server Action에 추가 인수를 전달할 수 있다.
'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>
)
}
hidden 타입의 입력 필드 전달<input type="hidden" name="userId" value={userId} /> 와 같이 폼에 숨겨진 입력 필드로 인수를 전달할 수 있다.
function ReviewEditor({ movieId }: { movieId: string }) {
return (
<section>
<form action={createReviewAction}>
<input name="movieId" value={movieId} hidden readOnly />
<input type="text" name="content" placeholder="리뷰를 입력하세요..." />
<input type="tex" name="author" placeholder="작성자" />
<button type="submit">리뷰 작성</button>
</form>
</section>
);
}
"use server";
export default async function createReviewAction(formData: FormData) {
const movieId = formData.get("movieId")?.toString();
const content = formData.get("content")?.toString();
const author = formData.get("author")?.toString();
if (!content || !author || !movieId) {
return;
}
}
HTML <form> 요소의 데이터는 자동으로 formData 객체로 묶여 단일 매개변수로 전달된다.
Server Action으로 데이터를 성공적으로 변경(추가, 삭제 등)한 후, 변경사항을 반영하여 UI를 업데이트하고 관련 캐시를 무효화해야 한다. 이를 통해 사용자는 항상 최신 정보를 볼 수 있다.
revalidatePath특정 경로에 해당하는 캐시된 데이터를 없앤다. 즉, 데이터를 다시 불러온다.

path: 특정 경로나 파일명이 올 수 있다.type: page이거나 layout이 올 수 있다.특정 주소에 해당하는 페이지만 재검증
revalidatePath(`/book/${bookId}`);
특정 경로의 모든 동적 페이지를 재검증
revalidatePath("/book/[id]", "page");
특정 레이아웃을 갖는 모든 페이지를 재검증
```tsx
// "레이아웃이 위치한 경로", "layout"
revalidatePath("/(with-searchbar)", "layout");
// 응용) 모든 데이터 재검증
revalidatePath("/", "layout")
```
revalidateTag태그 기준으로 데이터 캐시 재검증한다. “특정 주소에 해당하는 페이지만 재검증” 시 해당 페이지 내에서 서로 다른 데이터 페칭이 2개 이상 존재하는 경우, 불필요하게 페이지 내 모든 캐시를 재검증하는 경우가 생길 수 있다. 따라서 페칭 자체에 태그를 달아 이러한 문제를 해결할 수 있다.
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_SERVER_URL}/review/book/${bookId}`,
{ next: { tags: [`review-${bookId}`] } } // revalidateTag을 사용하기 위해 추가
);
// ---
revalidateTag(`review-${bookId}`); // 서버 액션 측