개발자
안녕하세요. react-query에서 useQuery와 useMutation을 쓰는 도중 의문이 생겨서 질문드립니다. 보통 Read 작업은 useQuery를 쓰고, Create/Update/Delete 작업은 useMutation을 쓰라고들 말씀하시는데요, 특정 페이지 렌더링 시 자동 fetch하지 않고, 사용자 액션 발생 시에만 fetch 요청을 하는 경우 useQuery가 반환하는 refetch 함수를 쓰거나 useMutation가 반환하는 mutate 함수를 사용하면 구현이 가능하더라구요. (제가 테스트한 코드는 gist에 있습니다.) 여기서 궁금한 건 Read 작업임에도 불구하고 useMutation으로도 구현이 가능하다면 굳이 위 상황에서 useQuery로 구현했을 때의 이점이 있을까요?
답변 3
인기 답변
안녕하세요! 위 케이스와 유사한 질문에 답한 기억이 있는데, 참고해보시면 좋을 것 같아요 - https://careerly.co.kr/qnas/1165 우선 useQuery와 useMutation의 일반적인 차이는 알고 계신 것 같아서 생략하겠습니다. 근본적으로 둘은 외부 요청을 래핑한 개념이라는 공통점이 있습니다. 하지만 메인테이너의 말을 빌리자면 둘의 차이는 선언적 vs 명령적 차이라고 합니다. (참고: https://tkdodo.eu/blog/mastering-mutations-in-react-query#differences-to-usequery) 즉, useQuery (선언적)를 쓴다면 react-query에서 제공하는 여러 기능들 (ex. refetchOnWindowFocus, enabled)이 "알아서" 돌아가지만, useMutation (명령적)의 경우는 이런 기능들이 "알아서" 돌아가지 않습니다. 이유는 useQuery의 경우 선언하는 시점에 observer가 생성되지만 useMutation은 mutate을 사용하기 전까지 observer가 생성되지 않습니다. 또한 useMutation의 observer는 mutate가 돌아갈때마다 이전 observer를 unsubscribe 합니다. 다시 질문에 답하자면, 질문자님의 코드에서 데이터를 요청하는 시점은 사용자가 버튼을 클릭했을 때입니다. 그러면 사실상 mutate이나 refetch나 API 요청을 한번 래핑한 함수라서 동일하게 작동할겁니다. 다만, useQuery의 경우 결과가 캐싱이되어서 다른 곳에서 사용해야 할 경우 동일한 키를 제공해주면 공유가 가능합니다. (결과뿐만 아니라 로딩 상태, 에러 상태 등도 같이 공유됩니다) 하지만 refetch/mutate로 가져온 데이터를 굳이 캐싱해서 사용할 이유가 없고 위에 성원님이 말씀하신 컨벤션 정도를 제외한다면, 질문자님의 질문처럼 useQuery를 사용하는 이점이 퇴색될 것 같습니다. 오히려 useQuery를 사용해서 "알아서" 돌아가는 기능들 때문에 의도치 않은 이슈가 발생할수도 있어보이네요. ex. 의도치 않은 refetchOnWindowFocus 같은게 돌아서 onSuccess가 또 호출되는 경우? 정도가 생각나네요. 이것에 대해서 찾아보다보니, 이미 깃허브 이슈에서 비슷한 논의를 한적이 있어서 이것도 링크 남길게요. 질문자와 TkDodo님의 답변을 참고하시면 좋을 것 같습니다 :) - https://github.com/TanStack/query/issues/2304#issuecomment-1011598184
적어주신 코드에서는 useQuery와 useMutation의 동작이 일치해서 큰 차이는 없지만 조금 확장해서 생각하면 2가지 정도가 있을 것 같아요. 1. data를 read하는 행위를 useQuery로 유지한다 - 언급해주신 대로 Create/Update/Delete 작업은 useMutation을 사용하는게 일반적이기 때문에 read작업을 useQuery로 사용하면서 컨벤션을 유지할 수 있는 장점이 있을 것 같습니다. 2. useQuery를 재사용 할 수 있다 - enable 값을 옵셔널하게 가져가면 다른 컴포넌트에서 용도가 다르게 같은 hook을 사용할 수 있겠네요.
익명
작성자
2022년 12월 10일
안녕하세요, 성원님 답변 감사드립니다. 확장성에 대해서는 미처 생각하지 못했었네요ㅎㅎ 사실 Read에 대해서는 항상 일관적으로 useQuery만 사용하고 있었고, 문득 useMutation과의 차이가 궁금해져서 여기저기 찾아보았는데, 어떤 이점도 찾아낼 수 없어서 조금 답답했었습니다. 성원님 덕분에 좋은 인사이트 얻어갑니다! :D
안녕하세요! 일반적으로는 Read 작업에 대해서는 useQuery, Create/Update/Delete 작업에 대해서는 useMutation을 사용하는 것이 추천되지만, 코드에서와 같이 useMutation을 사용하여 데이터 조회를 하고 refetch나 mutate 함수를 사용하여 구현하는 것이 가능합니다. 그러나 useQuery는 캐싱과 같은 기능을 제공하므로, 같은 데이터를 여러 번 불러올 경우에는 useQuery를 사용하는 것이 더욱 효율적입니다. 이 경우, useQuery를 사용하면 해당 쿼리가 이미 캐시되어 있는 경우 캐시된 데이터를 반환하고, 그렇지 않은 경우에만 서버에서 데이터를 가져옵니다. 반면 useMutation은 캐싱 기능을 제공하지 않기 때문에 매 요청마다 서버에서 데이터를 가져오게 됩니다. 또한, useMutation은 요청을 보내는 기능 외에도 새로운 데이터를 생성하고 기존 데이터를 업데이트 또는 삭제하는 기능을 제공합니다. 이런 이유로 Create/Update/Delete 작업에 대해서는 useMutation을 사용하는 것이 바람직합니다. 따라서 위 코드에서는 데이터 조회 작업에 대해서도 useMutation을 사용할 수는 있지만, 일반적으로는 useQuery를 사용하는 것이 좋습니다.
커리어리 AI 봇의 답변을 평가해 주세요!
지금 가입하면 모든 질문의 답변을 볼 수 있어요!
현직자들의 명쾌한 답변을 얻을 수 있어요.
이미 회원이신가요?
비슷한 질문 1
Q. React Query 데이터 재조회 방식
안녕하세요. 이번 프로젝트에서 React Query를 처음 써보게 되었는데요. 데이터를 조회해올 때, 페이지와 검색어가 바뀔 때마다 API Call을 하는 것이 목적입니다. 제가 아는 방식은 아래와 같이 2가지인데, 두 방식이 어떤 차이가 있고, 어떤 선택이 좋은 선택인지 분간이 잘 되지 않아서 질문드립니다. (폰으로 작성중이라 코드가 좀 가물가물해서 문법이 약간 틀렸을 수도 있습니다!) ---------------------------------------- 1. useQuery만 사용 useQuery(['someList', page, keyword], () => { const params = { page, keyword, }; fetchList(params); }, { onSuccess: () => { // do something } }); 2. useQuery, useMutation, useEffect 사용 useQuery('someList', () => { const params = { page, keyword, }; fetchList(params); }, { onSuccess: () => { // do something } }); const { mutate } = useMutation(fetchList, { onSuccess: () => { queryClient.invalidateQueries('someList'); } }); useEffect(() => { const params = { page, keyword, }; mutate(params); }, [page, keyword]); ---------------------------------------- 사실 제가 아는 useMutation의 용도(생성, 수정, 삭제)와 코드 사이즈 측면을 생각해서 실제론 1번 방식을 사용하고 있기는 한데, 동작상 두 방식의 정확한 차이를 모르겠어서 이렇게 질문드립니다. 혹시 두 방식 모두 일반적이지 않다면, 일반적인 방식도 함께 알려주시면 정말 감사드리겠습니다!
안녕하세요! 위에 올리신 코드를 기반으로 gist를 만들어보았는데요. https://gist.github.com/json9512/8bc447d0c7206ca73daca5da79531e59 1번 방법이 좀 더 일반적인 방법인 것 같습니다. 아시다시피 useMutation은 CUD을 위한 훅이고 useMutation으로 GET 요청을 하는 것은 설계 용도를 벗어나는 사용법이 아닌가 싶습니다. 우선 1번과 2번의 차이는 1번의 경우 param이 바뀔때 서버로 요청을 1번만 보내게 됩니다. 2번의 경우는 2번 보냅니다. mutate를 할때 fetchList로 한번 보내고 이후 "someList" 키가 invalidate 되면서 useQuery가 한번 더 fetchList를 호출합니다. 이론적으로 useQuery는 여러 컴포넌트에서 호출해도 같은 값을 공유하지만, useMutation의 경우는 값을 공유하지 않는 것으로 알고 있습니다. 다만, mutation의 상태를 저장/관리하기 위해 MutationCache라는 곳에 mutation 자체와 관련 데이터를 따로 저장하는데요. 이걸 감안한다면 2번 방법은 메모리도 더 많이 쓰는 방법이 되는 것 같네요. 정리를 하자면: - 1번 방법이 일반적인 방법인 것 같습니다. query key에 page, keywords를 넣어서 관리해도 무방해요. - 2번 방법이 메모리 차원에서 더 비효율적입니다 - 2번 방법은 네트워크 요청을 2번 보냅니다 - 2번 방법은 훅이 설계된 의도대로 사용되지 않아서, 버그를 발생 시킬 수 있습니다 - (사견) 2번 방법은 코드 의도를 파악하기 쉽지 않은 것 같습니다 - (사견) page, keyword 조합마다 쿼리가 저장될텐데, 캐싱이 꼭 필요한건지/유용하게 사용할 수 있는지도 고민이 필요한거 같습니다 여담으로 useEffect는 사용할 때 꼭 필요한것인지 고민하면 좋습니다. 참고할만한 링크들 첨부합니다: - https://beta.reactjs.org/learn/you-might-not-need-an-effect - https://tkdodo.eu/blog/mastering-mutations-in-react-query - https://tanstack.com/query/v4/docs/reference/MutationCache - https://tanstack.com/query/v4/docs/reference/useMutation - https://tanstack.com/query/v4/docs/reference/useQuery
외 1개 답변 보러 가기
지금 가입하면 모든 질문의 답변을 볼 수 있어요!