본문 바로가기
버전: 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