내 세상에는 react-query밖에 없었어

전해림·2025년 7월 13일
post-thumbnail

안녕하세요. 프론트엔드 개발을 시작한지는 3년차 현업에서 프론트엔드 개발자로 일한지 1년차인 주니어 입니다. 지금까지의 제 개발을 되돌아 볼 겸 react-query와 swr 내용을 준비했습니다.

1. 내 세상에는 React-Query만 존재했어

저는 개발을 하며 상태관리 라이브러리를 선택할때 항상 react-query만을 썼었는데요 누군가 너는 왜
react-query만 써? 라고 물으면 고장이 나버리면서 어.. 나도 몰라 라고 답할 수 밖에 없었습니다.
왜냐면 처음 상태관리 라이브러리를 접할때 react-query를 접한 뒤 다른걸 쓸 생각도 하지 못했습니다.
익숙함이 좋았으니까요. 그런데 회사를 다니다보면 내가 들어보지도 못하거나 한번도 쓰지 않았던 스택을 쓰는 경우가 허다했고 쓰다보니 각각의 장단점이 보였습니다. 그래서 크게 느꼈던 swr 내용을 소개하고싶었습니다.

2. SWR

"데이터 가져오기를 위한 React Hooks" swr 공식 홈페이지를 보면 나와있는 타이틀입니다. 잘 기억해주세요
SWR은 Stale-While-Revalidate의 약자로, React 컴포넌트에서 사용할 수 있는 훅 형태의 SWR 라이브러리 입니다. SWR을 사용하면 간편하게 데이터를 가져오고, 캐시 하여 이전 데이터를 재사용하며, 신선한 데이터를 가져와서 사용자 경험을 향상시킬 수 있습니다.
사용자 프로필을 GET하는 코드로 예시를 들어보겠습니다.

export function useMemberProfile(id?: number) {

  const key = useMemo(() => {
    const base = profileId != null ? `/member/profile/${id}` : token ? '/member/profile' : null;
    return base ? [base, Date.now()] : null;
  }, [profileId, token]);

  const fetcher = async ([url]: [string, number]) => {
    const res = await clientFetcher<ProfileIdRes>(url, 'GET', undefined);
    return res.response;
  };

  const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, {
    revalidateOnFocus: false,
  });

  return {
    data,
    error,
    isLoading,
    isValidating,
    mutate,
  };
}

코드를 간단히 설명하자면 내 프로필과, 타인 프로필을 profileId에 따라서 분기처리를 하는 코드입니다.
useMemo함수는 조금 뒤에 설명을 하기로 하고, fetcher 부터 설명하자면.
key를 받아 api를 호출하는 함수입니다. 여기선 key가 [url, timestamp]형태이므로, 구조 분해로 url만 사용합니다. clientFetcher는 공통 fetch wrapper입니다. react-query와 유사한 형태로 사용됩니다.

마지막 useSWR을 호출하는 곳의 구조는 아래와 같습니다.

  • data: 정상적으로 받아온 응답 데이터
  • error: 요청 실패 시 에러 객체
  • isLoading: 최초 요청 로딩 여부
  • isValidating: 백그라운드 재검증 중 여부
  • mutate: 수동으로 캐시를 갱신하거나 재요청을 트리거하는 함수

옵션에 대해서도 설명하겠습니다.

  • revalidateOnFocus: 탭(또는 창)이 다시 포커스를 받을 때 데이터를 자동으로 재요청할지 여부
  • revalidateOnReconnect: 네트워크 재연결 시 자동 재요청 true
  • refreshWhenHidden: 페이지가 숨겨져 있어도 refreshInterval 작동할지 false
  • refreshWhenOffline: 오프라인일 때도 refreshInterval 작동할지 false
  • focusThrottleInterval: 포커스 간 재요청 최소 간격 (ms)

여기서 문제점이 하나 있었는데요 메인 페이지에서 상대방을 팔로우 한뒤 내 프로필로 들어오게되면 useSWR의 장점인 같은 url로 여러번 요청해도 바뀐점이 없다면 캐싱되어있던 데이터를 내려주는것 때문에 내 팔로워 수가 업데이트 되지 않는 문제가 있었습니다.

팔로우를 하는 api에서 내 프로필에 대해 강제 Mutate를 해줄수도 있지만 그렇게 되면 모든 api에 대한 Mutate를 해줘야 했기 때문에 비효율적이라고 생각해서 다른 방법을 고민하던 중 매번 다른 요청으로 인지하게 해서 매번 데이터를 가져오게 하는 방법을 선택했습니다.

now.Date()로 url요청 뒤에 시간을 넣어 매번 다른 요청으로 인지하게 하여 내 프로필에 접근하게 되면 매번 새로운 데이터를 가져오도록 했습니다.

하지만 이 방법은 전역 mutate를 하지 못한다는 단점과, 서버에 부하가 갈 수 있다는 문제점을 가지고 있기 때문에 필요한 부분에만 써야합니다.

3. SWR vs React-Query

1) 기본 사용 방식

💡 Provider 사용
SWR : 별도의 provider 없이 컴포넌트에서 바로 사용할 수 있다. -> 설정이 간단해 프로젝트 구성 시 빠르게 시작

React-Query : app 파일에서 반드시 Provider로 컴포넌트를 감싸야한다. -> 이 구조는 애플리케이션 전체에 걸친 데이터 관리를 일관되게 해주며, 쿼리 상태를 더 잘 통합할 수 있게 해준다

2) 데이터 관리와 처리

💡 데이터 전송, mutation
SWR : useSWR()는 기본적으로 데이터를 읽어오는(Read)데 사용되며, 데이터를 클라이언트 측에서
직접 변경할 때는 mutate() 함수를 사용한다. mutate()는 캐시된 데이터를 업데이트 하고, 서버 요청 없이 클라이언트에서 즉시 데이터를 변경하는 데 사용된다. 예를 들어, 사용자가 특정 데이터를 업데이트하면 이를 즉시 화면에 반영하고, 나중에 서버와 동기화한다.
SWR의 이 방식은 클라이언트 측에서 데이터가 자주 변경되거나, 서버 요청과 관계없이 빠른 UI 업데이트가 필요할 때 유리하다.

React-Query : useMutation을 사용하여 서버와 직접 상호작용하여 데이터를 전송하고 변경한다. 이 방식은 서버 상태를 동기화하고 관리하는 데 중점을 두기 떄문에, 서버의 데이터를 변경하는 작업이 더 명확하게 처리된다.
예를 들어, 사용자가 특정 데이터를 업데이트하면, 서버로 직접 요청을 보내어 데이터를 변경하고, 성공 시 해당 쿼리의 데이터를 다시 가져오도록 하여 클라이언트와 서버 간의 상태가 일관되게 유지되도록 한다.

💡 상태관리
SWR : 클라이언트 전역 상태로 데이터를 관리하여 모든 컴포넌트에서 데이터를 공유할 수 있다. 주로 CSR 에서 데이터를 빠르게 사용할 수 있는 환경에서 유리하다

React-Query : 서버 상태와 클라이언트 상태를 명확히 구분하여 서버에서 데이터를 받아오는 작업과 이를 관리하는 작업이 분리된다. 이는 서버 데이터의 동기화가 중요한 대규모 애플리케이션에 유리하다.

3) 데이터 안정성과 오류처리

💡 데이터 안정성
SWR : 최신 데이터를 즉시 업데이트하는 방식이므로 빠른 데이터 업데이트가 가능하지만,
이로 인해 race condition이 발생할 수 있다.

React-Query : 데이터 요청 빈도와 캐싱을 제어할 수 있어 데이터 안정성이 더 높으며 race condition
발생 가능성을 줄인다.

💡 에러 핸들링
SWR : 오류 처리 시스템이 없어, 사용자가 직접 오류 처리를 구현해야 한다.

React-Query : 내장된 에러 핸들링 시스템이 있어, 애플리케이션에서 발생하는 오류를 쉽게 관리할 수 있다. 복잡한 오류 처리가 필요한 애플리케이션에서 유리하다.

4) 성능 및 최적화

💡 랜더링 최적화
SWR : 쿼리마다 개별적으로 컴포넌트를 업데이트 하기 때문에 쿼리 개수가 많으면 렌더링 성능이 떨어질 수 있다.

React-Query : 여러 컴포넌트가 동일한 쿼리를 사용한 경우, 한번에 묶어서 업데이트 하여 성능이 더 뛰어나다.

💡 캐싱 및 Garbage Collection
SWR : 자동으로 데이터를 캐싱하여 네트워크 요청을 줄일 수 있지만, 오래된 데이터를 관리하는 방법이 부족하다.

React-Query : 캐싱에 대한 세밀한 제어가 가능하며, 사용되지 않는 쿼리를 자동으로 Garbage Collection
할 수 있다. 데이터가 빈번하게 업데이트되는 환경에서 React-Query가 더 유리하다.

5. 결론

SWR : next.js에서 권장, 단순하고 가벼운 솔루션을 찾을 때 적합하다. 데이터가 자주 변경되지 않거나 캐싱이 간단한 프로젝트에 적절한다.

React-Query : 데이터 캐싱 및 상태 관리를 세밀하게 제어해야 하거나 복잡한 애플리케이션에서 상태를 효율적으로 관리해야 할 때 적합하다.

참고 : https://velog.io/@clydehan/SWR-vs-Tanstack-QueryReact-Query-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EB%B0%8F-%EC%84%A0%ED%83%9D-%EA%B0%80%EC%9D%B4%EB%93%9C#-1-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EC%8B%9D

profile
프론트엔드 개발자 전해림입니다

0개의 댓글