-
[SWR] Data의 reavalidate, mutate 활용프로그래밍/React 2023. 11. 3. 11:39
swr의 기본적인 사용방법에 대해서는 다른 글을 참고하는게 낫다
이것은 오로지 swr을 통해서 가져온 data를 어떻게 revalidate하는게 좋고 mutate를 어떨 때 사용하는게 좋고 optimistic UI를 어떻게 사용해야하는지에 대해서 내가 볼려고 적어 놓은 것임
개요
- SWR의 이점
- revalidate란?
- mutate란?
- optimisticUI란?
- key의 활용 (조건부 데이터 가져오기, token전달)
SWR 장점
- 빠르고, 가볍고, 재사용 가능한 데이터 가져오기
- React-query와 마찬가지로 데이터를 불러올 때 사용되는데 SWRconfig를 통해 전역설정만 하고 fetcher만 설정해 놓는다면 간단하게 사용할 수 있는 구조이다. (전역설정 안해도 됨 그러면 따로 fetcher를 매번 작성해야 함)
- key를 여러방식으로 넣을 수 있어서 이를 통해서 다양하게 활용할 수 있다.
- 내장된 캐시 및 요청 중복 제거
- 현재 보이는 코드구조에서 useUser()라는 swr을 통해서 데이터를 불러오는 method가 있다 이는 2개의 컴포넌트에서 각각 호출이 되는데 캐시된 데이터를 가지고있기 떄문에 실질적으로는 요청1번과 캐시된 데이터를 사용한 것 1번인 것이다.
- 이는 이전처럼 root에서 props로 내리는 전통적인 방식에 비해서 독립적이여서 성능에서도 좋으며 코드유지에 도움을 많이 줍니다. 이때 같은 swr key를 사용해야하는 것을 꼭 기억해야 한다.
- 그러면 데이터 최신화가 안되고 계속 캐시된 데이터를 끌어오는 것 아닐까라고 생각하기 쉽지만 그렇기 위해 여러 옵션이 있으니 그걸 알아보도록 하자.
// 페이지 컴포넌트 function useUser (id) { const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher) return { user: data, isLoading, isError: error } } function Page () { return <div> <Navbar /> <Content /> </div> } // 자식 컴포넌트 function Navbar () { return <div> ... <Avatar /> </div> } function Content () { const { user, isLoading } = useUser() if (isLoading) return <Spinner /> return <h1>Welcome back, {user.name}</h1> } function Avatar () { const { user, isLoading } = useUser() if (isLoading) return <Spinner /> return <img src={user.avatar} alt={user.name} /> }
- SSR / ISR / SSG 지원
- option에서 여러 방식을 통해서 구현 가능
Revalidate
swr은 기본적으로 revalidateOnFocus 기능이 활성화 되어있기때문에 사용자가 foucs하거나 탭을 전환할 시 자동으로 데이터를 갱신한다.
refreshInterval 를 통해서 일정 시간 간격으로 데이터를 갱신할 수도 있음
기타 등등 여러 옵션을 통해서 데이터를 갱신할 수 있음!
대표적인 사용경우
- 포커스 시 갱신
- 인터벌(시간간격) 시 갱신
- 재연결 시 갱신
- 마운트 시 갱신
하지만 revalidate의 경우 해당 키값에 해당하는 캐시된 데이터 전부를 무효화하고 새로 받아오기 때문에 조금 효율성이 떨어질 수 있다. 그래서 우리는 상황에 따라 mutate를 써서 캐시된 데이터의 일부만 업데이트하고 업데이트 된 캐시데이터와 실제 DB의 데이터를 일치하는 방법을 활용해야 한다.
Mutate
mutate를 통해서 클라이언트에 캐시되어있는 캐시데이터를 업데이트 할 순 있지만 원격데이터를 업데이트 할 수는 없다. 그러니 updateUser라는 post, put, delete 요청을 통해서 원격데이터를 업데이트 할 api를 따로 할당을 해주어야 한다.
mutate의 api상 요청을 저렇게 집어넣을 수도 있어서 코드가 깔끔해지겠구나
try { const user = await mutate('/api/user', updateUser(newUser)) } catch (error) { // 여기에서 사용자를 업데이트 하는 동안 발생한 오류를 처리하세요. }
useSWRMutation을 이용해서 이러한 방법으로 사용할 수도 있다.
import useSWRMutation from 'swr/mutation' async function sendRequest(url, { arg }: { arg: { username: string } }) { return fetch(url, { method: 'POST', body: JSON.stringify(arg) }).then(res => res.json()) } function App() { const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest, /* options */) return ( <button disabled={isMutating} onClick={async () => { try { const result = await trigger({ username: 'johndoe' }, /* options */) } catch (e) { // 에러 핸들링 } }} > Create User </button> ) }
useSWRMutation은 useSWR과 캐시 저장소를 공유하기 때문에 이로 인해서 발생하는 문제점도 있을 수 있을 것 같다
하지만 예상과는 달리 두개를 동시에 사용했을 경우 경합 상태를 알아서 피하도록 설계가 되어있는데 아래와 같이 코드를 구성을 했을 때 get요청이 더 일찍 시작되고 늦게 끝나는 경우에는 데이터의 경합이 일어날 수 있다.
이러한 경우 useSWRMutation이 자동으로 처리를 해서 useSWR에 진행중인 요청을 버리고 재검증을 하여 오래된 데이터가 표시되지 않도록 한다.
function Profile() { const { data } = useSWR('/api/user', getUser, { revalidateInterval: 3000 }) const { trigger } = useSWRMutation('/api/user', updateUser) return <> {data ? data.username : null} <button onClick={() => trigger()}>Update User</button> </> }
조건부 가져오기
간단하게 삼항연산자를 key값에 넣어서 활성화를 시키는 방법임
// 조건부 가져오기 const { data } = useSWR(shouldFetch ? '/api/data' : null, fetcher) // ...또는 falsy 값 반환 const { data } = useSWR(() => shouldFetch ? '/api/data' : null, fetcher) // ...또는 user.id가 정의되지 않았을 때 에러 throw const { data } = useSWR(() => '/api/data?uid=' + user.id, fetcher)
key값에 falsey한 값이 들어가면 에러를 내는 것이지 실행이 안되는 것인지 테스트를 해보자
function MyProjects () { const { data: user } = useSWR('/api/user') const { data: projects } = useSWR(() => '/api/projects?uid=' + user.id) // 함수를 전달할 때, SWR은 반환 값을 `key`로 사용합니다. // 함수가 falsy를 던지거나 반환한다면, // SWR은 일부 의존성이 준비되지 않은 것을 알게 됩니다. // 이 예시의 경우 `user.id`는 `user`가 로드되지 않았을 때 // 에러를 던집니다. if (!projects) return 'loading...' return 'You have ' + projects.length + ' projects' }
Token 전달
이런식으로 token을 전달 할 수도 있음
근데 사실 axios를 사용하면 interceptor에서 token에 대한 처리를 해주는 것이 가장 나을 것 같고 간단하게 swr만 이용해서 데이터를 관리하는 경우라면 이렇게 사용해볼만한 것 같다.
const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))
'프로그래밍 > React' 카테고리의 다른 글
[Hooks] useEffect (1) 2023.11.22 [Article] React 렌더링 동작에 대한 (거의) 완벽한 가이드(1편) (0) 2023.11.21 [전역상태관리] Recoil (1) 2023.11.02 [React] react-router-dom의 사용 (1) 2023.10.04 [Vite] ESlint 와 Prettier, Package.json 초기설정 (0) 2023.10.02