dev-guide

tRPC v11 완벽 가이드 — 타입 안전 풀스택 API 설계

tRPC v11의 타입 안전 프로시저 정의, React Query 통합, Next.js App Router 연동, 미들웨어와 컨텍스트 패턴을 실무 예제와 함께 정리하였다.

★★★★★적극 추천
tRPCTypeScriptAPINext.js풀스택
tRPC v11 완벽 가이드 — 타입 안전 풀스택 API 설계
  • ·tRPC v11 릴리스: 2024년 후반
  • ·별도의 스키마 언어 없이 TypeScript로만 API 계층 정의
  • ·TanStack Query(React Query) v5와 통합
  • ·HTTP, WebSocket, Server-Sent Events 트랜스포트 지원
REST API로 운영하던 풀스택 앱을 tRPC v11로 전환하면서 가장 크게 체감한 변화는 클라이언트-서버 간 타입 불일치 오류가 완전히 사라진 것이었다. 이전에는 API 응답 타입을 클라이언트에서 별도 선언하고 유지해야 했는데, tRPC 도입 이후 서버 라우터 정의만으로 클라이언트 타입이 자동으로 생성된다.

tRPC의 핵심 개념

타입 안전 API 계층의 필요성

REST API는 타입 정의가 런타임에만 검증되어, 백엔드 응답 구조가 바뀌면 프론트엔드 코드에서 런타임 오류로 뒤늦게 발견하는 경우가 많다. tRPC는 단일 TypeScript 코드베이스에서 서버 프로시저를 정의하고 클라이언트에서 동일한 타입으로 호출하는 방식으로, 스키마 파일이나 코드 생성 도구 없이 타입 안전성을 보장한다. 백엔드 함수의 입력과 반환 타입이 변경되면 프론트엔드 호출 코드에서 즉시 TypeScript 오류가 발생하여 불일치를 빌드 타임에 잡을 수 있다.

// server/router.ts
import { z } from 'zod'
import { router, publicProcedure } from './trpc'

export const appRouter = router({
  post: router({
    list: publicProcedure
      .input(z.object({ cursor: z.number().optional() }))
      .query(async ({ input }) => {
        return await db.post.findMany({ take: 10, skip: input.cursor })
      }),
    create: publicProcedure
      .input(z.object({ title: z.string().min(1), content: z.string() }))
      .mutation(async ({ input, ctx }) => {
        return await db.post.create({ data: { ...input, authorId: ctx.userId } })
      }),
  }),
})

export type AppRouter = typeof appRouter

프로시저 타입: query, mutation, subscription

publicProcedure.query()는 데이터를 읽는 GET 성격의 프로시저로 TanStack Query의 쿼리와 통합된다. publicProcedure.mutation()은 데이터를 변경하는 POST/PUT/DELETE 성격의 프로시저로 뮤테이션과 통합된다. v11에서는 subscription()을 통한 실시간 데이터 스트리밍도 지원하며, Server-Sent Events 방식으로 클라이언트에 데이터를 푸시할 수 있다. Zod로 입력 스키마를 선언하면 런타임 유효성 검사와 TypeScript 타입 추론이 동시에 처리된다.

미들웨어와 컨텍스트 패턴

인증 미들웨어로 보호된 프로시저

middleware 함수로 프로시저 체인에 인증 검사를 추가할 수 있다. protectedProcedure를 정의하면 미인증 사용자의 요청에서 오류를 반환하고, 인증된 사용자 정보를 컨텍스트에 추가하여 프로시저에서 접근할 수 있다. 미들웨어는 체이닝이 가능하여 인증 후 권한 검사, 요청 로깅, 레이트 리밋 등 다양한 횡단 관심사를 순서대로 적용할 수 있다. 이 패턴은 Server Actions의 개별 인증 검사보다 체계적인 API 보안 계층을 구성할 수 있게 한다.

React Query와의 통합

tRPC v11은 TanStack Query v5와 통합되어, api.post.list.useQuery()처럼 tRPC 프로시저를 React Query 훅으로 직접 사용할 수 있다. 캐싱, 백그라운드 리페치, Optimistic Updates, Infinite Query 등 React Query의 모든 기능을 tRPC 타입 안전 API와 함께 사용할 수 있다. api.post.create.useMutation()으로 뮤테이션을 처리하고, 성공 시 utils.post.list.invalidate()로 관련 쿼리를 무효화하는 패턴이 표준이다.

Next.js App Router와의 통합

App Router 설정

Next.js App Router에서 tRPC를 사용하려면 app/api/trpc/[trpc]/route.ts에 tRPC HTTP 핸들러를 등록한다. Server Components에서는 클라이언트 없이 서버 사이드 caller를 직접 호출할 수 있어, 불필요한 HTTP 왕복 없이 데이터를 가져올 수 있다. 클라이언트 컴포넌트에서는 TRPCProvider로 감싸고 React Query 훅을 통해 사용한다. Server Actions와 tRPC를 함께 사용하는 프로젝트에서는 뮤테이션은 Server Actions로, 복잡한 쿼리 관리는 tRPC+React Query로 분리하는 전략도 효과적이다.

tRPC vs Server Actions 선택 기준

단일 Next.js 프로젝트에서 간단한 데이터 뮤테이션이 주목적이라면 Server Actions가 설정 비용이 낮아 유리하다. 모바일 앱, 다른 프론트엔드, 외부 서비스가 같은 API를 사용하거나, React Query의 캐싱·재검증 기능이 필요하거나, 팀이 커서 타입 안전성을 체계적으로 보장해야 한다면 tRPC가 더 적합하다. 두 방식을 함께 사용하는 것도 가능하며, 프로젝트 성격에 따라 적절히 선택하면 된다.

자주 묻는 질문

tRPC는 REST API를 완전히 대체하나요?+

동일한 TypeScript 코드베이스를 공유하는 모노레포에서는 REST를 대체할 수 있습니다. 다만 외부 파트너가 API를 사용하거나 OpenAPI 문서가 필요한 경우 REST나 GraphQL이 더 적합합니다.

tRPC를 Next.js 없이도 사용할 수 있나요?+

Express, Fastify, Elysia 등 다양한 서버 프레임워크와 통합 가능합니다. 프론트엔드도 React 외에 Vue, Solid 등을 지원합니다.

관련 글