컨텐츠로 건너뛰기

React에서 tRPC 사용하기

Nx용 AWS 플러그인은 tRPC API를 React 웹사이트와 빠르게 통합할 수 있는 생성기를 제공합니다. AWS IAM 인증 지원과 적절한 오류 처리를 포함하여 tRPC 백엔드 연결에 필요한 모든 구성을 설정합니다. 이 통합은 프론트엔드와 tRPC 백엔드 간의 완전한 엔드투엔드 타입 안전성을 보장합니다.

필수 조건

이 생성기를 사용하기 전에 React 애플리케이션이 다음을 갖추었는지 확인하세요:

  1. 애플리케이션을 렌더링하는 main.tsx 파일
  2. tRPC 프로바이더가 자동으로 주입될 <App/> JSX 엘리먼트
  3. tRPC 백엔드 생성기로 생성된 작동 중인 tRPC 백엔드
필요한 main.tsx 구조 예시
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom/client';
import App from './app/app';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
);
root.render(
<StrictMode>
<App />
</StrictMode>,
);

사용 방법

생성기 실행

  1. 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
  2. VSCode에서 Nx 콘솔 열기
  3. 클릭 Generate (UI) "Common Nx Commands" 섹션에서
  4. 검색 @aws/nx-plugin - api-connection
  5. 필수 매개변수 입력
    • 클릭 Generate

    옵션

    매개변수 타입 기본값 설명
    sourceProject 필수 string - The source project which will call the API
    targetProject 필수 string - The target project containing your API
    auth string IAM Authentication strategy (choose from IAM or None)

    생성기 출력

    생성기는 React 애플리케이션에 다음 구조를 생성합니다:

    • 디렉터리src
      • 디렉터리components
        • 디렉터리TrpcClients
          • index.tsx
          • TrpcProvider.tsx 여러 tRPC API에 재사용되는 프로바이더
          • TrpcApis.tsx 모든 tRPC API 연결을 포함하는 객체
          • TrpcClientProviders.tsx tRPC 클라이언트 설정 및 백엔드 스키마 바인딩
        • QueryClientProvider.tsx TanStack React Query 클라이언트 프로바이더
      • 디렉터리hooks
        • useSigV4.tsx SigV4로 HTTP 요청 서명을 위한 훅 (IAM 전용)
        • use<ApiName>.tsx 특정 백엔드 API용 훅. ApiName은 API 이름으로 해석됨

    추가로 다음 의존성을 설치합니다:

    • @trpc/client
    • @trpc/tanstack-react-query
    • @tanstack/react-query
    • aws4fetch (IAM 인증 사용 시)

    생성된 코드 사용

    tRPC 훅 사용

    생성기는 타입 안전한 tRPC 클라이언트에 접근할 수 있는 use<ApiName> 훅을 제공합니다:

    import { useQuery, useMutation } from '@tanstack/react-query';
    import { useMyApi } from './hooks/useMyApi';
    function MyComponent() {
    const trpc = useMyApi();
    // 쿼리 예시
    const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
    // 뮤테이션 예시
    const mutation = useMutation(trpc.users.create.mutationOptions());
    const handleCreate = () => {
    mutation.mutate({
    name: 'John Doe',
    email: 'john@example.com',
    });
    };
    if (isLoading) return <div>Loading...</div>;
    return (
    <ul>
    {data.map((user) => (
    <li key={user.id}>{user.name}</li>
    ))}
    </ul>
    );
    }

    오류 처리

    통합에는 tRPC 오류를 적절히 처리하는 내장 오류 처리 기능이 포함되어 있습니다:

    function MyComponent() {
    const trpc = useMyApi();
    const { data, error } = useQuery(trpc.users.list.queryOptions());
    if (error) {
    return (
    <div>
    <h2>오류 발생:</h2>
    <p>{error.message}</p>
    {error.data?.code && <p>코드: {error.data.code}</p>}
    </div>
    );
    }
    return (
    <ul>
    {data.map((user) => (
    <li key={user.id}>{user.name}</li>
    ))}
    </ul>
    );
    }

    모범 사례

    로딩 상태 처리

    더 나은 사용자 경험을 위해 로딩 및 오류 상태를 항상 처리하세요:

    function UserList() {
    const trpc = useMyApi();
    const users = useQuery(trpc.users.list.queryOptions());
    if (users.isLoading) {
    return <LoadingSpinner />;
    }
    if (users.error) {
    return <ErrorMessage error={users.error} />;
    }
    return (
    <ul>
    {users.data.map((user) => (
    <li key={user.id}>{user.name}</li>
    ))}
    </ul>
    );
    }

    낙관적 업데이트

    더 나은 사용자 경험을 위해 낙관적 업데이트를 사용하세요:

    import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
    function UserList() {
    const trpc = useMyApi();
    const users = useQuery(trpc.users.list.queryOptions());
    const queryClient = useQueryClient();
    const deleteMutation = useMutation(
    trpc.users.delete.mutationOptions({
    onMutate: async (userId) => {
    // 진행 중인 요청 취소
    await queryClient.cancelQueries(trpc.users.list.queryFilter());
    // 현재 데이터 스냅샷 저장
    const previousUsers = queryClient.getQueryData(
    trpc.users.list.queryKey(),
    );
    // 낙관적으로 사용자 삭제
    queryClient.setQueryData(trpc.users.list.queryKey(), (old) =>
    old?.filter((user) => user.id !== userId),
    );
    return { previousUsers };
    },
    onError: (err, userId, context) => {
    // 오류 발생 시 이전 데이터 복원
    queryClient.setQueryData(
    trpc.users.list.queryKey(),
    context?.previousUsers,
    );
    },
    }),
    );
    return (
    <ul>
    {users.map((user) => (
    <li key={user.id}>
    {user.name}
    <button onClick={() => deleteMutation.mutate(user.id)}>삭제</button>
    </li>
    ))}
    </ul>
    );
    }

    데이터 프리페칭

    성능 향상을 위해 데이터를 미리 가져오세요:

    function UserList() {
    const trpc = useMyApi();
    const users = useQuery(trpc.users.list.queryOptions());
    const queryClient = useQueryClient();
    // 호버 시 사용자 상세 정보 미리 가져오기
    const prefetchUser = async (userId: string) => {
    await queryClient.prefetchQuery(trpc.users.getById.queryOptions(userId));
    };
    return (
    <ul>
    {users.map((user) => (
    <li key={user.id} onMouseEnter={() => prefetchUser(user.id)}>
    <Link to={`/users/${user.id}`}>{user.name}</Link>
    </li>
    ))}
    </ul>
    );
    }

    무한 쿼리

    페이지네이션을 무한 쿼리로 처리하세요:

    function UserList() {
    const trpc = useMyApi();
    const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
    useInfiniteQuery(
    trpc.users.list.infiniteQueryOptions(
    { limit: 10 },
    {
    getNextPageParam: (lastPage) => lastPage.nextCursor,
    },
    ),
    );
    return (
    <div>
    {data?.pages.map((page) =>
    page.users.map((user) => <UserCard key={user.id} user={user} />),
    )}
    {hasNextPage && (
    <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
    {isFetchingNextPage ? '불러오는 중...' : '더 보기'}
    </button>
    )}
    </div>
    );
    }

    무한 쿼리는 입력에 cursor 프로퍼티가 있는 프로시저에만 사용할 수 있습니다.

    타입 안전성

    이 통합은 완전한 엔드투엔드 타입 안전성을 제공합니다. IDE는 모든 API 호출에 대한 자동 완성과 타입 검사를 지원합니다:

    function UserForm() {
    const trpc = useMyApi();
    // ✅ 입력이 완전히 타입화됨
    const createUser = trpc.users.create.useMutation();
    const handleSubmit = (data: CreateUserInput) => {
    // ✅ 스키마와 일치하지 않을 경우 타입 오류
    createUser.mutate(data);
    };
    return <form onSubmit={handleSubmit}>{/* ... */}</form>;
    }

    타입은 백엔드의 라우터 및 스키마 정의에서 자동으로 유추되며, API 변경 사항이 즉시 프론트엔드 코드에 반영되어 빌드 없이도 적용됩니다.

    추가 정보

    자세한 내용은 tRPC TanStack React Query 문서를 참조하세요.

    TanStack Query 공식 문서도 직접 참조할 수 있습니다.