Performance & Request Waterfalls

이재철·2024년 1월 23일
0

tanstack-query-v5

목록 보기
4/4

tanstack query v5 Performance & Request Waterfalls

Request Waterfalls (요청폭포)가 발생할 수 있는 몇가지 예시를 설명하며, 해결방법을 제시합니다.

요청 폭포는 리소스(CODE, CSS, Image, DATA)에 대한 요청이 리소스에 대한 다른 요청이 완료될 때 까지 시작되지 않을 때 발생합니다.

Dependant Query (종속쿼리)

두 번째 쿼리에 enabled 옵션 설정으로 종속쿼리로 정의되어 있습니다.
첫 번째 쿼리가 실행 완료 후 두 번째 쿼리가 실행됩니다.
-> 요청폭포 발생

import { useQuery } from '@tanstack/react-query';

import { axiosClient } from '../../api/apiClient';

const Dependant = () => {
  const { data: todoList, isFetched } = useQuery({
    queryKey: ['todo'],
    queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/todos'),
  });
  const { data: postList } = useQuery({
    queryKey: ['posts'],
    queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/posts'),
    enabled: isFetched,
  });

  return (
    <div style={{ display: 'flex' }}>
      <section>
        <h2>Todo List</h2>
        {todoList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
      </section>
      <section>
        <h2>Post List</h2>
        {postList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
      </section>
    </div>
  );
};
export default Dependant;

network waterfall이 생성되고있습니다. 따라서 서비스의 Loading이 길어지는 현상이 발생합니다.

useSuspenseQuery

Suspense와 함께 React 쿼리를 사용하는 경우 발생합니다.

// SuspenseQuery.ts
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';

import { axiosClient } from '../../api/apiClient';

const SuspenseQuery = () => {
  const { data: todoList } = useSuspenseQuery({
    queryKey: ['todo'],
    queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/todos'),
  });
  const { data: postList } = useSuspenseQuery({
    queryKey: ['posts'],
    queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/posts'),
  });
  const { data: photoList } = useSuspenseQuery({
    queryKey: ['photo'],
    queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/photos'),
  });
  const { data: userList } = useSuspenseQuery({
    queryKey: ['users'],
    queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
  });

  return (
    <div style={{ display: 'flex' }}>
      <section>
        <h2>Todo List</h2>
        {todoList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
      </section>
      <section>
        <h2>Post List</h2>
        {postList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
      </section>
      <section>
        <h2>photo List</h2>
        {photoList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
      </section>
      <section>
        <h2>user List</h2>
        {userList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
      </section>
    </div>
  );
};
export default SuspenseQuery;
// QueryTest.tsx
import { Suspense } from 'react';
import SuspenseQuery from './SuspenseQuery';

const QueryTest = () => {
  return (
    <Suspense fallback={<div>...loading</div>}>
      <SuspenseQuery />
    </Suspense>
  );
};
export default QueryTest;

useSuspneseQueries 해결 방법

useSuspneseQueries 를 사용하면 간단하게 해결 할 수 있습니다.

// QueryTest.tsx
  const [todoList, postList, photoList, userList] = useSuspenseQueries({
    queries: [
      { queryKey: ['todo'], queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/todos') },
      {
        queryKey: ['posts'],
        queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/posts'),
      },
      {
        queryKey: ['photo'],
        queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/photos'),
      },
      {
        queryKey: ['users'],
        queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
      },
    ],
  });

network waterfall 해결되었습니다.

Nested Component Waterfalls

중첩 구성 요소 폭포는 상위 구성 요소와 하위 구성 요소 모두에 쿼리가 포함되어 있고
상위 구성 요소는 쿼리가 완료될 떄까지 하위 구성 요소를 렌더링하지 않는 경우입니다.

useQuery, useSuspenseQuery 모두 발생할 수 있습니다.


// User.tsx
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';

import { axiosClient } from '../../api/apiClient';
import Comments from './Comments';

const Users = () => {
  const { data: userList, isPending } = useQuery({
    queryKey: ['users'],
    queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
  });

  if (isPending) return <div>Loading....</div>;

  return (
    <div>
      <ul>
        {userList?.data.map((user: any) => {
          return <li key={user.id}>{user.name}</li>;
        })}
      </ul>
      <Comments postId={1}></Comments>
    </div>
  );
};
export default Users;

// Comments.tsx
import { useQuery } from '@tanstack/react-query';

import { axiosClient } from '../../api/apiClient';

const Comments = ({ postId }: { postId: number }) => {
  const { data: comments, isPending } = useQuery({
    queryKey: ['comments'],
    queryFn: () => axiosClient.get(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`),
  });

  if (isPending) return <div>Loading....</div>;

  return <div>{comments?.data.id}</div>;
};
export default Comments;

Nested Component Waterfalls 해결방법

상위 항목에서 <User> 컴포넌트를 가져오는 동안 해당 ID는 렌더링 시 이미 사용 가능하므로 동시에 기사와 댓글을 가져올 수 없는 이유가 없습니다.

평탄화를 위해 상위 항목으로 끌어올려 해결 할 수 있습니다.

import { useQuery } from '@tanstack/react-query';

import { axiosClient } from '../../api/apiClient';
import Comments from './Comments';
import Users from './Users';

interface AnswerProps {
  postId?: number;
}

const Answer = ({ postId = 1 }: AnswerProps) => {
  const { data: commentsData, isPending: commentsPending } = useQuery({
    queryKey: ['comments'],
    queryFn: () => axiosClient.get(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`),
  });

  const { data: usersData, isPending: usersPending } = useQuery({
    queryKey: ['users'],
    queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
  });

  return (
    <>
      <Users usersData={usersData} />
      {commentsPending ? 'Loading comments...' : <Comments commentsData={commentsData} />}
    </>
  );
};
export default Answer;

useQueries 를 사용하여 두 쿼리를 하나로 결합하는 방식이 더 좋습니다.

import { useQueries, useQuery, useSuspenseQueries } from '@tanstack/react-query';

import { axiosClient } from '../../api/apiClient';
import Comments from './Comments';
import Users from './Users';

interface AnswerProps {
  postId?: number;
}

const Answer = ({ postId = 1 }: AnswerProps) => {
  const queryResults = useQueries({
    queries: [
      {
        queryKey: ['users'],
        queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
      },
      {
        queryKey: ['comments'],
        queryFn: () => axiosClient.get(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`),
      },
    ],
  });

  return (
    <>
      <Users usersData={queryResults[0].data} />
      <Comments commentsData={queryResults[1].data} />
    </>
  );
};
export default Answer;

profile
혼신의 힘을 다하다 🤷‍♂️

1개의 댓글

comment-user-thumbnail
2024년 4월 5일

It's great to be exposed to these interesting programs. Knowledge geometry dash lite is always diverse and expanding. Easy to absorb a lot. Functions with constantly expanding and improving knowledge.

답글 달기