본문 바로가기

WEB/JavaScript

[SWR] 실시간 상태를 반영할 수 있는 라이브러리 SWR!

시작전 알려드립니다. 현재 SWR이 많은 업데이트가 진행이 된 상태이기 때문에 굵직한 변경점은 없지만 사용방법이 세부적으로 다를 수 있다는 점을 알려드립니다.

 

SWR은 비동기 작업을 도와주는 React Hooks 라이브러리이다.
Redux 특유의 복잡함 때문에 단점으로 작용하고 있고 대체할 라이브러리들이 속속들이 나오고 있다.

간단 설명

SWR은 stale-while-revalidate를 뜻하는 것으로, 데이터를 검증하는 동안 stale(Cache) 데이터를 사용하는 것을 말한다.
다시 설명하자면, Cache된 데이터를 보여주고, 데이터 요청을 보낸 후, 새롭게 받은 데이터를 보여주는 것을 말한다.


npm i swr

 

(Login.tsx)

const LogIn = () => {
  const { data, error, revalidate} = useSWR('/api/users', fetcher);
  const [logInError, setLogInError] = useState(false);
  const [email, onChangeEmail] = useInput('');
  const [password, onChangePassword] = useInput('');

  const onSubmit = useCallback(
    (e) => {
      e.preventDefault();
      setLogInError(false);
      axios
        .post(
          '/api/users/login',
          { email, password },
          {
            withCredentials: true,
          },
        )
        .then((response) => {
          revalidate()
        })
        .catch((error) => {
          setLogInError(error.response?.data?.statusCode === 401);
        });
    },
    [email, password],
  );
  
  if(data) {
    return <Redirect to="/workspace/channel" />
  }


이 코드는 리액트 Login 페이지에서 SWR을 사용한 코드이다.
하나씩 분리해서 분석 해보자면

useSWR

import useSWR from 'swr';


컴포넌트에 useSWR을 import 한다.
useSWR은 데이터를 전역으로 관리하기 위한 가장 기본적인 hook인데 API 통신 결과에 따라 data, error를 반환한다.

 

const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)

 

  • key : API 요청을 보낼 문자열, localStorage 또한 사용이 가능하다.
  • fetcher : fetcher는 axios, fetch API 요청을 보내는 로직이 필요하고 key를 매개변수로 한다. Promise 객체를 반환하는 함수를 입력해야 한다.
  • options : useSWR에 Option이다. API 요청이나 시간에 따른 캐시 데이터 삭제 재요청 등등.. 기능이 정말 많다. 공식문서 참조
 

SWR: React Hooks for Data Fetching

SWR is a React Hooks library for data fetching. SWR first returns the data from cache (stale), then sends the fetch request (revalidate), and finally comes with the up-to-date data again.

swr.vercel.app


useSWR의 parameter에서 유심히 봐야 할 것은 fetcher이다.
fetcher의 코드를 따로 분리해서 코딩했다.

 

(fetcher.js)

import axios from 'axios';

const fetcher = (url: string) =>
  axios
    .get(url, {
      withCredentials: true,
    })
    .then((response) => response.data);

export default fetcher;


fecher의 로직은 일반적인 axios나 fetch의 API 요청이다. fetcher는 useSWR에서 key를 parameter로 받는다.
이후 응답이 정상적으로 받으면 response.data가 useSWR의 data로 리턴된다.

useSWR를 통해 API 요청 후 성공적으로 응답이 왔다면 이제 저장된 데이터를 전역 컴포넌트에서 사용하기만 하면 끝이다..
반환되는 값들은 각각 아래와 같다.

  • data : fetcher 통신 후 응답받은 data가 저장
  • error : fetcher의 error가 저장
  • revalidate : 요청 또는 요청 재검증을 한다. SWR의 정의된 API 요청을 다시 한번 요청하여 데이터를 검증한다. 사용하는 데에 따라서 다소 비효율적일 수 있다.
  • mutate(data, shouldRevalidate) : 캐시 되는 데이터를 저장할 수 있다. mutate(data)를 설정해주면 data의 값이 변경된다. shouldRevalidate는 검증할지 말지의 여부를 정한다. 값은 boolean으로 true나 false를 받는다.

 

const onSubmit = useCallback(
    (e) => {
      e.preventDefault();
      setLogInError(false);
      axios
        .post(
          '/api/users/login',
          { email, password },
          {
            withCredentials: true,
          },
        )
        .then((response) => {
          revalidate()
        })
        .catch((error) => {
          setLogInError(error.response?.data?.statusCode === 401);
        });
    },
    [email, password],
  ); 


전체 코드에서 SWR fetcher에서 로그인 요청을 했음에도 불구하고 다시 Login API 요청 로직이 있어 혼란스럽다.
상단 코드에서 useSWR이 있는 것은 이미 key 값으로 설정한 URL에 캐시 데이터가 있는것을 확인하기 위해 있는 것이다.

따라서 첫 로그인할시에는 useSWR의 API 통신을 수행한다. 그리고 캐시데이터를 저장하여 다음에 다시 useSWR을 실행하면 API 통신을 수행하지 않고 캐시 데이터를 그대로 계속 사용한다.

즉, SWR은 key값으로 설정한 주소에 캐시 데이터가 저장되어 있다면 계속해서 상태를 유지한다.

/api/users/login API는 로그인 데이터를 보낸뒤 사용자의 정보를 response(응답) 해준다.

성공한다면 mutate의 response.data를 설정해준다.

mutate는 캐시 데이터를  설정한다.

 

if(data) { return <Redirect to="/workspace/channel" /> }


Redirect return 문은 hook들 중 가장 하단에서 사용해야 한다.
첫 접속 이후 로그인을 하여 캐시 데이터가 이미 있다면 바로 Workspace로 이동된다.
만약 없다면 로그인을 한 뒤에 data의 캐시 데이터를 저장한 뒤에 이동시킨다.

 

(Workspace.tsx)

import fetcher from '@utils/fetcher';
import axios from 'axios';
import React, { FC ,useCallback } from 'react'
import { Redirect } from 'react-router-dom';
import useSWR from 'swr';

const Workspace: FC = ({children}) => {
    const { data, error, revalidate} = useSWR('/api/users', fetcher);

    const onLogout = useCallback(() => {
            axios.post('/api/users/logout', null, {
                withCredentials: true,
            })
            .then(() => {
                revalidate();
            })
        }, [])
    
    if(!data){
        return <Redirect to="/login" />
    }
    return (
        <div>
            <button onClick={onLogout}>로그아웃</button>
            {children}
        </div>
    )
}

export default Workspace


Login 이후 Redirect가 되는 페이지이다.

 

 const { data, error, revalidate, mutate} = useSWR('/api/users', fetcher);


유심히 보면 Login.tsx에서 사용했던 useSWR과 같다.
흔히 오해하는 것 중 하나는 이러면 API 재요청을 하는 것 아닌가라는 오해가 생기기 쉽다.
SWR은 설정한 키 값이 이미 캐시 데이터가 존재한다면 재요청을 하지 않는다.

재요청이나 데이터 검증을 하고 싶다면 revalidate나 option을 통해서 해줘야 한다.

이후 로그아웃 button을 클릭하면 이벤트 핸들러에 연결된 onLogout 함수가 실행되는데
로그아웃 요청 성공 후에 mutate를 사용하여 data의 캐시데이터를 바꾸는 것을 볼 수 있다.

logout API는 null을 응답하는데 data의 캐시 데이터 바꿔주는 동시에 아래 조건문이 성립 되면서 로그인 페이지로 이동하게 된다.

 

if(!data){ return <Redirect to="/login" /> }

정리

이렇듯 SWR을 사용하여 전역 상태관리를 하면 useSWR 훅 하나로 컴포넌트 전역에서 Fetch API를 캐싱하여를 관리할 수 있다.

useSWR은
key 값이 같으면 캐시데이터가 유지된다. data의 값을 설정해주는 방법은 fetcher의 응답, mutate, revalidate이며 option을 설정하여 fetcher에 정의된 로직을 비동기적으로 실행할 수 있다.
어느 컴포넌트든지 키 값이 갔다면 저장된 캐시데이터 또한 같다.