공부공부/Next.js 공식문서

[next.js 공식문서] 17. Server Actions

고생쨩 2024. 3. 1. 09:38
728x90

next.js 13버전 때 학습내용 정리 문서로 현재와 다름 주의

Server Actions

React Actions 위에 구축된 알파 기능.
서버측 데이터 변형, 클라이언트 측 코드 감소 및 향상된 양식을 가능하게 함

// app/add-to-cart.js
import { cookies } from 'next/headers';
 
export default function AddToCart({ productId }) {
  async function addItem(data) {
    'use server';
 
    const cartId = cookies().get('cartId')?.value;
    await saveToDb({ cartId, data });
  }
 
  return (
    <form action={addItem}>
      <button type="submit">Add to Cart</button>
    </form>
  );
}

Rule

아래처럼 설정하여 활성화

module.exports = {
  experimental: {
    serverActions: true,
  },
};

만들기

'use server’를 비동기 함수에 선언하여 서버작업을 만듬.
이 함수에는 직렬화 가능한 인수와 반환값이 있어야함.

async function myAction() {
  'use server';
  // ...
}

최상위 지시문 사용 가능. export가 많은 action 파일

'use server';
export async function myAction() {
  // ...
}

Invocation (호출)

다음 방법을 사용하여 서버 작업을 호출함

  • action 사용 : React의 action prop를 사용하여 <form>에서 서버 액션을 호출할 수 있음
  • formAction 사용 : React의 formAction prop를 사용하면 <form>에서 <button>, <input type="submit">, <input type="image">를 처리할 수 있음.
  • startTransition을 사용한 사용자 정의 호출 : startTransition을 사용하여 action 또는 formAction을 사용하지 않고 서버 액션을 호출함. 이 방법은 프로그레시브 향상을 비활성화함.

action

export default function AddToCart({ productId }) {
  async function addItem(data) {
    'use server';
 
    const cartId = cookies().get('cartId')?.value;
    await saveToDb({ cartId, data });
  }
 
  return (
    <form action={addItem}>
      <button type="submit">Add to Cart</button>
    </form>
  );
}

액션은 html의 액션과 유사함.

formAction

export default function Form() {
  async function handleSubmit() {
    'use server';
    // ...
  }
 
  async function submitImage() {
    'use server';
    // ...
  }
 
  return (
    <form action={handleSubmit}>
      <input type="text" name="name" />
      <input type="image" formAction={submitImage} />
      <button type="submit">Submit</button>
    </form>
  );
}

formAction은 html의 기본 formAction임. React에서 이 속성에 함수를 전달할 수 있음.

startTransition을 사용한 사용자 정의 호출

useTransition 훅에서 제공되는 startTransition으로 사용할 수 있음.
form, button, input에서 서버 액션을 쓰고 싶은 경우 유용함.

// app/_components/ex-client-component.js
'use client';
 
import { useTransition } from 'react';
import { addItem } from '../_actions';
 
function ExampleClientComponent({ id }) {
  let [isPending, startTransition] = useTransition();
 
  return (
    <button onClick={() => startTransition(() => addItem(id))}>
      Add To Cart
    </button>
  );
}
// app/_actions.js
'use server';
 
export async function addItem(id) {
  await addItemToDb(id);
  revalidatePath(`/product/${id}`);
}

startTransition을 사용하지 않는 사용자 정의 호출

서버 변형(redirect, revalidatePath, revalidateTag)을 수행하지 않는 경우 다른 함수처럼 함수를 직접 프로퍼티로 전달할 수 있음.

향상된 기능

useOptimistic

앱의 반응성을 높여 사용자 경험을 향상시키는 기술.
서버 액션이 호출되면 응답을 기다리지 않고 예상결과를 반영하여 UI가 업데이트 됨

'use client';
 
import { experimental_useOptimistic as useOptimistic } from 'react';
import { send } from './_actions.js';
 
export function Thread({ messages }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [...state, { message: newMessage, sending: true }],
  );
  const formRef = useRef();
 
  return (
    <div>
      {optimisticMessages.map((m) => (
        <div>
          {m.message}
          {m.sending ? 'Sending...' : ''}
        </div>
      ))}
      <form
        action={async (formData) => {
          const message = formData.get('message');
          formRef.current.reset();
          addOptimisticMessage(message);
          await send(message);
        }}
        ref={formRef}
      >
        <input type="text" name="message" />
      </form>
    </div>
  );
}

useFormStatus

form에서 사용할 수 있고 pending 속성을 제공함.

import { experimental_useFormStatus as useFormStatus } from 'react-dom';
 
function Submit() {
  const { pending } = useFormStatus();
 
  return (
    <input
      type="submit"
      className={pending ? 'button-pending' : 'button-normal'}
      disabled={pending}
    >
      Submit
    </input>
  );
}

프로그레시브 향상

프로그레시브 향상을 이용하면 js가 없거나 비활성화된 상태에서도 <form>이 제대로 동작할 수 있음.

  • 서버 액션이 <form>에 직접 전달되는 경우 js가 비활성화되어 있어도 양식은 대화형으로 동작함
  • 클라이언트 액션이 <form>으로 전달되는 경우 form는 여전히 대화형이지만 form이 수화(?)될때 까지 대기열에 배치됨.

    form은 선택적 하이드레이션으로 우선순위가 지정되므로 빠르게 처리됨
'use client';
 
import { useState } from 'react';
import { handleSubmit } from './_actions.js';
 
export default function ExampleClientComponent({ myAction }) {
  const [input, setInput] = useState();
 
  return (
    <form action={handleSubmit} onChange={(e) => setInput(e.target.value)}>
      {/* ... */}
    </form>
  );
}

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.