ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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))

     

    댓글

Designed by Tistory.