본문 바로가기
버전: 11.x

빠른 시작

비공식 베타 번역

이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →

설치​

tRPC는 여러 패키지로 구성되어 있어 필요한 부분만 설치할 수 있습니다. 각 패키지를 코드베이스의 적절한 영역에 설치해야 합니다. 이 빠른 시작 가이드에서는 간단하게 바닐라 클라이언트만 사용할 것입니다. 프레임워크 가이드는 React와 함께 사용하기와 Next.js와 함께 사용하기를 참조하세요.

요구 사항
  • tRPC는 TypeScript >=5.7.2 버전이 필요합니다
  • 공식적으로 non-strict 모드를 지원하지 않으므로 tsconfig.json에서 "strict": true를 사용할 것을 강력히 권장합니다.

@trpc/server와 @trpc/client 패키지를 설치하는 것으로 시작합니다:

npm install @trpc/server @trpc/client
AI 에이전트

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

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

첫 번째 tRPC API​

tRPC로 타입 안전한 API를 구축하는 단계를 차근차근 살펴보겠습니다. 시작으로 이 API에는 다음 TypeScript 시그니처를 가진 세 개의 엔드포인트가 포함될 것입니다:

ts
type User = { id: string; name: string; };
userList: () => User[];
userById: (id: string) => User;
userCreate: (data: { name: string }) => User;
ts
type 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.ts
ts
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;
server/trpc.ts
ts
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;

다음으로 주 라우터 인스턴스(일반적으로 appRouter라고 함)를 초기화하고, 여기에 나중에 프로시저를 추가할 것입니다. 마지막으로 클라이언트 측에서 사용할 라우터 타입을 내보내야 합니다.

server/appRouter.ts
ts
import { router } from './trpc';
 
export const appRouter = router({
// ...
});
 
export type AppRouter = typeof appRouter;
server/appRouter.ts
ts
import { router } from './trpc';
 
export const appRouter = router({
// ...
});
 
export type AppRouter = typeof appRouter;

2. 쿼리 절차 추가하기​

라우터에 쿼리 절차를 추가하려면 publicProcedure.query()를 사용하세요.

다음은 사용자 목록을 반환하는 userList라는 쿼리 프로시저를 생성하는 예시입니다:

server/appRouter.ts
ts
import { publicProcedure, router } from './trpc';
 
export const appRouter = router({
userList: publicProcedure
.query(async () => {
const users: User[] = [{ id: '1', name: 'Katt' }];
 
return users;
}),
});
 
export type AppRouter = typeof appRouter;
server/appRouter.ts
ts
import { publicProcedure, router } from './trpc';
 
export const appRouter = router({
userList: publicProcedure
.query(async () => {
const users: User[] = [{ id: '1', name: 'Katt' }];
 
return users;
}),
});
 
export type AppRouter = typeof appRouter;

3. 입력 파서를 사용하여 절차 입력 검증하기​

userById 절차를 구현하려면 클라이언트의 입력을 받아야 합니다. tRPC는 입력을 검증하고 파싱하기 위한 입력 파서를 정의할 수 있게 합니다. 사용자 정의 입력 파서를 만들거나 zod, yup, superstruct 같은 검증 라이브러리를 선택할 수 있습니다.

입력 파서는 publicProcedure.input()에 정의하며, 아래와 같이 리졸버 함수에서 접근할 수 있습니다:

The input parser can be any ZodType, e.g. z.string() or z.object().


server/appRouter.ts
ts
import { publicProcedure, router } from './trpc';
import { z } from 'zod';
 
export const appRouter = router({
// ...
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const input: string
const user: User = { id: input, name: 'Katt' };
 
return user;
}),
});
 
export type AppRouter = typeof appRouter;
server/appRouter.ts
ts
import { publicProcedure, router } from './trpc';
import { z } from 'zod';
 
export const appRouter = router({
// ...
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const input: string
const user: User = { id: input, name: 'Katt' };
 
return user;
}),
});
 
export type AppRouter = typeof appRouter;

4. 변형(mutation) 절차 추가하기​

GraphQL과 유사하게 tRPC는 쿼리(Query)와 변형(Mutation) 프로시저를 구분합니다.

쿼리와 변형의 차이는 주로 의미론적입니다. 쿼리는 읽기 작업을 위한 HTTP GET을 사용하며, 변형은 부수 효과를 발생시키는 작업을 위한 HTTP POST를 사용합니다.

라우터 객체에 새 속성으로 userCreate 변형을 추가해 보겠습니다:

server/appRouter.ts
ts
import { publicProcedure, router } from './trpc';
 
export const appRouter = router({
// ...
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const input: { name: string; }
// Create the user in your DB
const user: User = { id: '1', ...input };
 
return user;
}),
});
 
export type AppRouter = typeof appRouter;
server/appRouter.ts
ts
import { publicProcedure, router } from './trpc';
 
export const appRouter = router({
// ...
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const input: { name: string; }
// Create the user in your DB
const user: User = { id: '1', ...input };
 
return user;
}),
});
 
export type AppRouter = typeof appRouter;

API 서빙하기​

이제 라우터를 정의했으므로 서빙할 수 있습니다. tRPC는 많은 인기 있는 웹 서버를 위한 일급 어댑터를 제공합니다. 간단하게 하기 위해 여기서는 Node.js용 standalone 어댑터를 사용하겠습니다.

server/index.ts
ts
import { createHTTPServer } from '@trpc/server/adapters/standalone';
import { appRouter } from './appRouter';
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
server/index.ts
ts
import { createHTTPServer } from '@trpc/server/adapters/standalone';
import { appRouter } from './appRouter';
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
See the full backend code
server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
export const router = t.router;
export const publicProcedure = t.procedure;
server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
export const router = t.router;
export const publicProcedure = t.procedure;

server/appRouter.ts
ts
import { z } from "zod";
import { publicProcedure, router } from "./trpc";
 
type User = { id: string; name: string };
 
export const appRouter = router({
userList: publicProcedure
.query(async () => {
const users: User[] = [{ id: '1', name: 'Katt' }];
return users;
}),
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const user: User = { id: input, name: 'Katt' };
return user;
}),
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const user: User = { id: '1', ...input };
return user;
}),
});
 
export type AppRouter = typeof appRouter;
server/appRouter.ts
ts
import { z } from "zod";
import { publicProcedure, router } from "./trpc";
 
type User = { id: string; name: string };
 
export const appRouter = router({
userList: publicProcedure
.query(async () => {
const users: User[] = [{ id: '1', name: 'Katt' }];
return users;
}),
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const user: User = { id: input, name: 'Katt' };
return user;
}),
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const user: User = { id: '1', ...input };
return user;
}),
});
 
export type AppRouter = typeof appRouter;

server/index.ts
ts
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { appRouter } from "./appRouter";
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
server/index.ts
ts
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { appRouter } from "./appRouter";
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);

클라이언트에서 새 백엔드 사용하기​

이제 클라이언트 측 코드로 이동하여 엔드투엔드 타입 안전성의 힘을 경험해 보겠습니다. 클라이언트에서 사용할 AppRouter 타입을 가져오면 구현 세부사항을 클라이언트에 노출시키지 않으면서도 시스템 전체에 완벽한 타입 안전성을 확보할 수 있습니다.

1. tRPC 클라이언트 설정​

client/index.ts
ts
import { 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.
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000',
}),
],
});
client/index.ts
ts
import { 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.
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000',
}),
],
});

tRPC의 링크는 GraphQL의 링크와 유사하며 서버로의 데이터 흐름을 제어할 수 있게 합니다. 위 예시에서는 여러 호출을 자동으로 단일 HTTP 요청으로 묶어주는 httpBatchLink를 사용합니다. 링크에 대한 심층적인 사용법은 링크 문서를 참조하세요.

2. 타입 추론 및 자동 완성​

이제 trpc 객체를 통해 API 프로시저에 접근할 수 있습니다. 직접 사용해 보세요!

client/index.ts
ts
// Inferred types
const user = await trpc.userById.query('1');
const user: User
 
const createdUser = await trpc.userCreate.mutate({ name: 'Katt' });
const createdUser: User
client/index.ts
ts
// Inferred types
const user = await trpc.userById.query('1');
const user: User
 
const createdUser = await trpc.userCreate.mutate({ name: 'Katt' });
const createdUser: User

클라이언트 측에서 API를 탐색할 때 자동 완성 기능을 활용할 수도 있습니다

client/index.ts
ts
trpc.u;
      
 
 
client/index.ts
ts
trpc.u;
      
 
 

다음 단계​

What's next?Description
Example AppsExplore tRPC in your chosen framework
TanStack React QueryRecommended React integration via @trpc/tanstack-react-query
Next.jsUsage with Next.js
Server AdaptersExpress, Fastify, and more
TransformersUse superjson to retain complex types like Date