Server Action And Mutations

  • server action을 실행하면 현재 경로로 네트워크 POST 요청이 실행됩니다.

server-actions

 


useOptimistic(실험 기능) + server action

useOptimistic + useTransition을 이용한 UI 동시성 처리

'use client';

import { useOptimistic, useTransition } from 'react';

import {
  AccordionPanel,
  AspectRatio,
  Button,
  Flex,
  Input,
  InputGroup,
  InputRightElement,
  Text,
} from '@chakra-ui/react';

import {
  IndividualInquiryRetrieveType,
  IndividualReplyType,
} from '@/swagger/@types/data-contracts';

import ImageAsNext from '@/components/ImageAsNext';
import { MyPageTranslations } from '@/containers/MyPage/MyPage';

import { AnswerIcon } from '@/icons';

import { createReply } from '../actions/create-reply';
import useQnaReplyForm from '../hooks/useQnaReplyForm';

interface QnaDetailProps {
  data: IndividualInquiryRetrieveType | undefined;
  qnaTranslations: MyPageTranslations['main']['qna'];
}
const QnaDetail = ({ data, qnaTranslations }: QnaDetailProps) => {
  const {
    handleSubmit,
    reset,
    register,
    formState: { isDirty },
  } = useQnaReplyForm();

  const [isPending, startTransition] = useTransition();
  const [optimisticReplies, addOptimisticReplies] = useOptimistic(
    // 초기 데이터 설정
    data?.replySet,
    (
      state: IndividualReplyType[] | undefined,
      newReply: IndividualReplyType,
    ) => {
      if (state) {
        state.push(newReply);
        return [...state];
      }
      return [newReply];
    },
  );

  const onSubmit = handleSubmit((res) => {
    if (!data) return;
    startTransition(async () => {
    // useOptimistic에서 반환한 addOptimisticReplies 로 낙관 업데이트 가능
    // 사용자의 작업을 실시간 반영
      addOptimisticReplies({
        id: optimisticReplies ? optimisticReplies[0].id + 1 : 0,
        isAdmin: false,
        body: res.reply,
        createdAt: res.createdAt,
      });
      reset();
      await createReply({
        data: {
          individualInquiry: data.id,
          body: res.reply,
        },
      });
    });
  });

  return (
    <AccordionPanel p="8px 16px" minH="65px">
      <Flex direction="column">
        {/* 답변 */}
        {data?.inquiryState === 2 && (
          <Flex direction="column">
            {optimisticReplies?.map(({ id, isAdmin, body }) => {
              return (
                <Flex
                  key={reply.id}
                  p={{ base: '0 0 20px', sm: '0 16px 20px' }}
                  gap="8px"
                >
                  <AnswerIcon boxSize="24px" fill="none" />
                  {isAdmin && (
                    <Text color="fanRed.500" textStyle="Body1_E">
                      {qnaTranslations.reply.admin}
                    </Text>
                  )}
                  <Text textStyle="Body1_R" whiteSpace="pre">
                    {body}
                  </Text>
                </Flex>
              );
            })}
          </Flex>
        )}
      </Flex>
    </AccordionPanel>
  );
};

export default QnaDetail;

useOptimistic + useTransition을 이용한 UI 동시성 처리

server action과 react api들을 조합해서 사용했을 때 UI 동시성 처리에 용이합니다.


server actions와 함께 useActionState, useFormStatus를 사용하면 react-hook-form을 대체할 수 있을까?

useActionState와 useFormStatus를 사용하면 자바스크립트가 실행되기 이전에 폼과 상호작용 하도록 할 수 있다는 큰 장점이 있습니다.
useActionState를 통하여 초기 상태 및 업데이트된 상태 표시가 가능하며, server action을 통한 응답 결과를 통해서 성공 및 에러 상태 표시가 가능합니다. 또한 useFormStatus의 pending을 통한 button disabled 및 loading 처리 또한 가능합니다.
위의 훅과 서버액션 조합을 사용하면 Core Web Vital 중 INP 수치가 향상되어 SEO에 좋은 영향을 줄 것 같습니다.

하지만 react-hook-form에서 제공하는 isValid, isDirty 등의 상태는 직접 구현해야 할 것으로 보이며 이벤트 핸들러 기반의 즉각적인 validation을 해야 할 경우라면 react-hook-form을 대체하긴 어려워 보입니다.

 


서버액션을 사용할 때 react에서 제공하는 API인 useTransition useOptimistic 등의 훅을 함께 사용하여 UI 동시성 처리가 가능하여 유저 경험을 향상할 수 있습니다.

서버액션은 내 현재 경로로 POST 요청이 가기 때문에 필요시에만 사용하는 것을 권장합니다.

 

📌 Only plain objects can be passed to Client Components from Server Components ⇒ server action의 반환값은 plain objects 이어야 합니다. 응답 전문을 그대로 클라이언트로 넘기면 안 됩니다.

react@canary에서 소개된 API들은 react 19에서 정식으로 릴리즈 됩니다.

+ Recent posts