라떼군 이야기


Next.js에서 useActionState 사용 시 'is not a function' 에러 해결하기

Problem

Next.js 프로젝트에서 React 19의 useActionState(액션 상태 훅)를 사용하려고 공식 예제를 따라 코드를 작성했습니다. 그러나 앱을 실행하는 순간 다음과 같은 에러가 발생했습니다.

Error: (0 , react__WEBPACK_IMPORTED_MODULE_1__.useActionState) is not a function or its return value is not iterable

React canary 버전과 Next.js 버전이 서로 맞지 않을 때 자주 나타나는 문제로, 서버 액션을 사용하는 폼을 만들려는 개발자들이 흔히 겪는 상황입니다.

Background

useActionState는 React 19에서 새로 추가된 훅으로, 서버 액션(server action)과 함께 이전 상태와 새 상태를 쉽게 관리할 수 있게 해줍니다. 이전에는 react-dom의 useFormState라는 이름으로 제공되었습니다.

Next.js는 React 19의 새로운 기능을 점진적으로 지원하고 있습니다. 특정 canary 버전부터 useActionState를 공식 지원하기 시작했지만, 개발자가 React canary만 업데이트하고 Next.js는 이전 버전을 사용하면 import 자체가 실패합니다.

이 에러는 useActionState가 실제로 로드되지 않아 이터러블(iterable) 값을 반환하지 못하면서 발생합니다. tsconfig.json 설정이나 React 버전이 최신이라도 Next.js 런타임이 해당 기능을 인식하지 못하면 같은 문제가 반복됩니다.

Solution

1. react-dom의 useFormState로 대체하기 (가장 빠른 해결책)

React 19가 안정화되기 전까지 임시로 react-dom 패키지의 useFormState를 사용하는 것이 가장 간단합니다.

// app/page.tsx
'use client';

import { useFormState } from 'react-dom';  // useActionState 대신 사용
import { createUser } from '../components/actions';

const initialState = {
  message: '',
};

export default function App() {
  // useFormState로 상태와 액션 함수를 반환받음
  const [state, formAction] = useFormState(createUser, initialState);

  return (
    <form action={formAction}>
      <input type="email" name="email" required />
      <button type="submit">회원가입</button>
      <p>{state?.message}</p>
    </form>
  );
}

서버 액션 파일은 그대로 유지할 수 있습니다.

// components/actions.ts
'use server';

export default async function createUser(prevState: any, formData: FormData) {
  // 실제로는 여기서 이메일 검증 로직을 작성
  return {
    message: 'Please enter a valid email',
  };
}

2. Next.js 최신 canary 버전으로 프로젝트 생성하기

React 19 기능을 완전히 지원하는 Next.js 버전을 사용하려면 아래 명령어로 새 프로젝트를 만듭니다.

npx create-next-app@14.3.0-canary.46 my-app

이미 기존 프로젝트가 있다면 package.json의 next 버전을 14.3.0-canary.46 이상으로 업데이트한 후 npm install을 실행하세요. 이 버전부터 useActionState를 React에서 직접 import해도 정상 동작합니다.

Deep Dive

useActionState는 React 19의 서버 액션과 밀접하게 연동되어 있습니다. 클라이언트 컴포넌트에서만 사용할 수 있으며, “use client” 지시어가 필수입니다. 프로덕션 환경에서는 아직 canary 버전을 권장하지 않으니, 안정화된 React 19와 Next.js 15가 나오면 업그레이드하는 것이 좋습니다.

TypeScript를 사용할 때는 initialState의 타입을 명시적으로 정의하면 상태 관리에서 발생할 수 있는 오류를 미리 방지할 수 있습니다. 또한 Next.js의 App Router를 사용 중이라면 server action 파일의 ‘use server’ 지시어를 반드시 포함해야 합니다.

이와 유사한 에러는 React 19의 다른 실험적 기능(예: useOptimistic, useFormStatus)을 사용할 때도 발생할 수 있으니, Next.js와 React의 버전 조합을 항상 확인하는 습관을 들이는 것이 중요합니다.

Conclusion

Next.js에서 useActionState 에러는 대부분 버전 호환성 문제입니다. 당장 안정적으로 개발을 진행하려면 react-dom의 useFormState를 사용하는 것이 현실적이며, React 19의 최신 기능을 모두 사용하고 싶다면 Next.js canary 버전으로 업데이트하는 것이 좋습니다.

React 19가 정식 출시되면 자연스럽게 해결될 문제이니, 현재 프로젝트의 안정성과 필요 기능 사이에서 적절한 선택을 하시기 바랍니다.

References

프리랜서로 제품 기획과 개발을 맡길 파트너가 필요하신가요? 개인, 팀, 기업 누구나 의뢰할 수 있으며 문제 정의부터 출시까지 함께합니다.