반응형
소개
React Query의 useQuery
와 useMutation
은 서버 상태 관리를 위한 핵심 훅입니다. 이번 글에서는 두 훅의 상세한 사용법과 실전 예제를 알아보겠습니다.
설치
# npm
npm install @tanstack/react-query
# yarn
yarn add @tanstack/react-query
# pnpm
pnpm add @tanstack/react-query
useQuery 기본 사용법
간단한 데이터 조회
import { useQuery } from '@tanstack/react-query';
// 사용자 조회 훅
function useUser(userId: string) {
return useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
staleTime: 5 * 60 * 1000, // 5분
cacheTime: 30 * 60 * 1000, // 30분
});
}
// 컴포넌트에서 사용
function UserProfile({ userId }: { userId: string }) {
const { data, isLoading, error } = useUser(userId);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
);
}
의존적 쿼리
function UserPosts({ userId }: { userId: string }) {
const { data: user } = useUser(userId);
const { data: posts } = useQuery({
queryKey: ['posts', userId],
queryFn: () => fetchUserPosts(userId),
enabled: !!user, // user 데이터가 있을 때만 실행
});
return (
<div>
{posts?.map(post => (
<PostItem key={post.id} post={post} />
))}
</div>
);
}
useMutation 기본 사용법
데이터 수정
function CreateUserForm() {
const mutation = useMutation({
mutationFn: (newUser: User) => {
return axios.post('/api/users', newUser);
},
onSuccess: (data) => {
console.log('User created:', data);
// 캐시 무효화 또는 업데이트
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
mutation.mutate({ name: 'John', email: 'john@example.com' });
};
return (
<form onSubmit={handleSubmit}>
{mutation.isLoading ? (
'Creating user...'
) : (
<>
{mutation.isError ? (
<div>Error: {mutation.error.message}</div>
) : null}
{mutation.isSuccess ? <div>User created!</div> : null}
<button type="submit">Create User</button>
</>
)}
</form>
);
}
고급 사용법
낙관적 업데이트
const updateUserMutation = useMutation({
mutationFn: updateUser,
onMutate: async (newUserData) => {
// 진행 중인 쿼리 취소
await queryClient.cancelQueries(['user', newUserData.id]);
// 이전 데이터 저장
const previousUser = queryClient.getQueryData(['user', newUserData.id]);
// 낙관적 업데이트
queryClient.setQueryData(['user', newUserData.id], newUserData);
// 롤백을 위한 컨텍스트 반환
return { previousUser };
},
onError: (err, newUserData, context) => {
// 에러 발생 시 롤백
queryClient.setQueryData(
['user', newUserData.id],
context.previousUser
);
},
onSettled: (newUserData) => {
// 캐시 무효화
queryClient.invalidateQueries(['user', newUserData.id]);
},
});
무한 스크롤
function InfinitePostList() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage
} = useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam = 0 }) => fetchPosts(pageParam),
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
return (
<div>
{data?.pages.map((page, i) => (
<React.Fragment key={i}>
{page.posts.map(post => (
<PostItem key={post.id} post={post} />
))}
</React.Fragment>
))}
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage
? 'Loading more...'
: hasNextPage
? 'Load More'
: 'Nothing more to load'}
</button>
</div>
);
}
실시간 데이터 동기화
function LiveUserData({ userId }: { userId: string }) {
const { data } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
refetchInterval: 1000, // 1초마다 갱신
});
return <div>{data?.name}</div>;
}
성능 최적화
선택적 데이터 변환
const { data } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
select: (users) => {
return users
.filter(user => user.isActive)
.sort((a, b) => b.lastActiveAt - a.lastActiveAt);
},
});
병렬 쿼리
function ParallelQueries() {
const users = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
const posts = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
return (
<div>
{users.data?.length} users
{posts.data?.length} posts
</div>
);
}
에러 처리
function ErrorBoundaryExample() {
const { data, error, isError } = useQuery({
queryKey: ['data'],
queryFn: fetchData,
retry: 3,
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
onError: (error) => {
// 에러 로깅
console.error('Query error:', error);
},
});
if (isError) {
return <ErrorComponent error={error} />;
}
return <div>{data}</div>;
}
마치며
useQuery
와 useMutation
은 React 애플리케이션에서 서버 상태를 관리하는 강력한 도구입니다. 적절한 설정과 최적화를 통해 효율적인 데이터 관리가 가능합니다.
'TypeScript' 카테고리의 다른 글
[React] React.lazy와 Suspense (2) | 2024.12.26 |
---|---|
[React] memo를 사용한 컴포넌트 최적화 (0) | 2024.12.25 |
[React] Zustand로 배우는 간단하고 강력한 상태 관리 (0) | 2024.12.20 |
[React Query] QueryClient : 효율적인 상태관리와 데이터 캐싱 (0) | 2024.12.19 |
프론트엔드와 백엔드 간 Enum 동기화 (0) | 2024.12.07 |