서버 액션은 클라이언트에서 직접 서버 함수를 호출할 수 있도록 도와주는 기능이다.
기존의 API 라우트 또는 서버리스 함수 없이도 폼 제출, 데이터 처리, 데이터베이스 업데이트 등을 간결하게 구현할 수 있다.
서버 액션은 서버에서 실행되는 비동기 함수이며, use server 디렉티브를 통해 선언할 수 있다.
이를 통해 클라이언트에서 직접 호출 가능하다.
function ReviewEditor() {
async function createReviewAction(formData: FormData) {
'use server'; // 'use server' 지시자로 서버액션 선언
const content = formData.get('content')?.toString();
const author = formData.get('author')?.toString();
console.log(content, author);
}
return (
<section>
<form action={createReviewAction}>
<input name="content" placeholder="리뷰 내용" />
<input name="author" placeholder="작성자" />
<button type="submit">작성하기</button>
</form>
</section>
);
}
폼을 제출하면 서버에서 createReviewAction 함수가 실행되고, 클라이언트는 응답을 받아 UI를 갱신할 수 있다.
API 라우트 없이 데이터 처리
기존에는 데이터를 처리하려면 /api/route.ts를 생성해야 했지만, 서버 액션을 사용하면 서버에서 직접 데이터를 처리할 수 있다.
클라이언트에서 직접 서버 함수 호출
기존의 API 요청(fetch, axios) 없이, 클라이언트에서 서버 함수를 직접 호출 가능하다.
보안성 강화
서버에서만 실행되므로 클라이언트에서 코드를 확인할 수 없으며, 환경 변수(process.env) 접근이 가능하다.
결론적으로, 서버 액션은 API 라우트 없이 서버에서 직접 데이터를 처리할 수 있는 강력한 기능이다.
간결한 코드로 서버 요청을 처리할 수 있어 폼 제출, 데이터베이스 업데이트, 보안이 필요한 로직 실행에 유용하다.
서버 액션을 사용할 때, Next.js는 기본적으로 페이지를 정적으로 캐시하여 성능을 최적화한다. 하지만, 데이터가 변경되었을 때 즉시 최신 데이터를 반영하려면 revalidatePath 함수를 사용해야 한다.
revalidatePath(경로)를 호출하면, 해당 경로의 정적 캐시를 무효화하고 새로운 데이터를 불러오도록 강제 갱신할 수 있다.
사용자를 추가한 후, 홈 페이지(/)의 캐시를 무효화하여 최신 데이터가 즉시 반영되도록 한다.
// app/actions.ts
"use server";
import { revalidatePath, revalidateTag } from 'next/cache';
export async function createReviewAction(formData: FormData) {
const bookId = formData.get('bookId')?.toString();
const content = formData.get('content')?.toString();
const author = formData.get('author')?.toString();
if (!bookId || !content || !author) {
return;
}
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_SERVER_URL}/review`,
{
method: 'post',
body: JSON.stringify({ bookId, content, author }),
},
);
// 1. 특정 주소에 해당하는 페이지만 재검증
revalidatePath(`/book/${bookId}`);
// 2. 특정 경로의 모든 동적 페이지를 재검증
revalidatePath('/book/[id]', 'page');
// 3. 특정 레이아웃을 갖는 모든 페이지 재검증
revalidatePath('/(with-searchbar)', 'layout');
// 4. 모든 데이터 재검증
revalidatePath('/', 'layout');
// 5. 특정 태그를 갖는 데이터 캐시 재검증
revalidateTag(`review-${bookId}`);
} catch (err) {
console.error(err);
return;
}
}