원격데이터
fetch
를 위한 커스텀 훅
SWR(stale-while-revalidate)
은 클라이언트 서버에서 data
를 가져오는 기능(fetching)
을 편리하게 사용할 수 있도록 도와주는 hook
이다.
SWR
은 먼저 캐시로부터 데이터를 반환한 후, fetch
요청(재검증)을 하고, 최종적으로 최신화된 데이터를 가져온다.
그래서 컴포넌트는 지속적이며 자동으로 데이터 업데이트 스트림을 받을 수 있다.
SWR
의 대표적인 장점은 다음과 같다.
자동화가 간단하다.
가볍고 빠르기 때문에 데이터의 업데이트 또한 빠르게 이루어져 reactive
한 환경을 갖출 수 있다.
SSR(Server-Side-Rendering)
, SSG(Static-Site-Generation)
, ISR(Incremental-Static-Generation)
환경에서 모두 사용할 수 있다.
vercel
에서 개발하여 Next.js
와 호환성능이 좋다.
같은 역활의 상태관리 라이브러리인 Redux
의 Reducer
, Saga
보다 간결하게 구현할 수 있어 코드의 양이 줄어든다.
단 SWR
은 클라이언트 서버에서만 사용할 수 있어 POST
, PATCH
, DELETE
...등 데이터베이스의 데이터를 바꿔야할 경우에는 적합하지 않다.
그래서 Load
와 같은 비동기 Action
또는 Get
요청을 처리하기 적합하다.
아래 npm명령어를 통해 SWR
을 설치한다.
npm i swr
SWR
은 useSWR(Key, Fetcher)
의 형태로 사용하며, Fetcher
함수를 이용하여 Key
주소에서 가져오는 데이터를 전역적으로 관리한다.
const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
이외에 사용되는 파라미터, 반환값의 종류는 다음과 같다.
key
: 요청을 위한 고유한 키 문자열(또는 함수 / 배열 / null)
fetcher(옵션)
: 데이터를 가져오기 위한 함수를 반환하는Promise
options(옵션)
:SWR hook
을 위한 옵션 객체
data
:fetcher
가 이행한 주어진 키에 대한 데이터(로드되지 않았다면 undefined)
error
:fetcher
가 던진 에러(또는 undefined)
isValidating
: 요청이나 갱신 로딩의 여부
mutate(data?, shouldRevalidate?)
: 캐시 된 데이터를 뮤테이트하기 위한 함수
import useSWR from 'swr'; //swr 불러오기
const fetcher = (url) => axios.get(url, { withCredentials: true }).then((result) => result.data);
const { data, error } = useSWR('/post', fetcher);
첫번째 인자는 두번째 fetch
함수의 첫번째 인자(url)
로 전달된다.
이 후 fetch
함수가 데이터를 로드하면 해당 응답이 data
로, 오류가 발생하면 해당 오류가 error
에 세팅된다.
컴포넌트에서는 data
와 error
상태에 따라 알맞게 결과를 렌더링 해주면 된다.
import useSWR from 'swr';
// fetcher는 첫번째 인자로 입력한 주소를 어떻게 실제로 가져올지를 작성
const fetcher = (url) => axios.get(url, { withCredentials: true }).then((result) => result.data);
// fetcher의 결과는 data, error는 error
// data, error 둘다 없다는 것은 아직 서버 요청중
const { data, error } = useSWR('/post', fetcher);
// 에러 발생
if (error) {
console.error(error);
return <div>에러가 발생합니다.</div>;
}
const Post = () => {
return (
<div>`포스트 정보: ${data}`</div>
)
};
아래 예제의 SWR
은 같은 url 주소를 사용하기 때문에, 동일한 캐시값을 받아 이를 통해 같은 상태값을 공유한다.
// FirstPost.js
import useSWR from 'swr'
export default () => {
const { data, error } = useSWR('/post', url => axios.get(url, { withCredentials: true }).then((result) => result.data);
}
// SecondPost.js
import useSWR from 'swr'
export default () => {
const { data, error } = useSWR('/post', url => axios.get(url, { withCredentials: true }).then((result) => result.data);
}
위에서 설명했듯 기본적으로 SWR
은 데이터를 가져오기 위한 React Hooks
이다.
최초 Fetcher
함수가 실행되면, 원격상태의 데이터를 내부적으로 캐시 즉, 클라이언트에 잠시 저장한다.
이 후 다른 컴포넌트에서 동일한 상태를 사용하게 되면 캐시했던 상태를 그대로 return
한다.
그래서 원격 데이터를 사용하는 서로 다른 컴포넌트가 같은 key
를 사용한다면 동일한 상태를 공유할 수 있다.
SWR
의 캐싱 간격은 위에서 설명한 Parameter 옵션
, Mutate 함수
를 통해 설정할 수 있다.
만약 지정하지 않는다면, 컴포넌트 렌더링에 따라 원격 데이터를 요청받는다.
SWR
의 세번째 Parameter
로 revalidateOnFocus
옵션을 지정하면, 창이 포커싱 됬을 때 자동으로 데이터가 갱신된다.
이외에 사용할 수 있는 Option Parameter
의 종류는 해당 홈페이지에서 확인 할 수 있다.
const {data, error} = useSWR('/post', fetcher, {
revalidateOnFocus: true,
});
Parameter
옵션이외에 Mutate
함수를 사용해도 데이터를 갱신할 수 있다.
Mutate
함수는 Promise
이며, 호출시 해당 상태를 즉시 Get
하여 데이터를 갱신한다.
사용방법은 다음과 같다.
// index.tsx
const {data, error} = useSWR('/post', fetcher);
const handleChange = async (post) => {
await updatePost(post)
return mutate()
}
또한 첫번째 인자에 갱신할 데이터를 직접 지정할 수 있으며, 두번째 인자로 데이터 Fetch여부도 지정할 수 있다.
const handleChange = async (post) => {
await updateUser(post)
mutate(post, false)
}
SWR
의 ServerSideRendering
방법은 다음과 같다.
getServerSideProps
에서 서버에서 받은 데이터를 return
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
const cookie = context.req ? context.req.headers.cookie : '';
axios.defaults.headers.Cookie = '';
if (context.req && cookie) {
axios.defaults.headers.Cookie = cookie;
}
context.store.dispatch({
type: LOAD_POST_REQUEST,
});
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
return { props: { data: 123 } } // 서버에서 받은 데이터를 전달
});
props
로 접근const Post = ({ data }) => { // 전달받은 데이터
const { data, error } = useSWR('/post', fetcher);
return (
:
:
)
};
props
를 SWR
의 세번째 인자({ initialData }
)로 추가한다.const fetcher = (url) => axios.get(url).then((result) => result.data);
const Post = ({ data }) => {
const { data, error } = useSWR('/post', fetcher, { data });
return (
:
:
)
};