입력 및 출력 검증기
이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →
tRPC 프로시저는 입력 및/또는 출력에 대한 검증 로직을 정의할 수 있으며, 검증기는 입력과 출력의 타입을 추론하는 데에도 사용됩니다(표준 스키마 인터페이스가 사용 가능한 경우 해당 인터페이스를, 그렇지 않으면 지원되는 검증기에 대한 커스텀 인터페이스 사용). 우리는 많은 인기 있는 검증기에 대해 일급 지원을 제공하며, 직접 지원하지 않는 검증기도 통합할 수 있습니다.
입력 검증기
입력 검증기를 정의하면 tRPC는 프로시저 호출이 올바른지 확인하고, 그렇지 않은 경우 검증 오류를 반환할 수 있습니다.
입력 검증기를 설정하려면 procedure.input() 메서드를 사용하세요:
ts// Our examples use Zod by default, but usage with other libraries is identicalimport {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .object ({name :z .string (),}),).query ((opts ) => {constname =opts .input .name ;return {greeting : `Hello ${opts .input .name }`,};}),});
ts// Our examples use Zod by default, but usage with other libraries is identicalimport {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .object ({name :z .string (),}),).query ((opts ) => {constname =opts .input .name ;return {greeting : `Hello ${opts .input .name }`,};}),});
입력 병합
.input()은 더 복잡한 타입을 구성하기 위해 중첩해서 사용할 수 있으며, 이는 특히 미들웨어에서 여러 프로시저에 공통으로 적용되는 입력을 활용하려는 경우에 유용합니다.
tsconstbaseProcedure =t .procedure .input (z .object ({townName :z .string () })).use ((opts ) => {constinput =opts .input ;console .log (`Handling request with user from: ${input .townName }`);returnopts .next ();});export constappRouter =t .router ({hello :baseProcedure .input (z .object ({name :z .string (),}),).query ((opts ) => {constinput =opts .input ;return {greeting : `Hello ${input .name }, my friend from ${input .townName }`,};}),});
tsconstbaseProcedure =t .procedure .input (z .object ({townName :z .string () })).use ((opts ) => {constinput =opts .input ;console .log (`Handling request with user from: ${input .townName }`);returnopts .next ();});export constappRouter =t .router ({hello :baseProcedure .input (z .object ({name :z .string (),}),).query ((opts ) => {constinput =opts .input ;return {greeting : `Hello ${input .name }, my friend from ${input .townName }`,};}),});
출력 검증기
tRPC는 프로시저의 반환 타입을 추론하여 자동 타입 안전성을 제공하므로, 출력 검증은 입력 정의만큼 항상 중요하지는 않습니다. 출력 검증기를 정의해야 하는 이유는 다음과 같습니다:
-
신뢰할 수 없는 소스에서 반환된 데이터가 올바른지 확인하기 위해
-
클라이언트에 불필요한 데이터를 반환하지 않도록 보장하기 위해
출력 검증이 실패하면 서버는 INTERNAL_SERVER_ERROR로 응답합니다.
tsimport {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .output (z .object ({greeting :z .string (),}),).query ((opts ) => {return {gre ,};}),});
tsimport {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .output (z .object ({greeting :z .string (),}),).query ((opts ) => {return {gre ,};}),});
구독의 출력 검증
구독은 비동기 이터레이터이므로, 위와 동일한 검증 기법을 사용할 수 있습니다.
자세한 내용은 구독 가이드를 참조하세요.
가장 기본적인 검증기: 함수
타사 의존성 없이 함수만으로 검증기를 정의할 수 있습니다.
특별한 필요가 없다면 커스텀 검증기를 만드는 것을 권장하지 않지만, 여기에는 어떤 마법도 없다는 점(단순히 타입스크립트일 뿐!)을 이해하는 것이 중요합니다!
대부분의 경우 검증 라이브러리 사용을 권장합니다.
tsimport {initTRPC } from '@trpc/server';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input ((value ): string => {if (typeofvalue === 'string') {returnvalue ;}throw newError ('Input is not a string');}).output ((value ): string => {if (typeofvalue === 'string') {returnvalue ;}throw newError ('Output is not a string');}).query ((opts ) => {const {input } =opts ;return `hello ${input }`;}),});export typeAppRouter = typeofappRouter ;
tsimport {initTRPC } from '@trpc/server';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input ((value ): string => {if (typeofvalue === 'string') {returnvalue ;}throw newError ('Input is not a string');}).output ((value ): string => {if (typeofvalue === 'string') {returnvalue ;}throw newError ('Output is not a string');}).query ((opts ) => {const {input } =opts ;return `hello ${input }`;}),});export typeAppRouter = typeofappRouter ;
라이브러리 통합
tRPC는 표준 스키마를 준수하는 모든 라이브러리를 포함한 여러 인기 있는 검증 및 파싱 라이브러리와 즉시 호환됩니다. 아래는 공식적으로 지원을 유지하는 검증기 사용 예시입니다.
Zod와 함께 사용하기
Zod는 우리의 기본 추천 라이브러리입니다. 강력한 생태계를 갖춰 코드베이스의 여러 부분에서 사용하기에 탁월한 선택입니다. 특별한 선호가 없고 향후 요구사항을 제한하지 않는 강력한 라이브러리를 원한다면 Zod가 훌륭한 선택입니다.
tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .object ({name :z .string (),}),).output (z .object ({greeting :z .string (),}),).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .object ({name :z .string (),}),).output (z .object ({greeting :z .string (),}),).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
Yup와 함께 사용하기
tsimport {initTRPC } from '@trpc/server';import * asyup from 'yup';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (yup .object ({name :yup .string ().required (),}),).output (yup .object ({greeting :yup .string ().required (),}),).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
tsimport {initTRPC } from '@trpc/server';import * asyup from 'yup';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (yup .object ({name :yup .string ().required (),}),).output (yup .object ({greeting :yup .string ().required (),}),).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
Superstruct와 함께 사용하기
tsimport {initTRPC } from '@trpc/server';import {object ,string } from 'superstruct';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (object ({name :string () })).output (object ({greeting :string () })).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
tsimport {initTRPC } from '@trpc/server';import {object ,string } from 'superstruct';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (object ({name :string () })).output (object ({greeting :string () })).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
scale-ts와 함께 사용하기
tsimport {initTRPC } from '@trpc/server';import * as$ from 'scale-codec';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input ($ .object ($ .field ('name',$ .str ))).output ($ .object ($ .field ('greeting',$ .str ))).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
tsimport {initTRPC } from '@trpc/server';import * as$ from 'scale-codec';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input ($ .object ($ .field ('name',$ .str ))).output ($ .object ($ .field ('greeting',$ .str ))).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
Typia와 함께 사용하기
tsimport { initTRPC } from '@trpc/server';import typia from 'typia';import { v4 } from 'uuid';import { IBbsArticle } from '../structures/IBbsArticle';const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({store: publicProcedure.input(typia.createAssert<IBbsArticle.IStore>()).output(typia.createAssert<IBbsArticle>()).query(({ input }) => {return {id: v4(),writer: input.writer,title: input.title,body: input.body,created_at: new Date().toString(),};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import typia from 'typia';import { v4 } from 'uuid';import { IBbsArticle } from '../structures/IBbsArticle';const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({store: publicProcedure.input(typia.createAssert<IBbsArticle.IStore>()).output(typia.createAssert<IBbsArticle>()).query(({ input }) => {return {id: v4(),writer: input.writer,title: input.title,body: input.body,created_at: new Date().toString(),};}),});export type AppRouter = typeof appRouter;
ArkType와 함께 사용하기
tsimport { initTRPC } from '@trpc/server';import { type } from 'arktype';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(type({ name: 'string' })).query((opts) => {return {greeting: `hello ${opts.input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import { type } from 'arktype';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(type({ name: 'string' })).query((opts) => {return {greeting: `hello ${opts.input.name}`,};}),});export type AppRouter = typeof appRouter;
effect와 함께 사용하기
tsimport { initTRPC } from '@trpc/server';import { Schema } from 'effect';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(Schema.standardSchemaV1(Schema.Struct({ name: Schema.String }))).output(Schema.standardSchemaV1(Schema.Struct({ greeting: Schema.String }))).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import { Schema } from 'effect';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(Schema.standardSchemaV1(Schema.Struct({ name: Schema.String }))).output(Schema.standardSchemaV1(Schema.Struct({ greeting: Schema.String }))).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
Valibot와 함께 사용하기
tsimport {initTRPC } from '@trpc/server';import * asv from 'valibot';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (v .object ({name :v .string () })).output (v .object ({greeting :v .string () })).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
tsimport {initTRPC } from '@trpc/server';import * asv from 'valibot';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (v .object ({name :v .string () })).output (v .object ({greeting :v .string () })).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
@robolex/sure와 함께 사용하기
필요한 경우 사용자 정의 오류 유형 및 오류 발생 함수를 직접 정의할 수 있습니다. 편의를 위해 @robolex/sure은 sure/src/err.ts를 제공합니다:
ts// sure/src/err.tsexport const err = (schema) => (input) => {const [good, result] = schema(input);if (good) return result;throw result;};
ts// sure/src/err.tsexport const err = (schema) => (input) => {const [good, result] = schema(input);if (good) return result;throw result;};
tsimport { err, object, string } from '@robolex/sure';import { initTRPC } from '@trpc/server';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(err(object({name: string,}),),).output(err(object({greeting: string,}),),).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { err, object, string } from '@robolex/sure';import { initTRPC } from '@trpc/server';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(err(object({name: string,}),),).output(err(object({greeting: string,}),),).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
TypeBox 사용하기
tsimport { Type } from '@sinclair/typebox';import { initTRPC } from '@trpc/server';import { wrap } from '@typeschema/typebox';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(wrap(Type.Object({ name: Type.String() }))).output(wrap(Type.Object({ greeting: Type.String() }))).query(({ input }) => {return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { Type } from '@sinclair/typebox';import { initTRPC } from '@trpc/server';import { wrap } from '@typeschema/typebox';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(wrap(Type.Object({ name: Type.String() }))).output(wrap(Type.Object({ greeting: Type.String() }))).query(({ input }) => {return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
커스텀 유효성 검사 라이브러리 기여하기
tRPC 사용을 지원하는 유효성 검사 라이브러리를 작업 중이라면, 이 페이지에 다른 예시와 동등한 사용법 및 문서 링크를 포함한 PR(Pull Request)을 자유롭게 열어주세요.
대부분의 경우 tRPC 통합은 기존 타입 인터페이스 중 하나를 충족하는 간단한 과정입니다. Standard Schema 준수를 권장하지만, 경우에 따라 새로운 지원 인터페이스 추가를 위한 PR을 수락할 수 있습니다. 논의를 위해 이슈를 열어주세요. 기존 지원 인터페이스 및 파싱/유효성 검사 함수는 코드에서 확인할 수 있습니다.