프론트엔드에서 UX를 크게 개선하는 패턴 중 하나가 Optimistic Update다.
이게 실제로 적용되어 있는지, 어디에 있는지, 체감 성능 차이는 어떤지까지 정리해봤다.
서버 응답을 기다리지 않고, 성공할 것이라 가정하고 UI를 먼저 업데이트하는 방식
핵심은 “사용자가 기다리지 않게 만든다”
해당 이미지처럼 Optimistic Updates를 구현함으로써 유저는 자신의 액션에 따른 즉각적인 피드백을 받을 수 있고, 향상된 UX를 경험할 수 있다.
현재 DashboardPage.tsx에서 총 3군데 적용되어 있다.
// optimistic: 서버 응답 없이 즉시 목록 앞에 추가
setRecentExpenses((prev) => [createdExpense, ...prev]);
// 서버 재동기화
await fetchDashboardData();
await fetchRecent();
서버 응답을 기다리지 않고 바로 UI에 추가됨
if (isThisMonth) {
setMonthlyTotal((prev) => prev + createdExpense.amount);
}
SummarySection의 예산 프로그레스 바가 즉시 반응
// optimistic
setRecentExpenses(prev => prev.filter(e => e.id !== id));
setMonthlyTotal(prev => prev - target.amount);
// 서버 요청
await deleteExpense(id);
fetchDashboardData();
fetchRecent();
삭제 버튼 누르면 바로 사라짐 (지연 없음)
const handleDelete = async (id: number) => {
const target = recentExpenses.find(e => e.id === id);
// 롤백용 스냅샷
const previousExpenses = recentExpenses;
const previousTotal = monthlyTotal;
// ① optimistic: UI 즉시 반영
setRecentExpenses(prev => prev.filter(e => e.id !== id));
if (target?.expenseAt) {
const expenseDate = new Date(target.expenseAt);
const now = new Date();
const isThisMonth =
expenseDate.getFullYear() === now.getFullYear() &&
expenseDate.getMonth() === now.getMonth();
if (isThisMonth) {
setMonthlyTotal(prev => prev - target.amount);
}
}
try {
// ② 서버 요청
await deleteExpense(id);
// ③ 서버 재동기화
await fetchDashboardData();
await fetchRecent();
} catch {
// ④ rollback
setRecentExpenses(previousExpenses);
setMonthlyTotal(previousTotal);
}
};
기존에는 optimistic update만 적용되어 있었지만,
현재는 서버 요청 실패 시 이전 상태로 되돌리는 rollback까지 포함된 구조로 개선했다.
이런 상황에서 rollback 없으면 UI와 서버 데이터 불일치 발생한다.
특히 삭제/수정 같은 작업에서는 사용자가 “이미 처리된 줄 알고” 잘못된 행동을 이어갈 수 있다.
| 상황 | Optimistic Update 있음 | 없음 |
|---|---|---|
| 등록 후 목록 반영 | 즉시 (~0ms) | 서버 응답 후 (~300~800ms) |
| 삭제 반영 | 즉시 | 서버 응답 후 |
| 프로그레스 바 | 즉시 변화 | 지연 후 변화 |
차이는 단순한 속도가 아니라 “사용자가 느끼는 반응성” 자체가 달라진다
크롬 DevTools → Network 탭에서 테스트 가능
✅ Optimistic 있는 경우 (현재 코드)
❌ Optimistic 없는 경우
이 차이는 생각보다 크다 → “버튼이 안 눌린 줄 착각”하는 UX 문제까지 발생
작성한 모든 코드 영역에 적용된 건 아니다.
Optimistic Update 미적용 영역
이유: 서버에서 집계/분석 로직이 필요한 데이터
결과: 등록 후에도 차트는 서버 응답 이후에만 갱신됨
체감 속도 개선 (가장 중요)
실제 네트워크 속도와 무관하게 즉각 반응
UX 향상
“앱이 빠르다”는 인상을 줌
사용자 이탈 감소
로딩 대기 스트레스 제거
실패 시 롤백 처리 필요
서버 데이터와 불일치 가능성
복잡한 로직에서는 적용 어려움
그래서 보통 CRUD (등록/삭제)에는 적극 사용하지만
집계/분석 데이터에는 잘 쓰지 않는다 (클라이언트에서 정확한 계산을 재현하기 어렵기 때문).
Optimistic Update는 실제 속도를 빠르게 만드는 게 아니라
사용자가 느끼는 속도를 0ms로 만드는 기술