본문 바로가기

React.js

[React]React useActionState Hook에 대해 알아보기

소개

useActionState는 React 18.3.1에서 도입된 새로운 Hook으로, form 액션을 기반으로 상태를 효율적으로 관리할 수 있게 해줍니다. 이 Hook은 React 서버 컴포넌트를 지원하는 프레임워크에서 사용할 때 가장 큰 이점을 얻을 수 있습니다.

참고: React Canary 버전에서는 이 Hook이 React DOM에 포함되어 있습니다.

 

기본 사용법

1. Hook 초기화

useActionState는 두 가지 매개변수를 받습니다:

  • 상태를 업데이트하는 함수
  • 초기 상태값
"use client";
import { useActionState } from "react";

const [state, formAction] = useActionState(
  (previousState, formData) => {
    return {
      ...previousState,
      ...formData
    };
  }, 
  { name: "", phone: "" }  // 초기 상태
);

 

 

2. 기본 구현 예시

다음은 이름과 전화번호를 관리하는 간단한 폼 구현입니다:

"use client";
import { useActionState } from "react";

export const UserForm = () => {
  const [state, formAction] = useActionState(
    (previousState, formData) => ({
      ...previousState,
      ...formData
    }), 
    { name: "", phone: "" }
  );

  const onChangeHandler = (e) => {
    const { name, value } = e.target;
    formAction({ ...state, [name]: value });
  };

  return (
    <form className="flex flex-col gap-4 w-1/4" method="post">
      <div>
        <p>이름: {state.name}</p>
        <input
          type="text"
          name="name"
          onChange={onChangeHandler}
          value={state.name}
          placeholder="이름 입력"
        />
      </div>
      
      <div>
        <p>전화번호: {state.phone}</p>
        <input
          type="text"
          name="phone"
          onChange={onChangeHandler}
          value={state.phone}
          placeholder="전화번호 입력"
        />
      </div>

      <button formAction={formAction}>저장</button>
    </form>
  );
};

 

useActionState vs react-hook-form

각각의 장단점

useActionState

  • 장점:
    • 서버 컴포넌트와의 통합이 원활함
    • 상태 관리가 직관적
    • form 액션 처리가 간단함
  • 단점:
    • 폼 유효성 검사 기능이 제한적
    • 에러 처리를 직접 구현해야 함

react-hook-form

  • 장점:
    • 강력한 폼 유효성 검사
    • 에러 처리가 내장되어 있음
    • 성능 최적화
  • 단점:
    • 서버 액션과의 통합이 복잡할 수 있음
    • 설정이 더 복잡함

대안적 접근 방법

1. useActionState만 사용하고 커스텀 유효성 검사 구현

const validate = (formData) => {
  const errors = {};
  if (!formData.name) errors.name = "이름은 필수입니다";
  if (!formData.phone.match(/^\d{3}-\d{4}-\d{4}$/)) {
    errors.phone = "올바른 전화번호 형식이 아닙니다";
  }
  return errors;
};

const onSubmit = (previousState, formData) => {
  const errors = validate(formData);
  if (Object.keys(errors).length > 0) {
    return { ...previousState, errors };
  }
  return { ...previousState, ...formData, errors: {} };
};

 

 

2. Zod나 Yup과 함께 useActionState 사용

import { z } from "zod";

const schema = z.object({
  name: z.string().min(1, "이름은 필수입니다"),
  phone: z.string().regex(/^\d{3}-\d{4}-\d{4}$/, "올바른 전화번호 형식이 아닙니다")
});

const onSubmit = (previousState, formData) => {
  try {
    const validated = schema.parse(formData);
    return { ...previousState, ...validated, errors: {} };
  } catch (error) {
    return { ...previousState, errors: error.flatten().fieldErrors };
  }
};

권장 사용 방식

useActionState와 react-hook-form을 함께 사용하는 것은 가능하지만, 각각의 장점을 최대한 활용하기 위해서는 다음과 같은 방식을 추천합니다:

  1. 단순한 폼: useActionState만 사용하고 필요한 경우 Zod/Yup으로 유효성 검사
  2. 복잡한 폼: react-hook-form 사용
  3. 서버 액션이 중요한 경우: useActionState를 주로 사용하고 필요한 경우에만 커스텀 유효성 검사 구현

 

결론

useActionState와 react-hook-form을 함께 사용하는 것은 가능하지만, 대부분의 경우 하나만 선택하여 사용하는 것이 코드 복잡성을 줄이고 유지보수를 용이하게 만듭니다. 폼의 복잡성과 서버 액션의 중요도를 고려하여 적절한 도구를 선택하는 것이 좋습니다.

 

느낀 점

기존 프로젝트에서는 react-hook-form으로 폼 관리를 주로 했었는데, useActionState를 사용해보니 useActionState는 상태 관리가 직관적이고 폼의 액션 처리가 간단하다는 장점이 있었습니다. 오히려 간단한 폼의 경우, react-hook-form을 사용하면 불필요한 보일러플레이트 코드가 많아질 수 있다는 점을 깨닫게 되었습니다. 특히 서버 컴포넌트와의 연동이 필요한 경우, useActionState가 더 자연스러운 흐름을 제공을 해서 편리했습니다. 하지만 복잡한 폼 유효성 검사가 필요한 경우에는 여전히 react-hook-form의 강력한 기능이 유용할 것 같습니다.