swr은 nextjs 를 만든 회사인 vercel 에서 제공하는 네트워크 패치 라이브러리이다.
설치
npm i swr
사용
const fetcher = (...args) => fetch(...args).then(res => res.json())
fetcher 란 패치를 할 때 뭘로 패치를 할 것인지에 대한 정의를 하는 부분이다.
axios fetch api 등 사용할 수 있고, 지금은 fetch 를 사용하겠다.
import useSWR from 'swr'
function Profile () {
const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return <div>failed to load</div>
if (isLoading) return <div>loading...</div>
// render data
return <div>hello {data.name}!</div>
}
swr을 사용하게 되면 기본적으로 data, error , isLoading 에 관한 상태를 사용할 수 있다.
위의 코드처럼 매번 fetcher 를 넣어주기 번거롭기 때문에 SWRconfig 를 사용해서
전역적으로 fetcher 를 설정해주고 컨텍스트 형태로 감싸주면 SWR 을 사용하는 쪽에서 fetcher를 매번 넣어주지 않아도 된다.
상태를 가지고 있으므로 use client 를 선언하고 사용한다.
"use client";
import { SWRConfig } from "swr";
type Props = {
children: React.ReactNode;
};
export default function SWRConfigContext({ children }: Props) {
return (
<SWRConfig
value={{
fetcher: (url: string) => fetch(url).then((res) => res.json()),
// axios 를 사용하거나 react query 를 사용하거나 fetch를 사용하거나
}}
>
{children}
</SWRConfig>
);
}
다음과 같이 설정한 뒤 next기준 layout 레벨에서 chlidren 을 감싸주면 사용할 수 있다.
const { data , error , isLoading } = useSWR("/api/home");
위와 같은 설정을 통해 fetcher를 명시해주지 않아도 다음과 같이 사용할 수 있게 된다.
useSWR 을 사용하게 되면 아직 데이터를 받아오지 않은 상태이므로 data 는 undefiend 상태이다.
여기서 isLoading과 isValidating 이 true 로 설정이 된다.
요청이 완료된 이후 정상적으로 처리가 됐다면, data에 value가 들어오게 되고
isLoading과 isValidating 이 false 로 바뀌게 된다.
사용자가 다시 접속을 했거나 전체적으로 mutate 가 일어나서 revalidate 를 해야하는 상황이 생긴다면, isValidating 은 true 로 바뀐다.
isLoading 이 true가 되지 않는 이유는 로딩하고 있는게 아니기 때문이다.
로딩은 데이터의 변화가 있어서 다시 해당 데이터를 가지고 올 때 발생하는데
아직까지의 데이터는 기존에 가지고 있던 value를 사용하기 때문에 변동이 없음으로 로딩 상태가 false 이고, data도 본인이 머금고 있는 value 를 보여주기 되는 것이다.
revalidate 가 끝난 후 데이터의 변화가 있을 때 로딩이 일어나고 완료 되고 난 뒤 새로운 데이터가 들어오게 된다.
클라이언트에서 "api/example" 의 경로로 데이터를 요청하게 되면
swr이 서버에 example 데이터 요청 > 이후 데이터를 받아오면
클라이언트에게 제공하고 swr 내부에서 example 의 키값으로 해당 데이터를 캐싱한다.
클라이언트에서 해당 경로로 다시 요청을 하면 우선 캐시되어있던 기존의 데이터를 반환해준다.
그리고 나서 다시 서버에 해당 example 데이터를 요청해서 받아온 후 캐시된 데이터를 새로 받아온 데이터로 업데이트하게 되는 것이다.
그리고 클라이언트에게 업데이트 됐다. 라고 알려주는데
여기서 기존의 데이터와 차이가 없다면 리액트 상에서 변화가 없을 것이고,
기존 데이터와 차이가 있다면 그 부분만 리액트 상에서 업데이트가 되는 것이다.
이로 인해 불필요한 요청(SWR에서 서버에 계속 요청하게 되는)을 방지할 수 있다 .
UX를 개선하기 위해 데이터를 요청하고 전달받기 전에 결과를 예상해 UI를 먼저 업데이트 시켜주는 것.
좋아요 버튼을 눌러 like를 업데이트한다고 해보자.
성공적으로 요청이 처리 됐다고 가정할 경우 mutate("전체 데이터 경로 또는 키")를 통해 기존 캐시되어있는 전체 데이터를 만료처리하고 refetch를 트리거함으로 revalidate 를 일으켜 새로운 데이터를 받아온다.(바뀐 부분만 업데이트)
저렇게 길고 긴 과정을 거쳐 화면을 업데이트하게 된다면 사용자 입장에선 무언가의 에러로 인지하게 될 가능성이 굉장히 크다.
좋아요 버튼 하나 누른거 뿐인데 2초 이상의 아주 길고 긴 시간이 걸리기 때문이다.
이와 같은 현상을 우린 옵티미스틱 업데이트로 개선할 수 있고, 해당 기능을 SWR에서도 제공해주고 있다.
이를 적용하게 되면
UI, 즉 사용자 입장에선 내가 누른 좋아요의 반응이 된거처럼 보이지만
실제 like 에 관한 요청은 아직 pending 상태이다.
(캡쳐하기 힘들었스빈다.)
이후 요청이 정상적으로 처리되게 된다.