tRPC 컨텍스트
tRPC 컨텍스트 (Context)
모든 procedure가 공통으로 접근해야 하는 데이터가 있어요. 대표적으로 인증 정보가 그렇죠. tRPC의 **컨텍스트(context)**가 바로 그 역할을 해요. 요청마다 구성돼서 모든 procedure가 쓸 수 있는 데이터를 담아요.
컨텍스트 구성은 두 단계예요. (1) 초기화 때 타입을 정의하고, (2) 각 요청에 대한 런타임 컨텍스트를 만드는 거예요.
초기화할 때는 initTRPC.context<TContext>()로 타입을 연결해요.
import { initTRPC } from '@trpc/server';
export const createContext = async (opts) => {
const token = opts.req.headers['authorization'];
return { token };
};
export type Context = Awaited<ReturnType<typeof createContext>>;
const t = initTRPC.context<Context>().create();
createContext()는 라우터를 마운트하는 핸들러에 전달돼요. HTTP와 서버 사이드 호출 양쪽에서 받을 수 있고, 요청마다 한 번 호출돼요. 그래서 배치된 하나의 요청 안의 모든 procedure는 같은 컨텍스트를 공유해요.
실무에서는 컨텍스트를 "inner"와 "outer"로 나누는 게 유용할 때가 있어요.
- Inner context: 요청에 의존하지 않는 것(예: DB 연결)을 정의해요. 테스트나 서버 사이드 호출처럼
req가 없을 때도 쓰임. 여기 정의된 건 항상 procedure에서 사용 가능해요. - Outer context: 요청에 의존하는 것(예: 사용자 세션)을 정의해요. HTTP로 호출된 procedure에서만 사용 가능해요.
DB 클라이언트(예: Prisma)를 inner context에 두는 건 편리하지만 흔한 트레이드오프가 있는데, 큰 생성 클라이언트는 컨텍스트 타입에 포함돼 procedure 전반의 타입 체크 비용을 키울 수 있어요. 그 부담이 커지면 컨텍스트를 작게 유지하고 필요한 곳에서 클라이언트를 직접 import하는 방식도 있어요.
컨텍스트 타입은 inner context에서 추론하는 게 옳아요 — 항상 사용 가능한 것만 타입에 넣어야 하기 때문이에요. req/res가 undefined가 아니도록 좁히고 싶다면, publicProcedure.use()로 재사용 가능한 procedure를 만들어 검증 후 컨텍스트를 교체하는 패턴을 써요.
더 알아보기
- 컨텍스트로 인증 procedure 만들기: Authorization
- 서버 사이드 호출: Server Side Calls