진심 코딩

20260417 updateOptimisticPosts

magpiebros 2026. 4. 17. 23:37
반응형

오늘도 공부를 한다.

애드센스는 내 글을 복붙이라고 생각한다. 

또는 가치 없는 글이라고 생각하는거 같은데... 

꾸준히 써나갈 계획읻다.

작지만 광고 수익을 얻어보고 싶은데 말이다.

 

오늘은 낙관적인 업데이트에 대해 공부하려고 한다.

서버 응답이 오던말던 UI에 먼저 반영하는 방식이다.

UI먼저 성공처럼 반영하고 이후에 성공이던 아니던 처리하는 방식이다.

전체 코드를 먼저 보자.

'use client';

import { formatDate } from '@/lib/format';
import LikeButton from './like-icon';
import { togglePostLikeStatus } from '@/actions/post';
import { useOptimistic } from 'react';

function Post({ post, action }) {
  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={action.bind(null, post.id)} className={post.isLiked ? 'liked' : ''}>
              <LikeButton />
            </form>
            
          </div>
        </header>
        <p>{post.content}</p>
      </div>
    </article>
  );
}

export default function Posts({ posts, action }) {
  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;
  });

  async function updatePost(postId) {
    updateOptimisticPosts(postId);
    await togglePostLikeStatus(postId);
  }

  if (!optimisticPosts || optimisticPosts.length === 0) {
    return <p>There are no posts yet. Maybe start sharing some?</p>;
  }

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

 

여기서 봐야 할 첫 대목은 아래와 같다.

const [optimisticPosts, updateOptimisticPosts] = useOptimistic(
  posts, 
  (prevPosts, updatedPostId) => { 
    return newPosts; 
  }
);

 

useOptimistic 함수에 posts는 전달 받는 실제 데이터 이다.

이전값과 Id를 받는 다고 가정하고, 데이터를 정재해서 새로운 Posts 데이터를 반환한다.

이 값은 optimisticPosts에 들어가고, 이를 기반으로 화면을 그린다.

이제 언제, 이 함수가 실행되는지에 대한 여부가 관건이다.

 

이 함수의 실행 시점은  updateOptimisticPosts 함수가 불리는 시점이다. 

이 함수는 아래에서 실행이 된다.

 

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

 

해당 화면에서 클릭이 이루어지면 아래 함수가 동작하고

 async function updatePost(postId) {
    updateOptimisticPosts(postId);
    await togglePostLikeStatus(postId);
  }

 

화면을 먼저 노출한다음에, 데이터를 설정하고 화면을 재구성한다.

 

어지럽다...

아직 몸에 와 닿지는 않는다.

반응형