본문 바로가기

cs/react

[React] useOptimistic

목차

1. useOptimistic이란

2. 예시

3. updateOptimistic훅의 제약


useOptimistic이란

사용자의 동작이 비동기 응답을 기다리지 않고 사용자 인터렉션에 대한 결과를 화면에 즉시 반영하도록 하는 훅

 

좋아요, 팔로우 등의 기능에 많이 사용됩니다.

 

예를 들어, 사용자가 좋아요 버튼을 클릭시 서버의 응답이 올때까지 기다렸다가 응답을 받으면 버튼을 활성화 시키는 것이 아니라 바로 버튼을 활성화 시키고, 이후에 응답을 받았을때의 결과를 적용합니다.

 

사용자 인터렉션에 대한 결과를 바로 보여주는 것을 낙관적 업데이트라고합니다.

 

낙관적 업데이트란?

사용자의 인터렉션 성공 유무에 상관없이 성공할 것이라 낙관적으로 생각하고 업데이트 하는 것.

인터페이스

const [optimisticValue, handleOptimisticValue] = useOptimistic(realValue,
        (currentOptimisticValue, newOptimisticValue)=> {
            //...
        })

반환 값

  • optimisticValue : 낙관적 상태 (초기에는 realValue로 초기화)
  • handleOptimisticValue : 낙관적 업데이트 트리거
    • 트리거 함수 호출시 useOptimistic의 두번째 인자인 updateFn 호출

인자

  • realValue : 훅의 초기 상태 및 낙관적 상태가 참조하는 진짜 상태
  • updateFn : 낙관적 트리거 발생시 호출되는 콜백
    • currentOptimisticValue : 현재 낙관적 상태
    • newOptimisticValue : 낙관적 트리거 함수에서 넘겨준 값

예시

import React, {startTransition, useOptimistic, useState} from "react";

const updateLikeStatue = async(value: boolean): Promise<boolean> => {
    console.log("타임아웃 시작");

    return new Promise((resolve)=> {
        setTimeout(()=>{
            console.log("타임아웃 끝")
            resolve(!value)
        }, 2000)
    })
}

export default function LikeButton({initialLiked}: {initialLiked: boolean}) {

    const [isLiked, setIsLiked] = useState(initialLiked);

    const [optimisticLiked, addOptimisticLike] = useOptimistic(isLiked,
        (state, newLikedValue: boolean)=>{

            return newLikedValue;
        })

    const handleLike = async() => {

        startTransition(async() => {
            //낙관적 트리거
            addOptimisticLike(!isLiked)

            try{
                const result = await updateLikeStatue(isLiked);

                setIsLiked(result);
            }
            catch (err) {
                console.error(err);
            }
        })
    }

    return (
        <button onClick={handleLike} style={{ fontSize: "2rem", cursor: "pointer" }}>
            {optimisticLiked ? "❤️" : "🤍"}
        </button>
    )
}

화면 결과

동작 흐름

  1. 대기 상태
    • 평소에는 useOptimistic의 첫 번째 인자인 초기값(isLiked)을 optimisticLiked로 내보냅니다.
  2. 낙관적 트리거
    • 낙관적 트리거인 addOptimisticLike가 호출되면 React는 실행중인 비동기 작업이 있다고 판단, updateFn을 호출하여 임시상태(newLikedValue)를 만들어서 낙관적 상태(optimisticLiked)에 반환합니다.
    • useOptimistic은 트랜지션 기반으로 동작하기 때문에 startTransition에 감싸서 트리거를 호출해야 합니다.
  3. 동기화 및 롤백 
    • 성공 시 : 2초 후 진짜 상태(isLiked) 업데이트 후 렌더링 됩니다. 이때 새로운 isLiked가 optimisticLiked에 반환되어 진짜 값이 됩니다.
    • 실패 시 : 트랜지션 내부에서 오류 발생시, 진짜 상태(isLiked)가 업데이트 되지 않고, 낙관적 상태는 기존 상태로 유지됩니다.

 

updateOptimistic훅의 제약

useOptimistic훅은 트랜지션 기반으로 동작하기 때문에 startTransition으로 감싸서 트리거를 발생시킨다고 했습니다.

만약 startTransition을 감싸지 않은 채로 렌더링하게 되면 아래와 같이 React의 트랜지션이나 액션으로 업데이트하라는 오류가 발생하게 됩니다.

오류 메세지

 

이러한 오류가 발생하는 이유는 낙관적 상태 업데이트를 위해서는 트랜지션(transition)과 액션(form action) 내부에서 동작해야한다는 제약(안전장치)가 있기 때문입니다.

 

제약의 존재 이유

비동기 작업의 성공 유무에 따른 자동 롤백 메커니즘과 여러 요청에 대한 상태의 일관성 유지를 위해 어디서 부터 언제까지 동작하는지 작업을 감싸는 경계가 필요하기 때문입니다.


예시

useTransition 사용

import {useTransition, useOptimistic} from 'react';

function LikeButton({isLiked, handleLikeApi}) {
    const [isPending, startTransition] = useTransition();//1. useTransition 선언
    const [optimisticLiked, addOptimisticLike] = useOptimistic(isLiked);
    
    const handleLike = () => {
    	//2. startTransition으로 감쌈
    	startTransition(async() => {
        	//3. 트리거 발생
        	addOptimisticLike(!isLiked);
            
            try{
            	await handleLikeApi(isLiked);
            }
            catch(e) {
            	console.error(e);
            }
        })
    }
    
    return <button onClick={handleLike}>{optimisticLiked ? "❤️" : "🤍"}</button>;
}

form action 사용

<form action={async (formData) => {
  // form action 내부는 자동으로 transition으로 취급됩니다.
  addOptimisticLike(true);
  await serverAction(formData);
}}>
  <button type="submit">좋아요</button>
</form>

참고

https://rudaks.tistory.com/entry/react-%EB%B2%88%EC%97%AD-useOptimistic

 

[react 번역] useOptimistic

출처: https://react.dev/reference/react/useOptimistic개요useOptimistic은 비동기 작업(예: 네트워크 요청) 중에 UI를 낙관적으로(optimistic) 업데이트할 수 있게 해주는 React Hook입니다. 사용자는 실제 작업이 끝나

rudaks.tistory.com

https://raccoonboy0803.tistory.com/105

 

useOptimistic

useOptimistic 훅은 낙관적 업데이트(Optimistic UI)를 구현할 수 있도록 도와주는 훅입니다. 이 훅을 사용하면 UI에서 비동기 작업이 진행될 때 일시적으로 예상 결과를 UI에 미리 반영하여 사용자 경험

raccoonboy0803.tistory.com

https://velog.io/@yonghyeun/%EB%82%99%EA%B4%80%EC%A0%81-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC-%EC%84%A4%EB%AA%85%ED%95%B4%EC%A3%BC%EC%84%B8%EC%9A%94

 

낙관적 업데이트에 관하여 설명해주세요

낙관적 업데이트란 클라이언트와 서버 단의 인터렉션에서 클라이언트의 상태 변경을 인터렉션 성공 유무에 상관 없이 성공 할 것이라 낙관적으로 생각하고 업데이트 하는 것을 의미한다.

velog.io

 

'cs > react' 카테고리의 다른 글

[React] TanStack Query (V5)  (1) 2026.01.20
[React] useActionState  (0) 2026.01.17
[React] Debounce vs useTransition  (0) 2026.01.11
[React] useTransition  (0) 2026.01.09
[React] Debounce  (0) 2026.01.08