Hyeon_E 2023. 10. 25. 00:20

SWR

swr이란 HTTP 캐시 무효화 전략인 stale-while-revalidate에서 유래된 데이터 패칭을 위한 리액트 라이브러리

SWR의 기본적인 동작은 우선 캐시(stale)에서 데이터를 불러오고 검증을 위한 요청(revalidate)을 보내고 최신 데이터로 업데이트 하는 과정을 거침

SWR을 사용하는 컴포넌트는 지속적이고 자동적으로 데이터 업데이트가 이루어짐으로 항상 최신 상태를 유지할 수 있음

SWR 장점

  • 자동화가 간단
  • 가볍고 빠르기 때문에 데이터의 업데이트 또한 빠르게 이루어져 reactive한 환경을 갖출 수 있음
  • SSR, SSG 환경에서 모두 사용할 수 있음
  • 최신인 React18의 Suspense 등의 기능들과 호환됨

SSR, SSG

  • SSR
    • 서버에서 사용자에게 보여줄 페이지를 모두 구성하여 사용자에게 페이지를 보여주는 방식
    • 요청시 서버에서 즉시 HTML을 만들어서 응답하기 때문에 데이터가 달라져서 미리 만들어 두기 어려운 페이지에 적합
  • SSG
    • SSR과 비슷하게 서버에서 구성하지만 즉시 만드는 것이 아닌 미리 다 만들어두고 요청시에 해당 페이지를 응답하는 방식
    • 미리 다 만들어두고 요청시에 해당 페이지를 응답하기 때문에 바뀔일이 거의 없어서 캐싱해두면 좋은 페이지에 사용

설치

yarn add swr

 

 

기본적인 사용법

const { data, error, isLoading } = useSWR(key, fetcher);

key

요청을 보낼 주소

const { data, error } = useSWR("/api/auth", fetcher);

fetcher

SWR의 핵심적인 API로써 key를 받아 데이터를 반환하는 비동기 함수

fetcher에는 데이터 패칭에 사용되는 axios 등 어떠한 라이브러리든 사용이 가능함

const fetcher = (url) => fetch(url).then((res) => res.json());

const { data, error } = useSWR(key, fetcher);

fetcher 함수를 선언하고 SWR에서 사용하거나

const { data, error } = useSWR(key, (url) => {
  fetch(url).then((res) => res.json());
});

useSWR함수 내에 적어서 사용해도 됨

전역으로 fetcher 설정

매번 fetcher를 선언해주는 것은 번거롭다면 SWRconfig를 통해 전역 fetcher를 설정할 수 있음

<SWRConfig value={options}>
  <Component />
</SWRConfig>

 

SWRConfig로 감싸주는 컴포넌트에선 value에 전달하는 options의 내용이 전역 설정으로써 전달됨

이를 활용하여 fetcher를 전역으로 설정할 수 있음

function App() {
  const options = {
    fetcher: (resource, init) =>
      fetch(resource, init).then((res) => res.json()),
  };
  return (
    <SWRConfig value={options}>
      <Home />
    </SWRConfig>
  );
}

 

<Home /> 컴포넌트 안에서 사용되는 모든 useSWR은 제공된 fetcher를 사용하게 됨

function Home() {
  const { data: first } = useSWR(firstKey);
  const { data: second } = useSWR(secondKey);
}

Home의 useSWR은 fetcher를 작성하지 않아도 App에서 작성한 fetcher를 사용하고 있는 것

data, error, isLoading

  • data: useSWR에 반환된 데이터를 의미
  • error: 데이터를 패칭하는 과정에서 오류가 발생할 시 반환
  • isLoading: 데이터를 패칭하는 과정중 loading중이라면 true 반환

예시

기본적인 사용 예시

import useSWR from "swr";

const fetcher = (url) => fetch(url).then((res) => res.json());

function User() {
  const { data, error, isLoading } = useSWR("/api/user", fetcher);

  if (error) return <div>failed to load</div>;
  if (isLoading) return <div>loading...</div>;

  return <div>당신의 이름은 {data.name} 입니다!</div>;
}

재사용 예시

function useUser (id) {
  const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
 
  return {
    user: data,
    isLoading,
    isError: error
  }
}

 

function Avatar ({ id }) {
  const { user, isLoading, isError } = useUser(id)
 
  if (isLoading) return <Spinner />
  if (isError) return <Error />
  return <img src={user.avatar} />
}

 

//페이지 컴포넌트
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} />
}

 

SWR API 심층탐구

API

const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)

파라미터

  • key: 요청을 위한 고유한 키 문자열
  • fetcher: (옵션) 데이터를 가져오기 위한 함수를 반환하는 Promise
  • options: (옵션) SWR hook을 위한 옵션 객체

반환값

  • data: fetcher가 이행한 주어진 키에 대한 데이터(로드되지 않았다면 undefined)
  • error: fetcher가 던진 에러(또는 undefined)
  • isLoading: 진행 중인 요청이 있고 "로드된 데이터"가 없는 경우. 폴백 데이터와 이전 데이터는 "로드된 데이터"로 간주하지 않습니다.
  • isValidating: 요청이나 갱신 로딩의 여부
  • mutate(data?, options?): 캐시 된 데이터를 뮤테이트하기 위한 함수

옵션

  • suspense = false: React Suspense 모드를 활성화
  • fetcher(args): fetcher 함수
  • revalidateIfStale = true: 오래된 데이터가 있더라도 자동으로 다시 확인
  • revalidateOnMount: 컴포넌트가 마운트되었을 때 자동 갱신 활성화 또는 비활성화
  • revalidateOnFocus = true: 창이 포커싱되었을 때 자동 갱신
  • revalidateOnReconnect = true: 브라우저가 네트워크 연결을 다시 얻었을 때 자동으로 갱신
  • refreshInterval:
    • 기본적으로는 비활성화: refreshInterval = 0
    • number로 설정된 경우, 폴링 인터벌(밀리초)
    • function으로 설정된 경우, 함수가 최신 데이터를 받고 인터벌 반환(밀리초)
  • refreshWhenHidden = false: 창이 보이지 않을 때 폴링(refreshInterval이 활성화된 경우)
  • refreshWhenOffline = false: 브라우저가 오프라인일 때 폴링(navigator.onLine에 의해 결정됨)
  • shouldRetryOnError = true: fetcher에 에러가 있을 때 재시도
  • dedupingInterval = 2000: 이 시간 범위내에 동일 키를 사용하는 요청 중복 제거(밀리초)
  • focusThrottleInterval = 5000: 이 시간 범위 동안 단 한 번만 갱신(밀리초)
  • loadingTimeout = 3000: onLoadingSlow 이벤트를 트리거 하기 위한 타임아웃(밀리초)
  • errorRetryInterval = 5000: 에러 재시도 인터벌(밀리초)
  • errorRetryCount: 최대 에러 재시도 수
  • fallback: 다중 폴백 데이터의 키-값 객체
  • fallbackData: 반환될 초기 데이터(노트: hook 별로 존재)
  • keepPreviousData = false: 새 데이터가 로드될 때까지 이전 키의 데이터를 반환
  • onLoadingSlow(key, config): 요청을 로드하는 데 너무 오래 걸리는 경우의 콜백 함수(loadingTimeout을 보세요)
  • onSuccess(data, key, config): 요청이 성공적으로 종료되었을 경우의 콜백 함수
  • onError(err, key, config): 요청이 에러를 반환했을 경우의 콜백 함수
  • onErrorRetry(err, key, config, revalidate, revalidateOps): 에러 재시도 핸들러
  • onDiscarded(key): 경합 상태(race condition)로 인해 요청이 무시될 경우 실행되는 콜백 함수
  • compare(a, b): 비논리적인 리렌더러를 회피하기 위해 반환된 데이터가 변경되었는지를 감지하는데 사용하는 비교 함수
  • isPaused(): 갱신의 중지 여부를 감지하는 함수. true가 반환될 경우 가져온 데이터와 에러는 무시함. 기본적으로는 false를 반환
  • use: 미들웨어 함수의 배열