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

프로시저 정의하기

비공식 베타 번역

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

프로시저는 클라이언트에 노출되는 함수로, 다음 중 하나입니다:

  • Query - 데이터를 가져오는 용도로, 일반적으로 데이터를 변경하지 않음

  • Mutation - 데이터를 전송하는 용도로, 주로 생성/수정/삭제 작업에 사용됨

  • Subscription - 이 기능이 필요하지 않을 수 있으며, 전용 문서가 별도로 마련되어 있습니다.

tRPC의 프로시저는 백엔드 함수를 생성하기 위한 매우 유연한 기본 요소입니다. 불변성 빌더 패턴을 사용하므로, 여러 프로시저 간에 기능을 공유하는 재사용 가능한 기본 프로시저를 생성할 수 있습니다.

프로시저 작성하기

tRPC 설정 시 생성하는 t 객체는 초기 t.procedure를 반환하며, 다른 모든 프로시저는 이를 기반으로 빌드됩니다:

ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
const t = initTRPC.context<{ signGuestBook: () => Promise<void> }>().create();
 
export const router = t.router;
export const publicProcedure = t.procedure;
 
const appRouter = router({
// Queries are the best place to fetch data
hello: publicProcedure.query(() => {
return {
message: 'hello world',
};
}),
 
// Mutations are the best place to do things like updating a database
goodbye: publicProcedure.mutation(async (opts) => {
await opts.ctx.signGuestBook();
 
return {
message: 'goodbye!',
};
}),
});
ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
const t = initTRPC.context<{ signGuestBook: () => Promise<void> }>().create();
 
export const router = t.router;
export const publicProcedure = t.procedure;
 
const appRouter = router({
// Queries are the best place to fetch data
hello: publicProcedure.query(() => {
return {
message: 'hello world',
};
}),
 
// Mutations are the best place to do things like updating a database
goodbye: publicProcedure.mutation(async (opts) => {
await opts.ctx.signGuestBook();
 
return {
message: 'goodbye!',
};
}),
});

재사용 가능한 "기본 프로시저"

일반적인 패턴으로 t.procedurepublicProcedure로 이름 변경하여 내보내는 것을 권장합니다. 이렇게 하면 특정 사용 사례를 위한 다른 명명된 프로시저를 생성하고 내보낼 공간이 생깁니다. 이 패턴을 "기본 프로시저"라고 하며, tRPC에서 코드와 동작을 재사용하는 핵심 패턴입니다. 거의 모든 애플리케이션에 필요합니다.

아래 예제는 사용자 입력을 받아 보호적인 마을 사람들처럼 인가하는 방식을 보여줍니다. 이는 단순화를 위한 인위적인 예시이며, 애플리케이션 사용자를 안전하게 인가하는 적절한 방법이 아닙니다. 실제로는 헤더, 컨텍스트, 미들웨어, 메타데이터를 조합하여 사용자를 인증하고 인가하는 것이 좋습니다.

ts
export const authorizedProcedure = publicProcedure
.input(z.object({ townName: z.string() }))
.use((opts) => {
if (opts.input.townName !== 'Pucklechurch') {
throw new TRPCError({
code: 'FORBIDDEN',
message: "We don't take kindly to out-of-town folk",
});
}
 
return opts.next();
});
 
export const appRouter = t.router({
hello: authorizedProcedure.query(() => {
return {
message: 'hello world',
};
}),
goodbye: authorizedProcedure.mutation(async (opts) => {
await opts.ctx.signGuestBook();
 
return {
message: 'goodbye!',
};
}),
});
ts
export const authorizedProcedure = publicProcedure
.input(z.object({ townName: z.string() }))
.use((opts) => {
if (opts.input.townName !== 'Pucklechurch') {
throw new TRPCError({
code: 'FORBIDDEN',
message: "We don't take kindly to out-of-town folk",
});
}
 
return opts.next();
});
 
export const appRouter = t.router({
hello: authorizedProcedure.query(() => {
return {
message: 'hello world',
};
}),
goodbye: authorizedProcedure.mutation(async (opts) => {
await opts.ctx.signGuestBook();
 
return {
message: 'goodbye!',
};
}),
});