본문으로 건너뛰기
버전: 11.x

TanStack React Query

기존 React Query 통합과 비교하면 이 클라이언트는 더 단순하고 TanStack Query 방식에 가깝습니다. QueryKeys, QueryOptions, MutationOptions 같은 일반적인 TanStack React Query 인터페이스의 팩토리도 제공합니다. 기존 클라이언트보다 이 클라이언트를 권장하며, 자세한 내용은 발표 게시물에서 확인할 수 있습니다.

tRPC.io 홈페이지에서 이 통합을 직접 사용해 볼 수 있습니다: https://trpc.io/?try=minimal-react#try-it-out

❓ 통합을 반드시 사용해야 하나요?

아니요! 통합은 완전히 선택 사항입니다. 바닐라 tRPC 클라이언트만 사용하여 @tanstack/react-query를 사용할 수 있지만, 이 경우 쿼리 키를 수동으로 관리해야 하며 통합 패키지를 사용할 때와 동일한 수준의 DX를 제공받지 못합니다.

utils/trpc.ts
ts
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '../server/router';
 
export const trpc = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: 'YOUR_API_URL' })],
});
utils/trpc.ts
ts
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '../server/router';
 
export const trpc = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: 'YOUR_API_URL' })],
});
components/PostList.tsx
tsx
import { useQuery } from '@tanstack/react-query';
import { trpc } from './utils/trpc';
 
function PostList() {
const { data } = useQuery({
queryKey: ['posts'] as const,
queryFn: () => trpc.post.list.query(),
});
data; // Post[]
 
// ...
}
components/PostList.tsx
tsx
import { useQuery } from '@tanstack/react-query';
import { trpc } from './utils/trpc';
 
function PostList() {
const { data } = useQuery({
queryKey: ['posts'] as const,
queryFn: () => trpc.post.list.query(),
});
data; // Post[]
 
// ...
}

설정

1. 의존성 설치

다음 의존성을 설치해야 합니다

npm install @trpc/server @trpc/client @trpc/tanstack-react-query @tanstack/react-query
AI 에이전트

AI 코딩 에이전트를 사용하는 경우, 더 나은 코드 생성을 위해 tRPC 스킬을 설치하세요:

bash
npx @tanstack/intent@latest install
bash
npx @tanstack/intent@latest install

2. AppRouter 가져오기

AppRouter 타입을 클라이언트 애플리케이션에 가져옵니다. 이 타입은 전체 API의 구조를 포함합니다.

utils/trpc.ts
ts
import type { AppRouter } from '../server/router';
utils/trpc.ts
ts
import type { AppRouter } from '../server/router';

import type를 사용하면 참조가 컴파일 타임에 제거되므로, 서버 측 코드가 클라이언트에 의도치 않게 가져와지는 것을 방지할 수 있습니다. 자세한 내용은 TypeScript 문서를 참조하세요.

3a. tRPC 컨텍스트 프로바이더 설정

Next.js와 같은 풀스택 프레임워크에서 서버 사이드 렌더링을 사용하는 경우와 같이 React 컨텍스트에 의존하는 경우, 사용자가 동일한 캐시를 공유하지 않도록 각 요청마다 새로운 QueryClient를 생성하는 것이 중요합니다. createTRPCContext를 사용하여 AppRouter 타입 시그니처에서 타입 안전한 컨텍스트 프로바이더와 컨슈머 세트를 생성할 수 있습니다.

utils/trpc.ts
tsx
import { createTRPCContext } from '@trpc/tanstack-react-query';
import type { AppRouter } from '../server/router';
 
export const { TRPCProvider, useTRPC, useTRPCClient } = createTRPCContext<AppRouter>();
utils/trpc.ts
tsx
import { createTRPCContext } from '@trpc/tanstack-react-query';
import type { AppRouter } from '../server/router';
 
export const { TRPCProvider, useTRPC, useTRPCClient } = createTRPCContext<AppRouter>();

그런 다음, 아래에 표시된 것처럼 tRPC 클라이언트를 생성하고 애플리케이션을 TRPCProvider로 감싸야 합니다. 또한 더 자세히 문서화된 React Query를 설정하고 연결해야 합니다.

애플리케이션에서 이미 React Query를 사용하는 경우, 이미 가지고 있는 QueryClientQueryClientProvider를 재사용하는 것이 좋습니다. QueryClient 초기화에 대한 자세한 내용은 React Query 문서에서 확인할 수 있습니다.

components/App.tsx
tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import { useState } from 'react';
import type { AppRouter } from '../server/router';
import { TRPCProvider } from '../utils/trpc';
 
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000,
},
},
});
}
 
let browserQueryClient: QueryClient | undefined = undefined;
 
function getQueryClient() {
if (typeof window === 'undefined') {
// Server: always make a new query client
return makeQueryClient();
} else {
// Browser: make a new query client if we don't already have one
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}
}
 
export function App() {
const queryClient = getQueryClient();
const [trpcClient] = useState(() =>
createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:2022',
}),
],
}),
);
 
return (
<QueryClientProvider client={queryClient}>
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
{null /* Your app here */}
</TRPCProvider>
</QueryClientProvider>
);
}
components/App.tsx
tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import { useState } from 'react';
import type { AppRouter } from '../server/router';
import { TRPCProvider } from '../utils/trpc';
 
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000,
},
},
});
}
 
let browserQueryClient: QueryClient | undefined = undefined;
 
function getQueryClient() {
if (typeof window === 'undefined') {
// Server: always make a new query client
return makeQueryClient();
} else {
// Browser: make a new query client if we don't already have one
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}
}
 
export function App() {
const queryClient = getQueryClient();
const [trpcClient] = useState(() =>
createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:2022',
}),
],
}),
);
 
return (
<QueryClientProvider client={queryClient}>
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
{null /* Your app here */}
</TRPCProvider>
</QueryClientProvider>
);
}

3b. 쿼리/뮤테이션 키 접두사 사용으로 설정

모든 쿼리와 뮤테이션에 특정 키를 접두사로 추가하려면 설정 및 사용 예시를 위해 쿼리 키 접두사를 참조하세요.

3c. React 컨텍스트 없이 설정

Vite와 같은 도구를 사용하여 클라이언트 사이드 렌더링만 사용하는 SPA를 구축할 때, QueryClient와 tRPC 클라이언트를 React 컨텍스트 외부에서 싱글턴으로 생성할 수 있습니다.

utils/trpc.ts
ts
import { QueryClient } from '@tanstack/react-query';
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import { createTRPCOptionsProxy } from '@trpc/tanstack-react-query';
import type { AppRouter } from '../server/router';
 
export const queryClient = new QueryClient();
 
const trpcClient = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: 'http://localhost:2022' })],
});
 
export const trpc = createTRPCOptionsProxy<AppRouter>({
client: trpcClient,
queryClient,
});
utils/trpc.ts
ts
import { QueryClient } from '@tanstack/react-query';
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import { createTRPCOptionsProxy } from '@trpc/tanstack-react-query';
import type { AppRouter } from '../server/router';
 
export const queryClient = new QueryClient();
 
const trpcClient = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: 'http://localhost:2022' })],
});
 
export const trpc = createTRPCOptionsProxy<AppRouter>({
client: trpcClient,
queryClient,
});
components/App.tsx
tsx
import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from '../utils/trpc';
 
export function App() {
return (
<QueryClientProvider client={queryClient}>
{/* Your app here */}
</QueryClientProvider>
);
}
components/App.tsx
tsx
import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from '../utils/trpc';
 
export function App() {
return (
<QueryClientProvider client={queryClient}>
{/* Your app here */}
</QueryClientProvider>
);
}

4. 데이터 가져오기

이제 tRPC React Query 통합을 사용하여 API에서 쿼리와 뮤테이션을 호출할 수 있습니다.

components/user-list.tsx
tsx
import { useMutation, useQuery } from '@tanstack/react-query';
import { useTRPC } from '../utils/trpc';
 
export default function UserList() {
const trpc = useTRPC(); // use `import { trpc } from './utils/trpc'` if you're using the singleton pattern
 
const userQuery = useQuery(trpc.getUser.queryOptions({ id: 'id_bilbo' }));
const userCreator = useMutation(trpc.createUser.mutationOptions());
 
return (
<div>
<p>{userQuery.data?.name}</p>
 
<button onClick={() => userCreator.mutate({ name: 'Frodo' })}>
Create Frodo
</button>
</div>
);
}
components/user-list.tsx
tsx
import { useMutation, useQuery } from '@tanstack/react-query';
import { useTRPC } from '../utils/trpc';
 
export default function UserList() {
const trpc = useTRPC(); // use `import { trpc } from './utils/trpc'` if you're using the singleton pattern
 
const userQuery = useQuery(trpc.getUser.queryOptions({ id: 'id_bilbo' }));
const userCreator = useMutation(trpc.createUser.mutationOptions());
 
return (
<div>
<p>{userQuery.data?.name}</p>
 
<button onClick={() => userCreator.mutate({ name: 'Frodo' })}>
Create Frodo
</button>
</div>
);
}