깃허브 링크
https://github.com/DongneMashil/dongnemashil-fe/blob/main/src/hooks/useUpdateUserInfo.tsx
[유저정보 불러오기 방법에 대한 고민을 팀원들과 공유하기 위해 slack에 작성한 글입니다.]
[글 아래 부분에 변경된 내용을 반영했습니다]
지금 프사가 각각 늦게뜨는 가장 큰 이유는, 매번 API를 호출해서 서버 응답을 기다리고 있기 때문입니다.
두가지 방법이 있습니다.
리엑트 쿼리 + Recoil에 유저정보 모두 저장 + 필요한 곳에서 Recoil 유저정보 업데이트하는 훅 호출 (원래 고민했던 방법)
- 장점
- Recoil으로 상태만 가져오면 끝이라 코드가 짧음(필요한 곳에만 hook으로 상태 업데이트)
const {nickName, profileUrl} = useRecoilValue(userData);
- 로그인만 하면 navbar, mypage에서 유저정보가 즉시 보여짐
- 단점
- 모든 페이지에서 정보를 업데이트하는 경우 -> 리엑트 쿼리를 사용하는 의미가 크게 없음(즉시 캐시값 보여준뒤, 업데이트하는 정도), API호출이 많아짐.
- 특정 페이지에서만 정보를 업데이트하는 경우 -> 그 페이지에 도달하지 않으면 유저 정보는 시간이 지나도 최신화가 불가능함.
->여기서 부터 본론
리엑트쿼리 캐싱 적극 사용 + Recoil로 queryKey에 필요한 값(아이디값)만 관리 (저는 이 방법이 더 좋아보임)
- 장점
- 지정된 조건에 도달하지 않는 경우 서버 요청을 추가로 보내지 않고 캐싱된 값을 사용하므로, 진정한 서버상태관리.
staleTime, 캐시타임을 지정해서, 주기적으로 정보 업데이트가 가능함. (무한정 오래된 정보 가지고 있는것 방지)
- 즉시 업데이트가 필요할시 쿼리 무효화를 하면 바로 업데이트도 가능함.
단점
- 코드가 4~5줄 길어짐 (useQuery호출 + Recoil 호출)
const userID = useRecoilValue(userDataKey);
const { data } = useQuery<MyProfile>({
queryKey: [userID, 'userData'],
queryFn: getMyProfile,
});
- 결국 Recoil이랑 같이 쓰기는 해야함 (쿼리key 공유)
주의해야 할 부분
- 해당 정보를 사용하는 모든 곳에서 useQuery를 사용하고,추가 설정값을 넣지 않으며(최초 설정 덮어써짐), 정확한 key값을 공유해야 함
- key 값은 Recoil에 저장하고, 로그인/로그아웃시 해당 Recoil을 업데이트해줘야 함.
- 로그인시 유저 정보를 한번 불러와서 캐싱을 해줘야함. + 여기가 최초 쿼리 호출이므로, 여기서 캐시타임과 stale타임을 설정
- 쿼리key에는 유저 닉네임을 사용하면 변경시 예외처리 로직이 추가되어서, 변경이 없는 userID(email)을 사용
두번째 방법의 경우 아래와 같은 작업이 필요합니다.
공통부분
- 유저정보 (userID)를 가지고 있는 Recoil atom 생성 : 공유하는 쿼리 key에 들어가는 값 관리
- [필수아님] 각각 필요한 쿼리키 배열을 selector로 관리 (휴먼에러방지, 추후 일괄수정 편리함 등)
로그인시 (승현님 부분)
- useQuery -> getMyProfile API호출해서 캐싱하는 부분 추가 (staleTime, cacheTime, onSuccess, onError 초기값 설정->추후 설정 필요 없음)
const [userID, serUserID] = useRecoilState(userDataKey);
//여기에 useVarifyUser에서 가져온 userID(email)을 setUserID로 업데이트하는 로직 추가 필요 //
const { data } = useQuery<MyProfile, Error>({
queryKey: [userID, 'userData'],
queryFn: getMyProfile,
onSuccess: (data) => {
console.log('getMyProfile쿼리data :' + data);
},
onError: (error) => {
console.log('getMyProfile쿼리오류 :' + error);
},
refetchOnWindowFocus: false, // 필요시 true.
refetchOnReconnect: true,
refetchOnMount: true,
cacheTime: 60000 * 60, // 60분 동안 캐시로 저장 -> refreshToken이랑 동일한 시간으로 설정
staleTime: 60000 * 10, // 10분 이내에는 캐시된 결과를 사용 -> 어차피 업데이트시 즉시 무효화됨
retry: 1, // 1번은 재시도 해보기
});
Recoil에 userID저장
const serUserID = useSetRecoilState(userDataKey);
setUserID(로그인시 받아온 userID(email))
로그아웃시 + 토큰 무효라 로그아웃이 될때 ( 태현, 승현님 부분)
- useQuery getMyProfile부분 캐시 삭제(바로 다시 재로그인 하는 경우, 새 정보를 받아오기 위해)
queryClient.removeQueries({ userID }) // exact:true이면 정확히 일치하는 것만 해당, 아니면 포함되는것 해당. 기본값:false
- Recoil에 저장된 userID 삭제
const serUserID = useSetRecoilState(userDataKey);
setUserID('')
마이페이지, navBar (태현, 수진님 부분)
- useQuery로 유저정보를 가지고와서 뿌려줌. (아래와 같이 사용)
const userID = useRecoilValue(userDataKey);
const { data } = useQuery<MyProfile>({
queryKey: [userID, 'userData'],
queryFn: getMyProfile,
});
프로필 수정 페이지 (태현 부분)
- useQuery로 최초 유저정보를 바로 불러옴.
- 수정 후 useQuery 즉시 캐시 무효화후 페이지이동
헷갈리는 개념 정리
- stale time : 해당 데이터가 얼마동안 유효한(신선한) 것으로 판단할지?
- cache time : 해당 데이터가 무효하더라도, 얼마동안 데이터를 가지고 있을지?(신선한 정보를 불러오기 전까지의 짧은 시간동안 예전 정보라도 화면에 뿌려주기 위해 저정된 데이터. 반응성이 좋게 느껴짐.)
- queryKey : 해당 배열과 완전히 일치하는 값을 가지고 있는 모든 쿼리가 영향을 받음
예1) queryKey: [고양이, 사슴, 비둘기] 는 queryKey: [고양이, 사슴] 이 무효화되면 영향을 받습니다.
예2) queryKey: [고양이, 비둘기] 는 queryKey: [고양이, 사슴]이 무효화되면 영향을 받지 않습니다.
수정된 내용
내용 변경되었습니다!->
사용자가 새로고침을 했을때 Recoil등이 다 날아가는 문제가 있어서, 로그인 했을때 데이터를 불러오는 부분을 커스텀훅으로 만들어서 사용해야할듯 합니다.
아래와 같은 작업이 필요합니다.
공통부분
유저정보 (userID)를 가지고 있는 Recoil atom 생성 : 공유하는 쿼리 key에 들어가는 값 관리
각각 필요한 쿼리키 배열을 selector로 관리 (휴먼에러방지, 추후 일괄수정 편리함 등)
커스텀훅 (토큰으로 정보 가져와서 recoil에 넣어주고, 리액트쿼리 캐시에 저장까지 )
아래는 들어가야하는 내용들 예시
const [userID, serUserID] = useRecoilState(userDataKey);
//여기에 useVarifyUser에서 가져온 userID(email)을 setUserID로 업데이트하는 로직 추가 필요 //
const { data } = useQuery<MyProfile, Error>({
queryKey: [userID, 'userData'],
queryFn: getMyProfile,
onSuccess: (data) => {
console.log('getMyProfile쿼리data :' + data);
},
onError: (error) => {
console.log('getMyProfile쿼리오류 :' + error);
},
refetchOnWindowFocus: false, // 필요시 true로 변경.
refetchOnReconnect: true,
refetchOnMount: true,
cacheTime: 60000 * 60, // 60분 동안 캐시로 저장 -> refreshToken이랑 동일한 시간으로 설정
staleTime: 60000 * 10, // 10분 이내에는 캐시된 결과를 사용 -> 어차피 업데이트시 즉시 무효화됨
retry: 1, // 1번은 재시도 해보기
});
로그인시 (승현님 부분)
커스텀훅으로 데이터 가져오기
로그아웃시 + 토큰 무효라 로그아웃이 될때 ( 태현, 승현님 부분)
useQuery getMyProfile부분 캐시 삭제(바로 다시 재로그인 하는 경우, 새 정보를 받아오기 위해)
queryClient.removeQueries({ userID }) // exact:true이면 정확히 일치하는 것만 해당, 아니면 포함되는것 해당. 기본값:false
Recoil에 저장된 userID 삭제
const serUserID = useSetRecoilState(userDataKey);
setUserID('')
마이페이지, navBar (태현, 수진님 부분)
커스텀훅으로 데이터 가져오기
프로필 수정 페이지 (태현 부분)
커스텀훅으로 데이터 가져오기
수정 후 useQuery 캐시 무효화