핵심 요약
API 설계 패러다임 선택은 결국 "TypeScript 풀스택 단일 팀"이냐 "다양한 클라이언트·여러 팀"이냐의 트레이드오프. tRPC v12는 단일 코드베이스에서 압도적 DX, GraphQL은 여러 클라이언트·팀이 각자 진화해야 할 때 강함.
- tRPC v12 (2026-03): React Server Components 통합, streamedQuery
- GraphQL: Federation v3, persisted documents 표준화
- 둘 다 운영 중인 회사 많음 — 자체 BFF로 GraphQL, 내부 마이크로서비스로 tRPC
1. tRPC의 강점 — 풀스택 TypeScript
// server/router.ts
export const appRouter = router({
user: router({
byId: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return await db.user.findUnique({ where: { id: input.id } })
}),
create: protectedProcedure
.input(z.object({ name: z.string(), email: z.string().email() }))
.mutation(async ({ input }) => {
return await db.user.create({ data: input })
}),
}),
})
export type AppRouter = typeof appRouter
// client.tsx
import { trpc } from './client'
const user = trpc.user.byId.useQuery({ id: 'abc' })
// 타입 자동 — input·output 모두 추론스키마 정의 = 타입 정의. 별도 codegen 불필요.
2. GraphQL의 강점 — 다양한 클라이언트
# schema.graphql
type User {
id: ID!
name: String!
posts(limit: Int = 10): [Post!]!
}
type Query {
user(id: ID!): User
}# 클라이언트가 필요한 필드만 선택
query {
user(id: "abc") {
name
posts(limit: 5) {
title
}
}
}iOS·Android·Web·CLI·외부 파트너가 각자 다른 필드 셋을 요청해도 단일 엔드포인트.
3. v12 신기능 — RSC 통합
// app/page.tsx (RSC)
import { trpc } from '@/server'
export default async function Page() {
// 직접 호출 — HTTP 안 거침
const users = await trpc.user.list()
return <UserList users={users} />
}
// app/client.tsx
'use client'
import { useTRPC } from '@/trpc'
function RealtimeUsers() {
const trpc = useTRPC()
const { data } = trpc.user.list.useQuery()
return ...
}4. 비교 매트릭스
| 항목 | tRPC v12 | GraphQL |
|---|---|---|
| 타입 안전성 | ★★★★★ (자동) | ★★★★ (codegen) |
| DX | ★★★★★ | ★★★★ |
| 성능 (단일 호출) | ★★★★★ | ★★★ (런타임 파싱) |
| 클라이언트 다양성 | ★★ (TS만) | ★★★★★ |
| over-fetching 방지 | ★★★ (procedure 단위) | ★★★★★ (필드 단위) |
| 스키마 진화 | ★★ (코드 = 스키마) | ★★★★★ (deprecated, federation) |
| 배우는 부담 | 적음 | 큼 |
| 도구·생태계 | 중간 | 거대 (Apollo·Relay·Hasura·Federation) |
5. 의사결정 매트릭스
tRPC를 선택해야 하는 경우
- 단일 팀, TypeScript 풀스택 (Next.js·Bun)
- API 사용처가 자기 앱 한정
- 스키마 변경이 코드와 함께 일어나도 OK
- over-fetching 우려 적음 (도메인 작음)
GraphQL을 선택해야 하는 경우
- iOS·Android·Web 등 여러 클라이언트
- 외부 파트너 API
- 여러 팀이 BFF·서비스 분담
- field-level 권한 제어 필요
- schema federation 필요 (마이크로서비스 통합)
6. 실전 — 하이브리드 패턴
실제 큰 회사는 둘 다 사용:
- 외부·다 클라이언트 BFF: GraphQL (Apollo·Hasura)
- 내부 마이크로서비스 간 통신: tRPC (TypeScript) 또는 gRPC
- 관리자 도구: tRPC (단일 팀, 빠른 개발)
7. 성능 비교
| 지표 | tRPC | GraphQL |
|---|---|---|
| 요청 처리 (단순 query) | 0.8ms | 3.2ms |
| 네트워크 페이로드 | JSON 그대로 | 약간 더 큼 (errors·extensions) |
| over-fetching 방지 효과 | 약함 | 강함 |
단순 호출은 tRPC가 빠름. 복잡한 nested 쿼리는 GraphQL이 over-fetching 줄여 net 효율 높음.
8. 운영 노하우
tRPC
- 큰 router는 분할 (router.merge)
- Zod input validation 일관성 유지
- middleware로 auth·logging 표준화
- tRPC 내부 batching 활용 (httpBatchLink)
GraphQL
- persisted queries — 클라이언트 쿼리를 ID로 전달, 페이로드 감소·보안 강화
- DataLoader 필수 — N+1 방지
- complexity analysis로 악의적 큰 쿼리 차단
- schema federation으로 마이크로서비스 통합
자주 묻는 질문
tRPC가 GraphQL을 대체하나?
아니다. 도메인이 다르다. 단일 팀+TypeScript는 tRPC가 압도, 외부·다 클라이언트는 GraphQL이 압도.
tRPC + GraphQL 동시 운영?
가능. 내부는 tRPC, 외부는 GraphQL. 단 인지 부담이 크니 명확한 경계 필요.
v11 → v12 마이그레이션?
codemod 제공, 90% 자동. 주요 변경은 RSC 통합 + streamedQuery API. 작은 프로젝트는 1~2일.

댓글 0