원격데이터
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에서 서버에서 받은 데이터를 returnexport 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 (
:
:
)
};