
공부한 내용을 정리했다.
경쟁 도구 :
react-querySWR
장점
Redux와 함께 사용해도 좋지만, Redux와 상관없이 사용할 수 있도록 독자적으로 설계되어있다.api를 여러개의 컴포넌트에 사용해 관리해줄 수 있다.Redux 없이 rtk-query 사용Redux와 함께 rtk-query 사용설치
npx create-react-app my-app —template redux
npx create-react-app my-app —template redux-typescript
npm i @reduxjs/toolkit
yarn add @reduxjs/toolkit
RTK-Query 사용법 ( 화면 : client 입장 )[ Server 팀에서 넘겨준 Server Hook 정보 ]
상황
1. React 잘함 O, RTK Query 못함 X
2. 서버팀에서 서버랑 통신하는 Hook들을 RTK Query를 이용해 만들었다.
3. RTK Query를 이용할 순 없어도 개발은 할 수 있다.
구조
App.js안에 모든 것이 다 들어있다.API : RTK Query를 이용해 만든 Hook을 사용한다./src/app/api.js )Server Hook 사용법
1. “./app/api의 api 객체를 이용하세요.”
2. RTK Qeury로 서버와 통신하고 있습니다.
3. 읽기 : useGetCountQuery({ name })
4. 쓰기 : useSetCountMutation()
RTK Query의 특징
“use ~ Query”는 읽기전용“use ~ Mutation”은 쓰기전용[ app.js 전체 코드 ]
import React from "react";
import { api } from "./app/api";
const Count = ({ name }) => {
const query = api.useGetCountQuery({ name });
const mutation = api.useSetCountMutation();
const setCount = mutation[0];
if (query.isLoading) {
return <>Loading</>;
}
return (
<div>
<button
onClick={async () => {
await setCount({ name, value: query.data + 1 });
}}
>
{mutation[1].isLoading ? "updating..." : ""}
{query.isFetching ? "fetching..." : ""}
{name} {query.data}
</button>
</div>
);
};
export default function App() {
return (
<>
<Count name="egoing" />
<Count name="egoing" />
<Count name="jane" />
<Count name="steve" />
</>
);
}
app.js에서 console.log(api);를 찍어 만든 api hook을 확인한다.

Count라는 컴포넌트에 파라미터로 name을 받아온다.
const query = api.useGetCountQuery({ name });

data : 서버에서 받아온 데이터 프로퍼티isLoading : 현재 서버에서 데이터를 가지고 오는 지를 블리언 값으로 확인false면 가지고 왔음을 의미 )isFetching : 현재 서버에서 데이터를 읽어오고 있는지를 블리언 값으로 확인flase면 읽기 전을 의미 )status : 어떤 작업이 진행 중인지 나타나준다.Fetching이 다 되고 데이터를 다 가져왔을 때, data를 undefind에서 필요한 값을 받는다.status가 fulfilled로 바뀐 것을 보면 ?
isLoading, isFetching 모두 false로 바뀌었다.data는 10으로 바뀐것을 확인 할 수 있다.use ~ Query사용시 isLoading or isFetching의 차이점isLoading : 최초 한번만 실행된다. ( 컴포넌트를 초기화할 때 사용 )isFetching : 서버와 통신을 할 때마다 실행된다. ( 로딩할 때 마다 사용 )isLoading보다 isFetching을 더 많이 사용하는 편이다.if ( query.isLoading ) return <>Loading</>;<button>
{query.isFetching ? “fetching...” : “ “} {name} {query.data}
</button>
useSetCountMutation() Hook을 사용한다.use ~ Queryreturn 한다.use ~ Mutation은 쓰기전용promise로 응답 값을 전달 )use ~ Mutation() Hook을 사용하면, 배열을 return 한다.isFetching이 없고, 서버와 통신할 땐 무조건 isLoading을 사용한다.isLoading이 있다.const setCount = mutation[0];name, value 프로퍼티를 사용하라고 알려줬다.<button onClick={() => {
setCount({ name, value: query.data + 1 });
}}>
// .. 생략
</button>
NetWork를 확인하면,POST로 전송되고 받아오는 value값이 기존 10인 숫자에서 11로 변경된 값을 확인할 수 있다.별개로 경우에 따라서 서버가 응답한 결과를 바로! 즉시 ! 받아보고 싶을다면 ?
:promise로 응답 값을 전달할 수 있다.<button onClick={ async () => { const result = await setCount({ name, value: query.data + 1 }); console.log( ‘result’, result ); }}> // .. 생략 </button>
onClick을 했을 때,
서버에서 돌려준 데이터의 값인result { data: 11 }이console.log()에 찍히는 걸 확인할 수 있다.
Mutation의 데이터인 isLoading을 조건을 걸어준다<button>
{ mutation[1].isLoading ? “Updating…” : ““ }
{query.isFetching ? “fetching...” : “ “}
{name}{query.data}
</button>
[ 코드의 흐름 ]
1. <>Loading</> ( use ~ Query : 읽기전용 )
2. 버튼 클릭 시, updating… ( use ~ Mutation : 쓰기전용 )
3. 바로, fetching… ( use ~ Query : 읽기전용 )
4. 마지막으로 { name } { query.data } : 데이터 값
/* api.js */
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: "https://example.com/api" }),
tagTypes: ["Count"],
endpoints: (builder) => ({
getCount: builder.query({
query: ({ name }) => `count/${name}`,
providesTags: (result, error, arg) => {
console.log(result, error, arg);
return [{ type: "Count", id: arg.name }];
}
}),
setCount: builder.mutation({
query: ({ name, value }) => {
return {
url: `count/${name}`,
method: "POST",
body: { value }
};
},
invalidatesTags: (result, error, arg) => [{ type: "Count", id: arg.name }]
})
})
});
// 자동생성되어 app파일에서 사용할 수 있다.
export const { useGetCountQuery } = countAPI
export const { useSettCountMutation } = countAPI
createApi : 서버와 통신할 url주소를 지정할 때, 사용endpoints : 가져오고, 추가하고, 수정하고, 삭제하기 위해 사용API를 문서로 만든 url을 보며 GET, POST, PATCH, DELETE 등의 메소드를 사용한다. )getCount : 서버캐시의 이름getCount의 build.query를 호출하면, getCount는 서버에서 어떻게 가져오는지의 정책이라고 한다.getCount를 호출하면 반드시, query프로퍼티를 적어줘야한다.query프로퍼티에는 함수가 따라오는데, 함수안에 url프로퍼티가 return값으로 온다.name값을 적어준다. )url의 경로로 서버가 접속해 데이터를 가져와 그 데이터를 getCount -> query의 콜백인자의 캐시에 저장한다.setCount : 서버캐시의 이름getCount에서 사용한 방법과 비슷하다.build.query 대신, build.mutation을 호출한다.createApi가 자동으로 useSetCountMutation이라는 Hook을 생성해준다.query프로퍼티로 함수를 지정하는데, app에서 호출할 때 사용한 값인 name, value을 지정해준다.return값으로는 url, method, body를 지정해준다.useGetCountQuery : 캐시의 데이터를 저장할 인터페이스이다.GetCount가 된 것으로 createApi를 사용하면 자동생된 것이다. )createApi가 서버캐시를 갱신할 수 있는 Hook을 자동으로 만들어 주는 제너레이터라고 기억하자.auto fetching( Hook만들기 설명에 빠진 나머지 것들의 설명 )
데이터 흐름
버튼을 클릭 시 👉🏻 서버에 데이터가 보내짐 POST 👉🏻 서버에 최신의 데이터를 받아옴 GET
: But, 밑의 태그들을 사용하지 않으면 !
서버에 데이터를 잘 보냈지만, 최신의 데이터를 가져오지 못하는 상황이 발생한다.
tagTypes : 해당 api의 Type name을 지정해주어야한다.rtk-query에선 tagTypes가 자동으로 가져오는 역할을 한다.providesTags를 사용하면,getCount로 파생된 useGetCountQuery() Hook이 실행되면 providesTage태그가 만들어 낸 tags와 동일한 서버 Cache가 자동으로 생성되면서 지정한 반환값이 생성된다.invalidatesTags를 사용하면,setCount로 파생된 useSetCountMatation() Hook이 실행이 되면 invalidatesTags태그가 만들어 낸 tags와 동일한 서버 Cache가 자동으로 삭제되고 서버에서 가져오는 절차를 자동으로 밟게 된다.데이터 가져오기
1. Server에 /count라는 1이라는 값이 있다고 가정해보자.
2. Client에 여러개의 컴포넌트 중 하나에 컴포넌트 안에 useGetCountQuery() Hook을 사용한다.
그 말은 즉, GetCount라는 Server쪽 데이터를 저장하는 Cache가 존재한다.
3. 개발자가 useGetCountQuery()를 사용한다는 말은 GetCount라는 Cache를 구독하고 있다는 말을 뜻한다.
변경사항을 받는 것을 의미한다.
4. useGetCountQuery()가 존재하고, 사용하는데 GetCount라는 Cache안에 내용이 비어있을 경우, RTK-query는 자동으로 Server에 접속해 데이터 ( 1 )를 가져와 Cache에 데이터 ( 1 )를 채워넣는다.
5. 그럼, Cache를 구독하고 있던 컴포넌트들이 전부, 새롭게 렌더링되면서 새로운 값을 가져가 화면에 그려준다.
6. 만약, 또 다른 컴포넌트에도 useGetCountQuery()를 사용한다면, 그 컴포넌트에 데이터 값이 자동으로 저장된다.
데이터 업데이트
1. useUpdateCountmutation() Hook을 실행시켜 Server쪽의 데이터를 2로 값을 변경했다고 가정해보자.
Server쪽에 데이터를 바꿨다고 한들, Client데이터도 바뀐다고 확정 지을 순 없다.
2. createApi를 사용해 Hook을 만들 때, 사용할 태그명의 이름을 지정해준다.
3. useUpdateCountMutation()을 실행시키면 서버의 데이터를 변경시키고 난 후 ( 2 ), counter라는 동일한 이름을 가지고 있는 cache들을 모두 지우는 일을 자동으로 해준다.
Client에 저장된 데이터인( 1 )을 모두 지운다.
4. Cache에 모두 빈 데이터가 되면, 서버에서 새로운 값을 가져온다. ( 2 )
5. Cache의 데이터가 바뀌었고, 채워졌으니 Cache를 구독하고 있는 컴포넌트들에 데이터가 자동으로 렌더링되어 채워진다.
6. 그걸 가능하게 해주는 것이 providesTags, invalidatesTags이다.
getCount => providesTags : 제공하다.setCount => invalidatesTags : 무료화 되다.Cache를 무효화한다. )return 값으로 1, 2, 3번 중 하나를 이용해 태그의 어떠한 형태를 지정해줘야한다.result : 서버에서 가져온 데이터 값error : 서버와 통신했을 때, 에러가 나면 에러의 값이 호출arg : 서버에서 useGetCountQuery를 호출할 때 파라미터로 들어간 값providesTage : ( result, error, arg ) => {
return [{ type: ‘Count’, id: arg.name }]
}
Count라는 type에 id는 name이 생긴 것이다.
지금 현재의 상태관리의 보안해야할 점이 있다.
store는 페이지를 새로고침 할 경우state가 날아가는 상황이 발생한다.
이것에 대한 대응 방안으로localStorage또는session에 저장하고자 하는reducer state를 저장하여, 새로고침 하여도 저장공간에 있는 데이터를redux에 불러오는 형식redux-persist을 사용해보자.
redux-persist의 사용법을 테스트하며 공부해보고 정리해서 올려보도록 하겠다.
❗️요즘은 복잡한 구조의 상태관리나 코드가 길어져버리는redux의 상태관리를 많이 사용을 줄인다고 한다.
가볍고, 간결한 Zustand를 사용해 클라이언트를 관리를 하고,
서버는 react-query를 사용해 서버의 상태를 관리하는 방법도 있다.