48. react-query & json-server

yeah·2023년 8월 3일
0

Today I Learned

목록 보기
35/70
post-thumbnail
post-custom-banner

Mission: react-query & json-server를 활용해 게시글 페이지를 구현하며 발생한 에러 및 주요 개념 정리

방법

1. json-server & axios & react-query 설치하기

npm install json-server axios react-query

2. db.json 파일에 초기 데이터 추가

{
  "posts": [
    {
      "id": "1",
      "title": "첫 번째 게시물",
      "content": "첫 번째 게시물의 내용입니다.",
      "author": "작성자1"
    },
    {
      "id": "2",
      "title": "두 번째 게시물",
      "content": "두 번째 게시물의 내용입니다.",
      "author": "작성자2"
    }
  ]
}

3. 메인페이지 - 데이터 조회 (useQuery 활용)

import { useQuery } from "react-query";
import axios from "axios";
// ...
const { data, isLoading, isError, error } = useQuery("posts", async () => {
  const response = await axios.get("http://localhost:3001/posts");
  return response.data;
});
// ...

4. 추가페이지 - 데이터 추가 (useMutation 활용)

import { useMutation, useQueryClient } from "react-query";
import axios from "axios";
// ...
const queryClient = useQueryClient();
const addData = useMutation(
  async (newData) => {
    await axios.post("http://localhost:3001/posts", newData);
  },
  {
    onSuccess: () => {
      queryClient.invalidateQueries("posts");
    },
  }
);
// ...

5. 상세페이지 - 데이터 조회 (useQuery 활용)

import { useQuery } from "react-query";
import axios from "axios";
// ...
const { data: post, isLoading, isError, error } = useQuery(
  ["post", postId], // 고유 키로 postId 사용
  async () => {
    const response = await axios.get(`http://localhost:3001/posts/${postId}`);
    return response.data;
  }
);
// ...

6. 메인페이지와 상세페이지 - 데이터 삭제 (useMutation 활용)

import { useMutation, useQueryClient } from "react-query";
import axios from "axios";
// ...
const queryClient = useQueryClient();
const deleteMutation = useMutation(
  async (postId) => {
    await axios.delete(`http://localhost:3001/posts/${postId}`);
  },
  {
    onSuccess: () => {
      queryClient.invalidateQueries("posts");
    },
  }
);
// ...

발생 에러

1. QueryClient를 찾을 수 없음

에러코드:
Cannot find context value "QueryClient"

원인:
리액트 쿼리를 사용하는 컴포넌트에서 QueryClientProvider로 감싸지 않았을 경우 발생.

해결방안:
해당 컴포넌트를 QueryClientProvider로 감싸서 QueryClient를 제공

import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  );
}

2. useQuery 요청 실패

원인:
서버와 통신 중 문제가 발생하여 데이터를 가져오지 못한 경우

해결방안:
네트워크 연결 확인, 서버 상태 확인 등을 통해 문제 해결을 시도

import { useQuery } from 'react-query';
const { data, isLoading, isError, error } = useQuery('posts', async () => {
  const response = await fetch('http://localhost:3001/posts');
  if (!response.ok) {
    throw new Error('서버로부터 데이터를 불러오는 중 오류가 발생했습니다.');
  }
  return response.json();
});

3. useMutation 요청 실패

원인:
데이터 수정, 추가, 삭제 시 서버에서 오류가 발생한 경우 또는 요청에 문제가 있는 경우

해결방안:
서버 요청을 확인하고, 요청 데이터나 파라미터를 올바르게 조작하여 다시 시도

import { useMutation } from 'react-query';
const deleteMutation = useMutation(
  async (postId) => {
    const response = await fetch(`http://localhost:3001/posts/${postId}`, {
      method: 'DELETE',
    });
    if (!response.ok) {
      throw new Error('데이터 삭제 중 오류가 발생했습니다.');
    }
  },
  {
    onSuccess: () => {
      // ...
    },
  }
);

주요 개념

1. Query

  • useQuery 훅을 사용하여 비동기 데이터를 가져오는 데 사용된다.
  • 요청 결과를 캐싱하고, 필요할 때 재사용한다.
  • data, isLoading, isError, error 등의 상태를 제공하여 데이터 상태를 관리한다.
import { useQuery } from "react-query";
const { data, isLoading, isError, error } = useQuery("posts", async () => {
  const response = await axios.get("http://localhost:3001/posts");
  return response.data;
});

2. Mutation

  • useMutation 훅을 사용하여 비동기 작업을 수행하고 상태를 업데이트한다.
  • 데이터를 추가, 수정, 삭제하는 작업을 처리할 때 사용된다.
  • 성공 시 onSuccess 콜백을 활용하여 업데이트된 데이터를 다시 가져오거나 UI를 갱신할 수 있다.
import { useMutation } from "react-query";
const deleteMutation = useMutation(
  async (postId) => {
    await axios.delete(`http://localhost:3001/posts/${postId}`);
  },
  {
    onSuccess: () => {
      queryClient.invalidateQueries("posts");
    },
  }
);

3. QueryClient

  • QueryClient는 리액트 쿼리의 핵심 개체로, 쿼리와 뮤테이션의 상태를 관리한다.
  • 여러 컴포넌트에서 동일한 쿼리 상태를 공유할 수 있도록 도와준다.
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>
  {/* ... */}
</QueryClientProvider>

4. QueryClientProvider

  • QueryClient를 제공하는 컨텍스트 프로바이더이다.
  • 리액트 앱 전체에서 QueryClient 인스턴스를 사용할 수 있게 해준다.
queryClient.invalidateQueries("posts");

5. invalidateQueries

  • 캐시된 쿼리 데이터를 무효화하고 새로운 데이터를 다시 가져온다.
  • 주로 데이터를 추가, 수정, 삭제한 후에 사용하여 UI를 갱신하는 데 활용된다.
const { data, isLoading, isError, error } = useQuery(
  "posts",
  async () => { /* ... */ },
  {
    onError: (error) => {
      console.error("Error fetching data:", error);
    },
  }
);
const deleteMutation = useMutation(
  async (postId) => { /* ... */ },
  {
    onSuccess: () => { /* ... */ },
    onError: (error) => { /* ... */ },
  }
);

6. UseQueryOptions / UseMutationOptions

  • useQueryuseMutation 훅에 전달되는 옵션 객체로, 쿼리 또는 뮤테이션의 동작을 커스터마이징한다.
  • onSuccess, onError, onSettled 등의 콜백 함수를 정의하여 비동기 작업 후의 동작을 제어한다.
const { data } = useQuery(["post", postId], async () => {
  // ...
});

7. Query Keys

  • 쿼리를 구분하기 위한 식별자로 문자열, 배열 등을 사용한다.
  • 데이터를 가져오는 데 필요한 정보를 담고 있어야 한다.
const { data } = useQuery("posts", async () => {
  const response = await axios.get("http://localhost:3001/posts");
  return response.data;
});

8. Query Function

  • useQuery 훅 내에서 정의되는 함수로, 비동기 작업을 수행하여 데이터를 가져온다.
  • 서버 요청, 데이터 가공 등이 이루어진다.
import { ReactQueryDevtools } from "react-query/devtools";
function App() {
  // ...
  return (
    <div>
      {/* ... */}
      <ReactQueryDevtools />
    </div>
  );
}

9. React Query Devtools

  • 리액트 쿼리의 상태와 캐시를 모니터링하고 디버깅할 수 있는 브라우저 확장 도구이다.
  • 개발 과정에서 쿼리 상태를 시각적으로 확인할 수 있다.

10. React Query Cache

  • 캐시된 데이터의 저장 및 관리를 담당한다.
  • 쿼리 결과 데이터를 메모리에 저장하여 재사용한다.
const { data } = useQuery("posts", async () => {
  const response = await axios.get("http://localhost:3001/posts");
  return response.data;
});
profile
기록과 회고
post-custom-banner

0개의 댓글