소스 : https://github.com/braverokmc79/nextjs-ex06
낙관적 업데이트(Optimistic UI)는 사용자가 데이터를 변경할 때, 서버의 응답을 기다리지 않고 즉시 사용자 인터페이스(UI)를 업데이트하는 방법입니다. 이렇게 하면 애플리케이션이 더 빠르고 응답성이 좋게 느껴집니다. 다음은 Next.js의 App Router 방식에서 낙관적 업데이트를 구현하는 방법에 대한 설명입니다.
Next.js App Router 방식에서 낙관적 업데이트
상태 관리:
- 낙관적 업데이트를 위해서는 클라이언트 측 상태 관리가 필요합니다. React의 상태 관리 훅인 useState 또는 더 복잡한 상태 관리를 위해 useReducer를 사용할 수 있습니다.
Optimistic UI 구현:
- useState나 useReducer를 사용하여 상태를 관리하면서, 사용자의 액션에 따라 상태를 즉시 업데이트합니다.
- 서버에 요청을 보낸 후 응답을 받으면 상태를 다시 업데이트하여 최종 상태를 반영합니다.
에러 처리:
- 서버 요청이 실패할 경우, 이전 상태로 롤백할 수 있는 로직을 구현합니다.
구현 예시
아래는 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> ); }
댓글 ( 0)
댓글 남기기