소스 : 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)
댓글 남기기