TanStack Query의 자동 동기화 이해하기: 프론트엔드 데이터 신선하게 유지하기
Takashi Yamamoto
Infrastructure Engineer · Leapcell

소개
웹 개발의 역동적인 세계에서 사용자에게 표시되는 데이터가 항상 최신 상태이며 백엔드 서버와 일치하는지 확인하는 것은 지속적인 과제입니다. 기존 접근 방식은 수동 데이터 가져오기, 복잡한 상태 관리 또는 빈번하고 비효율적일 수 있는 폴링 메커니즘을 자주 사용합니다. 이러한 방법은 UI가 오래되거나, 서버 부하가 증가하거나, 사용자 경험이 이상적이지 않을 수 있습니다. 바로 여기서 TanStack Query(이전 React Query)와 같은 최신 데이터 가져오기 라이브러리가 빛을 발합니다. 개발자 경험과 성능을 염두에 두고 설계된 TanStack Query는 서버 상태 관리를 위한 강력한 기능을 제공하며, 특히 백그라운드에서 데이터를 지능적으로 동기화하고 새로고침하는 기능이 뛰어납니다. 이러한 자동 동기화가 어떻게 달성되는지 이해하는 것은 견고하고 반응성이 뛰어나며 효율적인 프론트엔드 애플리케이션을 구축하는 데 중요합니다.
TanStack Query의 핵심 개념
자동 동기화의 구체적인 내용으로 들어가기 전에 TanStack Query 내의 몇 가지 핵심 개념에 대한 기본적인 이해를 확립해 보겠습니다.
- 쿼리(Query): 데이터를 가져오기 위한 비동기 요청(예:
useQuery)을 나타냅니다. "쿼리 키"를 사용하여 데이터를 고유하게 식별합니다. - 쿼리 키(Query Key): 캐시된 각 데이터 조각의 고유 식별자(배열 또는 문자열)입니다. TanStack Query가 어떤 데이터가 어떤 것인지 알 수 있는 방법입니다.
- 캐시(Cache): TanStack Query는 해결된 쿼리의 인메모리 캐시를 유지합니다. 특정 쿼리 키로 데이터를 요청하면 TanStack Query는 먼저 캐시를 확인합니다. 데이터가 사용 가능하고 최신 상태라면 즉시 반환됩니다.
- 신선 시간(Stale Time): 캐시된 데이터가 "오래된" 것으로 간주된 후의 기간입니다. 데이터가 오래되면 TanStack Query는 해당 데이터를 다시 액세스하거나 관찰하는 다음번에 백그라운드에서 다시 가져오려고 시도합니다.
- 캐시 시간(Cache Time): 비활성 캐시 쿼리 데이터가 가비지 수집되는 기간입니다. 쿼리 컴포넌트가 마운트 해제되고 데이터가 더 이상 관찰되지 않으면
cacheTime이 만료될 때까지 캐시에 남아 있습니다. - 다시 가져오기(Refetching): 백엔드에서 최신 데이터를 검색하기 위해 쿼리 함수를 다시 실행하는 프로세스입니다. 자동으로 발생하거나 수동으로 트리거될 수 있습니다.
- 백그라운드 다시 가져오기(Background Refetching): TanStack Query가 초기 렌더링 시 로딩 스피너를 표시하지 않고 데이터를 다시 가져와 UI가 더 빠르게 느껴지도록 합니다. 사용자는 신선한 데이터가 도착하기 전에 잠시 동안 오래된 데이터를 봅니다.
자동 동기화의 원리
TanStack Query의 자동 동기화는 "stale-while-revalidate"라는 원리를 기반으로 합니다. 즉, 쿼리 데이터가 오래된 것으로 간주되면 TanStack Query는 다음을 수행합니다.
- 캐시된(오래된) 데이터를 UI에 즉시 반환합니다.
- 백그라운드에서 서버로부터 최신 데이터를 가져오기 위한 다기능 작업을 시작합니다.
- 다시 가져오기가 완료되면 새 데이터로 캐시를 업데이트하고 신선한 정보로 UI를 다시 렌더링합니다.
이 우아한 메커니즘은 로딩 상태에서 UI 깜박임을 방지하고 데이터가 잠시 동안 최신이 아닐 수 있더라도 사용자에게 즉각적인 응답을 제공합니다.
자동 데이터 새로고침 메커니즘
TanStack Query는 명시적인 폴링 로직 없이 데이터를 자동으로 다시 가져오고 동기화하기 위해 몇 가지 지능형 메커니즘을 사용합니다.
1. 창 포커스 시 다시 가져오기
이것은 아마도 가장 강력하고 종종 간과되는 기능일 것입니다. 사용자가 애플리케이션에서 벗어났다가(예: 탭 전환, 창 최소화) 다시 돌아오면 TanStack Query는 활성적이고 오래된 모든 쿼리를 자동으로 다시 가져옵니다. 이를 통해 사용자가 앱에 다시 참여할 때 백엔드 업데이트로 인해 앱이 벗어난 동안 발생한 격차를 해소하는 최신 데이터를 제시하게 됩니다.
예시:
import { useQuery } from '@tanstack/react-query'; import axios from 'axios'; interface Todo { id: number; title: string; completed: boolean; } const fetchTodos = async (): Promise<Todo[]> => { const { data } = await axios.get<Todo[]>('/api/todos'); return data; }; function TodoList() { const { data, isLoading, error } = useQuery<Todo[], Error>({ queryKey: ['todos'], queryFn: fetchTodos, staleTime: 5 * 60 * 1000, // 5분 후 데이터가 오래된 것으로 간주됨 // 기본적으로 refetchOnWindowFocus는 true입니다. }); if (isLoading) return <div>Loading todos...</div>; if (error) return <div>An error occurred: {error.message}</div>; return ( <ul> {data?.map(todo => ( <li key={todo.id}>{todo.title} - {todo.completed ? 'Done' : 'Pending'}</li> ))} </ul> ); } export default TodoList;
이 예에서 사용자가 TodoList가 마운트된 탭을 5분 이상(staleTime) 떠났다가 다시 돌아오면 TanStack Query는 백그라운드에서 todos를 자동으로 다시 가져옵니다.
2. 마운트 시 다시 가져오기
useQuery 훅의 인스턴스가 마운트될 때 데이터가 오래된 경우 TanStack Query는 자동으로 백그라운드 다시 가져오기를 시작합니다. 이는 자주 마운트 및 마운트 해제되는 컴포넌트에 유용하며 항상 최신 데이터를 표시하도록 합니다.
3. 재연결 시 다시 가져오기
사용자의 네트워크 연결이 끊어졌다가 복구되면 TanStack Query는 이를 지능적으로 감지하고 활성 및 오래된 모든 쿼리를 자동으로 다시 가져옵니다. 이 기능은 간헐적인 연결이 있는 환경에서 사용자 경험을 크게 향상시킵니다. 애플리케이션이 재연결 시 자동으로 "치유"되기 때문입니다.
4. 간격별 다시 가져오기(폴링)
TanStack Query는 수동 폴링의 필요성을 줄이는 것을 목표로 하지만, 다른 메커니즘으로는 부족한 실시간에 가까운 업데이트가 중요한 사용 사례를 위해 지정된 간격으로 데이터를 다시 가져오는 옵션을 제공합니다.
예시:
import { useQuery } from '@tanstack/react-query'; import axios from 'axios'; interface StockPrice { symbol: string; price: number; timestamp: string; } const fetchStockPrice = async (symbol: string): Promise<StockPrice> => { const { data } = await axios.get<StockPrice>(`/api/stock/${symbol}`); return data; }; function StockPriceDisplay({ symbol }: { symbol: string }) { const { data, isLoading, error } = useQuery<StockPrice, Error>({ queryKey: ['stockPrice', symbol], queryFn: () => fetchStockPrice(symbol), refetchInterval: 5000, // 5초마다 다시 가져오기 staleTime: Infinity, // 간격별로만 신선도를 유지하며 데이터 자체는 절대 오래되지 않음 }); if (isLoading) return <div>Loading stock price...</div>; if (error) return <div>An error occurred: {error.message}</div>; return ( <div> <h3>{data?.symbol} Price: ${data?.price.toFixed(2)}</h3> <p>Last updated: {new Date(data?.timestamp || '').toLocaleTimeString()}</p> </div> ); } export default StockPriceDisplay;
여기서 지정된 symbol에 대한 stockPrice는 5초마다 다시 가져와 거의 실시간 업데이트를 제공합니다. refetchInterval이 여기서는 신선도의 주요 메커니즘이므로 staleTime: Infinity가 사용된다는 점에 유의하십시오.
5. 수동 무효화 및 변경
위의 메커니즘은 자동 백그라운드 동기화를 처리하지만, 서버의 특정 데이터가 변경되었으며 다시 가져와야 함을 TanStack Query에 명시적으로 알려야 하는 경우가 있습니다. 이는 리소스를 생성, 업데이트 또는 삭제하는 작업을 수행한 후 특히 관련이 있습니다.
queryClient.invalidateQueries(queryKey): queryKey와 일치하는 쿼리를 오래된 것으로 표시하는 강력한 방법입니다. 무효화되면 해당 쿼리가 관찰/액세스되는 다음번에 백그라운드에서 다시 가져오게 됩니다.
변경을 사용한 예시:
import { useMutation, useQueryClient } from '@tanstack/react-query'; import axios from 'axios'; interface NewTodo { title: string; } interface CreatedTodo extends NewTodo { id: number; } const createTodo = async (newTodo: NewTodo): Promise<CreatedTodo> => { const { data } = await axios.post<CreatedTodo>('/api/todos', newTodo); return data; }; function AddTodoForm() { const queryClient = useQueryClient(); const mutation = useMutation<CreatedTodo, Error, NewTodo>({ mutationFn: createTodo, onSuccess: () => { // 성공적인 생성 후 'todos' 쿼리를 무효화합니다. // 이렇게 하면 TodoList 컴포넌트에서 데이터가 다시 가져와집니다. queryClient.invalidateQueries({ queryKey: ['todos'] }); }, }); const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); const formData = new FormData(event.currentTarget as HTMLFormElement); const title = formData.get('title') as string; mutation.mutate({ title }); }; return ( <form onSubmit={handleSubmit}> <input name="title" placeholder="New todo title" /> <button type="submit" disabled={mutation.isPending}> {mutation.isPending ? 'Adding...' : 'Add Todo'} </button> {mutation.isError && <div>Error adding todo: {mutation.error?.message}</div>} </form> ); } export default AddTodoForm;
이 시나리오에서는 AddTodoForm을 통해 새 할 일이 성공적으로 생성된 후 queryClient.invalidateQueries({ queryKey: ['todos'] })가 호출됩니다. 이는 TanStack Query에 ['todos'] 키(우리 TodoList에서 사용하는)와 관련된 데이터가 이제 오래되었음을 알립니다. 그러면 TodoList는 페이지 새로고침이나 수동 상태 업데이트 없이 새로 추가된 할 일을 표시하기 위해 백그라운드 다시 가져오기를 자동으로 트리거합니다.
결론
TanStack Query는 프론트엔드 애플리케이션에서 서버 상태를 관리하는 방식을 근본적으로 변화시켜 수동 데이터 동기화에서 지능적이고 선언적인 접근 방식으로 전환합니다. 창 포커스, 마운트, 재연결 시 다시 가져오기 및 유연한 무효화 전략과 같은 메커니즘을 활용하여 애플리케이션 UI가 백엔드 데이터와 일관되게 동기화되도록 보장합니다. 이는 반응성 있는 인터페이스, 더 적은 개발자 보일러플레이트 코드, 궁극적으로 더 성능이 뛰어나고 유지 관리하기 쉬운 애플리케이션을 통해 사용자 경험을 크게 향상시킵니다. TanStack Query의 자동 동기화 기능을 채택하는 것은 진정으로 현대적이고 역동적인 웹 경험을 구축하는 데 중요한 단계입니다.

