리액트 up 12. react19 추가된 Hook

Posted by : on

Category : react-up


React 19에 새로 추가된 내장 훅들 React 19에서는 use 외에도 여러 새로운 내장 훅들이 추가되었습니다:

1. useFormStatus

핵심 목적: 폼 제출 과정에서 발생하는 비동기 상태 관리 문제 해결

이전에는 폼 제출 상태를 추적하려면 직접 상태를 만들고 관리해야 했다. 이 훅은 폼의 로딩 상태를 자동으로 추적해 사용자 피드백을 쉽게 제공할 수 있게 한다.

import { useFormStatus } from "react";

function SubmitButton() {
  const { pending, data, method } = useFormStatus();

  return (
    <button disabled={pending} type="submit">
      {pending ? "제출 중..." : "제출하기"}
    </button>
  );
}

function SignupForm() {
  return (
    <form action={submitForm}>
      <input name="email" />
      <SubmitButton />
    </form>
  );
}

언제 사용하나요?

  • 폼이 제출 중일 때 버튼을 비활성화하거나 로딩 상태를 표시할 때
  • 폼 제출 진행 상황에 따라 UI를 다르게 보여줘야 할 때
  • 여러 폼 요소에서 공통된 제출 상태를 공유해야 할 때

로그인 폼: 로그인 버튼을 클릭하면 “로그인 중…” 표시와 함께 버튼 비활성화 파일 업로드: 업로드 버튼 클릭 후 진행 상태 표시와 취소 버튼 활성화 결제 프로세스: 결제 버튼 클릭 시 “처리 중…” 표시와 중복 클릭 방지 회원가입: 가입 진행 중 모든 입력 필드 비활성화 및 진행 상태 표시


2. useFormState

핵심 목적: 서버 측 폼 처리 결과를 클라이언트에 반영하기 위한 문제 해결

서버 액션으로부터 반환된 상태(오류 메시지, 성공 데이터 등)를 폼에 자연스럽게 연결해 서버-클라이언트 간 상태 동기화를 간소화합니다.

import { useFormState } from "react";

function Form() {
  const [state, formAction] = useFormState(serverAction, initialState);

  return (
    <form action={formAction}>
      {state.error && <p className="error">{state.error}</p>}
      <input name="username" />
      <button type="submit">저장</button>
    </form>
  );
}

언제 사용하나요?

  • 폼 제출 후 서버에서 반환된 상태나 에러를 처리할 때
  • 서버 유효성 검사 결과를 사용자에게 보여줘야 할 때
  • 폼 제출 후 UI를 업데이트해야 할 때 (성공/실패 메시지 표시 등)

로그인 실패 처리: “아이디 또는 비밀번호가 일치하지 않습니다” 메시지 표시 회원가입 유효성 검사: “이미 사용 중인 이메일입니다” 같은 서버 검증 결과 표시 주문 폼: 할인 코드 적용 후 서버에서 계산된 최종 가격 표시 설문조사: 각 단계별 제출 후 다음 질문 세트 로드


3. useOptimistic

핵심 목적: 네트워크 지연으로 인한 사용자 경험 저하 문제 해결

서버 응답을 기다리지 않고 즉시 UI를 업데이트함으로써 애플리케이션이 더 반응적으로 느껴지게 만듭니다. 특히 네트워크 환경이 좋지 않은 상황에서 체감 성능을 크게 향상시킵니다.

import { useOptimistic } from "react";

function TodoList({ todos }) {
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (currentTodos, newTodo) => [...currentTodos, { ...newTodo, pending: true }]
  );

  async function addTodo(formData) {
    const text = formData.get("text");
    const newTodo = { id: Math.random(), text };

    // 낙관적 업데이트 적용
    addOptimisticTodo(newTodo);

    // 실제 서버 요청
    await saveTodo(newTodo);
  }

  return (
    <>
      <form action={addTodo}>
        <input name="text" />
        <button type="submit">추가</button>
      </form>
      <ul>
        {optimisticTodos.map((todo) => (
          <li key={todo.id} style=>
            {todo.text}
          </li>
        ))}
      </ul>
    </>
  );
}

언제 사용하나요?

  • 네트워크 지연이 사용자 경험을 저하시킬 수 있는 상황에서
  • 사용자가 데이터 변경 결과를 즉시 확인해야 할 때 (좋아요, 댓글, 할일 추가 등)
  • 대부분의 요청이 성공할 것으로 예상되어 바로 UI를 업데이트해도 될 때
  • 소셜 미디어 인터랙션, 장바구니 업데이트, 실시간 채팅 등에 유용

소셜 미디어 좋아요: 버튼 클릭 즉시 카운트 증가, 실패 시에만 롤백 채팅 애플리케이션: 메시지 전송 즉시 대화창에 표시, 전송 중 상태 표시 쇼핑 카트: “장바구니에 추가” 즉시 반영, 네트워크 지연 없는 경험 제공 댓글 시스템: 댓글 작성 즉시 목록에 표시, 배경에서 서버 저장 진행 실시간 협업 도구: 문서 편집 내용 즉시 반영, 동기화는 배경에서 처리


4. useActionState

핵심 목적: 복잡한 서버 액션 상태 관리 문제 해결

서버 액션의 다양한 상태(대기, 성공, 실패 등)를 체계적으로 관리하고 이에 따른 UI 피드백을 제공하는 과정을 단순화합니다.

import { useActionState } from "react";

function ProfileForm() {
  const [state, action] = useActionState(updateProfile, { status: "idle" });

  return (
    <form action={action}>
      {state.status === "error" && <p>{state.error}</p>}
      {state.status === "success" && <p>프로필이 업데이트되었습니다!</p>}
      <input name="name" />
      <button type="submit">
        {state.status === "pending" ? "저장 중..." : "저장"}
      </button>
    </form>
  );
}

언제 사용하나요?

  • 서버 액션의 여러 상태(idle, pending, success, error)를 한 번에 관리할 때
  • 액션 상태에 따라 다양한 UI 피드백을 제공해야 할 때
  • 폼 외부에서도 서버 액션 상태를 참조해야 할 때
  • 복잡한 폼 워크플로우나 다단계 프로세스를 관리할 때

다단계 가입 프로세스: 각 단계별 상태(완료, 진행 중, 오류) 관리 및 표시 복잡한 예약 시스템: 날짜 확인, 좌석 확인, 결제 처리 등 여러 단계 상태 추적 대용량 미디어 업로드: 업로드, 처리, 인코딩, 완료 등 다양한 상태 관리 비즈니스 대시보드: 여러 데이터 소스 업데이트 상태를 한 번에 관리


5. useTransition의 개선

핵심 목적: 무거운 UI 업데이트로 인한 응답성 저하 문제 해결

사용자 인터랙션은 즉시 반영하면서 무거운 계산과 렌더링은 우선순위를 낮춰 UI가 끊기는 문제를 해결합니다.

import { useTransition } from "react";

function SearchFilter() {
  const [isPending, startTransition] = useTransition();
  const [filter, setFilter] = useState("");

  function handleChange(e) {
    // 입력은 즉시 반영하되
    setFilter(e.target.value);

    // 무거운 필터링 작업은 우선순위를 낮춤
    startTransition(() => {
      applyComplexFilter(e.target.value);
    });
  }

  return (
    <>
      <input value={filter} onChange={handleChange} />
      {isPending && <div>필터링 ...</div>}
      <ItemList />
    </>
  );
}

언제 사용하나요?

  • 사용자 입력에 즉시 반응해야 하지만, 그로 인한 처리는 무거울 때
  • 타이핑이나 UI 인터랙션이 무거운 렌더링으로 인해 느려지는 것을 방지할 때
  • 대량의 데이터 필터링, 복잡한 계산, 큰 리스트 렌더링 등을 지연시켜야 할 때
  • 라이브 검색, 데이터 시각화, 복잡한 폼 유효성 검사 등에 특히 유용

제품 카탈로그 필터링: 수천 개 상품 중 실시간 필터링 시 UI 응답성 유지 데이터 시각화 대시보드: 복잡한 차트와 그래프 데이터 변경 시 UI 끊김 방지 고급 검색 인터페이스: 검색어 입력 즉시 반영하되 결과 계산은 우선순위 낮춤 이미지 갤러리: 수백 장의 고해상도 이미지 필터링/정렬 시 UI 반응성 유지 코드 에디터: 구문 강조, 자동 완성 등 무거운 계산 처리하면서도 타이핑 지연 없음


About 유재석
유재석

개발자 유재석 입니다. Web Developer.

Email : jaeseok9405@gmail.com

Website : https://github.com/yoo94