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

빠른 시작

비공식 베타 번역

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

tRPC는 RESTGraphQL의 개념을 결합합니다. 둘 중 하나에 익숙하지 않다면 핵심 개념을 살펴보세요.

설치

tRPC는 여러 패키지로 분리되어 있어 필요한 부분만 설치할 수 있습니다. 원하는 패키지를 코드베이스의 적절한 위치에 설치해야 합니다. 이 빠른 시작 가이드에서는 간단하게 기본 클라이언트만 사용하겠습니다. 프레임워크별 가이드는 React에서 사용하기Next.js에서 사용하기를 참조하세요.

요구 사항
  • tRPC는 TypeScript >= 4.7.0 버전이 필요합니다
  • 공식적으로 비엄격 모드를 지원하지 않으므로 tsconfig.json에서 "strict": true 설정을 강력히 권장합니다

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

sh
npm install @trpc/server @trpc/client
sh
npm install @trpc/server @trpc/client

백엔드 라우터 정의하기

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;

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/index.ts
ts
import { router } from './trpc';
 
const appRouter = router({
// ...
});
 
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;
server/index.ts
ts
import { router } from './trpc';
 
const appRouter = router({
// ...
});
 
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;

2. 쿼리 절차 추가하기

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

다음은 데이터베이스에서 사용자 목록을 반환하는 userList 쿼리 절차를 생성하는 예시입니다:

server/index.ts
ts
import { db } from './db';
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
userList: publicProcedure.query(async () => {
// Retrieve users from a datasource, this is an imaginary database
const users = await db.user.findMany();
const users: User[]
return users;
}),
});
server/index.ts
ts
import { db } from './db';
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
userList: publicProcedure.query(async () => {
// Retrieve users from a datasource, this is an imaginary database
const users = await db.user.findMany();
const users: User[]
return users;
}),
});

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

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

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

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



server.ts
ts
import { z } from 'zod';
import { db } from './db';
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
// ...
userById: publicProcedure.input(z.string()).query(async (opts) => {
const { input } = opts;
const input: string
// Retrieve the user with the given ID
const user = await db.user.findById(input);
const user: User | undefined
return user;
}),
});
server.ts
ts
import { z } from 'zod';
import { db } from './db';
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
// ...
userById: publicProcedure.input(z.string()).query(async (opts) => {
const { input } = opts;
const input: string
// Retrieve the user with the given ID
const user = await db.user.findById(input);
const user: User | undefined
return user;
}),
});
정보

본 문서의 남은 부분에서는 검증 라이브러리로 zod를 사용할 것입니다

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

GraphQL과 유사하게 tRPC는 쿼리와 변형 절차를 구분합니다.

서버 측 절차의 작동 방식은 쿼리와 변형 간에 크게 다르지 않습니다. 메서드 이름이 다르고 클라이언트가 이 절차를 사용하는 방식이 변경되지만 그 외에는 모두 동일합니다!

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

server.ts
ts
const appRouter = router({
// ...
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const input: { name: string; }
// Create a new user in the database
const user = await db.user.create(input);
const user: { name: string; id: string; }
return user;
}),
});
server.ts
ts
const appRouter = router({
// ...
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const input: { name: string; }
// Create a new user in the database
const user = await db.user.create(input);
const user: { name: string; id: string; }
return user;
}),
});

API 서빙하기

이제 라우터를 정의했으므로 서빙할 수 있습니다. tRPC에는 다양한 어댑터가 있어 원하는 백엔드 프레임워크를 사용할 수 있습니다. 간단하게 하기 위해 standalone 어댑터를 사용하겠습니다.

server/index.ts
ts
import { createHTTPServer } from '@trpc/server/adapters/standalone';
import { router } from './trpc';
 
const appRouter = router({
// ...
});
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
server/index.ts
ts
import { createHTTPServer } from '@trpc/server/adapters/standalone';
import { router } from './trpc';
 
const appRouter = router({
// ...
});
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
See the full backend code
server/db.ts
ts
type User = { id: string; name: string };
 
// Imaginary database
const users: User[] = [];
export const db = {
user: {
findMany: async () => users,
findById: async (id: string) => users.find((user) => user.id === id),
create: async (data: { name: string }) => {
const user = { id: String(users.length + 1), ...data };
users.push(user);
return user;
},
},
};
server/db.ts
ts
type User = { id: string; name: string };
 
// Imaginary database
const users: User[] = [];
export const db = {
user: {
findMany: async () => users,
findById: async (id: string) => users.find((user) => user.id === id),
create: async (data: { name: string }) => {
const user = { id: String(users.length + 1), ...data };
users.push(user);
return user;
},
},
};

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/index.ts
ts
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { z } from "zod";
import { db } from "./db";
import { publicProcedure, router } from "./trpc";
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
const users = await db.user.findMany();
return users;
}),
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const user = await db.user.findById(input);
return user;
}),
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const user = await db.user.create(input);
return user;
}),
});
 
export type AppRouter = typeof appRouter;
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
server/index.ts
ts
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { z } from "zod";
import { db } from "./db";
import { publicProcedure, router } from "./trpc";
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
const users = await db.user.findMany();
return users;
}),
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const user = await db.user.findById(input);
return user;
}),
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const user = await db.user.create(input);
return user;
}),
});
 
export type AppRouter = typeof appRouter;
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);

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

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

1. tRPC 클라이언트 설정

client/index.ts
ts
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';
 
// 👆 **type-only** import
 
// Pass AppRouter as generic here. 👇 This lets the `trpc` object know
// what procedures are available on the server and their input/output types.
const trpc = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000',
}),
],
});
client/index.ts
ts
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';
 
// 👆 **type-only** import
 
// Pass AppRouter as generic here. 👇 This lets the `trpc` object know
// what procedures are available on the server and their input/output types.
const trpc = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000',
}),
],
});

tRPC의 링크(Link)는 GraphQL의 링크와 유사하게 데이터가 서버로 전송되기 전에 데이터 흐름을 제어할 수 있게 해줍니다. 위 예제에서는 httpBatchLink를 사용하는데, 이 링크는 여러 호출을 자동으로 단일 HTTP 요청으로 묶어줍니다. 링크에 대한 더 깊은 활용법은 링크 문서를 참조하세요.

2. 쿼리 및 뮤테이션 실행

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

client/index.ts
ts
// Inferred types
const user = await trpc.userById.query('1');
const user: { name: string; id: string; } | undefined
 
const createdUser = await trpc.userCreate.mutate({ name: 'sachinraja' });
const createdUser: { name: string; id: string; }
client/index.ts
ts
// Inferred types
const user = await trpc.userById.query('1');
const user: { name: string; id: string; } | undefined
 
const createdUser = await trpc.userCreate.mutate({ name: 'sachinraja' });
const createdUser: { name: string; id: string; }

완전한 자동 완성

인텔리센스(Intellisense)를 열어 프론트엔드에서 API를 탐색해 보세요. 모든 프로시저 경로와 호출 방법이 그대로 제공되는 것을 확인할 수 있습니다.

client/index.ts
ts
// Full autocompletion on your routes
trpc.u;
      
client/index.ts
ts
// Full autocompletion on your routes
trpc.u;
      

직접 사용해 보세요!

다음 단계

선호하는 프레임워크에 tRPC를 설치하는 방법을 배우려면 예제 앱을 꼭 확인해 보시기 바랍니다.

기본적으로 tRPC는 Date 같은 복합 타입을 JSON 호환 타입_(예: Datestring으로)_으로 변환합니다. 타입 무결성을 유지하려면 Data Transformer로 superjson 사용하기가 가장 간편한 방법입니다.

tRPC는 React 프로젝트와 Next.js를 위해 특별히 설계된 고급 클라이언트 측 도구를 제공합니다.