THE DEVLOG

scribbly.

Next JS 치트 시트

2025.05.16 12:22:16

아래는 Next.js App Router에서의 Server Action 개념과 revalidateTag, form action과 함께 쓰는 예시를 정리한 내용이다.

1. Server Action이란?

  • 서버에서 직접 실행되는 함수
  • 클라이언트에서 form, useFormState, useTransition 등으로 호출
  • use server 디렉티브가 반드시 함수 내에 있어야 함
  • 서버 컴포넌트에서만 정의 가능
// app/actions/createPost.ts
'use server';

export async function createPost(formData: FormData) {
  const title = formData.get('title');
  const content = formData.get('content');
  // DB에 저장 등 서버 로직
}

2. revalidateTag()로 캐시 무효화하기

  • 특정 fetch에 부여한 next: { tags: ['posts'] }를 무효화해서 ISR 캐시를 다시 불러오게 함
  • Server Action 내부에서만 호출 가능
import { revalidateTag } from 'next/cache';

'use server';

export async function createPost(formData: FormData) {
  const title = formData.get('title');
  const content = formData.get('content');

  // DB 저장 로직 (예시)
  await db.insert({ title, content });

  // 'posts' 태그 캐시 무효화
  revalidateTag('posts');
}

3. <form action={serverAction}> 사용 예시

예시 1: Form에서 바로 Server Action 호출

// app/posts/new/page.tsx
import { createPost } from '@/actions/createPost';

export default function NewPostPage() {
  return (
    <form action={createPost}>
      <input name="title" placeholder="제목" required />
      <textarea name="content" placeholder="내용" required />
      <button type="submit">게시하기</button>
    </form>
  );
}

동작 흐름

  1. 사용자가 submit
  2. createPost(formData) 서버에서 실행
  3. 내부에서 DB 저장 및 revalidateTag('posts')
  4. /posts 페이지가 다음 방문 시 새로운 데이터 반영

4. fetch + 캐시 태그 예시

// app/lib/fetchPosts.ts
export async function fetchPosts() {
  return fetch(`${process.env.API_URL}/posts`, {
    next: { tags: ['posts'] }, // ISR 캐시 태그
  }).then(res => res.json());
}
  • 위에서 revalidateTag('posts') 호출 시 이 fetch는 다음 요청에서 다시 실행됨 (ISR 재생성)

전체 흐름 요약

flowchart TD
  A[사용자 form 제출] --> B[Server Action 실행]
  B --> C[DB에 저장]
  B --> D[revalidateTag('posts')]
  D --> E[클라이언트 컴포넌트에서 posts 태그 캐시 무효화]
  E --> F[다음 방문 시 fresh 데이터 fetch]

요약

항목설명
server action서버에서 실행되는 함수 ('use server')
form action={함수}직접 해당 server action 호출
revalidateTag()캐시된 fetch를 강제로 무효화
fetch(..., next: { tags })ISR 태그 캐시 지정

Server Action의 동작 원리

React + Next.js가 Server Component를 통한 “함수 직렬화(serialization)”와 “form POST 처리”를 결합해, 함수 호출을 브라우저에서 서버로 위임하는 구조다.

1. Server Action의 실행 구조 요약

동작 흐름

  1. Server Component 안에 정의된 함수'use server' 선언
  2. Next.js는 이 함수를 reference로 변환
  3. <form action={action}> 혹은 action(formData) 호출 시,
    → 브라우저는 Next.js의 **특수 endpoint (/app/action)**로 POST 요청 전송
  4. Next.js 서버는 이 요청을 받아 → 해당 함수로 deserialize 및 실행
  5. 실행 결과는 서버에서 처리되고 클라이언트에 리디렉션 혹은 응답으로 전송됨

2. 실제로 내부에서 일어나는 것

예시 코드

'use server';
export async function createPost(formData: FormData) { ... }

Next.js가 빌드시 처리하는 방식

// 내부적으로 createPost는 이런 ID를 가지게 됨
__ACTION_ID__ = "app/actions/createPost.ts:createPost"

폼 제출 시

<form action="/app/action" method="POST">
  <input type="hidden" name="__action_id__" value="app/actions/createPost.ts:createPost" />
  ...
</form>

서버에서 실행될 때

Next.js 내부에서:

// pseudo-code
if (req.url === '/app/action') {
  const id = req.body.__action_id__;
  const formData = req.body;
  const fn = deserialize(id); // 해당 ID로 server action 복원
  await fn(formData);         // 서버에서 실행
}