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
useQuery
와useMutation
훅에 전달되는 옵션 객체로, 쿼리 또는 뮤테이션의 동작을 커스터마이징한다.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; });