[React19] useOptimistic 훅을 활용한 낙관적 업데이트

2025. 8. 18.·🎨 프론트엔드 공부/React & Next

📌목표

1. 낙관적 UI 업데이트 알아보기

2. React19 useOptimistic 훅 작동 원리 이해하기

3. 간단한 카운터 만들고 낙관적 업데이트 적용하기

 

📌낙관적 UI 업데이트

- 예를 들어, 인스타그램 앱에서 게시물의 좋아요 버튼을 클릭하는 행동은 서버로 요청을 보내고, 요청이 성공했을 때 UI를 업데이트 한다. 하지만, 사용자 디바이스가 느린 네트워크 환경에 있거나, 서버의 응답이 오래 걸려서 UI 업데이트가 늦어지면 UX에 악영향을 줄 수 있다.

- 사용자가 좋아요 버튼을 클릭함과 동시에 좋아요 UI를 업데이트하여 (서버 응답을 받기 전) 사용자의 행동이 원활하게 처리되고 있음을 보여주면 UX를 개선할 수 있다. 만약 서버 응답이 실패하면, UI를 롤백하면 된다.

- 이러한 패턴을 서버 응답이 성공할 것이라 낙관적으로 예상하고, 먼저 UI를 업데이트한다고 하여, 낙관적 UI (업데이트) 라고 부른다.

 

📌useOptimistic Hook

- React 19 버전에서 추가된 훅으로, UI를 낙관적으로 업데이트할 수 있게 해준다. 즉 비동기 작업이 진행중일 때, 실제 상태가 아닌 다른 상태(낙관적 상태 - 실제 상태의 복사본)를 보여준다.

- 비동기 작업이 실패하면, 낙관적 상태는 원래 상태값으로 자동 롤백된다 (에러 시 상태를 직접 수정할 필요 없어서 편리)

 

import { useOptimistic } from 'react';

function AppContainer() {
  const [optimisticState, addOptimistic] = useOptimistic(
    state,
    // updateFn
    (currentState, optimisticValue) => {
      // merge and return new state
      // with optimistic value
    }
  );
}

 

 

매개변수

- state: 낙관적으로 만들 상태

- updateFn: 추후 addOptimistic으로 호출할 콜백 함수. currentState(매개변수 state)와 optimisticValue(addOptimistic 함수로 전달할 인수)를 조합하여 새로운 상태 반환

 

반환값

- optimisiticState: 낙관적으로 작동할 상태

- addOptimistic 함수: optimisticValue를 인수로 받아 호출하여 낙관적 상태 업데이트

 

📌간단한 카운터 만들고 낙관적 업데이트 적용하기

기능

- 화면에 실제 상태(count)와 낙관적 상태(optimisticCount)를 표시

- 버튼 클릭 시 1.5초의 의도된 지연(서버 요청과 응답을 mocking) 후 50% 확률로 성공 or 실패

- 성공 시: 실제 상태(count)를 1 증가

- 실패 시: addOptimistic 함수가 실제 상태를 변경하지 않으므로 transition 종료와 함께 낙관적 상태가 자동 롤백되어 count로 수렴

- transition 진행중일 때 버튼 비활성화

 

주의사항

- 낙관적 업데이트, 서버 대기, 실제 커밋(상태 업데이트) 과정을 한 흐름으로 묶어 transition 하나로 래핑해야 함

- 그렇지 않으면 낙관적 상태가 중간에 조기 리셋되어 깜빡임 현상(버튼 클릭 후 낙관적 값이 자동 롤백되어버리는 현상) 발생

"use client";
import { useOptimistic, useState, useTransition } from "react";
import { Button } from "@/components/ui/button";

export default function PlayGroundPage() {
  const [count, setCount] = useState<number>(0);
  const [error, setError] = useState<string | null>(null);
  const [isPending, startUiTransition] = useTransition();
  const [optimisticCount, addOptimisticCount] = useOptimistic<number, number>(
    count,
    (state, increment) => {
      return state + increment;
    }
  );

  const handleClick = () => {
    startUiTransition(async () => {
      setError(null);
      addOptimisticCount(1); // 낙관적 업데이트

      try {
        await new Promise<void>(res => setTimeout(() => res(), 1500));
        const isSuccess = Math.round(Math.random()) === 0;
        if (!isSuccess) throw new Error("increment failed!");
        setCount(prev => prev + 1);
      } catch (err) {
        setError((err as Error).message);
        // 실패 시 optimisticCount가 count로 자동 롤백
      }
    });
  };

  return (
    <div>
      <h2 className="mb-4 text-2xl">Hello from Playground</h2>
      <h3 className="text-xl">낙관적 카운터</h3>
      <div className="flex items-center gap-4">
        <span>count: {count}</span>
        <span>optimisticCount: {optimisticCount}</span>
        <Button onClick={handleClick} disabled={isPending}>
          {isPending ? "Updating..." : "click me"}
        </Button>
      </div>
      {error && <p className="text-sm text-red-500">{error}</p>}
    </div>
  );
}
'🎨 프론트엔드 공부/React & Next' 카테고리의 다른 글
  • [Tanstack Query] query key 내용 정리
  • 값vs타입, 원시 타입
  • 구조적 타이핑, 구조적 서브타이핑, 덕 타이핑
  • [React] drag로 element 옮기기 직접 구현
지식물원
지식물원
지식이 자라는 식물원!
  • 지식물원
    지식물원
    지식물원
  • 전체
    오늘
    어제
    • 분류 전체보기 (523) N
      • 🎨 프론트엔드 공부 (247)
        • JS & TS (93)
        • HTML & CSS (24)
        • React & Next (51)
        • Vue & Nuxt (22)
        • 기타 (57)
      • 🤓 기술 학습 & 공부 기록 (116)
        • Node.js (0)
        • Python (37)
        • 백엔드 (0)
        • 딥러닝 (1)
        • 컴퓨터 일반 (72)
        • 개발 인프라 (6)
      • 👨‍💻 프로젝트 경험 (16)
        • Work (0)
        • Toy (16)
      • ⚙️ 개발 팁 & 노하우 (24) N
        • 프론트엔드 (6)
        • 기타 (18) N
      • ☕️ 커리어 & 인터뷰 준비 (88)
        • 코딩 테스트 (88)
      • 📰 기술 트렌드 & 생각 정리 (4)
      • 📚 기타 (25)
        • 마케팅 (15)
        • 비개발서적 (10)
  • 블로그 메뉴

    • 태그
  • 링크

  • 공지사항

    • 모바일 접속 시 코드 하이라이팅 깨질 때
  • 인기 글

  • 태그

    DP
    자료구조
    프로그래머스
    좋은코드나쁜코드
    Python
    react-query
    javascript
    머신러닝
    DFS
    컴포넌트
    백트래킹
    타이탄의도구들
    브루트포스
    PostgreSQL
    react
    nuxt
    태블로
    웹접근성
    cssbattle
    SQL
    Vue.js
    그리디
    BFS
    GATSBY
    프로그래머의뇌
    컴퓨터구조
    typescript
    nextjs
    객체지향의사실과오해
    AWS
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
지식물원
[React19] useOptimistic 훅을 활용한 낙관적 업데이트
상단으로

티스토리툴바