빠른 시작
설치
tRPC는 여러 패키지로 나뉘어 있으므로 필요한 것만 설치할 수 있습니다. 코드베이스의 적절한 섹션에 원하는 패키지를 설치했는지 확인하세요. 이 빠른 시작 가이드에서는 간단하게 유지하기 위해 기본 클라이언트만 사용합니다. 프레임워크 가이드는 React 사용법 및 Next.js 사용법을 확인하세요.
- tRPC에는 TypeScript >=5.7.2가 필요합니다.
- 비엄격 모드는 공식적으로 지원하지 않으므로
tsconfig.json에서"strict": true를 사용하는 것을 강력히 권장합니다.
@trpc/server 및 @trpc/client 패키지를 설치하여 시작합니다:
- npm
- yarn
- pnpm
- bun
- deno
npm install @trpc/server @trpc/client
yarn add @trpc/server @trpc/client
pnpm add @trpc/server @trpc/client
bun add @trpc/server @trpc/client
deno add npm:@trpc/server npm:@trpc/client
AI 코딩 에이전트를 사용하는 경우, 더 나은 코드 생성을 위해 tRPC 스킬을 설치하세요:
bashnpx @tanstack/intent@latest install
bashnpx @tanstack/intent@latest install
첫 번째 tRPC API
tRPC로 타입 안전한 API를 구축하는 단계를 살펴보겠습니다. 이 가이드에서는 다음 TypeScript 시그니처를 갖는 세 가지 엔드포인트를 만듭니다:
tstype User = { id: string; name: string; };userList: () => User[];userById: (id: string) => User;userCreate: (data: { name: string }) => User;
tstype User = { id: string; name: string; };userList: () => User[];userById: (id: string) => User;userCreate: (data: { name: string }) => User;
다음은 구축할 파일 구조입니다. 순환 의존성을 방지하기 위해 tRPC 초기화, 라우터 정의, 서버 설정을 별도의 파일로 분리하는 것을 권장합니다:
.├── server/│ ├── trpc.ts # tRPC instantiation & setup│ ├── appRouter.ts # Your API logic and type export│ └── index.ts # HTTP server└── client/└── index.ts # tRPC client
.├── server/│ ├── trpc.ts # tRPC instantiation & setup│ ├── appRouter.ts # Your API logic and type export│ └── index.ts # HTTP server└── client/└── index.ts # tRPC client
1. 라우터 인스턴스 생성
먼저, tRPC 백엔드를 초기화합니다. 이는 별도의 파일에서 수행하고, 전체 tRPC 객체 대신 재사용 가능한 헬퍼 함수를 내보내는 것이 좋은 관행입니다.
server/trpc.tstsimport {initTRPC } from '@trpc/server';/*** Initialization of tRPC backend* Should be done only once per backend!*/constt =initTRPC .create ();/*** Export reusable router and procedure helpers* that can be used throughout the router*/export constrouter =t .router ;export constpublicProcedure =t .procedure ;
server/trpc.tstsimport {initTRPC } from '@trpc/server';/*** Initialization of tRPC backend* Should be done only once per backend!*/constt =initTRPC .create ();/*** Export reusable router and procedure helpers* that can be used throughout the router*/export constrouter =t .router ;export constpublicProcedure =t .procedure ;
다음으로, 나중에 프로시저를 추가할 주 라우터 인스턴스인 appRouter를 초기화합니다. 마지막으로, 나중에 클라이언트 측에서 사용할 라우터의 타입을 내보내야 합니다.
server/appRouter.tstsimport {router } from './trpc';export constappRouter =router ({// ...});export typeAppRouter = typeofappRouter ;
server/appRouter.tstsimport {router } from './trpc';export constappRouter =router ({// ...});export typeAppRouter = typeofappRouter ;
2. 쿼리 프로시저 추가
publicProcedure.query()를 사용하여 라우터에 쿼리 프로시저를 추가합니다.
다음 코드는 사용자 목록을 반환하는 userList 쿼리 프로시저를 생성합니다:
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';export constappRouter =router ({userList :publicProcedure .query (async () => {constusers :User [] = [{id : '1',name : 'Katt' }];returnusers ;}),});export typeAppRouter = typeofappRouter ;
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';export constappRouter =router ({userList :publicProcedure .query (async () => {constusers :User [] = [{id : '1',name : 'Katt' }];returnusers ;}),});export typeAppRouter = typeofappRouter ;
3. 입력 파서를 사용하여 프로시저 입력 검증
userById 프로시저를 구현하려면 클라이언트에서 입력을 받아야 합니다. tRPC는 입력을 검증하고 파싱하기 위해 입력 파서를 정의할 수 있습니다. zod, yup, 또는 superstruct와 같은 원하는 검증 라이브러리를 사용하거나 자체 입력 파서를 정의할 수 있습니다.
publicProcedure.input()에 입력 파서를 정의하면, 아래에 표시된 것처럼 리졸버 함수에서 접근할 수 있습니다:
- Vanilla
- Zod
- Yup
- Valibot
입력 파서는 이 프로시저의 입력을 검증하고 변환하는 함수여야 합니다. 입력이 유효하면 타입이 정확히 지정된 값을 반환하고, 유효하지 않으면 오류를 발생시켜야 합니다.
이 문서의 나머지 부분에서는 zod를 검증 라이브러리로 사용할 것입니다.
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';export constappRouter =router ({// ...userById :publicProcedure // The input is unknown at this time. A client could have sent// us anything so we won't assume a certain data type..input ((val : unknown) => {// If the value is of type string, return it.// It will now be inferred as a string.if (typeofval === 'string') returnval ;// Uh oh, looks like that input wasn't a string.// We will throw an error instead of running the procedure.throw newError (`Invalid input: ${typeofval }`);}).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),});export typeAppRouter = typeofappRouter ;
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';export constappRouter =router ({// ...userById :publicProcedure // The input is unknown at this time. A client could have sent// us anything so we won't assume a certain data type..input ((val : unknown) => {// If the value is of type string, return it.// It will now be inferred as a string.if (typeofval === 'string') returnval ;// Uh oh, looks like that input wasn't a string.// We will throw an error instead of running the procedure.throw newError (`Invalid input: ${typeofval }`);}).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),});export typeAppRouter = typeofappRouter ;
입력 파서는 어떤 ZodType이든 될 수 있으며, 예를 들어 z.string() 또는 z.object()입니다.
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';import {z } from 'zod';export constappRouter =router ({// ...userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),});export typeAppRouter = typeofappRouter ;
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';import {z } from 'zod';export constappRouter =router ({// ...userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),});export typeAppRouter = typeofappRouter ;
입력 파서는 어떤 YupSchema이든 될 수 있으며, 예를 들어 yup.string() 또는 yup.object()입니다.
이 문서의 나머지 부분에서는 zod를 검증 라이브러리로 사용할 것입니다.
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';import * asyup from 'yup';export constappRouter =router ({// ...userById :publicProcedure .input (yup .string ().required ()).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),});export typeAppRouter = typeofappRouter ;
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';import * asyup from 'yup';export constappRouter =router ({// ...userById :publicProcedure .input (yup .string ().required ()).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),});export typeAppRouter = typeofappRouter ;
입력 파서는 어떤 Valibot 스키마든 될 수 있으며, 예를 들어 v.string() 또는 v.object()입니다.
이 문서의 나머지 부분에서는 zod를 검증 라이브러리로 사용할 것입니다.
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';import * asv from 'valibot';export constappRouter =router ({// ...userById :publicProcedure .input (v .string ()).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),});export typeAppRouter = typeofappRouter ;
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';import * asv from 'valibot';export constappRouter =router ({// ...userById :publicProcedure .input (v .string ()).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),});export typeAppRouter = typeofappRouter ;
4. 뮤테이션 프로시저 추가
GraphQL과 유사하게, tRPC는 쿼리와 뮤테이션 프로시저를 구분합니다.
쿼리와 뮤테이션은 주로 의미에 따라 구분합니다. 쿼리는 HTTP GET을 사용하는 읽기 작업용이고, 뮤테이션은 HTTP POST를 사용하며 부수 효과를 일으키는 작업용입니다.
라우터 객체에 새로운 속성으로 추가하여 userCreate 뮤테이션을 추가해 보겠습니다:
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';export constappRouter =router ({// ...userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;// Create the user in your DBconstuser :User = {id : '1', ...input };returnuser ;}),});export typeAppRouter = typeofappRouter ;
server/appRouter.tstsimport {publicProcedure ,router } from './trpc';export constappRouter =router ({// ...userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;// Create the user in your DBconstuser :User = {id : '1', ...input };returnuser ;}),});export typeAppRouter = typeofappRouter ;
API 제공
라우터를 정의했으므로 이제 API로 제공할 차례입니다. tRPC는 널리 쓰이는 여러 웹 서버를 위한 일급 어댑터를 제공합니다. 여기서는 간단한 standalone Node.js 어댑터를 사용합니다.
server/index.tstsimport {createHTTPServer } from '@trpc/server/adapters/standalone';import {appRouter } from './appRouter';constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
server/index.tstsimport {createHTTPServer } from '@trpc/server/adapters/standalone';import {appRouter } from './appRouter';constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
전체 백엔드 코드 보기
server/trpc.tstsimport {initTRPC } from '@trpc/server';constt =initTRPC .create ();export constrouter =t .router ;export constpublicProcedure =t .procedure ;
server/trpc.tstsimport {initTRPC } from '@trpc/server';constt =initTRPC .create ();export constrouter =t .router ;export constpublicProcedure =t .procedure ;
server/appRouter.tstsimport {z } from "zod";import {publicProcedure ,router } from "./trpc";typeUser = {id : string;name : string };export constappRouter =router ({userList :publicProcedure .query (async () => {constusers :User [] = [{id : '1',name : 'Katt' }];returnusers ;}),userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;constuser :User = {id : '1', ...input };returnuser ;}),});export typeAppRouter = typeofappRouter ;
server/appRouter.tstsimport {z } from "zod";import {publicProcedure ,router } from "./trpc";typeUser = {id : string;name : string };export constappRouter =router ({userList :publicProcedure .query (async () => {constusers :User [] = [{id : '1',name : 'Katt' }];returnusers ;}),userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;constuser :User = {id :input ,name : 'Katt' };returnuser ;}),userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;constuser :User = {id : '1', ...input };returnuser ;}),});export typeAppRouter = typeofappRouter ;
server/index.tstsimport {createHTTPServer } from "@trpc/server/adapters/standalone";import {appRouter } from "./appRouter";constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
server/index.tstsimport {createHTTPServer } from "@trpc/server/adapters/standalone";import {appRouter } from "./appRouter";constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
클라이언트에서 새 백엔드 사용
이제 클라이언트 코드에서 엔드투엔드 타입 안전성을 활용해 보겠습니다. 클라이언트에서 AppRouter 타입을 가져오면 구현 세부 사항을 노출하지 않고도 시스템 전체에 완전한 타입 안전성을 적용할 수 있습니다.
1. tRPC 클라이언트 설정
client/index.tstsimport {createTRPCClient ,httpBatchLink } from '@trpc/client';import type {AppRouter } from './appRouter';// 👆 **type-only** imports are stripped at build time// Pass AppRouter as a type parameter. 👇 This lets `trpc` know// what procedures are available on the server and their input/output types.consttrpc =createTRPCClient <AppRouter >({links : [httpBatchLink ({url : 'http://localhost:3000',}),],});
client/index.tstsimport {createTRPCClient ,httpBatchLink } from '@trpc/client';import type {AppRouter } from './appRouter';// 👆 **type-only** imports are stripped at build time// Pass AppRouter as a type parameter. 👇 This lets `trpc` know// what procedures are available on the server and their input/output types.consttrpc =createTRPCClient <AppRouter >({links : [httpBatchLink ({url : 'http://localhost:3000',}),],});
tRPC의 링크는 GraphQL의 링크와 유사하며, 서버로의 데이터 흐름을 제어할 수 있게 해줍니다. 위의 예제에서는 httpBatchLink를 사용하여 여러 호출을 자동으로 하나의 HTTP 요청으로 배치 처리합니다. 링크의 심층적인 사용법에 대해서는 링크 문서를 참조하세요.
2. 타입 추론 및 자동완성
이제 trpc 객체를 통해 API 프로시저에 접근할 수 있습니다. 직접 시도해 보세요!
client/index.tsts// Inferred typesconstuser = awaittrpc .userById .query ('1');constcreatedUser = awaittrpc .userCreate .mutate ({name : 'Katt' });
client/index.tsts// Inferred typesconstuser = awaittrpc .userById .query ('1');constcreatedUser = awaittrpc .userCreate .mutate ({name : 'Katt' });
자동완성을 사용하여 클라이언트에서 API를 탐색할 수도 있습니다.
client/index.tststrpc .u ;
client/index.tststrpc .u ;
다음 단계
| 다음으로 무엇을 할까요? | 설명 |
|---|---|
| 예제 앱 | 선택한 프레임워크에서 tRPC 탐색 |
| TanStack React Query | @trpc/tanstack-react-query를 통한 권장 React 통합 |
| Next.js | Next.js 사용법 |
| 서버 어댑터 | Express, Fastify 등 |
| 트랜스포머 | superjson을 사용하여 Date와 같은 복잡한 타입 유지 |