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

HTTP 배치 스트림 링크

비공식 베타 번역

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

httpBatchStreamLink는 개별 tRPC 작업 배열을 단일 tRPC 프로시저로 전송되는 하나의 HTTP 요청으로 일괄 처리하는 종단 링크입니다(httpBatchLink와 동등). 단, 배치의 모든 응답이 준비될 때까지 기다리지 않고 데이터가 사용 가능해지는 즉시 응답을 스트리밍합니다.

옵션

옵션은 httpBatchLink options과 동일합니다.

사용법

모든 사용법과 옵션은 httpBatchLink와 동일합니다.

참고

프로시저 내에서 응답 헤더(쿠키 포함)를 변경/설정해야 하는 경우 반드시 httpBatchLink를 대신 사용하세요! 이는 httpBatchStreamLink가 스트림 시작 후 헤더 설정을 지원하지 않기 때문입니다. 자세히 보기.

다음과 같이 httpBatchStreamLink를 가져와 links 배열에 추가할 수 있습니다:

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

이후 모든 프로시저를 Promise.all로 설정하면 일괄 처리를 활용할 수 있습니다. 아래 코드는 정확히 하나의 HTTP 요청을 생성하고 서버에서 정확히 하나의 데이터베이스 쿼리를 실행합니다:

ts
const somePosts = await Promise.all([
trpc.post.byId.query(1),
trpc.post.byId.query(2),
trpc.post.byId.query(3),
]);
ts
const somePosts = await Promise.all([
trpc.post.byId.query(1),
trpc.post.byId.query(2),
trpc.post.byId.query(3),
]);

스트리밍 모드

요청을 일괄 처리할 때 일반적인 httpBatchLink의 동작은 모든 요청이 완료될 때까지 응답 전송을 대기하는 것입니다. 응답이 준비되는 즉시 전송하려면 httpBatchStreamLink를 대신 사용할 수 있습니다. 이는 장기 실행 요청에 유용합니다.

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

일반적인 httpBatchLink와 비교하여 httpBatchStreamLink는 다음과 같은 특징을 가집니다:

  • 요청을 trpc-accept: application/jsonl 헤더와 함께 전송

  • 응답을 transfer-encoding: chunkedcontent-type: application/jsonl과 함께 전송

  • responseMeta에 전달되는 인수 객체에서 data 키 제거 (스트리밍 응답 시 데이터가 사용 가능해지기 전에 헤더가 전송되기 때문)

비동기 생성자와 지연된 프로미스

tRPC.io 홈페이지에서 직접 체험해 볼 수 있습니다: https://trpc.io/?try=minimal#try-it-out

ts
// @filename: server.ts
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
examples: {
iterable: publicProcedure.query(async function* () {
for (let i = 0; i < 3; i++) {
await new Promise((resolve) => setTimeout(resolve, 500));
yield i;
}
}),
},
});
 
export type AppRouter = typeof appRouter;
 
 
// @filename: client.ts
import { createTRPCClient, httpBatchStreamLink } from '@trpc/client';
import type { AppRouter } from './server';
 
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchStreamLink({
url: 'http://localhost:3000',
}),
],
});
const iterable = await trpc.examples.iterable.query();
const iterable: AsyncIterable<number, never, unknown>
 
for await (const value of iterable) {
console.log('Iterable:', value);
const value: number
}
ts
// @filename: server.ts
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
examples: {
iterable: publicProcedure.query(async function* () {
for (let i = 0; i < 3; i++) {
await new Promise((resolve) => setTimeout(resolve, 500));
yield i;
}
}),
},
});
 
export type AppRouter = typeof appRouter;
 
 
// @filename: client.ts
import { createTRPCClient, httpBatchStreamLink } from '@trpc/client';
import type { AppRouter } from './server';
 
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchStreamLink({
url: 'http://localhost:3000',
}),
],
});
const iterable = await trpc.examples.iterable.query();
const iterable: AsyncIterable<number, never, unknown>
 
for await (const value of iterable) {
console.log('Iterable:', value);
const value: number
}

호환성 (클라이언트 측)

브라우저

브라우저 지원은 fetch 지원과 동일해야 합니다.

Node.js / Deno

브라우저 이외의 런타임에서는 fetch 구현이 스트리밍을 지원해야 합니다. 즉, await fetch(...)로 얻은 응답은 ReadableStream<Uint8Array> | NodeJS.ReadableStream 유형의 body 속성을 가져야 합니다:

  • response.body.getReaderReadableStreamDefaultReader<Uint8Array> 객체를 반환하는 함수이거나

  • response.bodyUint8Array Buffer이어야 함

여기에는 undici, node-fetch, 네이티브 Node.js fetch 구현 및 WebAPI fetch 구현(브라우저)에 대한 지원이 포함됩니다.

React Native

스트림 수신은 TextDecoderTextDecoderStream API에 의존하는데, 이는 React Native에서 사용할 수 없습니다. TextDecoderStream 폴리필이 ReadableStreamWritableStream을 자동으로 폴리필하지 않는 경우 이들도 폴리필해야 합니다. 스트리밍을 활성화하려면 이들을 폴리필해야 합니다.

또한 httpBatchStreamLink 설정 옵션에서 기본 fetch 구현을 재정의해야 합니다. 아래 예시에서는 fetch 구현으로 Expo fetch 패키지를 사용합니다.

typescript
httpBatchStreamLink({
fetch: (url, opts) =>
fetch(url, {
...opts,
reactNative: { textStreaming: true },
}),
...restOfConfig,
});
typescript
httpBatchStreamLink({
fetch: (url, opts) =>
fetch(url, {
...opts,
reactNative: { textStreaming: true },
}),
...restOfConfig,
});

호환성 (서버 측)

⚠️ AWS Lambda에서는 httpBatchStreamLink가 지원되지 않습니다(일반 httpBatchLink처럼 동작함). 활성화해도 오류는 발생하지 않지만 효과가 없습니다.

⚠️ Cloudflare Workers에서는 기능 플래그 streams_enable_constructors를 통해 ReadableStream API를 활성화해야 합니다.

참조

이 링크의 소스 코드는 GitHub에서 확인할 수 있습니다.

연결 유지를 위한 ping 옵션 설정

루트 설정 시 jsonl 옵션을 전달하여 연결 유지를 위한 ping 설정을 구성할 수 있습니다.

ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create({
jsonl: {
pingMs: 1000,
},
});
ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create({
jsonl: {
pingMs: 1000,
},
});