-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[선물 등록, 선물방 편집] 리액트 쿼리 관련 데이터 실시간 업데이트 문제 해결 #339
Conversation
|
안녕하세요 !!
빠르게 반영 되는 영상 화면을 보니 속이 편안하네요 ㅎㅎㅎ 수정 및 반영 너무 감사드립니다 !!! 저도 덕분에 react-query 다시 한번 더 코드 볼 수 있게 됐어요 !!! 😊 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
콘솔이나 주석 등 간단한 리뷰 남겼습니다 !
- 화살표 함수가 선언식으로 변경 된 부분에 대해서도 남겨서 해당 부분 답글 부탁드립니다 !
function onClickBtn() { | ||
//빌드 에러 해결용 콘솔 | ||
console.log(fileName); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
console.log 부분도 해결이 완료 됐다면 한번 싹 지우는것도 좋을 거 같아요 !!! 콘솔은 이 부분만 코리 남기겠습니다 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리마인드 감사합니다~!!
mutation.mutate({ | ||
roomId: itemInfo.roomId, | ||
name: name, | ||
cost: cost, | ||
imageUrl: imageUrl, | ||
url: link, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
흠.. 뭔가 이 객체를 따로 관리 할 수 있는 방법이 없을까요 ??? 상수화 하여 가독성이 좋아지도록이요!! 이렇게 직접적으로 드러내는게 더 좋은 것 같다면 그것도 오케이인데, if 부분과 else 부분 모두 이 코드가 중복 되어지는거 같아서요!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
조건문의 경우 presignedUrl을 막으면서 임시로 추가된 것이어서 다른 이슈 해결하면서 중복 제거했습니다! 말씀해주신 대로 상수화를 통해 가독성을 개선하면 훨씬 좋을 것 같네요 감사합니다🥰 context 브랜치에서 상수화 가능할 것 같아서 다른 PR에서 적용해보겠습니다:)
onClick: () => void; | ||
onClick: VoidFunction; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오 !!!! 매우매우 굳입니다 ㅎㅎㅎ 이거 그때 합숙때 알게 된 것이군요 !! 저도 반영해야겠어요😊
const [isAdVisible, setIsAdVisible] = useState(myGiftData.length <= 1); | ||
// const [isAdVisible, setIsAdVisible] = useState(myGiftData.length <= 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주석 부분 삭제해도 좋을 거 같아요 ! 😊
export const useDeleteMyGift = (roomId: number) => { | ||
export function useDeleteMyGift(roomId: number) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
화살표 함수가 아닌 선언식의 형태로 함수를 작성한 이유가 궁금합니다 !!
초반 컨벤션을 정할때 화살표 함수로 지정했던 거 같아서요 !! react-query에선 특별한 이유가 있나요 ~??
다른 부분에도 모두 동일하게 변동 되어 있길래, 해당 부분에만 리뷰 남깁니다 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이것은 그저 저의 착각으로 인한 실수입니다!ㅠㅠ 작업하다가 갑자기 컨벤션 착각해서 막 고쳤는데 알려주셔서 감사합니다✨
// 주소가 path variable 형태이기 때문에 location.search로 쿼리 파라미터를 가져올 수 없어 | ||
// 룸아이디가 null로 받아지고 있었고, 이로 인해 400에러가 발생해서 임의로 수정했습니다 | ||
// const location = useLocation(); | ||
// const searchParams = new URLSearchParams(location.search); | ||
|
||
// const roomIdString = searchParams.get('roomId'); | ||
|
||
const params = useParams(); | ||
const roomIdString = params.roomId; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pr 내용 봤습니다 !! : ) 해당 부분 주석 삭제해주셔도 될거 같아요 !! ㅎㅎ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
대박 너무 깔끔한 정리와 pr내용 덕분에 쿼리 클라이언트의 invalidateQueries의 선택 기준에 대해서 다시 알게 되었네요 🥰
너무 고생많으셨어요! 왜 query와 mutation을 따로 나눴을까? 궁금해져서 강의를 듣고 찾아보니,
왜 query와 mutation을 따로 나눈 이유는 mutation은 사이드 이펙트를 일으키는 방법이고, 반면, 데이터를 조회하는 것은 사이드 이펙트를 발생시키지 않습니당. React Query는 이 둘의 개념을 나누고, 사용하는 방법도 다르게 구성해두었더라고요!
"query는 선언적으로 사용할 수 있으며, mutation은 명령형으로 밖에 사용하지 않도록 말이다."
그렇기에 query는 마운트 될 때 즉시 실행되어 데이터를 조회합니다.
하지만 mutation은 서버 데이터를 변형시키는 것이기 때문에, 마운트 될 때 실행되면 안되죠! 그래서 useMutation 훅을 사용해 mutation 객체를 가져오고, 명령적으로 필요할 때 호출하도록 하고 있다고 합니다..! 대박,,너무 재밌네요🧐
그래서 제가 카공하다 지민님께 드린 질문 "mutation은 서버 데이터를 변경하는건가..?" 에 대한 정확한 답변으론
: mutation이 실행된 이후 서버의 데이터는 변경되었지만, React Query의 mutation 실행 이후 클라이언트의 서버 데이터는 변경 되지 않은 상태라 이해하면 될 것 같아요. 즉 클라이언트의 서버 데이터라함은, query로 조회해온 서버 데이터가 클라이언트의 상태로 넘어온 것
이니까 저희가 mutation 이후 어떻게 화면에 데이터가 업데이트 되는지를 따로 설정해줘야 했고 지민님은 invalidateQureis로 업데이트 되게 해준 것 같네요!
관련 좋은 글도 공유해드립니다. 정말 이해가 쏙쏙 되니 꼭 읽어보세요!
React Query에서 mutation 이후 데이터를 업데이트하는 4가지 방법
const queryClient = useQueryClient(); | ||
|
||
const mutation = useMutation({ | ||
mutationKey: [MY_GIFT_QUERY_KEY[0]], | ||
mutationFn: deleteMyGift, | ||
onSuccess() { | ||
console.log('선물 삭제 성공'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mutationKey 제거 대박 너무 좋아요!
제가 최근에 강의를 봤는데 사용자에게 변경사항을 실제로 보여줄 땐 3가지 방법이 있다합니다!
- 낙관적인 업데이트 : 서버 호출이 잘될 거라 가정하고, 잘 안됐을 경우 되돌리는 방식
- 서버에서 받은 데이터를 가져오는 겁니다 : mutation을 실행 할 때 업데이트 된 데이터를 가져와 리액트 쿼리 캐시를 업데이트하는 방식
- 마지막으로 관련 쿼리를 무효화하는 방식 : 쿼리를 무효화하면, 클라 데이터를 서버의 데이터와 동기화하기 위해 서버에 재요청이 발생하는 방식
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
대박.. 이 리뷰 너무너무 좋아요 이렇게 3가지 방식이 있고, 저희는 3번째 방식으로 진행하고 있다는 사실이 이해가 쏙쏙 된거 같습니다 !!!!! 너무너무 감사해요 너무너무 좋아요 !!!!!!!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와,, 정말 리액트 쿼리 설명 너무 이해 쏙쏙 되게 깊이 있게 해주셔서 감사해요!! 덕분에 더 깊은 내용까지 이해하게 되었습니다🫶 좋은 레퍼런스까지,, 정말 감사해요!
const mutation = useMutation({ | ||
mutationFn: postNewGift, | ||
onSuccess: () => { | ||
console.log('roomId, targetDate', roomId, targetDate); | ||
console.log('선물 등록 성공!!'); | ||
queryClient.invalidateQueries({ queryKey: [MY_GIFT_QUERY_KEY[0], roomId] }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아! 혹시 쿼리클라이언트의 setQueryData 가 있던데 invalidateQuries를 사용한 이유가 궁금해서 또 찾아보니
setQueryData : 낙관적 업데이트(위에서 언급한 1번)에 가장 적합하거나 서버 상태에 영향을 주지 않고 일시적으로 캐시를 재정의해야 하는 경우에 가장 적합하다 하네요!
invalidateQueries : 변형이나 기타 중요한 변경 후 클라이언트 캐시가 서버의 현재 상태를 반영하도록 하는 데 더 적합하다하여 아항 이럴경우 invalidateQureis를 사용해야함을 깨달았습니다..! 네이밍이 뭔가 setQueryData도 될 것 같아서 찾아보았습니다 ! ㅎㅎ
tanstack쿼리 클라이언트 공식문서
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게나 섬세한 리뷰 너무나 소중하네요❣️ setQueryData에 대해서는 제대로 알지 못했는데 말씀해주신 대로 더 적절할 것 같네요! 수정해보겠습니다:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰해주신 부분 참고해서 POST 관련된 부분들은 setQueryData
로 수정했습니다 감사합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
클라이언트 캐시가 선물이 등록된 상태의 서버 상태를 반영해야 하는 상황이라 2번이 더 적합한 것 같더라고요..! setQueryData
는 실시간 업데이트가 안 되는 문제 사항을 발견해서 모두 invalidateQueries
로 돌려놓았습니다🥲 리액트 쿼리의 데이터 업데이트 방법에 대해 더 공부해보겠습니다!
이슈 넘버
구현 사항
선물등록 🎁
선물방 편집 ✍️
Need Review
1. 불필요하게 설정되어 있던 mutationKey 값 제거 및 수정된 데이터 자동으로 반영되도록 수정
mutationKey
를 지정하지 않더라도invalidateQueries
를 활용하면 특정 쿼리를 무효화해서 해당 쿼리를 다시 부를 때 refetch가 이루어지도록 수정할 수 있어,useMutate
함수에 설정된 불필요한mutationKey
들을 모두 제거했습니다.invalidateQueries
와refetchQueries
모두 실시간 업데이트를 위해 사용할 수 있으나, 불필요한 네트워크 호출을 줄이기 위해 invalidateQueries로 결정하였습니다.refetchQueries
는 호출되는 즉시 refetch가 작동되는 반면,invalidateQueries
는 쿼리 키 무효화만 우선 진행하기 때문입니다. 다만invalidateQueries
사용 시 무효화할 쿼리에 대한 쿼리키를 지정할 때, 요소가 다르면 제대로 반영이 안 되므로 꼼꼼히 체크해야 합니다!선물홈, 선물방 편집에 쓰이는 delete 관련 서버 통신 코드를 모두 같은 방식으로 수정했습니다.
2. onSuccess 중복 코드 삭제
3. 선물방 편집 서버통신 관련 사소한 수정사항
선물방 편집 화면에서 멤버 삭제 실시간 반영을 위해 사소한 수정사항 세 가지가 있었는데, 혹시 수정사항으로 인해 다른 뷰에 영향이 있을까봐 자세히 남깁니다!
선물방 참여 멤버 리스트가 뜨지 않는 이유가 map을 적용하는
roomMemberData
가roomWholeMemberData.members
로 되어 있었는데, 들어오는 데이터 형태과 다른 것 같아const roomMemberData = roomWholeMemberData;
로 수정했습니다.선물방 개설자 프로필 이미지 변수명이 들어오는 데이터 타입과 이름이 살짝 달라 이미지가 안 뜨는 이슈가 있어 아래와 같이 수정했습니다
useLocation()
은 현재 주소에 쿼리 파라미터(물음표 있는 형식)가 있는 경우location.search
를 사용해서 파라미터 값을 가져오는 것인데, 저희의 주소값은 Path Variable형식이어서 주소값을 가져오고 있지 못했습니다. 따라서useParams()
를 이용해roomId
를 가져오는 방식으로 수정했습니다~🐝 이번 PR의 리액트 쿼리 관련된 고민들에 대한 자세한 내용은 추후 아티클 작성 후 링크 첨부하겠습니다~!
📸 스크린샷
⬇️ 내가 등록한 선물 수정된 화면
Screen.Recording.2024-02-10.at.8.30.36.PM.mov
⬇️ 선물방 편집 수정된 화면
Screen.Recording.2024-02-10.at.7.02.40.PM.mov
Reference
Path Variable vs Query Parameter