상태 관리 playground
원문 글의 어휘 — Client/Server State · Derived · Optimistic Update · Cache Invalidation — 을 직접 입력하고 클릭하면서 익혀보는 페이지.
1. Client State vs Server State
왼쪽은 client state (앱이 주인 — 새로고침 시 사라짐), 오른쪽은 server state (서버가 주인 — 캐시 형태). "페이지 새로고침" 을 눌러서 둘의 차이를 보세요.
다크모드 토글 (client state)
주인: 앱
현재 값: light
사용자 정보 (server state)
주인: 서버 (캐시 사본)
캐시: 비어있음
새로고침하면 client state 는 초기값으로 돌아가고, server state 는 다시 fetch 해야 합니다.
// React 진영의 분리 패턴
const [theme, setTheme] = useState('light'); // client
const { data: user } = useQuery(['user'], fetchUser); // server
2. Derived (파생 상태) — 저장하지 말고 계산해서 써라
아래는 장바구니. 물품 가격·수량은 원본 상태 로 저장, 총액은 derived (계산값). 값을 바꿔보세요. 총액이 자동 갱신됩니다.
물품 수: 0 종 / 0 개
총액: 0 원
// 원본만 저장
const items = [{ name, price, qty }, ...];
// derived — 계산해서 씀 (저장 X)
const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
3. Optimistic Update — 즉시 UI 변경 + 실패 시 롤백
하트 버튼을 누르면 UI 는 즉시 채워집니다. 백그라운드에서 가짜 서버 요청이 1.5초 후 응답. 실패 확률을 조정해서 롤백 동작을 확인해보세요.
좋아요 0
UI 상태 (즉시 반영)
😐 비어있음
서버 상태 (캐시)
😐 비어있음
// 1. UI 먼저 바꿈
setLiked(true);
// 2. 서버 요청
try {
await api.like(postId);
// 3a. 성공 → 그대로
} catch (e) {
// 3b. 실패 → 롤백 + 사용자 알림
setLiked(false);
toast.error("좋아요 실패");
}
4. Cache Invalidation — 4가지 트리거 패턴
오른쪽이 캐시. "서버 데이터 변경" 으로 캐시가 낡아지면 (stale), 4가지 트리거로 갱신할 수 있습니다.
🗄 서버 (진짜 원본)
{ posts: 3 }
📦 앱 캐시 (fresh)
{ posts: 3 }
트리거 패턴 — 캐시 무효화 → 재fetch
// TanStack Query 예
// 이벤트: mutation 성공 시 무효화
mutation.onSuccess(() => qc.invalidateQueries(['posts']));
// 시간: staleTime + refetchInterval
useQuery(['posts'], fetchPosts, {
staleTime: 10_000, // 10초 후 stale
refetchOnWindowFocus: true,
});