React

 

 

https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#optimistic-updates

 

소스 :   https://github.com/braverokmc79/nextjs-ex06

 

낙관적 업데이트(Optimistic UI)는 사용자가 데이터를 변경할 때, 서버의 응답을 기다리지 않고 즉시 사용자 인터페이스(UI)를 업데이트하는 방법입니다. 이렇게 하면 애플리케이션이 더 빠르고 응답성이 좋게 느껴집니다. 다음은 Next.js의 App Router 방식에서 낙관적 업데이트를 구현하는 방법에 대한 설명입니다.

Next.js App Router 방식에서 낙관적 업데이트

  1. 상태 관리:

    • 낙관적 업데이트를 위해서는 클라이언트 측 상태 관리가 필요합니다. React의 상태 관리 훅인 useState 또는 더 복잡한 상태 관리를 위해 useReducer를 사용할 수 있습니다.
  2. Optimistic UI 구현:

    • useState나 useReducer를 사용하여 상태를 관리하면서, 사용자의 액션에 따라 상태를 즉시 업데이트합니다.
    • 서버에 요청을 보낸 후 응답을 받으면 상태를 다시 업데이트하여 최종 상태를 반영합니다.
  3. 에러 처리:

    • 서버 요청이 실패할 경우, 이전 상태로 롤백할 수 있는 로직을 구현합니다.

구현 예시

아래는 Next.js의 App Router 방식에서 낙관적 업데이트를 구현하는 간단한 예시입니다. 이 예시에서는 포스트의 좋아요 상태를 토글하는 기능을 구현합니다.

 

 

1. 상태 초기화 및 업데이트 함수

'use client';
import { useState } from 'react';
import { togglePostLikeStatus } from '@/actions/posts'; // 서버 액션 함수

const Posts = ({ initialPosts }) => {
  const [posts, setPosts] = useState(initialPosts);

  // 낙관적 업데이트 함수
  const handleLikeToggle = async (postId) => {
    // 현재 포스트 상태 복사
    const updatedPosts = posts.map(post =>
      post.id === postId ? { ...post, isLiked: !post.isLiked, likes: post.isLiked ? post.likes - 1 : post.likes + 1 } : post
    );
    
    // 즉시 UI 업데이트
    setPosts(updatedPosts);

    try {
      // 서버 요청
      await togglePostLikeStatus(postId);
    } catch (error) {
      // 실패 시 이전 상태로 롤백
      setPosts(posts);
      console.error('Failed to update like status:', error);
    }
  };

  return (
    <ul className="posts">
      {posts.map((post) => (
        <li key={post.id}>
          <Post post={post} onLikeToggle={() => handleLikeToggle(post.id)} />
        </li>
      ))}
    </ul>
  );
};

export default Posts;

 

 

 

2. Post 컴포넌트

const Post = ({ post, onLikeToggle }) => {
  return (
    <article className="post">
      <div className="post-image">
        <img src={post.image} alt={post.title} />
      </div>
      <div className="post-content">
        <header>
          <div>
            <h2>{post.title}</h2>
            <p>Likes: {post.likes}</p>
            <button onClick={onLikeToggle}>
              {post.isLiked ? 'Unlike' : 'Like'}
            </button>
          </div>
        </header>
        <p>{post.content}</p>
      </div>
    </article>
  );
};

 

3. 서버 액션 함수 (예시)

// actions/posts.js

export async function togglePostLikeStatus(postId) {
  const response = await fetch(`/api/posts/${postId}/like`, {
    method: 'POST'
  });

  if (!response.ok) {
    throw new Error('Failed to toggle like status');
  }

  return response.json();
}

 

요약

  • 낙관적 업데이트는 사용자 인터페이스를 더 빠르고 응답성 있게 만듭니다.
  • 클라이언트 상태를 즉시 업데이트한 후, 서버에 요청을 보냅니다.
  • 서버 요청이 실패하면, 이전 상태로 롤백합니다.
  • Next.js의 App Router 방식에서도 낙관적 업데이트를 적용할 수 있으며, 이는 사용자 경험을 크게 향상시킬 수 있습니다.

 

 

 

 

실질적인 넥스트 에서 사용하는 코드 예 useOptimistic

 

"use client"
import { useOptimistic } from 'react'; // 낙관적 UI 업데이트를 위해 사용

import { formatDate } from '@/lib/format'; // 날짜 형식 변환 함수 가져오기
import LikeButton from './like-icon'; // 좋아요 버튼 컴포넌트 가져오기
import { togglePostLikeStatus } from '@/actions/posts'; // 좋아요 상태를 토글하는 함수 가져오기

// 개별 포스트 컴포넌트
function Post({ post }) {
  return (
    <article className="post">
      <div className="post-image">
        <img src={post.image} alt={post.title} /> {/* 포스트 이미지 */}
      </div>
      <div className="post-content">
        <header>
          <div>
            <h2>{post.title}</h2> {/* 포스트 제목 */}
            <p>
              Shared by {post.userFirstName} on{' '}
              <time dateTime={post.createdAt}>
                {formatDate(post.createdAt)} {/* 포스트 작성 날짜 */}
              </time>
            </p>
          </div>
          <div>
            <form action={togglePostLikeStatus.bind(null, post.id)} className={post.isLiked ? 'liked' : ''}>
              <LikeButton /> {/* 좋아요 버튼 */}
            </form>
          </div>
        </header>
        <p>{post.content}</p> {/* 포스트 내용 */}
      </div>
    </article>
  );
}

// 포스트 목록 컴포넌트
export default function Posts({ posts }) {
  // 낙관적 UI 업데이트를 위한 상태와 업데이트 함수 정의
  const [optimisticPosts, updateOptimisticPosts] = useOptimistic(posts, (prevPosts, updatedPostId) => {
    // 업데이트할 포스트의 인덱스를 찾기
    const updatedPostIndex = prevPosts.findIndex(post => post.id === updatedPostId);

    // 포스트가 목록에 없으면 이전 상태를 그대로 반환
    if (updatedPostIndex === -1) {
      return prevPosts;
    }

    // 포스트의 좋아요 상태를 변경하여 새로운 상태로 업데이트
    const updatedPost = { ...prevPosts[updatedPostIndex] };
    updatedPost.likes = updatedPost.likes + (updatedPost.isLiked ? -1 : 1);
    updatedPost.isLiked = !updatedPost.isLiked;
    const newPosts = [...prevPosts];
    newPosts[updatedPostIndex] = updatedPost;
    return newPosts;
  });

  // 포스트가 없을 때 표시할 메시지
  if (!optimisticPosts || optimisticPosts.length === 0) {
    return <p>There are no posts yet. Maybe start sharing some?</p>;
  }

  // 좋아요 상태를 업데이트하는 함수
  async function updatedPost(postId) {
    updateOptimisticPosts(postId); // 낙관적 업데이트 실행
    await togglePostLikeStatus(postId); // 실제 서버에 좋아요 상태 토글 요청
  }

  // 포스트 목록 렌더링
  return (
    <ul className="posts">
      {optimisticPosts.map((post) => (
        <li key={post.id}>
          <Post post={post} action={updatedPost} /> {/* 개별 포스트 컴포넌트 */}
        </li>
      ))}
    </ul>
  );
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

옛날 성왕(聖王)의 정치는 도덕으로써 천하를 다스렸으나 후세인 지금은 법률로써 천하를 잡아 통치하려고 한다. 법치만으로 참된 정치는 할 수 없는 것이다. -근사록

댓글 ( 0)

댓글 남기기

작성