TypeScript에서의 tRPC: API 개발 단순화하기

API를 구축한다는 것은 보통 REST와 GraphQL 중 하나를 선택하는 것을 의미합니다. 라우트를 설정하고, 스키마를 관리하며, 프론트엔드와 백엔드 간의 타입을 동기화하는 데 많은 시간을 소비하게 됩니다. 양쪽 모두에서 TypeScript를 사용한다면 이 과정은 매우 번거롭게 느껴질 것입니다. 결국 같은 타입을 두 번 작성하게 되기 때문입니다.

tRPC는 이러한 워크플로우를 변화시킵니다. 별도의 API 계약(contract)이 필요하지 않게 해줍니다. 대신, TypeScript를 사용하여 서버와 클라이언트 간에 타입을 자동으로 공유합니다.

왜 tRPC를 사용해야 할까요?

  • 수동 타입 동기화 불필요: 서버에서 함수를 작성하면 클라이언트가 즉시 입력 및 출력 타입을 알 수 있습니다.
  • 코드 생성 불필요: 타입을 생성하기 위해 별도의 도구를 실행할 필요가 없습니다.
  • 스키마 드리프트(Schema drift) 방지: 클라이언트가 서버 타입을 직접 사용하므로 프론트엔드가 백엔드와 항상 동기화된 상태를 유지합니다.
  • 빠른 개발 속도: 네트워크 요청을 보내는 것이 아니라, 마치 로컬 파일에 있는 함수를 호출하는 것 같은 느낌을 줍니다.

전통적인 방식에는 각각의 트레이드오프(trade-offs)가 있습니다. REST는 수동 fetch 호출과 중복된 타입 작성이 필요합니다. GraphQL은 스키마를 제공하지만, resolver와 코드 생성(codegen)으로 인해 복잡성이 증가합니다.

tRPC는 백엔드를 타입 안전(type-safe)한 함수들의 집합으로 취급합니다. 라우터에서 프로시저(procedure)를 정의하면, 클라이언트는 라우터 타입을 임포트하여 이러한 프로시저를 직접 호출할 수 있습니다.

예시 흐름:

  1. 서버에서 검증 로직(Zod 등)과 함께 프로시저를 정의합니다.
  2. 라우터 타입을 내보냅니다(export).
  3. 클라이언트에서 완전한 자동 완성 및 타입 안전성을 보장받으며 해당 프로시저를 호출합니다.

tRPC를 사용해야 할 때:

  • 프론트엔드와 백엔드 모두 TypeScript를 사용하는 경우.
  • 스택의 양쪽을 모두 제어할 수 있는 경우.
  • 내부 도구, 관리자 대시보드 또는 풀스택 Next.js 앱을 구축하는 경우.
  • 모노레포(monorepo) 환경에서 작업하는 경우.

tRPC 사용을 피해야 할 때:

  • 다양한 사용자를 위한 공개 API를 구축하는 경우.
  • 클라이언트가 Python이나 Go와 같은 다른 언어를 사용하는 경우.
  • 복잡한 API 버전 관리가 필요한 경우.

tRPC가 모든 시나리오에서 REST나 GraphQL을 대체하는 것은 아닙니다. tRPC는 스택이 통합되어 있을 때 속도와 안전성을 제공하는 도구입니다. API 경계로 인한 마찰을 제거하여 로직을 작성하는 데 집중할 수 있게 해줍니다.

출처: https://dev.to/geekyants/trpc-in-typescript-simplify-api-development-without-boilerplate-3lm3