Use Cases

tRPC API: Claude Code 활용 가이드

trpc api: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.

tRPCで타입安全なAPI개발を実現する

tRPCは서버と클라이언트間の타입を自動共有する프레임워크です。Claude Code는 tRPCのルーター설계からプロシージャの구현まで、타입安全な구성を一貫して생성してくれます。

기본적인ルーター구성

Claude Code에ルーターの구성を依頼합시다。

> tRPCのルーターを설계して。
> 사용자CRUD、인증미들웨어、입력유효성 검사付きで。
import { initTRPC, TRPCError } from "@trpc/server";
import { z } from "zod";

type Context = {
  session: { userId: string; role: string } | null;
  db: PrismaClient;
};

const t = initTRPC.context<Context>().create();

// 미들웨어
const isAuthenticated = t.middleware(({ ctx, next }) => {
  if (!ctx.session) {
    throw new TRPCError({ code: "UNAUTHORIZED" });
  }
  return next({ ctx: { ...ctx, session: ctx.session } });
});

const protectedProcedure = t.procedure.use(isAuthenticated);

// 사용자ルーター
export const userRouter = t.router({
  list: protectedProcedure
    .input(
      z.object({
        page: z.number().int().positive().default(1),
        perPage: z.number().int().min(1).max(100).default(20),
        search: z.string().optional(),
      })
    )
    .query(async ({ ctx, input }) => {
      const { page, perPage, search } = input;
      const where = search
        ? { name: { contains: search, mode: "insensitive" as const } }
        : {};

      const [users, total] = await Promise.all([
        ctx.db.user.findMany({
          where,
          skip: (page - 1) * perPage,
          take: perPage,
          orderBy: { createdAt: "desc" },
        }),
        ctx.db.user.count({ where }),
      ]);

      return { users, total, totalPages: Math.ceil(total / perPage) };
    }),

  getById: protectedProcedure
    .input(z.object({ id: z.string().uuid() }))
    .query(async ({ ctx, input }) => {
      const user = await ctx.db.user.findUnique({
        where: { id: input.id },
      });
      if (!user) throw new TRPCError({ code: "NOT_FOUND" });
      return user;
    }),

  create: protectedProcedure
    .input(
      z.object({
        name: z.string().min(2).max(50),
        email: z.string().email(),
        role: z.enum(["admin", "editor", "viewer"]),
      })
    )
    .mutation(async ({ ctx, input }) => {
      return ctx.db.user.create({ data: input });
    }),

  update: protectedProcedure
    .input(
      z.object({
        id: z.string().uuid(),
        name: z.string().min(2).max(50).optional(),
        email: z.string().email().optional(),
        role: z.enum(["admin", "editor", "viewer"]).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input;
      return ctx.db.user.update({ where: { id }, data });
    }),
});

클라이언트側の타입安全な呼び出し

tRPCの最大の利点は、클라이언트側で서버の타입が그まま使えることです。

import { createTRPCReact } from "@trpc/react-query";
import type { AppRouter } from "../server/router";

export const trpc = createTRPCReact<AppRouter>();

function UserList() {
  const { data, isLoading } = trpc.user.list.useQuery({
    page: 1,
    perPage: 20,
  });

  const createUser = trpc.user.create.useMutation({
    onSuccess: () => {
      utils.user.list.invalidate();
    },
  });

  const utils = trpc.useUtils();

  if (isLoading) return <div>読み込み中...</div>;

  return (
    <div>
      {data?.users.map((user) => (
        <div key={user.id}>
          <span>{user.name}</span>
          <span>{user.email}</span>
        </div>
      ))}
    </div>
  );
}

에러 핸들링の설계

Claude Code에에러 핸들링の仕組みも설계してもらいましょう。

import { TRPCError } from "@trpc/server";

// カスタム에러포매터
const t = initTRPC.context<Context>().create({
  errorFormatter({ shape, error }) {
    return {
      ...shape,
      data: {
        ...shape.data,
        zodError:
          error.cause instanceof ZodError ? error.cause.flatten() : null,
      },
    };
  },
});

// 에러をキャッチする미들웨어
const errorHandler = t.middleware(async ({ next }) => {
  try {
    return await next();
  } catch (error) {
    if (error instanceof TRPCError) throw error;

    console.error("Unexpected error:", error);
    throw new TRPCError({
      code: "INTERNAL_SERVER_ERROR",
      message: "予期せぬエラーが発生しました",
    });
  }
});

サブスクリプション(실시간通信)

WebSocket経由の실시간通信もtRPCで타입安全に구현할 수 있습니다。

import { observable } from "@trpc/server/observable";

export const notificationRouter = t.router({
  onNew: protectedProcedure.subscription(({ ctx }) => {
    return observable<Notification>((emit) => {
      const handler = (notification: Notification) => {
        if (notification.userId === ctx.session.userId) {
          emit.next(notification);
        }
      };

      eventEmitter.on("notification", handler);
      return () => eventEmitter.off("notification", handler);
    });
  }),
});

정리

tRPCとClaude Codeの組み合わせは、타입安全な풀스택개발を最速で実現する手段です。Zod에 의한입력유효성 검사との통합で、ランタイムの安全性も同時に確保할 수 있습니다。

입력유효성 검사의 상세 정보는Zod유효성 검사実践가이드を、클라이언트側の데이터취득はTanStack Query활용가이드를 참고하세요.tRPC공식 문서も併せて확인합시다。

#Claude Code #tRPC #TypeScript #API #type safety