Next.js의 Optimistic Update(낙관적 업데이트)

LEE GYUHO·2025년 2월 25일

Next.js에서 낙관적 업데이트(Optimistic Update) 적용하기

웹 애플리케이션에서 데이터를 갱신할 때, 일반적으로 사용자는 서버 응답을 기다려야 합니다. 하지만 "낙관적 업데이트(Optimistic Update)"를 사용하면, 서버 응답을 기다리기 전에 UI를 먼저 업데이트하여 사용자 경험을 향상시킬 수 있습니다.

이 글에서는 Next.js와 React Query를 사용하여 낙관적 업데이트를 적용하는 방법을 알아보겠습니다.


1. 낙관적 업데이트란?

낙관적 업데이트는 "서버 요청이 성공할 것이라고 가정"하고 클라이언트 UI를 즉시 변경하는 기법입니다. 이렇게 하면 사용자 인터페이스가 더욱 빠르고 반응성이 좋아집니다. 만약 요청이 실패하면 이전 상태로 롤백할 수 있도록 대비해야 합니다.

예제 시나리오

  • 사용자가 게시글에 "좋아요(Like)"를 클릭하면, 즉시 UI에서 좋아요 개수가 증가합니다.
  • 동시에 서버로 요청을 보내 실제로 데이터베이스를 업데이트합니다.
  • 요청이 실패하면 이전 상태로 롤백하여 일관성을 유지합니다.

2. Next.js에서 낙관적 업데이트 적용하기

Next.js에서 낙관적 업데이트를 구현할 때, @tanstack/react-query의 useMutation 훅을 활용하면 편리합니다.

2.1. 기본적인 구조

React Query의 useMutation을 사용하여 onMutate, onError, onSettled 등의 옵션을 활용하면 낙관적 업데이트를 쉽게 구현할 수 있습니다.

2.2. 코드 예제

아래는 게시글의 "좋아요" 버튼을 클릭할 때, 낙관적 업데이트를 적용하는 코드입니다.

const postLikeMutation = useMutation({
  mutationFn: ({ articleId: id }: PostLikeRequestType) => postLike(id),
  onMutate: async () => {
    // 기존 쿼리를 취소하여 낙관적 업데이트가 기존 데이터와 충돌하지 않도록 함
    await queryClient.cancelQueries({ queryKey: ['getArticle', id] });
    
    // 현재 데이터를 가져와서 백업
    const previousArticle = queryClient.getQueryData<GetArticleIdResponseType>(['getArticle', id]);
    
    // 낙관적으로 UI를 업데이트 (좋아요 수 증가, isLiked 상태 변경)
    if (previousArticle) {
      queryClient.setQueryData<GetArticleIdResponseType>(['getArticle', id], {
        ...previousArticle,
        likeCount: previousArticle.likeCount + 1,
        isLiked: true,
      });
    }
    
    // 이전 상태를 컨텍스트로 반환하여 필요 시 롤백 가능하게 함
    return { previousArticle };
  },
  onError: (err, variables, context) => {
    // 요청이 실패하면 이전 상태로 롤백
    if (context?.previousArticle) {
      queryClient.setQueryData<GetArticleIdResponseType>(['getArticle', id], context.previousArticle);
    }
  },
  onSettled: () => {
    // 요청이 완료되면 서버 데이터를 다시 가져와서 최신 상태 유지
    queryClient.invalidateQueries({ queryKey: ['getArticle', id] });
  },
});

2.3. onMutate 단계

  1. 현재 getArticle 쿼리를 취소하여 낙관적 업데이트가 서버 응답과 충돌하지 않도록 함.
  2. queryClient.getQueryData를 사용해 기존 데이터를 백업하여 실패 시 롤백할 수 있도록 저장.
  3. queryClient.setQueryData로 좋아요 수를 즉시 증가시켜 UI에 반영.
  4. 컨텍스트에 이전 데이터를 반환하여 에러 발생 시 복구할 수 있도록 함.

2.4. onError 단계

  • 요청이 실패하면 context에서 백업한 데이터를 사용하여 원래 상태로 복구.

2.5. onSettled 단계

  • 요청이 완료되면 queryClient.invalidateQueries를 사용해 서버 데이터를 다시 가져와 최신 상태로 동기화.

3. 낙관적 업데이트의 장점

✅ 즉각적인 반응성 제공

  • 서버 응답을 기다리지 않고 UI를 즉시 업데이트하여 사용자 경험(UX)이 향상됩니다.

✅ 네트워크 지연 최소화

  • 서버 요청이 성공할 확률이 높다면, 굳이 네트워크 지연을 기다릴 필요가 없습니다.

✅ 롤백 가능성 제공

  • 요청이 실패하면 이전 상태로 쉽게 복구할 수 있습니다.

4. 낙관적 업데이트 적용 시 고려할 점

⚠️ 서버 요청 실패 가능성 고려

  • 요청이 실패할 가능성을 항상 염두에 두고, onError에서 롤백 처리 로직을 구현해야 합니다.

⚠️ 실시간 동기화 필요

  • onSettled에서 queryClient.invalidateQueries를 호출하여 서버 데이터를 최신 상태로 유지해야 합니다.

⚠️ 충돌 방지

  • onMutate에서 queryClient.cancelQueries를 호출하여 중복 요청이나 예상치 못한 상태 변경을 방지해야 합니다.

5. 결론

Next.js에서 낙관적 업데이트를 활용하면 UI 반응성을 높이고, 사용자의 인터랙션을 보다 원활하게 만들 수 있습니다. React Query의 useMutation을 활용하면 백업, 롤백, 동기화 과정을 쉽게 구현할 수 있으며, queryClient를 활용한 캐싱 관리로 성능도 최적화할 수 있습니다.

profile
누구나 같은 팀으로 되길 바라는 개발자가 되자

0개의 댓글