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

비 JSON 콘텐츠 유형

비공식 베타 번역

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

JSON으로 직렬화 가능한 데이터 외에도 tRPC는 프로시저 입력으로 FormData, File 및 기타 바이너리 유형을 사용할 수 있습니다.

클라이언트 설정

정보

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

httpLink는 비 JSON 콘텐츠 유형을 즉시 지원하므로, 이 링크만 사용한다면 기존 설정이 바로 작동할 것입니다.

ts
import { httpLink } from '@trpc/client';
trpc.createClient({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});
ts
import { httpLink } from '@trpc/client';
trpc.createClient({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});

그러나 모든 링크가 이러한 새로운 콘텐츠 유형을 지원하는 것은 아닙니다. httpBatchLinkhttpBatchStreamLink를 사용 중이라면 splitLink를 포함하고 콘텐츠에 따라 사용할 링크를 확인해야 합니다.

ts
import {
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
trpc.createClient({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});
ts
import {
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
trpc.createClient({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});

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

ts
import {
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
trpc.createClient({
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: superjson.deserialize, // or your other transformer
},
}),
false: httpBatchLink({
url,
transformers: superjson, // or your other transformer
}),
}),
],
});
ts
import {
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
trpc.createClient({
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: superjson.deserialize, // or your other transformer
},
}),
false: httpBatchLink({
url,
transformers: superjson, // or your other transformer
}),
}),
],
});

서버 사용법

정보

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

ts
// Example in express
// incorrect
const app = express();
app.use(express.json()); // this try to parse body before tRPC.
app.post('/express/hello', (req,res) => {/* ... */ }); // normal express route handler
app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC fails to parse body
// correct
const app = express();
app.use('/express', express.json()); // do it only in "/express/*" path
app.post('/express/hello', (req,res) => {/* ... */ });
app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC can parse body
ts
// Example in express
// incorrect
const app = express();
app.use(express.json()); // this try to parse body before tRPC.
app.post('/express/hello', (req,res) => {/* ... */ }); // normal express route handler
app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC fails to parse body
// correct
const app = express();
app.use('/express', express.json()); // do it only in "/express/*" path
app.post('/express/hello', (req,res) => {/* ... */ });
app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// 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,
};
}),
});