데이터 fetching
, 캐싱
, 동기화
, 데이터 업데이트
를 보다 쉽게 다룰 수 있도록 하는 상태관리 라이브러리간편한 데이터 관리
간단하고 직관적으로 API 사용 가능
핵심로직
에 집중 가능
🌱 react-query를 사용하지 않은 기존 데이터 패칭 코드
// 1. 데이터 패칭 코드 작성
const getNikaData = async () => {
const data = await fetch("https://musma.net/user/nika")
.then((response) => response.json());
return data;
};
// 2. 데이터를 담을 상태 생성
const [nika, setNika] = useState();
// 3. useEffecct를 이용하여 컴포넌트 마운트 시, 데이터 패칭 후 상태 저장
useEffect(() => {
getNikaData()
.then((res) => setState(res))
.catch((err) => {
console.log(err.response.data)
});
}, []);
🌱 react-query를 사용한 코드
// 1. 데이터 패칭 코드 작성
const getNikaData = async () => {
const data = await fetch("https://musma.net/user/nika")
.then((response) => response.json());
return data;
};
// 데이터 패칭 및 상태 저장
const { data } = useQuery(["nika"], getNikaData);
🌱 Query - useQuery
useQuery
훅으로 진행됨.GET
으로 받아올 때 주로 useQuery
사용.상태
를 불러와 사용할 때 용이비동기
로 작동함.enabled
옵션을 사용할 경우, 동기적으로 사용 가능// 가장 기본적인 형태의 React Query useQuery Hook 사용 예시
const { data } = useQuery(
queryKey, // 이 Query 요청에 대한 응답 데이터를 캐시할 때 사용할 Unique Key (required)
fetchFn, // 이 Query 요청을 수행하기 위한 Promise를 Return 하는 함수 (required)
options, // useQuery에서 사용되는 Option 객체 (optional)
);
Unique Key
를 반드시 필요로 함.function Users() {
const user = useQuery(
['userInfo'], // 'userInfo'를 Key로 사용하여 데이터 캐싱
// 다른 컴포넌트에서 'userInfo'를 QueryKey로 사용한 useQuery Hook이 있다면 캐시된 데이터를 우선 사용
() => axios.get('/users').then(({ data }) => data),
);
if (user.isLoading) return <div> 로딩중... </div>;
// 이렇게 해두면, n번의 로딩 반복 → 에러 상태로 변함
// 에러 상태로 변하기 전, 로딩이 반복될 때 isError를 찍어보면 false로 나옴.
if (user.isError) return <div> 에러: {user.error.message} </div>;
return (
<div>
{' '}
{data?.map(({ id, name }) => (
<span key={id}> {name} </span>
))}{' '}
</div>
);
}
function UserInfo({ userId }) {
const userDetail = useQuery(
// 'userInfo', userId를 Key로 사용하여 데이터 캐싱
['userInfo', userId],
() => axios.get(`/users/${userId}`)
);
if (userDetail.isLoading) return <div> 로딩중... </div>;
if (userDetail.isError) return <div> 에러: {userDetail.error.message} </div>;
return <div> {...} </div>;
}
🌱 Mutation - useMutation
useMutation
훅으로 진행됨.side effect
를 발생시켜 서버 상태를 변경시킬 때 사용POST
, PUT
, DELETE
등에 활용// 가장 기본적인 형태의 React Query useMutation Hook 사용 예시
const { mutate } = useMutation(
mutationFn, // 해당 Mutation 요청을 수행하기 위해 Promise를 반환하는 함수 (required)
options, // useMutation에서 사용되는 Option 객체 (optional)
);
첫 번째 파라미터
는 useMutation의 return 값 중 mutate(또는 mutateAsync) 함수를 호출하여 서버에 side effect 발생시킬 수 있음.function NotificationSwitch({ value }) {
// mutate 함수를 호출하여 mutationFn 실행
const { mutate, isLoading } = useMutation(
(value) => axios.post(URL, { value }), // mutationFn
);
return (
<Switch
checked={value}
disabled={isLoading}
onChange={(checked) => {
// mutationFn의 파라미터 'value'로 checked 값 전달
mutate(checked);
}}
/>
);
}
import { atom } from "recoil";
export const aroomingInfo = atom({
key: "aroomingInfo",
default: '',
});
// selector 생성
import { selector } from "recoil";
import { aroomingInfo } from "../atom/AroomingInfo";
export const aroomingMessage = selector({
key: "aroomingMessage/get",
// ✨ 메세지 목록만 가져오기
get: ({ get }) => {
return get(aroomingInfo).messageList;
},
// ✨ 메세지 목록 업데이트
set: ({ get, set }, newMessage) => {
const newAroomingInfo = {
...get(aroomingInfo),
messageList: [...get(aroomingInfo).messageList, newMessage],
};
set(aroomingInfo, newAroomingInfo);
},
});
// selector를 컴포넌트에서 활용하는 코드
import { aroomingeMessage } from "../selector/AroomingInfo";
import { useRecoilState } from "recoil";
import styled from "styled-components";
import { useRef } from "react";
const AtomArooming = () => {
const [aroomingInfo, setAroomingInfo] = useRecoilState(aroomingMessage);
const inputRef = useRef();
return (
<>
<St.MessageWrapper>
// ✅ 메세지 보여주기
{aroomingInfo.map((message) => (
<div key={message}>{message}</div>
))}
</St.MessageWrapper>
<St.InputWrapper>
<input type="text" ref={inputRef} />
// ✅ 메세지 추가
<button onClick={() => setAroomingInfo(inputRef.current.value)}>
메세지 추가
</button>
</St.InputWrapper>
</>
);
};
export default AtomArooming;
export const aroomingSelecteMessage = selectorFamily({
key: "aroomingSelecteMessage/get",
get:
(selectIdx) =>
({ get }) => {
return get(aroomignInfo).messageList.filter(
(_, idx) => idx === selectIdx
)[0];
},
});
const aroomingLastMessage = useRecoilValue(aroomingSelecteMessage(1));
async
만 붙여주면 비동기로 사용할 수 있다 !비동기 set을 지원하지 않는다 !
만약 비동기로 데이터를 가져와서 그 값을 기준으로 값을 세팅해야 하는 경우에는?!
→ custom hook을 활용할 수 있음. (중간에 custom hook을 배치하여 hook 내부에 비동기 작업을 수행하고 그 값을 set 함수에 넘기는 방법)
// 기존의 selector 코드에 async-await 추가
import { selector } from "recoil";
import { aroomingInfo } from "../atom/AroomingInfo";
export const aroomingMessage = selector({
key: "aroomingMessage/get",
// ✨ async-await 추가
get: async({ get }) => {
return await get(aroomingInfo).messageList;
},
set: ({ get, set }, newMessage) => {
const newAroomingInfo = {
...get(aroomingInfo),
messageList: [...get(aroomingInfo).messageList, newMessage],
};
set(aroomingInfo, newAroomingInfo);
},
});
호출 완료 여부
인지 → 데이터 불러오기를 완료할 때까지 fallback
속성 값으로 넣어준 컴포넌트를 표시해줌.아래 코드처럼 fallback 함수로 Loading 문구를 반환하게 해줬다면, 데이터가 불러와지기 전까지 Loading…
을 띄워줌!
// App.tsx
function App() {
return (
<RecoilRoot>
{/* 여기 */}
<React.Suspense fallback={<div>Loading...</div>}>
<CurrentUserInfo />
</React.Suspense>
</RecoilRoot>
);
}
// App.tsx
function App() {
return (
<RecoilRoot>
{/* 여기 */}
<ErrorBoundary>
<React.Suspense fallback={<div>Loading...</div>}>
<CurrentUserInfo />
</React.Suspense>
</ErrorBoundary>
</RecoilRoot>
);
}
🌱 기존 코드
<Spinner />
를, error 발생 시에는 <ErrorMessage />
를, 그게 아니라면 <DataView />
를 보여주는 …! UI를 어떻게 보여줄지에 집중한 명령형 프로그래밍
방식으로 처리// 기본 App.tsx
function App() {
const {loading, data, error} = useSelector(somethingSelector);
if (loading) {
return <Spinner/>
}
if (error) {
return <ErrorMessage error={error}/>
}
return <DataView data={data}/>;
}
🌱 recoil 활용 코드
<Suspense />
와 <ErrorBoundary />
를 사용하여 무엇 보여줄지에 집중한 선언형 프로그래
밍 방식으로 처리function App() {
const data = useRecoilValue(somethingSelector)
return (
<ErrorBoundary FallbackComponent={ErrorMessage}>
<Suspense fallback={Spinner}>
<DataView data={data}/>
</Suspense>
</ErrorBoundary>
);
}
오 드디어 데이터 관리 토픽이군요,,
제가 가장 겉핥기식으로 알고있어서 자세히 어린이,,😣 입장으로 읽어보았습니다!!
리액트 쿼리에서 "보일러플레이트가 작아 편하게 사용 가능하다" 는 말을 이해하기 위해서 보일러플레이트에 대해 찾아보았습니다.
🌟 보일러플레이트 코드란 : 프로그래밍에서 자주 반복되는 작업에나 패턴에 대한 일종의 표준화된 코드를 말한다고 하네요.
즉 "보일러플레이트가 적다"는 용어는 어떤 기술이나 라이브러리를 사용할 때, 초기 설정이나 기본 구조를 구축하는 데 필요한 반복적이고 지루한 코드가 적다는 것을 의미하는 것으로 이해 했습니다!
그리고 글을 쭉 읽어보니 간단한 API 디자인, 리액트와 통합된 설계 등의 이유로 적다고 말한 것을 이해한 것 같습니다!
🌟 "리액트 쿼리를 사용하면 간편하게 데이터 관리를 할 수 있다에서 서버의 상태를 불러오고, 캐싱하고 업데이트를 간편하게 처리할 수 있다" 이 부분에서 캐싱을 제가 그냥 정말 두루무실하게,,,😣알고 있어서 이번 기회에 더 정확하게 알 수 있었던 것 같습니당.
캐싱이란, 파일 복사본을 캐시 또는 임시 저장 위치에 저장하여 보다 빠르게 액세스할 수 있도록 하는 프로세스이다.
웹 브라우저는 웹 사이트를 더 빨리 로드하기 위해 HTML 파일, JavaScript, 이미지를 캐시하고, DNS 서버는 더 빠른 조회를 위해 DNS 레코드를 캐시하며 시간을 줄인다고 합니다 아항.
즉 다시 본론으로 돌아와 리액트 쿼리는 이러한 캐싱을 사용하여 서버에서 가져온 데이터를 메모리에 저장하고, 이를 활용하여 애플리케이션의 상태를 관리합니다. 즉 리액트 쿼리는 컴포넌트의 렌더링 사이에도 데이터를 유지하며, 이를 통해 빠르고 일관된 사용자 경험을 제공합니다
예시를 찾아 본 결과,, 사용자가 애플리케이션을 사용하면서 여러 번 화면을 전환하거나 다른 동작을 수행할 때마다 서버에서 데이터를 다시 불러오지 않고, 이미 캐싱된 데이터를 활용하여 빠르게 화면을 업데이트할 수 있다는 사아실!
이번 댓글은 저번 스장님 제안대로 아티클에서 생겨나는 질문점들을 위주로 작성해보았습니다! 그 외에도 recoil을 썼었는데 리액트 쿼리에 대해 자세히 예시와 함께 알 수 있게 되어서 굉장히 정말 유익했던 아티클인 것 같습니다! 감사합니다
과제를 하면서 Props drilling
때문에 정말 더러워지고 귀찮아지는 코드를 작성해보니 전역 상태 관리 라이브러리 사용에 대한 필요성이 느껴지고 있었어요,, 하지만 아직 한번도 안써봤다는 이유로 아직 경험해보지는 못했네요 😢 그래서 이번 실습이 더더더더더욱 기대가 됩니다!
데이터 패칭 코드예시를 보면서 react-query를 사용하는 이유가 확!! 와닿았어요 아티클을 보면서 느낀점은 내부적으로 전역 상태 관리를 할 경우에는 Recoil, 비동기 통신을 진행해야 할 경우에는 React Query를 사용하면 정말 편하고 좋을 것 같다는 생각이 들었어요!
recoil과 다른 라이브러리에 대해 더 찾아보면서 알게 된 것인데요, jotai
라는 라이브러리를 사용하면 atom에 key값을 넣어줄 필요가 없어 코드의 길이가 줄어드는 효과도 존재한다고 합니다! 또 이 라이브러리는 최소한의 API를 제공하여 React에서 상태 관리를 간단하게 관리할 수 있다고 하는데요 가볍고 간단하게 사용하기 좋을 것 같아요 코드를 보면서 입문하기 좋겠다는 생각을 했습니다
import { useAtom } from 'jotai';
function Counter() {
const [count, setCount] = useAtom(counterAtom);
function increment() {
setCount((c) => c + 1);
}
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
더 찾아보면서 정말 많은 라이브러리가 있음을 알게 되었는데요 프로젝트 규모나 커뮤니티, 생태계, 성능과 메모리 사용등을 고려하여 가장 적합한 라이브러리를 사용하는 것이 중요할 것 같습니다!
너무 깔끔하게 정리해주셔서 감사합니다!!!
진짜 이해가 너무 잘됐어요,,,,
selector를 한번도 안 써봐서 도대체 무엇을 하는 친구인가에 대한 고민을 많이 하곤 했는데, 예시 너무 명확하게 보여주셔서 감동티비,,입니다!
selector에 set은 비동기를 지원하지 않는다고 하셔서, get은 지원하는가? 그냥 엄청 단순한 생각으로 찾아봤는데, 그냥 우리가 아는 것처럼 Promise
를 리턴하거나 asnyc
를 사용하면 되더군요,,,
비동기 데이터 쿼리부분에 또 다른 옵션이 없나 살펴 봤는데, waitForAll
과 같은 동시성 helper도 있더라구요! waitForAll
을 사용하면 여러 비동기 속성을 한번에 처리할 수 있어서(동시 요청에 대한 병렬 철) 반복문 등을 굳이 사용하지 않아도 된다고 하더라고요,,,?
그래서 찾아보니까 waitFor~
시리즈가 좀 있는 것 같은데, waitForAll 다음으로 많이 쓰는게 waitForNone
인 것 같습니다. 똑같이 동시 요청에 대해 병렬 처리를 해주지만, 결과값을 Loadable 객체로 받아 해들링을 할 수 있습니다.
그래서
watiForAll을 사용할 때,
export const usersSelector = selector({
key: 'usersSelector',
get: () => {
const userIdList: number[] = [1,2,3,4,5];
const users = waitForAll(
userIdList.map((userId: number) => userSelector(userId))
);
return users;
}
});
waitForNone을 사용할 떄
export const usersSelector = selector({
key: 'usersSelector',
get: ({ get }) => {
const userIdList: number[] = [1,2,3,4,5];
const userLoadables = get(waitForNone(
userIdList.map((userId: number) => userSelector(userId))
));
return userLoadables
.filter(({state}) => state === 'hasValue')
.map(({contents}) => contents);
}
});
요렇게 차이가 나고,
전자는 response가 모두 완료된 후 업데이트 상태를 반환하고,
후자는 response가 정상적으로 완료된 요청부터 반환하여, 요청이 먼저 완료된 일부 데이터에 대해 추가적인 UI업데이트를 적용할 수 있다고 합니다.
그리고 Suspense
를 사용하는 게 필수는 아니라고 해서 또 알아봤는데여,
useRecoilValueLoadable()
훅을 이용하면 렌더링 중 상태를 확인할 수 있다고 합니당 겁나 신기 방기,,,
그래서 또 예시가 있는데,
function UserInfo({userID}) {
const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID));
switch (userNameLoadable.state) {
case 'hasValue':
return <div>{userNameLoadable.contents}</div>;
case 'loading':
return <div>Loading...</div>;
case 'hasError':
throw userNameLoadable.contents;
}
}
요로로콤이고,
그리고 react-query처럼 새로운 데이터로 갱신해야 할 떄 사용하는 훅도 있다고,,,
useRecoilRefresher_UNSTABLE()
: selector의 모든 캐시를 제거하고 강제로 다시 selector를 재평가하는 콜백 함수를 만들어 준다고 합니당!!
출처 : https://recoiljs.org/ko/docs/guides/asynchronous-data-queries/
저는 데이터 패칭 라이브러리를 한번도 사용해본 경험이 없는데요, 사용 전후 예시를 들어주셔서 사용하는 이유에 대해 명확하게 알 수 있었어요!!!
특히 저번 주 생각과제를 하면서 찾아보면서 Suspense와 ErrorBoundary를 사용해서 로딩과 에러처리를 할 수 있다는 것을 알게 되었는데요. 아티클에서 예제를 들어주셔서 쉽게 이해를 할 수 있었어요. 세미나에서 다루겠지만 react-query를 안 쓸 이유가 없겠네요!!!
저는 기존에 전역상태관리 라이브러리로 redux를 사용했는데요, 이번에 recoil에 대한 내용을 보고 redux와 recoil의 장단점이 문득 궁금해져서 한번 찾아봤어요!!
redux는 작은 상태 하나를 변경하려고 해도, actions, reducer, type 등 recoil에 비해 코드를 많이 작성해야 하는 번거로움이 있다고 하네요. 하지만 상태값의 변경 사항을 Redux Devtools를 이용해 직관적으로 볼 수 있어 전역으로 관리해야 하는 상태값이 많아질 경우 recoil에 비해 디버깅이 쉽다는 장점도 있다고 해요!!
한번도 다뤄보지 않은 라이브러리인만큼 실습이 굉장히 기대가 되네요!!! 이번 아티클 너무너무 잘 읽었습니다!! 고생하셨습니다 :)
react-query와 recoil에 관한 글 잘 읽었습니다!
데이터 패칭 라이브러리를 아직 적극적으로 사용해 본적은 없는데, 이번 아티클을 통해 추상적으로 알고 있던 개념을 배워갈수 있어 좋았습니다!!
또 recoil을 사용해보았지만, selector를 활용해 비동기로 asynchronous recoil을 사용할수 있다는 점을 배워갈수 있어 좋았습니다!!
suspense, error boundary와 같은 비교적 최근에 나온 기술이 asynchronous recoil에서 적절히 활용될수 있다는 점도 흥미로웠어요 !!
아티클을 읽다가 react-query의 가장 큰 장점 중 하나인 '캐싱'이 어떻게, 얼마나 지속되어 일어나는지 궁금해서 더 찾아봤는데요! 내용 공유합니다~
공식문서에 따르면, React-Query의 캐싱 개념은 stale과 cachetime을 통해 이루어진다고 합니다!
staletime
: 전달 받은 데이터는 react-query의 캐시 자료구조에 저장 되는데, 이 캐시 데이터의 "신선한 상태"가 언제까지 될지를 말해주는 옵션
-> default는 0, 받아오는 즉시 stale하다고 판단하여 캐싱 데이터와 무관하게 계속 fetch를 진행한다.
-> '신선하다'의 의미는 서버에서 조회한 데이터는 그 때 당시의 데이터 snapshot이고, 외부 요청으로 서버 데이터가 변경이 되었다면 해당 되이터는 낡은 데이터가 되었다고 판단한다.
cachetime
: 캐시에 저장된 데이터는 메모리에 저장되게 되는데, 메모리에 저장되어 있는 이 데이터가 언제까지 유지될지를 말해주는 옵션이다.
-> 즉, 캐싱된 쿼리의 결과 값은 계속 유지되는 것이 아니라 시간이 지나면 메모리에서 사라진다.
-> default는 5분
staletime, cachetime을 구분해서 이해하니 react-query의 '캐싱'이라는 개념이 더 잘 이해되는것 같습니다!
아티클 쓰느라 고생 많으셨습니다! 실습도 벌써 기대되네요 Ṑṑ
좋은 아티클 감사합니닷 ♥
아직 해당 라이브러리를 직접 사용해 본적이 없는데도 너무 술술 잘읽혀서 좋았어용 ㅎ.ㅎ
읽으면서 궁금한 것들을 메모해봤는데
러닝커브
와 컴포넌트 마운트
에 대해 좀 더 찾아봤어용. 두 용어 모두 어렴풋이는 추측이 되는데 정확히 어떤걸 똣할지 궁금했습니닷!추가로 ,, 저는 react query를 보며 useQuery에서는 get을 주로 한다길래, 아 query가 질의
라는 뜻이라 맥락이 맞는거같다!라고 생각했습니다. 근데 post put등은 그럼 뭘 사용하지? 하고 읽어보니 useMutation이라고 나와있어서, 흠 왜 mutation이라는 단어를 썼을까? 생각했는데 mutation의 뜻이 돌연변이
라는거 다들 알고계셨나요? 전 이번에 알게되어서 좀 재밌었습니다 ㅎ.ㅎ
좋은 아티클 감사해용!!
간단하고 쉬운 예시로 리액트쿼리를 설명해주셔서 사용방법에 대한 이해가 완벽하게 다가왔습니다. 저는 항상 왜를 고민하는 만큼 리액트 쿼리를 왜 사용해야하지? 나는 지금 어떤 불편함을 가지고 있고 리액트 쿼리는 어떤 불편함을 해결하기 위해 존재하지? 라는 생각을 하며 왜에 대해 공부를 해보았습니다.
대부분 기존 상태 관리 라이브러리는 클라이언트 상태 관리는 적합하지만 비동기 서버 상태 작업은 그다지 적합하지 않다고 합니다. 그 이유는 서버 상태는 아래와 같은 ”다름”을 가지고 있기 때문인데
개발자가 통제할 수 없는 위치에 존재한다.
데이터 페칭과 업데이트에 대해 비동기 API를 요구한다.
소유권을 공유하며 다른 사람에 의해 변경될 수 있다.
잠재적으로 out of date가 발생할 수 있다.
이러한 다름을 해결하기 위해 React query를 사용하고 리액트 쿼리를 쓰는 이유를 정리해보았습니다!
복잡한 여러줄의 코드를 제거하기 위해 단 몇줄의 React Query를 사용할 수 있고 애플리케이션 유지 보수를 쉽게 만들며 유저에게 더 빠르고 더 반응성이 뛰어난 느낌을 줄 수 있고 대역폭을 절약하고 메모리 성능을 높일 수 있습니다.
더 알아본 것들이 있지만 댓글이 길어져 링크로 남기겠습니다!
https://tanstack.com/query/v3/docs/react/overview
https://velog.io/@moonshadow/%ED%81%B0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-React-Query%EC%9D%98-7%EA%B0%80%EC%A7%80-%ED%8C%81
react-query와 asynchronous recoil에 대해 개괄적으로 비교하며 이해해볼 수 있어서 정말 유익했습니다! 둘 중 뭐하나도 제대로 이해해본 적이 없는 제게 딱 필요한 정보들...
아직 어려운 개념들이 정말 많지만 저는 32기 합세때 recoil selector 관련해서 애를 많이 먹었던 터라 이 부분에 대해 공유하고 싶어졌어요. 당시 서버에서 페칭해오는 데이터를 A페이지에서는 가져와서 조작하고, B페이지에서는 가져와서 보여주기만 하는 작업이 필요했는데요, B페이지에서 보여지는 state의 초기값 자체를 서버에서 불러와야하는 태스크가 있었고 이를 selector로 해결한 경험이 있습니다!
export const cartDataState = atom<CartData>({
key: 'cartDataState',
default: selector({
key: 'cartDataSelector',
get: async () => {
const {
data: { data },
} = await getCartData();
return data;
},
}),
});
export const totalQuantitySelector = selector({
key: 'totalQuantitySelector',
get: ({ get }) => {
const cartData = get(cartDataState);
return cartData.cartProducts.length;
},
});
cartDataState의 default 값 자체를 아예 서버 통신을 해서 불러오는 거예요! 더불어 하단의 totalQuantitySelector는 이미 생성된 recoil state를 이용해서 selector로 또다른 state를 만드는, 아티클에서 언급한 여러 상태(atom)를 조합한 새로운 상태
의 예시라고 볼 수 있겠습니다.
요롷게 실례로 경험은 해봤지만 정확히 recoil에서 selector의 역할이 뭐라고 정의할 수 있을까-에 대해서 궁금해져서 알아봤는데요, 기본적으로 여러 atom을 가공하는데에 있어서 atom을 직접 변경하지 않고 불변성을 유지하기때문에 side effect를 확실히 줄일 수 있는 방법입니다. 또한 참조한 atom이 변경되면 selector로 가공된 값도 최신화가 되기 때문에, 복잡한 의존성 관리에 효과적이고 다른 상태의 변경으로 인한 리렌더링을 최소화할 수 있는 방법이라고 합니다.
React를 사용하기 시작한지 얼마 되지 않았는데요.. state 관리에 너무나도 어려움을 겪고 있었습니다. 과제를 하면서 끊임없는 state 전달과.. props drilling을 경험하고 아차 싶기도 했었어요. 이런 문제를 상태관리 라이브러리를 통해 해결할 수 있다는걸 어렴풋이 알고는 있었는데 아티클을 읽으면서 더 잘 이해할 수 있었던거 같아요!
하지만 제 이해력과 지식이 부족한 탓에 아티클을 읽고도 여전히 상태관리 라이브러리가 state를 관리한다는건 알겠지만.. 그걸 써서 좋은 점이 무엇이지? 왜 사용하는거지?에 대한 의문을 떨쳐버릴 수가 없었답니다. 이번 기회에 완벽하게 알아가고자 더 찾아보았는데 확실히 이해할 수 있었던 코드가 있어 가져와보았습니다.
function App() {
return (
<RecoilRoot>
<CharacterCounter />
</RecoilRoot>
);
}
const textState = atom({
key: 'textState', // unique ID (with respect to other atoms/selectors)
default: '', // default value (aka initial value)
});
function CharacterCounter() {
return (
<div>
<TextInput />
<CharacterCount />
</div>
);
}
function TextInput() {
const [text, setText] = useRecoilState(textState);
const onChange = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
}
바로 요렇게..! TextState를 TextInput( )에게 props로 직접 전달하지 않았는데 TextInput function에서 useRecoilState로 textState를 불러오는걸 볼 수 있지요?! 플로우를 한 눈에 보니까 이해가 더 잘 되는거 같아서 코드를 가져와보았습니다!
그렇다면 리코일 사용의 이점이 무엇인가?가 또 궁금해져 더 찾아보았는데요..! 리덕스도 리액트의 상태관리 라이브러리로 많이 사용하긴 하는데 리덕스의 단방향 흐름은 상태를 디버깅하기 쉽게 해주지만, 초기 세팅(action, reducer, selector, store)을 하는게 번거롭고 많은 코드를 추가하도록 한다해요. 최근에는 toolkit이라는 라이브러리가 나와 코드를 줄여주고 간단해졌다고는 하지만 리액트와의 궁합은 좋지 않을수도..있다고 합니다. 리코일은 리액트를 만든 페이스북에서 만들었고, API, 의미, 동작을 최대한 리액트스럽게 유지해 리덕스의 불편한 점을 개선하였다고 합니다.
리코일에 대해 더 알아보니 많이 공부해서 앞으로 사용해보고 싶은 생각이 들어요..! 그 시작을 요 완벽한 아티클로 시작할 수 있어 너무너무 감사드려용♡
huks,, 둘 다 들어는 봤지만 한 번도 사용은 못해봤던 것들인데 이렇게 깔끔하게 정리 해주셔서 이에 대해 공부해볼 수 있는 시간을 가질 수 있었던 거 같습니다!
이번 합세 때 react-query를 사용하기로 하여, 이에 대해 공부를 했어야 했는데 덕분에 react-query에 대해 어느 정도 감이 잡히게 되었습니다 :)
react-query는 예시 코드를 보니 너무 간결하게 정리가 되는 거 같아서 빨리 써보고 싶다는 생각이 들었습니다!! 특히, loading 처리하는 로직을 state를 이용해서 구현을 하곤 했었는데 이를 isLoading이라는 것을 통해 쉽게 구현할 수 있는 점도 흥미로운 거 같습니다 :) (error도 마찬가지로요!)
react-query key 값으로 배열이 들어가는 거 같기도 하고, 문자열이 들어가는 거 같기도 해서 react-query key 값의 특징은 무엇이 있을 지 궁금해졌습니다!!
참고자료
https://velog.io/@rgfdds98/React-Query-queryKeys
query key의 특징은 아래와 같습니다.
1. key를 이용하여 query caching을 한다.
2. key로는 문자열, 문자열의 배열 혹은 중첩된 객체가 올 수 있다.
3. key는 query data에 고유하여야 한다.
규칙으로는 key는 반드시 배열이어야 한다는 것입니다! v4 부터 변경이 되었다고 해요.
작성 규칙으로는 아래와 같아요.
1. string key를 먼저 작성.
2. 여러 개인 경우 고유한 key를 작성.
3. query에 필요한 추가 정보는 key 뒤에 작성.
query key는 해싱이 되기 때문에
만약
이와 반대로
마지막으로,
이런 식으로 queryFn 안에서 사용되는 todoId는 key 값으로 들어가야 한다고 합니다!!
이번 아티클을 통해 react query와 recoil에 대해 처음으로 배워 보는 시간을 가질 수 있었는데 너무나도 유익한 거 같습니다!!
좋은 아티클 작성해주셔서 너무나도 감사드리고, 고생하셨습니다!!