frontend

useSWR 똑똑하게 사용하기 (2)

하리하링웹 2023. 1. 14. 17:27

https://jjongsk.tistory.com/entry/useSWR-%EB%98%91%EB%98%91%ED%95%98%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

useSWR 똑똑하게 사용하기

useSWR이 좋은 라이브러리라고는 이미 널리 알려져 있음에도 불구하고 useSWR에 관련된 대부분의 글들에서 좀 더 다양하게 응용하여 사용하는 방법에 대해 다루는 경우는 많이 보지못했기에 가볍

jjongsk.tistory.com

추가)이 방식대로 코딩을 진행하다가 아주 큰 문제를 발견하였다. 일단 결론적으로 말하면 이러한 방식으로 swr을 사용하지 않는것이 좋다.

아래의 방식대로 swr을 사용하게 되면 해당 key와 일치하는 mutation을 동작하지 않게 해버리는 치명적인 버그가 있다.

mutation을 아예 사용하지 않는것이 아니거나 직접 fetcher를 매 번 주입시켜주는것이 아니라면 아래의 방법을 사용하지 않는것을 추천한다.

이러한 버그를 해결하기 위해 이곳저곳에서 사용하기보다는 복잡한 동작을 하는 swr과 연동된 hook의 경우에만 별도의 fetcher를 주입시켜 사용하는 방식으로 해결하였다.

 

 

위 링크의 글을 읽고 보면 이해가 잘 될것이다.

 

React 환경에서 SWR과 Custom Hook을 적극적으로 사용하여 코딩을 하다보면 생각보다 자주 겪게 되는 일이다.

 

뭔가 더 나은 패턴이 있을 것 같긴한데 이번에 생각해낸 방법도 크게 문제는 없을 것 같아서 일단 글로 작성하게 되었다.

 

import { useEffect } from 'react';
import useSWRImmutable from 'swr/immutable';

import { userSwrKey } from '@/define/swr/user';

export default function useUser() {
  const { data: user, isLoading } = useSWRImmutable(userSwrKey, () => ({ name: 'jongsik' }));

  useEffect(() => {
    console.log('redirect 처리로직');
  }, []);

  return { user, isLoading };
}

 

먼저 정말 심플하게 유저를 fetch 하여 불러오고 useEffect 안에서 redirect 로직을 처리하는 useUser hook을 만들어 보았다.

 

이제 이 훅을 사용하여 유저 정보를 페이지의 여기저기에서 사용하게 될 것이다.

 

하지만 여기에서 문제가 생기게 된다.

 

예를들어 헤더, 페이지, 사이드바 컴포넌트에서 useUser hook의 유저 정보를 가져와서 사용한다고 가정해보자.

 

redirect 처리로직이 3번 출력된 사진

그러면 위 이미지처럼 총 3번의 useEffect가 실행되게 되어버린다. 물론 이러한 방법으로 인하여 문제가 발생하지 않을수도 있지만 충분히 문제가 발생할 가능성이 존재하게되며 성능상으로도 영 좋지 않게 되어버린다.

 

그렇다고 한 번 요청하여 props로 넘겨주기에는 props drilling의 확률이 너무나도 높아지게 되어버린다.

 

이럴 때 아마 redux (개인적으로 redux보다는 Jotai 혹은 Zustand 를 적절히 사용하는 것을 추천한다.) 와 같은 라이브러리를 사용하여

글로벌하게 상태관리를 하는 방식으로 해결하게 될 것이다.

 

하지만 SWR의 트릭을 적절히 사용하면 간단하게 이러한 문제를 해결할 수 있다.

 

제일 처음에 적어놓은 링크의 예시중 하나이다.

예시코드 이미지

위 코드에서 첫번째 인자에는 key string을 저장하며 두번째 인자에서 teamList를 fetch하여 받아오는 함수를 실행시킨다.

이 때 swr은 내부적으로 받아온 데이터를 첫 번째 key string에 캐싱해놓고 동인한 key string에 대해서는 해당 value를 즉시 반환하는 역할을 해준다.

 

이는 이 key string만 가지고 있으면 세션이 유지되는 이상 어디서든 해당 value를 즉시 가져올 수 있다는 말이기도 하다.

 

이를 응용하여 하나의 간단한 hook을 만들어 줄 수 있다.

import useSWRImmutable from 'swr/immutable';

export default function useSWRDataFromKey(key: string) {
  return useSWRImmutable(key).data;
}

위 hook은 간단하게 key 값을 넣어주면 해당 key에 맞는 swr의 value를 반환해주는 hook이다. 

 

이제 헤더, 페이지, 사이드바 총 3군대에서 호출하던 useUser를 핵심적인 페이지 혹은 레이아웃과 같은 곳에서 단 한 번만 호출하고

나머지는 위의 hook을 사용하여 useUser에 해당하는 key 값을 인자로 넣어주면 redux와 같은 글로벌 상태 관리 라이브러리 없이도 간단하게 해당 기능 구현이 완료되게된다.

 

이 때 key값에 해당하는 value는 하나의 상태로써 글로벌하게 존재하며 useUser의 호출에 의해서만 변경되며 다른 여러가지 방법으로 응용하여 사용할 수 있을것이다.

 

이러한 방식의 상태 관리 패턴은 약 1년 전쯤 SWR 커뮤니티에서 발견되어 꽤 많은 토론을 나눴던 패턴이다. 

이를 잘 응용한다면 글로벌한 상태관리를 SWR을 사용하여 해낼 수 있으며 어떤것이 더 나은 방법인지에 대해서는 개발자 본인이 잘 판단하여 사용하는것이 중요할 것 같다고 생각한다.

 

자기전에 이것저것 생각하다가 문득 생각이 난 방법인데 실제로도 잘 작동하여 글을 한 번 작성해보았다.

 

기존의 글로벌 상태 관리 방식과 사용 라이브러리만 다를 뿐 동작 방식은 굉장히 유사하기에 별 문제가 없을 것 같긴 하지만 혹시라도 여기에서 올 수 있는 문제점이나 단점에 대해 발견하면 추가적으로 서술하도록 하겠다.

 

실제로 이를 도와주기위해 만든 라이브러리도 있다. (절대 안쓸 것 같긴함)

https://www.npmjs.com/package/swr-global-state