TRPC 적용기
6 minute read
2024-07-24
설치
npm i @tanstack/react-query @trpc/react-query @trpc/client @trpc/server추가로
npm i zod스케폴딩
src
├── app
│ ├── _trpc
│ └── api
│
├── provider
│ └── TrpcProvider.tsx
│
└── server
├── caller.ts
├── index.ts
└── trpc.tsserver
우선 가장 먼저 trpc.ts 를 만들어준다. 여기서 컨텍스트를 지니는 싱글톤 객체인 t 를 만들어준다.
import { initTRPC } from '@trpc/server';
/**
* Initialization of tRPC backend
* Should be done only once per backend!
*/
const t = initTRPC.create();
/**
* Export reusable router and procedure helpers
* that can be used throughout the router
*/
export const router = t.router;
export const publicProcedure = t.procedure;
export const createCallerFactory = t.createCallerFactory;싱글톤 객체인 t 는 항상 하나의 인스턴스로 관리되어야 한다. 때문에 생성한 컨텍스트를 기준으로 하는 router, procedure, callerFactory 를 여기서 export 해주고 다른 곳에서 사용하도록 해준다.
그런 다음 핵심이 되는 appRouter 를 만들어준다. 방금 생성한 export 한 router 를 이용한다.
import { publicProcedure, router } from './trpc';
export const appRouter = router({
// ...
})
export type AppRouter = typeof appRouter;router 인자로 handler 함수를 넣어줄 수 있다.
router({
getNumList: publicProcedure.query(async () => {
const nums = Array.from({ length: 10 }, (_, i) =>
i % 2 !== 0 ? i : i * 2
);
return nums;
}),
add: publicProcedure
.input(
z.object({
one: z.number(),
two: z.number(),
})
)
.query(async (opts) => {
const { input } = opts;
const { one, two } = input;
const result = await new Promise<number>((r) =>
setTimeout(() => r(one + two), 1200)
);
return result;
}),
})API 등록
이렇게 만들어 준 api 를 접근 가능하도록 등록해줘야 한다.
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server';
const getHandler = (req: Request) => {
// NOTE: return 으로 NextResponse 를 해야하는데, 그 역할을 `fetchRequestHandler` 가 해줌
return fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: () => ({}),
});
};
export { getHandler as GET };관련 공식문서
이렇게 세팅했다면, 브라우저로 직접 요청 가능해진다.

그렇다면 이제 컴포넌트 에서 요청가능하도록 해줘야 한다. 클라이언트 단에서 이용하기 위한 객체가 필요하다.
client
import { AppRouter } from '@/server';
import { createTRPCReact } from '@trpc/react-query';
export const trpc = createTRPCReact<AppRouter>({});이를 이용해 Trpc 용 Provider 를 따로 만들어준다.
'use client';
import { trpc } from '@/app/_trpc/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import React, { PropsWithChildren, useState } from 'react';
const TrpcProvider = ({ children }: PropsWithChildren) => {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
})
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
};
export default TrpcProvider;
link 에 관련된 건 공식문서를 확인해보면 좋다.
이렇게 만든 Provider 를 layout.ts 에 당연히 적용해줘야 한다.
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<TrpcProvider>
<body className={inter.className}>{children}</body>
</TrpcProvider>
</html>
);
}이렇게 클라이언트는 아래와 같이 사용 가능해진다.
'use client';
const Acomp = () => {
const { data } = trpc.getNumList.useQuery();
}이렇게 클라이언트에서 타입스크립트 기반의 간편한 rpc 가 이용가능해졌다. 그렇다면 server component 에선 어떻게 해야할까?
Server
아까 initTRPC 로 생성된 싱글톤 객체을 이용해 export 한 createCallerFactory 가 필요하다.
import { appRouter } from '.';
import { createCallerFactory } from './trpc';
const createCaller = createCallerFactory(appRouter);
export const caller = createCaller({});여기서 생성한 caller 를 가지고 서버 컴포넌트에서 아래와 같이 사용 가능해진다.
import { caller } from '@/server/caller';
export default async function Home() {
const data = await caller.getNumList();
// ...
}참고한 영상