카테고리 없음

[모리딥] 서버 액션

Ahyeon, Jung 2024. 3. 3. 09:40

서버 액션(alpha)

서버 액션은 API를 작성하지 않고 함수 수준에서 서버에 직접 접근해 데이터 요청 등을 수행할 수 있는 기능이다. 서버 액션을 사용하기 위해서는 함수 내부 혹은 파일 상단에 'use server' 지시자를 선언해야 하며, 해당 함수는 반드시 async여야 한다.


form의 action

export default function Page() {
  async function handleSubmit() {
    **'use server'**

    console.log('해당 작업은 서버에서 수행합니다. 따라서 CORS 이슈가 없습니다.')

    const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'post',
      body: JSON.stringify({
        title: 'foo',
        body: 'bar',
        userId: 1,
      }),
      headers: {
        'Content-type': 'application/json; charset=UTF-8',
      },
    })

    const result = await response.json()
    console.log(result)
  }
  return (
    <div className="space-y-4">
      <form **action={handleSubmit}**>
        <button type="submit">form 요청 보내보기</button>
      </form>
    </div>
  )
}

input의 submit과 image의 formAction

input type="submit"과 input type="image"에 fromAction prop으로 서버 액션을 추가할 수 있다.

startTransition과의 연동

startTransition을 사용해 서버 액션을 실행할 수도 있으며, isPending을 활용해 컴포넌트 단위의 로딩 처리도 가능하다.

export default async function Page({ params }: { params: { id: string } }) {
  const key = `test:${params.id}`;
  const data = await kv.get<Data>(key);

  return (
    <div className="space-y-4">
      <h1 className="text-xl font-medium text-gray-400/80">form with data</h1>
      <h2 className="text-l font-medium text-gray-400/80">
        서버에 저장된 정보: {data?.name} {data?.age}
      </h2>

      <div className="space-y-4">
        <ul className="list-disc space-y-2 pl-4 text-sm text-gray-300">
          <li>아래 버튼을 누르면 서버에서 직접 form 요청을 보냅니다.</li>
          <li>이 작업은 useTransition을 기반으로 실행됩니다.</li>
          <li>
            <ClientButtonComponent id={params.id} />
          </li>
        </ul>
      </div>
    </div>
  );
}
"use client";
import { useCallback, useTransition } from "react";
import { updateData } from "#server-action";
import { SkeletonBtn } from "#components/components";

export function ClientButtonComponent({ id }: { id: string }) {
  const [isPending, startTransition] = useTransition();

  const handleClick = useCallback(() => {
    startTransition(() => updateData(id, { name: "기본값", age: 0 }));
  }, []);

  return isPending ? (
    <SkeletonBtn />
  ) : (
    <button onClick={handleClick}>기본값으로 돌리기</button>
  );
}
"use server";

import kv from "@vercel/kv";
import { revalidatePath } from "next/cache";
import { cookies } from "next/headers";

export async function updateData(
  id: string,
  data: { name: string; age: number }
) {
  const key = `test:${id}`;

  await kv.set(key, {
    name: data.name,
    age: data.age,
  });

  revalidatePath(`/server-action/form/${id}`);
}

server mutation이 없는 작업

server mutation이 필요하다면 서버 액션을 useTransition으로 감싸서 써야하지만, 별도의 server mutation이 없다면 생략해도 된다.

서버 액션 사용 시 주의할 점

서버 액션은 클라이언트 컴포넌트 내에 정의 될 수 없다. "use server"가 선언된 파일에 서버 액션을 작성하고 이를 import해야 한다. 서버 액션을 props 형태로 클라이언트 컴포넌트에 넘길 수도 있다.

 

Reference

모던 리액트 딥다이브

https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#