8화. TanStack Query & SWR — 데이터 통신의 꽃🌐
안녕하세요 🌟 오늘은 프론트엔드 필수 라이브러리 시리즈의 여덟 번째 이야기로, 서버 데이터 관리의 두 강자 TanStack Query와 SWR에 대해 알아볼게요! 이 두 라이브러리는 2025년 현재, API 통신과 서버 상태 관리의 표준으로 자리 잡았답니다! 💾

왜 서버 상태 관리가 중요할까요? 🤔
현대 웹 애플리케이션은 서버에서 데이터를 가져와 화면에 표시하는 작업이 핵심이죠. 하지만 이 과정에는 생각보다 많은 복잡성이 숨어 있어요:
- 캐싱: 같은 데이터를 반복해서 요청하지 않도록 관리
- 로딩/에러 상태: API 요청의 다양한 상태 처리
- 데이터 동기화: 서버 데이터 변경 시 UI 자동 갱신
- 페이지네이션/무한 스크롤: 대량 데이터의 효율적인 로딩
- 낙관적 업데이트: UI를 즉시 업데이트하고 서버 응답 대기
- 요청 중복 제거: 동일 데이터에 대한 중복 요청 방지
이런 복잡한 문제들을 직접 구현하려면 많은 코드와 시간이 필요하죠. TanStack Query와 SWR은 이 모든 문제를 우아하게 해결해주는 라이브러리예요! 함께 살펴볼까요?
TanStack Query: 강력한 데이터 동기화의 정석 💪
TanStack Query(이전 명칭: React Query)는 Tanner Linsley가 개발한 라이브러리로, 서버 상태 관리를 위한 강력한 기능을 제공해요. 2025년 현재 TanStack Query는 React뿐만 아니라 Vue, Svelte, Solid, Angular 등 다양한 프레임워크를 지원하는 범용 라이브러리로 발전했답니다!

TanStack Query의 주요 특징:
- 선언적 데이터 페칭 📝
- 복잡한 로직을 간결한 hook으로 추상화
- 데이터 요청 상태(로딩, 성공, 에러)를 자동으로 관리
- 스마트 캐싱 시스템 💾
- 지능적인 캐싱으로 중복 요청 방지
- 캐시 무효화와 자동 리페칭(refetching) 제공
- 자동 동기화와 백그라운드 업데이트 🔄
- 창 포커스, 네트워크 재연결 시 자동 데이터 갱신
- 오래된 데이터를 표시하면서 백그라운드에서 갱신
- 페이지네이션과 무한 스크롤 📚
- 대량 데이터 로딩을 위한 내장 유틸리티
- 이전 페이지 데이터를 유지하면서 새 페이지 요청
// TanStack Query 기본 사용 예시
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// 데이터 페칭 함수
const fetchTodos = async () => {
const response = await fetch('https://api.example.com/todos');
if (!response.ok) {
throw new Error('네트워크 응답이 올바르지 않습니다');
}
return response.json();
};
// 할 일 추가 함수
const addTodo = async (newTodo) => {
const response = await fetch('https://api.example.com/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newTodo),
});
return response.json();
};
function TodoList() {
const queryClient = useQueryClient();
// 데이터 조회
const { data, isLoading, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
staleTime: 60000, // 1분 동안 데이터를 최신 상태로 간주
});
// 데이터 변경
const mutation = useMutation({
mutationFn: addTodo,
onSuccess: () => {
// 성공 시 'todos' 쿼리 무효화하여 자동으로 다시 가져오기
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
const handleAddTodo = () => {
mutation.mutate({ title: '새 할 일', completed: false });
};
if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러: {error.message}</div>;
return (
<div>
<h1>할 일 목록</h1>
<ul>
{data.map(todo => (
<li key={todo.id}>
{todo.title} {todo.completed ? '✅' : '❌'}
</li>
))}
</ul>
<button onClick={handleAddTodo} disabled={mutation.isPending}>
{mutation.isPending ? '추가 중...' : '할 일 추가'}
</button>
</div>
);
}
TanStack Query의 고급 기능:
useQuery의 강력한 옵션들
const { data, isLoading } = useQuery({
queryKey: ['todos', { status, page }], // 동적 쿼리 키
queryFn: () => fetchTodos(status, page),
staleTime: 60000, // 1분 동안 데이터를 신선한 상태로 유지
gcTime: 300000, // 5분 동안 캐시된 데이터 유지
refetchOnWindowFocus: true, // 윈도우 포커스 시 자동 리페칭
retry: 3, // 실패 시 최대 3번 재시도
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000), // 지수 백오프
onSuccess: data => console.log('데이터 로드 성공:', data),
onError: error => console.error('데이터 로드 실패:', error),
});
- 의존적 쿼리 (Dependent Queries)
// 첫 번째 쿼리
const { data: user } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
// 사용자 데이터가 있을 때만 프로젝트 쿼리 실행
const { data: projects } = useQuery({
queryKey: ['projects', user?.id],
queryFn: () => fetchProjects(user.id),
// user가 없으면 쿼리가 실행되지 않음
enabled: !!user,
});
- 무한 쿼리와 페이지네이션
// 무한 스크롤 구현
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['projects'],
queryFn: ({ pageParam = 1 }) => fetchProjects(pageParam),
getNextPageParam: (lastPage, allPages) => {
return lastPage.nextPage ?? undefined;
},
});
// 사용 예
return (
<div>
{data.pages.map((page, i) => (
<React.Fragment key={i}>
{page.projects.map(project => (
<p key={project.id}>{project.name}</p>
))}
</React.Fragment>
))}
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage
? '로딩 중...'
: hasNextPage
? '더 보기'
: '더 이상 데이터가 없습니다'}
</button>
</div>
);
- 낙관적 업데이트 (Optimistic Updates)
const queryClient = useQueryClient();
// 낙관적 업데이트로 즉각적인 UI 반응
const mutation = useMutation({
mutationFn: updateTodo,
// 변경 전 낙관적으로 UI 업데이트
onMutate: async (newTodo) => {
// 진행 중인 관련 쿼리 취소
await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] });
// 이전 상태 저장
const previousTodo = queryClient.getQueryData(['todos', newTodo.id]);
// 새 값으로 캐시 업데이트
queryClient.setQueryData(['todos', newTodo.id], newTodo);
// 롤백을 위해 이전 값 반환
return { previousTodo };
},
// 에러 발생 시 롤백
onError: (err, newTodo, context) => {
queryClient.setQueryData(
['todos', newTodo.id],
context.previousTodo
);
},
// 성공 또는 실패 후 갱신
onSettled: (newTodo) => {
queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] });
},
});
2025년 TanStack Query v6의 최신 기능:
- 서스펜스 모드 개선: React의 Suspense와 더 자연스러운 통합
- 자동 타입 추론 강화: TypeScript와의 더 나은 통합으로 타입 안전성 향상
- 타이머 기반 GC: 성능 최적화를 위한 새로운 가비지 컬렉션 알고리즘
- 쿼리 그룹화: 관련 쿼리를 그룹으로 관리하고 일괄 처리
- 액션 관리자: 서버 액션과의 통합을 위한 새로운 유틸리티
SWR: 간결함과 직관성의 대명사 🔄
SWR(Stale-While-Revalidate)은 Vercel 팀이 개발한 데이터 페칭 라이브러리로, HTTP 캐시 무효화 전략에서 이름을 따왔어요. 간결한 API와 직관적인 사용법으로 많은 사랑을 받고 있답니다!

SWR의 주요 특징:
- 최소한의 API 🧘♀️
- 단 하나의 핵심 함수
useSWR로 대부분의 기능 제공 - 간결하고 직관적인 API 설계
- 단 하나의 핵심 함수
- 빠른 페이지 전환 ⚡
- 캐시된 데이터를 즉시 표시하고 백그라운드에서 갱신
- 사용자 경험을 극대화하는 설계 철학
- 자동 리페칭 🔄
- 창 포커스, 네트워크 재연결, 일정 간격으로 자동 갱신
- 항상 최신 데이터 유지 가능
- 타입스크립트 지원 📘
- 완벽한 타입 안정성
- 자동 타입 추론으로 개발 경험 향상
// SWR 기본 사용 예시
import useSWR from 'swr';
// fetcher 함수
const fetcher = (url) => fetch(url).then(res => {
if (!res.ok) throw new Error('API 요청 실패');
return res.json();
});
function Profile() {
const { data, error, isLoading } = useSWR(
'https://api.example.com/user',
fetcher
);
if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러: {error.message}</div>;
return (
<div>
<h1>{data.name}</h1>
<p>직업: {data.profession}</p>
</div>
);
}
SWR의 고급 기능:
- 조건부 페칭
// 조건부로 데이터 가져오기
function UserProfile({ userId }) {
const { data } = useSWR(
userId ? `/api/user/${userId}` : null,
fetcher
);
// userId가 없으면 요청이 실행되지 않음
if (!userId) return <div>사용자를 선택하세요</div>;
if (!data) return <div>로딩 중...</div>;
return <h1>{data.name}의 프로필</h1>;
}
- 데이터 변경과 재검증
import useSWR, { mutate } from 'swr';
function TodoList() {
const { data } = useSWR('/api/todos', fetcher);
// 낙관적 UI 업데이트와 재검증
const addTodo = async (text) => {
// 1. 현재 캐시된 데이터 가져오기
const newTodo = { id: Date.now(), text, completed: false };
const updatedTodos = [...(data || []), newTodo];
// 2. 낙관적 업데이트를 위해 로컬 데이터 변경
mutate('/api/todos', updatedTodos, false); // false는 재검증 건너뛰기를 의미
// 3. 실제 API 호출
try {
await fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
// 4. 성공 시 데이터 재검증
mutate('/api/todos');
} catch (error) {
// 5. 실패 시 원래 데이터로 롤백
mutate('/api/todos');
}
};
return (
<div>
<ul>
{data?.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
<button onClick={() => addTodo('새 할 일')}>추가</button>
</div>
);
}
- 무한 스크롤
import { useSWRInfinite } from 'swr/infinite';
function InfiniteProjects() {
// 페이지 키 생성 함수
const getKey = (pageIndex, previousPageData) => {
// 끝에 도달
if (previousPageData && !previousPageData.length) return null;
// 첫 페이지이면 `previousPageData`는 `null`
return `/api/projects?page=${pageIndex + 1}&limit=10`;
};
const { data, size, setSize, isValidating } = useSWRInfinite(
getKey,
fetcher
);
// 모든 프로젝트를 하나의 배열로 평탄화
const projects = data ? [].concat(...data) : [];
const isEmpty = data?.[0]?.length === 0;
const isReachingEnd = isEmpty || (data && data[data.length - 1]?.length < 10);
return (
<div>
<ul>
{projects.map(project => (
<li key={project.id}>{project.name}</li>
))}
</ul>
<button
onClick={() => setSize(size + 1)}
disabled={isReachingEnd || isValidating}
>
{isValidating
? '로딩 중...'
: isReachingEnd
? '더 이상 없음'
: '더 보기'}
</button>
</div>
);
}
- 데이터 포커스 리페칭
import useSWR from 'swr';
function Dashboard() {
const { data } = useSWR('/api/dashboard', fetcher, {
// 옵션 설정
refreshInterval: 3000, // 3초마다 자동 재검증
revalidateOnFocus: true, // 창 포커스 시 재검증
revalidateOnReconnect: true, // 네트워크 재연결 시 재검증
refreshWhenHidden: false, // 페이지가 숨겨져 있을 때는 자동 재검증 중지
});
return <div>{/* 데이터 표시 */}</div>;
}
2025년 SWR v4의 최신 기능:
- 스마트 인터벌: 사용자 상호작용에 따라 자동으로 갱신 간격 조정
- 뮤테이션 플러그인: 더 강력한 낙관적 UI 업데이트 기능
- 확장 가능한 캐시 제공자: 커스텀 스토리지 구현 지원
- 디버깅 도구 개선: 더 나은 개발자 경험을 위한 DevTools
- 교차 탭 동기화: 여러 탭 간의 데이터 상태 동기화
TanStack Query vs SWR: 어떤 선택이 좋을까? 🤔
두 라이브러리 모두 훌륭한 선택이지만, 프로젝트의 특성과 팀의 선호도에 따라 선택이 달라질 수 있어요. 주요 차이점을 비교해볼게요!
크기와 번들 사이즈:
- TanStack Query: 약 12KB (gzip)
- SWR: 약 5KB (gzip)
API 스타일:
- TanStack Query: 기능이 풍부하고 세밀한 제어 가능, 다양한 훅 제공
- SWR: 미니멀하고 직관적인 API, 핵심 기능에 집중
기능 세트:
- TanStack Query: 더 많은 내장 기능과 미들웨어 제공
- SWR: 필수 기능에 집중하며 간결한 API 유지
프레임워크 지원:
- TanStack Query: React, Vue, Svelte, Solid, Angular 등 광범위한 지원
- SWR: 주로 React에 집중 (Next.js와의 통합이 특히 뛰어남)
사용 시나리오별 추천:
TanStack Query를 선택해야 할 때:
- 복잡한 데이터 요구사항이 있는 대규모 애플리케이션
- 페이지네이션, 무한 스크롤 등의 고급 기능이 자주 필요한 경우
- 다양한 프레임워크를 사용하는 프로젝트
- 세밀한 캐싱 제어와 다양한 옵션이 필요한 경우
SWR을 선택해야 할 때:
- 간결한 API와 빠른 학습 곡선을 선호할 때
- Next.js 프로젝트와의 통합이 중요할 때
- 번들 크기를 최소화하고 싶을 때
- 기본 설정으로도 충분한 간단한 데이터 요구사항
실제 사용 사례 💼
실제 프로젝트에서 두 라이브러리를 어떻게 활용할 수 있는지 살펴볼게요!
1. 실시간 대시보드 (TanStack Query)
// TanStack Query로 실시간 대시보드 구현
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
// API 함수
const fetchDashboardData = async (timeRange) => {
const response = await fetch(`/api/dashboard?range=${timeRange}`);
return response.json();
};
function Dashboard() {
const [timeRange, setTimeRange] = useState('day');
const queryClient = useQueryClient();
const { data, isLoading, error } = useQuery({
queryKey: ['dashboard', timeRange],
queryFn: () => fetchDashboardData(timeRange),
refetchInterval: 30000, // 30초마다 자동 갱신
});
// 시간 범위 변경 핸들러
const handleRangeChange = (newRange) => {
setTimeRange(newRange);
};
// 수동 새로고침
const handleRefresh = () => {
queryClient.invalidateQueries({ queryKey: ['dashboard', timeRange] });
};
if (isLoading) return <div>대시보드 로딩 중...</div>;
if (error) return <div>에러: {error.message}</div>;
return (
<div>
<h1>실시간 대시보드</h1>
<div className="controls">
<select
value={timeRange}
onChange={(e) => handleRangeChange(e.target.value)}
>
<option value="day">오늘</option>
<option value="week">이번 주</option>
<option value="month">이번 달</option>
</select>
<button onClick={handleRefresh}>새로고침</button>
</div>
<div className="stats">
<div className="stat-card">
<h3>방문자 수</h3>
<p className="stat-value">{data.visitors}</p>
</div>
<div className="stat-card">
<h3>판매량</h3>
<p className="stat-value">{data.sales}</p>
</div>
<div className="stat-card">
<h3>전환율</h3>
<p className="stat-value">{data.conversionRate}%</p>
</div>
</div>
<div className="chart">
{/* 차트 컴포넌트 렌더링 */}
<LineChart data={data.timeSeriesData} />
</div>
</div>
);
}
2. 소셜 미디어 피드 (SWR)
// SWR로 소셜 미디어 피드 구현
import useSWR, { useSWRInfinite, mutate } from 'swr';
import { useState } from 'react';
// API 함수
const fetcher = url => fetch(url).then(res => res.json());
function SocialFeed() {
const [newPostText, setNewPostText] = useState('');
// 무한 스크롤 데이터 가져오기
const getKey = (pageIndex, previousPageData) => {
if (previousPageData && !previousPageData.posts.length) return null;
return `/api/posts?page=${pageIndex + 1}&limit=10`;
};
const { data, size, setSize, isValidating } = useSWRInfinite(
getKey,
fetcher
);
// 사용자 정보 가져오기
const { data: userData } = useSWR('/api/user', fetcher);
// 모든 포스트를 하나의 배열로 평탄화
const posts = data ? data.flatMap(page => page.posts) : [];
const isReachingEnd = data && data[data.length - 1]?.posts.length < 10;
// 새 포스트 작성
const handlePostSubmit = async (e) => {
e.preventDefault();
if (!newPostText.trim()) return;
// 낙관적 업데이트
const newPost = {
id: Date.now(),
text: newPostText,
user: userData,
likes: 0,
createdAt: new Date().toISOString(),
};
// 첫 페이지 데이터 가져오기
const firstPageKey = getKey(0, null);
const firstPageData = data?.[0] || { posts: [] };
// 새 포스트를 맨 위에 추가
mutate(
firstPageKey,
{ ...firstPageData, posts: [newPost, ...firstPageData.posts] },
false
);
// 입력 필드 초기화
setNewPostText('');
try {
// 실제 API 호출
await fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: newPostText }),
});
// 성공 시 첫 페이지 데이터 재검증
mutate(firstPageKey);
} catch (error) {
console.error('포스트 작성 실패:', error);
// 실패 시 원래 데이터로 롤백 (자동으로 재검증)
mutate(firstPageKey);
}
};
// 좋아요 토글
const handleLikeToggle = async (postId) => {
// 모든 페이지에서 해당 포스트 업데이트
const updatePost = (pageData) => {
return {
...pageData,
posts: pageData.posts.map(post => {
if (post.id === postId) {
const liked = post.liked || false;
return {
...post,
liked: !liked,
likes: liked ? post.likes - 1 : post.likes + 1,
};
}
return post;
}),
};
};
// 각 페이지 데이터 업데이트
for (let i = 0; i < data.length; i++) {
const pageKey = getKey(i, i > 0 ? data[i-1] : null);
mutate(pageKey, updatePost(data[i]), false);
}
try {
// 실제 API 호출
await fetch(`/api/posts/${postId}/like`, {
method: 'POST',
});
// 모든 데이터 재검증
mutate('/api/posts');
} catch (error) {
console.error('좋아요 토글 실패:', error);
// 실패 시 자동으로 재검증됨
}
};
return (
<div className="social-feed">
<h1>소셜 피드</h1>
{/* 새 포스트 작성 폼 */}
<form onSubmit={handlePostSubmit} className="post-form">
<textarea
value={newPostText}
onChange={(e) => setNewPostText(e.target.value)}
placeholder="무슨 생각을 하고 계신가요?"
maxLength={280}
/>
<button type="submit">게시</button>
</form>
{/* 포스트 목록 */}
<div className="posts-list">
{posts.map(post => (
<div key={post.id} className="post-card">
<div className="post-header">
<img src={post.user.avatar} alt={post.user.name} />
<div>
<h3>{post.user.name}</h3>
<time>{new Date(post.createdAt).toLocaleString()}</time>
</div>
</div>
<p className="post-text">{post.text}</p>
<div className="post-actions">
<button
className={`like-button ${post.liked ? 'liked' : ''}`}
onClick={() => handleLikeToggle(post.id)}
>
{post.liked ? '♥' : '♡'} {post.likes}
</button>
</div>
</div>
))}
{/* 더 보기 버튼 */}
<button
onClick={() => setSize(size + 1)}
disabled={isReachingEnd || isValidating}
className="load-more-button"
>
{isValidating
? '로딩 중...'
: isReachingEnd
? '더 이상 포스트가 없습니다'
: '더 보기'}
</button>
</div>
</div>
);
}
서버 상태 관리의 베스트 프랙티스 🌟
어떤 라이브러리를 선택하든 효과적인 서버 상태 관리를 위한 몇 가지 권장 사항이 있어요!
1. 적절한 캐싱 전략 수립
// TanStack Query에서 캐싱 설정
const queryOptions = {
staleTime: 60 * 1000, // 1분 동안 데이터를 신선하게 유지
gcTime: 5 * 60 * 1000, // 5분 동안 비활성 캐시 유지
};
// SWR에서 캐싱 설정
const swrOptions = {
dedupingInterval: 2000, // 2초 내 중복 요청 방지
revalidateOnFocus: false, // 특정 경우 자동 재검증 끄기
};
2. 에러 처리와 재시도
// TanStack Query의 에러 처리 및 재시도
const { data, error } = useQuery({
queryKey: ['data'],
queryFn: fetchData,
retry: (failureCount, error) => {
// 특정 에러에 대해서만 재시도
if (error.status === 404) return false; // 404는 재시도 안 함
return failureCount < 3; // 다른 에러는 최대 3번 재시도
},
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
});
// SWR의 에러 처리 및 재시도
const { data, error } = useSWR('/api/data', fetcher, {
errorRetryCount: 3, // 최대 3번 재시도
errorRetryInterval: 5000, // 5초 간격으로 재시도
onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
// 404는 재시도 안 함
if (error.status === 404) return;
// 최대 10번까지만 재시도
if (retryCount >= 10) return;
// 5초 후 재시도
setTimeout(() => revalidate({ retryCount }), 5000);
},
});
3. 백그라운드 데이터 동기화 조정
// TanStack Query 백그라운드 동기화 설정
const { data } = useQuery({
queryKey: ['user'],
queryFn: fetchUser,
refetchOnWindowFocus: 'always', // 항상 창 포커스 시 리페칭
refetchOnMount: false, // 마운트 시 리페칭 비활성화
refetchOnReconnect: true, // 네트워크 재연결 시 리페칭
refetchInterval: isUserActive ? 30000 : false, // 사용자 활성 시에만 30초마다 리페칭
});
// SWR 백그라운드 동기화 설정
const { data } = useSWR('/api/user', fetcher, {
revalidateOnFocus: true,
revalidateIfStale: true,
revalidateOnReconnect: true,
refreshInterval: isUserActive ? 30000 : 0,
refreshWhenHidden: false, // 페이지가 숨겨져 있을 때는 리페칭 중지
});
4. 낙관적 업데이트와 롤백
// 낙관적 업데이트 패턴
const handleUpdateUser = async (newData) => {
// 1. 현재 데이터 저장
const previousData = queryClient.getQueryData(['user']);
// 2. 낙관적 업데이트
queryClient.setQueryData(['user'], old => ({ ...old, ...newData }));
try {
// 3. 실제 API 호출
await updateUser(newData);
// 4. 성공 시 서버 데이터로 재검증
queryClient.invalidateQueries({ queryKey: ['user'] });
} catch (error) {
// 5. 실패 시 이전 데이터로 롤백
queryClient.setQueryData(['user'], previousData);
// 6. 사용자에게 에러 알림
toast.error('사용자 정보 업데이트에 실패했습니다.');
}
};
마무리 🎁
TanStack Query와 SWR은 모두 현대 웹 애플리케이션의 데이터 페칭과 서버 상태 관리를 크게 개선해주는 강력한 도구예요! 두 라이브러리는 각자의 장점이 있으며, 프로젝트의 특성과 팀의 선호도에 따라 적절한 선택을 할 수 있어요.
- TanStack Query: 더 많은 기능과 세밀한 제어가 필요한 복잡한 프로젝트에 적합
- SWR: 간결함과 직관성을 중시하는 프로젝트에 적합
한 가지 중요한 점은, 이 두 라이브러리는 클라이언트 상태(예: 폼 상태, UI 상태)를 관리하는 것이 아니라 서버 상태(API에서 가져온 데이터)를 관리하는 데 특화되어 있다는 것이에요! 클라이언트 상태 관리는 앞서 살펴본 Zustand, Jotai, Redux 등의 라이브러리가 담당하죠.
다음 시간에는 프론트엔드 개발자의 생산성을 높여주는 유틸리티 라이브러리들인 Lodash, Day.js, Lucide에 대해 알아볼 예정이니 기대해주세요! 🧰
여러분의 서버 상태 관리가 더욱 효율적이고 즐거워지길 바랍니다! 😄👋
'개발' 카테고리의 다른 글
| 프론트 필수 라이브러리 모음 : 10화 Jest, React Testing Library, Cypress - 테스트 자동화🧩✨ (0) | 2025.04.26 |
|---|---|
| 프론트 필수 라이브러리 모음 : 9화. Lodash, Day.js, Lucide - 유용한 유틸리티 모음 🧩✨ (0) | 2025.04.25 |
| 프론트 필수 라이브러리 모음 : 7화 Zustand & Jotai & Redux - 상태 관리 비교 🧩✨ (0) | 2025.04.23 |
| 프론트 필수 라이브러리 모음 : 6화. React Hook Form & Zod - 폼과 유효성 검증🧩✨ (0) | 2025.04.22 |
| 프론트 필수 라이브러리 모음 : 5화. Lottie & Motion One - 감성 애니메이션 만들기🧩✨ (0) | 2025.04.21 |