[Typescript] tRPC: ์๊ฐ
tRPC๋ typescript์ ๊ธฐ๋ฐํ RPC ํ๋กํ ์ฝ์ด๋ค.
gRPC์ ํ์
์คํฌ๋ฆฝํธ ๋ฒ์ ์ด๋ผ ์๊ฐํ๋ฉด ๋๋ค.
MSA์๋ ์ฌ์ฉํ ์ ์๊ณ , ๋จ์ํ ์น์๋ฒ<->์น ํด๋ผ์ด์ธํธ์ ๊ตฌ์กฐ๋ก๋ ์ฌ์ฉํ ์ ์๋ค.
๋จ์ํ๊ณ ํธ๋ฆฌํ ํํ์, Typescript ํ๊ฒฝ์ ์ต์ ํ๋์ด์๋ค๋๊ฒ ์ฅ์ ์ด์ง๋ง, ๊ทธ๊ฒ ๊ณง ๋จ์ ์ด๊ธฐ๋ ํ๋ค.
gRPC๋ง์ ๋ ์ธ์ด๋ง๋ค ๊ตฌํ์ฒด์ ๋ฏธํกํจ์ด ๊ฝค๋ ์กด์ฌํ๋ ๋ง๋น์ธ๋ฐ, Typescript์ ์ข
์๋ tRPC๋ ์ํ๊ณ๊ฐ ์ผ๋ง๋ ์ข๊ฒ ๋๊ฐ? ์ฌ์ค์ Node.js ํ๊ฒฝ์์๋ง ์ด์์ ์ธ ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
๊ทธ๋์ MSA์ ์ฅ์ ์ ์ด๋์ด๋ด๊ธฐ์๋ ์ ํ์ด ์ข ์๋ค.
์๋ฒ ์ธก ๊ตฌํ
์๋ฒ๋ถํฐ ํด๋ณด๊ฒ ๋ค.
๋จผ์ ์๋ฒ์ฉ ๋ชจ๋์ ์ค์นํ๋ค.
npm i @trpc/server

์ด๋ฒ ์์ ์์๋ "์ ์ ๋ชฉ๋ก"์ด ์๊ณ , ์ ์ ๋ฅผ ์กฐํํ ์ ์๋ ๊ธฐ๋ฅ์ ๋ง๋ค์ด๋ณด๋๋ก ํ๊ฒ ๋ค.
๋จผ์ ๊ธฐ๋ณธ tRPC ๊ฐ์ฒด์ ๋ผ์ฐํฐ๋ฅผ ์ ์ํด์ผ ํ๋ค.
import { initTRPC } from '@trpc/server';
const trpc = initTRPC.create();
const router = trpc.router;
const userList: User[] = [
{
id: '1',
name: 'John Doe',
},
{
id: '2',
name: 'Sam Smith',
},
];
interface User {
id: string;
name: string;
}
const appRouter = router({});
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;
๊ทธ๋ฆฌ ์ด๋ ค์ธ ๊ฒ์ ์๋ค.
์ด์ ์ฌ๊ธฐ๋ค๊ฐ ์ ์ ๋ฅผ ์กฐํํ ์ ์๋ userById ๋ฉ์๋๋ฅผ ์ ์ํด๋ณด๊ฒ ๋ค.
import { initTRPC } from '@trpc/server';
const trpc = initTRPC.create();
const router = trpc.router;
const userList: User[] = [
{
id: '1',
name: 'John Doe',
},
{
id: '2',
name: 'Sam Smith',
},
];
interface User {
id: string;
name: string;
}
interface UserByIdInput {
id: string;
}
const appRouter = router({
userById: trpc.procedure
.input((value: any)=>{
if(typeof value?.id !== 'string'){
throw new Error('input.id must be an string');
}
return value as UserByIdInput;
})
.query((request)=>{
const input = request.input;
return userList.find((user)=>user.id === input.id);
})
});
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;
๊ทธ๋ฅ ๋ผ์ฐํฐ์ key-value ํํ๋ก ๊ฐ์ ์ง์ด๋ฃ์ผ๋ฉด ๊ฐ๊ฐ์ route๊ฐ ๋๋ค.
input ํจ์๋ ์
๋ ฅ ๊ฐ์ ์ด๋ป๊ฒ ๊ฒ์ฆํ๊ณ ๊ฐ๊ณตํ ์ง๋ฅผ ์ฒ๋ฆฌํ๊ณ ,
query๋ input์์ ๊ฐ๊ณตํ ๊ฐ์ ๋ฐ์์ ์ต์ข
์ ์ผ๋ก ํด๋ผ์ด์ธํธ์๊ฒ ๋๋ ค์ค ๊ฐ์ ๋ฆฌํดํ๋ค.
๊ธฐ๋ณธ์ ์ธ ์๋ฆฌ๋ ์ด๋ ค์ธ ๊ฒ์ด ์๋ค.
์ด "๋ผ์ฐํฐ" ๋จ์๋ ์๋ฒ์์๋ง ์ฌ์ฉ๋๋๊ฒ ์๋๋ผ, ์ด ์์ฒด๋ก ํ๋์ ๊ท๊ฒฉ์ด ๋๋ค. ํด๋ผ์ด์ธํธ์์ ์ฐ๋ํ ๊ฒฝ์ฐ์๋ ์ ๋ผ์ฐํฐ ์ฝ๋๋ฅผ ๊ณต์ ํ๋ฉด ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ ๊ฒ๋ง์ผ๋ก๋ ์๋ฌด๊ฒ๋ ํ์ง ๋ชปํ๋ค. ๊ทธ๋ฅ tRPC๋ฅผ ์ ์ํ๋ ๊ฒ์ผ ๋ฟ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
์ค์ ๋ก ์ ๊ฒ ์๋ฒ๋ก์ ๋์ํ๊ฒ ํ๋ ค๋ฉด, express ๊ฐ์ ๊ธฐ์กด ์๋ฒ ํ๊ฒฝ์ ๋ถ์ฌ์ ์จ์ผ ํ๋ค.
import express from 'express'
import * as trpcExpress from '@trpc/server/adapters/express';
const server = express();
server.get('/', (req, res)=>{
res.json('OK');
})
server.get('/', (req, res)=>{
res.json('OK');
})
// created for each request
const createTRPCContext = ({req, res}: trpcExpress.CreateExpressContextOptions) => ({});
server.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext: createTRPCContext,
}),
);
server.listen(12345, ()=>{console.log('Server is Running...')});

ํด๋ผ์ด์ธํธ ์ธก ๊ตฌํ
์ด๋ฒ์๋ ํด๋ผ์ด์ธํธ์์ ์ ๊ฑธ ํธ์ถํด๋ณด์.
ํด๋ผ์ด์ธํธ์ฉ ๋ชจ๋์ ๋จผ์ ์ค์นํ๋ค.
npm i @trpc/client

๊ทธ๋ฆฌ๊ณ ์ด๋ฐ์์ผ๋ก client ๊ฐ์ฒด๋ฅผ ์์ฑ ํ, ๋ฉ์๋๋ฅผ ํธ์ถํด์ ์ฌ์ฉํ๋ฉด ๋๋ค.
// @filename: client.ts
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';
// Notice the <AppRouter> generic here.
const trpcClient = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:12345/trpc',
}),
],
});
async function main() {
const user = await trpcClient.userById.query({id: '1'});
console.log(user);
}
main();
url์๋ ์๋ฒ๊ฐ ๋ ์๋ URL ๊ฒฝ๋ก๋ฅผ ์ ์ด์ฃผ๋ฉด ๋๊ณ

API๋ฅผ ์ฌ์ฉํ ๋๋ ์๋ฒ์์ ์ ์ํ๋๊ฑธ ๊ทธ๋๋ก ๊บผ๋ด์ธ ์ ์๋ค.

์ด๋ ๊ฒ.
๊ธฐ๋ณธ์ ์ธ ๊ตฌ์กฐ๋ ์ด๋ ๊ณ , react ๋ฑ์ ํ๋ก ํธ์๋ ํ๋ ์์ํฌ๋ค์์๋ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์๋ค.
์ฐธ์กฐ
tRPC | tRPC