"SWR"이라는 이름은 HTTP RFC 5861(opens in a new tab)에 의해 알려진 HTTP 캐시 무효 전략인
stale-while-revalidate에서 유래되었습니다. SWR은 먼저 캐시(stale)로부터 데이터를 반환한 후, fetch 요청(revalidate)을 하고, 최종적으로 최신화된 데이터를 가져오는 전략입니다.
*Cache-Control
HTTP 헤더 필드 / 요청과 응답 내의 캐싱 메커니즘을 위한 방향을 정하기 위해 사용된다.
*stale-while-revalidate는 HTTP 캐싱 표준 문서에 속하지 않고, 캐시가 캐시에서 유효성을 재확인하는 동안 오래된 응답을 재사용 할 수 있다는 값을 의미한다.
const { data, error, isLoading } = useSWR(key, fetcher, config);
key는 데이터 고유 식별자 임과 동시에 fetcher로 전달되는 하나의 인자,
fetcher는 데이터 반환하는 비동기 함수,
config는 이 요청에 대한 옵션을 설정하는 값이다. 사용하는데 크나큰 무리는 없을 듯 하다.
반환값으로는 함수의 대한 결과, 로딩 상태, 에러를 반환한다. 다만 주의할 점은 v1에서는 isLoading이 존재 하지 않고 v2에서 release되었다.
(isLoading을 사용하려고 했지만 안돼서 찾아보니 types.d.ts 파일 자체에서도 isLoading 필드는 존재하지 않았음… 문서 블로그에 가보면 있더라 …)
v1 기준
패키지 번들링 개선으로 path 임포트, 즉 사용하지 않는 기능을 애플리케이션에 포함하지 않는다.
v1.2.2
export interface SWRHook {
<Data = any, Error = any, SWRKey extends Key = null>(key: SWRKey): SWRResponse<Data, Error>;
<Data = any, Error = any, SWRKey extends Key = null>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null): SWRResponse<Data, Error>;
<Data = any, Error = any, SWRKey extends Key = null>(key: SWRKey, config: SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined): SWRResponse<Data, Error>;
<Data = any, Error = any, SWRKey extends Key = null>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null, config: SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined): SWRResponse<Data, Error>;
<Data = any, Error = any>(key: Key): SWRResponse<Data, Error>;
<Data = any, Error = any>(key: Key, fetcher: BareFetcher<Data> | null): SWRResponse<Data, Error>;
<Data = any, Error = any>(key: Key, config: SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined): SWRResponse<Data, Error>;
<Data = any, Error = any>(key: Key, fetcher: BareFetcher<Data> | null, config: SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined): SWRResponse<Data, Error>;
}
export interface SWRResponse<Data = any, Error = any> {
data?: Data;
error?: Error;
mutate: KeyedMutator<Data>;
isValidating: boolean;
}
isLoading은 v2 이상 사용이 가능하다.
SWR은 전역 캐시를 사용해 모든 컴포넌트 사이에 데이터를 저장하고 공유한다.
기본 캐시는 애플리케이션의 메모리 내에 저장이 되고 페이지 리로드가 된다면 이 캐시가 초기화가 된다. swr은 SWRConfig를 통해서 캐시 제공자를 커스텀 할 수 있다.
import useSWR, { SWRConfig } from 'swr'
function App() {
return (
<SWRConfig value={{ provider: () => new Map() }}>
<Page/>
</SWRConfig>
)
}
Page 컴포넌트 내의 SWR hook은 Map 에서 캐시를 읽고 사용한다. 외부 컴포넌트는 이 Map을 사용하지 못하고 기본 캐시 제공자 (애플리케이션 메모리) 를 사용하게 된다.
아래는 각 컴포넌트(Member와 Company) 별로 캐시 제공자를 따로 두는 예시이다.
const memberCacheProvider = () => new Map();
const companyCacheProvider = () => new Map();
import React from "react";
import useSWR, { SWRConfig } from "swr";
const memberFetcher= async () => {
const response = await axios('/member-list');
if (response) return response.data;
throw new Error('Failed to fetch member');
};
const companyFetcher= async () => {
const response = await axios('/company-list');
if (response) return response.data;
throw new Error('Failed to fetch company');
};
function Member() {
const { data, error, isLoading } = useSWR('/member-list', memberFetcher, { refreshInterval: 5000 });
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error</div>;
return <div>{data}</div>;
}
function Company() {
const { data, error, isLoading } = useSWR('/company-list', companyFetcher, { refreshInterval: 5000 });
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error</div>;
return <div>{data}</div>;
}
export default function MyComponent() {
return (
<>
<SWRConfig value={{ provider: memberCacheProvider }}>
<Member/>
</SWRConfig>
<SWRConfig value={{ provider: companyCacheProvider }}>
<Company/>
</SWRConfig>
</>
);
}
여기서 만일 memberCache의 값을 가져오거나 설정하고 싶다면 아래와 같이 우리가 평소에 사용하는 Map 객체의 인스턴스 메서드를 사용하면 된다.
import { cache } from 'swr';
const cachedData = cache.get('/member-list');
cache.set('/member-list', { memberId: "0202" });
처음엔 React-query (Tanstack Query)를 사용하려 했지만 프로젝트의 리액트 버전이 17이어서 그와 비슷한 useSWR를 택하게 되었다. 오히려 useSWR이 간단하고 러닝커브가 낮아 바로 프로젝트에 적용이 가능해 개발하는데 시간 낭비가 없었다. 굿~