로그인 성공 시 다시 로그인 정보를 가져오는 get request를 mutate
함수를 통해 실행했다. 하지만 해당 함수를 사용했을 때 불필요하게 서버에 API 요청을 보내 비효율적일 수 있다. 실제 발생한 내용을 로컬 안에서 mutate
시키고, API reuest는 사용하지 않는 방법이다.
front/pages/Login/index.tsx
const { data, error, mutate } = useSWR<IUser | false>('/api/users', fetcher, {
dedupingInterval: 100000,
});
const onSubmit = useCallback(
(e) => {
e.preventDefault();
setLogInError(false);
axios
.post(
'/api/users/login',
{ email, password },
{
withCredentials: true,
},
)
.then((response: AxiosResponse<any>) => {
mutate(response.data, false); // 여기에서 제어
})
.catch((error) => {
setLogInError(error.response?.data?.code === 401);
});
},
[email, password],
);
먼저 mutate
함수에 두 개의 인자를 추가해주는데, 첫 번째 인자로는 mutate 할 대상, 두 번째 인자는 shouldRevalidate
속성(서버에 값이 변경되었는지 확인 요청)에 대한 값을 설정해준다. 만약 두 번째 인자를 별도로 설정하지 않을 경우 true
가 기본 값이므로 서버에 변경된 데이터가 맞는지 확인하는 요청이 발생한다. 따라서 정말 서버 측에 변경사항을 요청하지 않기 위해서는 false
를 두 번째 옵션 값으로 줘야한다. 이러한 방법으로 서버에 요청을 보내지 않고, 데이터를 수정하여 지속적인 요청을 줄일 수 있다.
그러나 무조건 서버에 요청을 줄이는 것만이 능사는 아니다. 요청한 값이 정말 서버에 제대로 반영되어있는지 확인할 필요가 있을 때가 있기 때문이다. 예를 들면 페이스북이나 인스타그램 같은 경우 좋아요
를 눌렀을 때 서버에 요청 후 응답에 따라 UI를 변경시켜주는 것이 아닌 먼저 UI를 변경시킨 뒤 해당 내용이 제대로 반영되어있는지 점검하는 식으로 작업을 한다. UI 변경 후 점검 시 어떤 이유에서건 해당 내용이 서버에 반영되어 있지 않으면 좋아요
반영을 취소해버리는 것이다.
이를 성공할 것이라고 낙관적으로 예측하여 UI를 변경한 뒤 나중에 점검하는 방법이라고 해서 Optimistic UI
라고 한다. UX로는 훨씬 빠르고 매끈한 플로우를 제공하지만, 데이터가 원복되어 자칫 혼란을 불러일으킬 수도 있다. 이와 반대되는 것을 Pessimitic UI
라고 하는데 이는 곧, 실패를 예상한 폐쇄적인 상황을 예측하여 서버에 요청을 보낸 뒤 성공했을 때 UI를 업데이트 시키는 방법이다. 이 방법은 우리가 보통 사용하는 방법이다.
우리는 위에서 mutate
함수의 두 번째 인자를 false로 설정하였으므로 서버에 점검하는 기능이 수행되지 않는다. 만약 Optimistic UI
를 구현하고자 한다면 해당 인자를 true로 설정해야 한다.
useSWR
에 있는 mutate
함수로 데이터를 업데이트해줬다. 그러나 mutate
함수는 전역에서 Import 하여 사용할 수도 있다.
import useSWR, { mutate } from 'swr';
전역 mutate
를 사용할 때에는 첫 번째 인자에 요청할 api Path, 두 번째, 세 번째 인자에는 기존에 사용하던 대로 넣어주면 된다. 전역에서 사용하는 mutate
는 보통 필요할 때에만 요청을 처리하고 싶을 때를 사용할 수 있다.
front/layouts/Workspace.tsx
import useSWR, { mutate } from 'swr';
const Workspace: FC = ({ children }) => {
// Workspace 컴포넌트에서 users 정보를 요청하는 코드가 과연 필요한가?
// const { data, error, mutate } = useSWR<IUser | false>('/api/users', fetcher);
const onLogout = useCallback(
() =>
axios.post('/api/users/logout').then(() => {
mutate('/api/users', false, false);
}),
[],
);
// ..
};
export default Workspace;
로그인 후 이동된 Workspace의 최상단에서는 로그인 정보를 가져오는 swr 코드가 실행되었다. 하지만 이는 비효율적일 수 있다. 이미 로그인된 상태로 넘어왔기 때문이다. 따라서 해당 요청 코드를 지워버린다. 그리고 이후 로그아웃을 실행했을 때 성공 시 전역의 mutate
함수로 users 정보를 재요청한다면 비효율 요청을 줄여버릴 수 있다. 위와 같이 필요한 위치에서만 data-fetching을 하고싶을 때에는 전역 mutate
함수를 사용하면 효율적이다.
swr 쓰임이 단순히 비동기에만 사용되는 것은 아니다. 전역 데이터를 다루는 시점에서 원한다면 fetcher
함수를 다양하게 구성하여 커스텀할 수 있다. 바로 아래처럼 말이다.
Page1.tsx
const Page1 = () => {
// 로컬 스토리지에 데이터를 저장 후 해당 데이터를 리턴하는 fetcher 설계
const { data } = useSWR('hello', (key) => {
localStorage.setItem('data', key);
return localStorage.getItem('data');
});
};
Page2.tsx