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

콘텐츠 유형

비공식 베타 번역

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

tRPC는 프로시저 입력으로 JSON 직렬화 가능 데이터, FormData, File, Blob 및 기타 이진 유형을 포함한 다양한 콘텐츠 유형을 지원합니다.

JSON (기본값)

기본적으로 tRPC는 JSON 직렬화 가능 데이터를 주고받습니다. 추가 설정이 필요 없습니다 — JSON으로 직렬화할 수 있는 모든 입력은 모든 링크(httpLink, httpBatchLink, httpBatchStreamLink)에서 즉시 작동합니다.

ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.object({ name: z.string() })).query((opts) => {
return { greeting: `Hello ${opts.input.name}` };
}),
});
ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.object({ name: z.string() })).query((opts) => {
return { greeting: `Hello ${opts.input.name}` };
}),
});

비 JSON 콘텐츠 유형

tRPC는 JSON 외에도 FormData, File 및 기타 이진 유형을 프로시저 입력으로 사용할 수 있습니다.

클라이언트 설정

정보

tRPC는 여러 비 JSON 직렬화 가능 유형을 기본적으로 지원하지만, 클라이언트에서는 설정에 따라 이러한 유형을 지원하기 위해 링크를 약간 구성해야 할 수 있습니다.

httpLink는 비 JSON 콘텐츠 유형을 즉시 지원합니다 — 이 링크만 사용하는 경우 기존 설정이 즉시 작동해야 합니다.

ts
import { createTRPCClient, httpLink } from '@trpc/client';
import type { AppRouter } from './server';
 
createTRPCClient<AppRouter>({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});
ts
import { createTRPCClient, httpLink } from '@trpc/client';
import type { AppRouter } from './server';
 
createTRPCClient<AppRouter>({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});

그러나 모든 링크가 이러한 콘텐츠 유형을 지원하는 것은 아닙니다. httpBatchLink 또는 httpBatchStreamLink를 사용하는 경우 splitLink를 포함하고 콘텐츠 유형에 따라 요청을 라우팅해야 합니다.

ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});
ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});

tRPC 서버에서 transformer를 사용하는 경우, TypeScript에서는 tRPC 클라이언트 링크에도 transformer를 정의해야 합니다. 다음 예제를 기반으로 사용하세요:

ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
transformer: {
// request - convert data before sending to the tRPC server
serialize: (data) => data,
// response - convert the tRPC response before using it in client
deserialize: (data) => superjson.deserialize(data), // or your other transformer
},
}),
false: httpBatchLink({
url,
transformer: superjson, // or your other transformer
}),
}),
],
});
ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
transformer: {
// request - convert data before sending to the tRPC server
serialize: (data) => data,
// response - convert the tRPC response before using it in client
deserialize: (data) => superjson.deserialize(data), // or your other transformer
},
}),
false: httpBatchLink({
url,
transformer: superjson, // or your other transformer
}),
}),
],
});

서버 설정

정보

tRPC가 요청을 처리할 때는 요청의 Content-Type 헤더를 기반으로 요청 본문을 파싱합니다.
Failed to parse body as XXX와 같은 오류가 발생하면 서버(예: Express, Next.js)가 tRPC가 처리하기 전에 요청 본문을 파싱하지 않도록 해야 합니다.

ts
// Example in express
import express from 'express';
import * as trpcExpress from '@trpc/server/adapters/express';
import { appRouter } from './router';
 
// incorrect
const app1 = express();
app1.use(express.json()); // this tries to parse body before tRPC.
app1.post('/express/hello', (req, res) => { res.end(); }); // normal express route handler
app1.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC fails to parse body
 
// correct
const app2 = express();
app2.use('/express', express.json()); // do it only in "/express/*" path
app2.post('/express/hello', (req, res) => { res.end(); });
app2.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC can parse body
ts
// Example in express
import express from 'express';
import * as trpcExpress from '@trpc/server/adapters/express';
import { appRouter } from './router';
 
// incorrect
const app1 = express();
app1.use(express.json()); // this tries to parse body before tRPC.
app1.post('/express/hello', (req, res) => { res.end(); }); // normal express route handler
app1.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC fails to parse body
 
// correct
const app2 = express();
app2.use('/express', express.json()); // do it only in "/express/*" path
app2.post('/express/hello', (req, res) => { res.end(); });
app2.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC can parse body

FormData 입력

FormData는 기본적으로 지원되며, 더 고급 사용법을 원한다면 zod-form-data 같은 라이브러리와 조합하여 타입 안전한 방식으로 입력을 검증할 수 있습니다.

ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.instanceof(FormData)).mutation((opts) => {
const data = opts.input;
const data: FormData
return {
greeting: `Hello ${data.get('name')}`,
};
}),
});
ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.instanceof(FormData)).mutation((opts) => {
const data = opts.input;
const data: FormData
return {
greeting: `Hello ${data.get('name')}`,
};
}),
});

더 고급 코드 샘플은 예제 프로젝트에서 확인할 수 있습니다.

File 및 기타 이진 유형 입력

tRPC는 다양한 옥텟 콘텐츠 유형을 프로시저에서 사용 가능한 ReadableStream으로 변환합니다. 현재 지원하는 유형은 Blob, Uint8Array, File입니다.

ts
import { octetInputParser } from '@trpc/server/http';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
upload: publicProcedure.input(octetInputParser).mutation((opts) => {
const data = opts.input;
const data: ReadableStream<any>
return {
valid: true,
};
}),
});
ts
import { octetInputParser } from '@trpc/server/http';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
upload: publicProcedure.input(octetInputParser).mutation((opts) => {
const data = opts.input;
const data: ReadableStream<any>
return {
valid: true,
};
}),
});