프로젝트를 하면서 리액트 쿼리를 쓰며 많은 고민한 부분과 해결과정을 정리해보려고 한다.
초기 랜더링
아래와 같은 예약 사이트가 있다고 하자
아래 화면은 리액트 쿼리의 서버 데이터를 받아 랜더링 된 화면이다.
받은 데이터는 일단 다음과 같다.
"updated": datetime string, // "2024-09-07T09:37:12.881Z"
"start": datetime string,
"end": datetime string,
"attendees": [
{
"email": "mooboongofficial@gmail.com",
"displayName": "{\"id\":1,\"type\":\"personal\",\"name\":\"관리자\"}",
"responseStatus": "accepted",
"comment": "{\"id\":1,\"type\":\"personal\",\"name\":\"관리자\"}"
// comment 가 참석자 json
}
],
"recurrence": boolean
}
]
이런식으로 가공되지 않은 순수 데이터이다.그렇다면 이걸 어떻게 써야 내가 원하는 형식에 맞추어 쓸 수 있을까?
바로 query의 slect를 이용하면 된다!
참고로 반환된 데이터에는 영향을 주지만 쿼리 캐시에 저장되는 데이터에는 영향을 주지 않는다.
query select를 통한 데이터 변형
const { data, isLoading } = useQuery({
queryKey: ['reservation', formattedStartWeek],
staleTime: 1000 * 60 * 3,
queryFn: ({ signal }) => loadData({ formattedStartWeek, token, signal }),
select: (data: any) => reservationMapConverting(data, startOfTheWeek),
});
모달이 열릴 때 생긴 문제점
처음 랜더링까지는 좋았으나 그 이후가 문제였다...
아래와 같이 드래그 한 후에 확정되지 않은 클라이언트 데이터를 보여주어야 하는 것이었다
처음에는 쿼리를 state에 넣어서 해결할려 했으나 그렇게 되면 불필요한 행동을 한번 더 하게 되는 것이다
굳이 캐시에 저장되어 있는 데이터를 state저장소에 한번 더 넣는 비효율적인 일임과 동시에
데이터의 동기화를 추가로 신경 써야 하는 문제가 생긴다.
캐시 - 서버데이터
select - 클라이언트 데이터
위와 같이 볼 수 있다. 그렇다면 낙관적 업데이트를 이용해서 해결할 수 있다.
캐시에 임의의 데이터를 넣어 줌으로써 클라이언트 데이터를 변형시켜주는 것이다.
낙관적 업데이트를 통한 임시 클라이언트 데이터
export function mergeClientReservation(
strIdx: number,
endIdx: number,
items: Pick<
reservationInterface,
| 'eventId'
| 'summary'
| 'updated'
| 'start'
| 'end'
| 'attendees'
| 'recurrence'
>[],
) {
const formattedStartWeek = useDateStore.getState().formattedStartWeek;
//const summary = `${queryClient.getQueryData(['userinfo']).name} 개인연습`;
//console.log('!!merge', summary);
const strTime = items[strIdx].start;
const endTime = items[endIdx].end;
const targetData = {
eventId: '', // 새로 추가하는 데이터의 경우 빈 값
summary: '미예약', // 예약명 title
updated: new Date().toISOString(), // ISO 8601 형식의 문자열로 변환
start: strTime,
end: addThirtyMinutes(endTime), // 30분 추가된 시간
attendees: [{}], // 참석자 배열
recurrence: false, // 반복 예약 여부
};
queryClient.setQueryData(
['reservation', formattedStartWeek],
[
...(queryClient.getQueryData(['reservation', formattedStartWeek]) as any),
targetData,
],
);
}
여기서 주의할 것은 가공되지 않은 데이터를 추가해 주어야 한다.
모달이 닫혔을 때 생긴 문제점
그리고 이제 예약을 하지 않고 모달이 닫혔을 때는 아래와 같이 원래 상태로 돌아가야 한다.
이건 어떻게 해야 할까? 의외로 간단할 수 있다 바로 queryClient.invalidateData를 써주어 캐시를 초기화 시켜주는 것이다
그러면 서버 데이터에서 원래 데이터를 받아오기에 사라지게 된다.
하지만 문제가 있다 바로 서버를 거치기 때문에 랜더링이 늦어진다는 점이다 그렇다면 이걸 어떻게 해결할 수 있을까?
바로 setquery를 통해 초기 캐시 데이터로 만들어 버리는 것이다.
setquery를 통한 원상 복구
export function deleteClientReservation(restoreData: reservationLoadType[]) {
const formattedStartWeek = useDateStore.getState().formattedStartWeek;
queryClient.setQueryData(['reservation', formattedStartWeek], restoreData);
}
이렇게하면 서버를 거치지 않기기 때문에 빠른 랜더링을 할 수 있다.
++ 추후 수정 (중요)
낙관적 업데이트 외에는 setquerydata의 사용을 권장하지 않는다.
처음부터 클라이언트 데이터와 서버 데이터를 분리해서 구조를 짜는게 맞다.
뒤집어 엎고 다시 구현할 계획이다...
아래 참고
https://be-senior-developer.tistory.com/241
[React] Query의 올바른 사용에 관해
목차 먼저 react query를 들어가기 전에 useState의 남용 문제를 살펴보아야 한다.useState의 남용서버로부터 데이터를 불러오고(카테고리 리스트) 사용자가 카테고리를 필터링할 수 있도록 하는 코
be-senior-developer.tistory.com
'프로젝트 > 예약 사이트 프로젝트' 카테고리의 다른 글
[프로젝트] 랜더링 최적화: 전역 상태의 문제? (0) | 2024.10.05 |
---|---|
[프로젝트][React] 예약 사이트 기능 구현하기 (when to meet) (0) | 2024.09.12 |