최근 프론트엔드와 관련 한 카카오톡 오픈 채팅방을 통해 스터디를 시작했고 관련해서 공부했던 내용을 토대로 포스팅을 진행해봤습니다.
SWR이란?
클라이언트는 서버에서 데이터를 받는 순간 이 데이터가 최신 데이터인지 알 수 없다. 그렇다고 무작정 서버에서 데이터를 요청하면 아래처럼 무한의 굴레에 빠져버려 본인 서버에 디도스 공격을 하는 꼴이 돼버린다.
그래서 효율적으로 최신 데이터를 유지하기 위해 여러 HTTP 캐싱 전략이라는게 존재하는데, 그중 하나가 SWR(Stale-While-Revalidate)이라는 방식이다. 이 외에 Cache-First, Network-First, Network-Only, Cache-Only 등 있다.
SWR훅은 Next.js로 유명한 vercel에서 만들었으며, Stale-While-Revalidate라는 캐싱 전략을 리액트 훅으로 간단하게 사용할 수 있다. SWR에서 사용하는 캐싱 방법은 아래와 같다.
전달받은 데이터가 최신 상태인지 아닌지는 Cache-Control 헤더에 포함된 max-age로 판단한다.
Cache-Control: max-age=600, stale-while-revalidate=30
위 값에 대한 전략은 아래와 같다.
- HTTP 요청이 600초 안에 반복적으로 일어난다면 빠른 응답을 위해 캐싱된 데이터를 전달한다.
- 요청이 601~630초 안에 일어난다면 캐싱된 데이터를 사용자에게 먼저 보여주고 백그라운드에 재검증을 한다.
- 재검증 결과 캐싱된 데이터와 새로 받은 데이터가 다른 경우 새롭게 캐싱을 하고 리렌더링을 통해 사용자에게 새로운 데이터를 보여준다.
- 요청이 630초 이후 일어나면 서버에 데이터를 요청한다.
사용자 입장에서 아무 의미 없는 이야기지만, 개발자 입장에서는 흥미롭니다.
이전 캐싱된 데이터는 사용자가 이미 본 데이터인데, 위 처럼 601~630초 안에 데이터를 요청한다면 프론트에서는 캐싱되어있는 데이터를 다시 보여주고 백그라운드 작업으로 서버에 데이터를 가져와 캐싱된 데이터와 비교해 변화가 있다면 사용자에게 업데이트해준다.
만약 변경된 데이터가 없다면 사용자는 최신 데이터를 이미 서버에서 가져오기도 전에 미리 확인한 샘이다. 또한 사용자는 빠른 속도로 데이터를 가져온다는 느낌을 경험을 하게 될 것이다. 그것이 속임수일 지라도..
SWR 설치
npm i swr
useSWR Hook
const { data, error, isValidating, mutate } = useSWR(key, fetcher, options);
// key: 요청할 API 주소값
// fetcher: 데이터를 가져오기 위한 Promise 반환 함수
// options: SWR Hook 옵션값
- data: useSWR을 사용해 데이터를 받아와 담기며, 전에는 undefined로 정의된다.
- error: 데이터를 받아왔지만 data가 undefined로 정의되면 에러 값을 반환한다.
- isValidating: 요청 또는 재검증(revalidate) 중인지 여부를 나타내는 boolean 값
- mutate(data?, shouldRevalidate?): 인위적으로 데이터 검증(revalidate) 하기 위한 함수로 shouldRevalidate에 false를 주면 API 호출을 하지 않고 캐싱된 데이터를 반환
fetcher
// Fetch
import fetch from 'unfetch'
const fetcher = url => fetch(url).then(r => r.json())
function App () {
const { data, error } = useSWR('/api/data', fetcher)
// ...
}
// Axios
import axios from 'axios'
const fetcher = url => axios.get(url).then(res => res.data)
function App () {
const { data, error } = useSWR('/api/data', fetcher)
// ...
}
자주 사용하는 Axios를 사용해 fetcher 함수를 만들어 useSWR 파라미터에 포함시켜준다.
useSWR Options
SWR에는 여러 가지 옵션을 제공하는데, 기본적으로 네트워크 부담을 줄이기 위해 dedupingInterval 값을 밀리초 단위로 설정해놓으면 그 시간 안에서 이루어지는 요청은 API 호출이 이루어지지 않고 캐싱된 데이터만 반환한다.
잦은 호출이 필요 없는 API의 경우 dedupingInterval 값을 길게 주면 서버에 부담을 줄일 수 있다.
부가적으로 사용자 경험을 향상할 수 있는 옵션들이 있는데, 브라우저가 네트워크 연결을 회복할 때 자동으로 데이터 재검증을 한다던가 아래와 같이 브라우저 탭이 포커싱 될 때 자동으로 데이터를 재검증하는 작업을 자동으로 실행시켜준다.
(Next.js에서 SWR을 기본적으로 제공한다)
- shouldRetryOnError(default: true): fetcher에 오류가 있을 때 재시도
- errorRetryInterval: 오류 재시도 간격(밀리초)
- errorRetryCount: 최대 오류 재시도 횟수
- focusThrottleInterval: 밀리초 단위의 시간 범위 동안 한 번만 재검증
- onSuccess: 요청이 성공하면 실행될 콜백 함수
- revalidateOnReconnect(default: true): 브라우저가 네트워크 연결이 됐을 때 자동으로 재검증
- revalidateOnFocus(default: true): 브라우저 탭이 포커싱 됐을 때, 자동으로 재검증
- revalidateOnMount: 구성요소가 마운트 될 때 자동 재검증을 활성화 또는 비활성화
추가적인 옵션은 아래에서 참고
여담으로 SWR을 사용하면 프론트에서 네트워크 요청 작업에 쓰로틀링이나 디바운스 같은 최적화 작업이 필요 없어집니다. SWR과 비슷한 라이브러리로 React-Query가 있는데 같은 캐싱 전략을 사용하고 기능도 더 많아 인기가 더 높습니다.