[React] 비동기 통신 useSWR의 모든것

GONI·2022년 2월 24일
6
post-thumbnail

은 아니고...ㅎ



리액트를 다루면서 사용할 수 있는 비동기 통신은 여러개가 있다. 내가 알고 있는 방법으로는 redux-saga, useSwr, react-query 정도? 가 있는 것 같다.

자주 사용했던 것은 redux-saga, 간단해 보여서 사용하고 싶었던 것은 useSwr과 react-query 이다. 개인적으로 redux-saga는 다들 말하는 문제점

하나의 액션을 작성하는 데 너무 많은 부가적인 코드와 타입 선언이 필요하다.

에 동의하는 바이다. 새로운 것을 적용하기 귀찮은 감이 없지않아 있어서 지금까지 typesafe-action을 이용하여 각각의 타입을 모두 지정해주는 (번거로운) 일들을 거치며 redux-saga를 고집했는데, 마침 이번에 회사에서 새로운 프로젝트를 하면서 useSwr을 사용해보고 싶어서 (조르고 졸라서) useSwr을 적용하여 프로젝트를 마무리 하게 되었다. 그래서 사용 후기에 대한 글을 남겨보려고 한다!


1. SWR 사용법

먼저 간단한 예제부터 시작해보자. 매개변수가 없다는 것은 코드 작성 난이도를 낮추는데 정말이지 큰 이점이 아닐 수 없다. (하지만 피해갈 수는 없지)


hooks/useXXX.tsx 파일

export const useXXX = () => {
  const { data, isValidating, error } = useSWR<ReturnDataType>(
    'key', async () => {
      // 비동기 통신 부분
      const res = await ...
      return res.data
    },
  );

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

사용한 코드 중 가장 기초가 되는 코드를 가져왔다. 아마 공식문서만 봐도 사용법은 대충 알 수 있을 것이다.

const { data, isValidating, error }
  1. 먼저 data는 말그대로 비동기 통신으로 받아오는 데이터이다. 이에 대한 타입은 아래와 같이 선언해 줄 수 있다. 타입 선언도 굉~장~히 간단하다!
useSWR<ReturnDataType>
  1. 다음은 isValidation이다. 데이터 fetching중인지 알아내는 boolean 값이다. 쉽게 말하면 로딩중을 의미한다.

    의미는 간단하지만 개인적으로 생각하는 비동기 통신을 이용하는 가장 큰 이유인 로딩을 이렇게 쉽게 사용할 수 있다는 것에 너무 감사할 따름이다.

const isLoading = (!data && !error) || isValidating
  • 번외 : stack overflow를 찾아보면 로딩을 위와 같이 작성한다고도 한다. 뭔가 완벽하게 예외를 제거하기 위해서는 dataerror모두 고려해주는게 맞는 것 같기도 하지만 아직까지는 isValidation를 사용하면서 별다른 이상은 찾지 못했다...!
  1. error...는 에러,, 그냥 넘어가도 될 것 같다. (ㅎ)

  2. key값은 queryKey 개념으로, map을 사용하는 key처럼 고유 값을 넣어주면 된다.

  • 가끔 어 이게 왜 이렇게 되지? 라는 생각이 들 때 다른 화면에서 동일한 queryKey를 사용한 경우가 가끔 있었다고 한다...

- hooks 사용하기

 const { data, isLoading, error } = useXXX();

세상 간단하게 data를 불러오고 로딩 여부를 알 수 있다.
(🌟그저 빛🌟)


2. 매개변수가 존재하는 SWR 사용법

그렇다면 매개변수가 존재하여 통신할 때 param이나 query로 넣어줘야 한다면 어떻게 해야 할까?? 이 부분이 가장 힘들었다. 물론 알고 난 뒤에는 너무나 쉽게 사용할 수 있지만...

export const useXXX = (param: string) => {
  const { data, isValidating, error } = useSWR<ReturnDataType>(
    ['key', param], async () => {
      // 비동기 통신 부분
      const res = await axios.get('/api', { param : swrParams})...
      return res.data
    },
  );

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

이런 방식으로 매개변수를 넣어줄 수 있다. key값 부분을 배열로 변경한 뒤, 필요한 param을 배열 안에 넣어주면, useEffect의 dependency와 같이 작동하여, param 값이 변경될 때마다 api가 요청된다.

변경 가능한 매개변수가 2개 이상일 경우 배열 안에 나열해주면 된다.

['key', param, param2, param3...]

위와 같이 계속 나열해주면 된다. 이후에는 axios에서 option으로 사용할 수 있다. 간단하죠?


- hooks 사용하기

 const { data, isLoading, error } = useXXX(param);

사용하고자 하는 param만 넣어주면 끝이다.
역시 빛)


3. 리팩토링

사람의 욕심은 끝이 없다. 코드가 아직도 조금 긴 듯 하다. 공식문서에는 아예 fetcher 밖으로 빼놓고 사용하고 있어서, 이 방법도 한번 적용해 보고 싶었다.

역시 매개변수가 없을 때부터 시작해보자.

// fetcher 함수
const fetcher = async() => {
  const res = await axios.get(...)
  return res.data
}

export const useXXX = () => {
  const { data, isValidating, error } = useSWR<ReturnDataType>(
    'key', fetcher
  )
  return {
    data,
    isLoading: isValidating,
    error,
  };
};

다음은 매개변수가 있을 경우이다.

// fetcher 함수
const fetcher = async(param1: string) => {
  const res = await axios.get(...)
  return res.data
}

export const useXXX = () => {
  const { data, isValidating, error } = useSWR<ReturnDataType>(
    ['key', param1], 
    fetcher
  )
  return {
    data,
    isLoading: isValidating,
    error,
  };
};

앞서 말한 것처럼 매개변수가 있는 경우, 해당 매개변수가 key값으로 들어가기 때문에 따로 key값을 선언해주지 않아도 된다. 공식문서나 검색해보면 보통 param1부분에 url을 많이 넣어주는 데, 이 url이 결국 key값이 되는 것이다. 만약 매개변수가 두 개 이상일 경우

export const useXXX = () => {
  const { data, isValidating, error } = useSWR<ReturnDataType>(
    ['key', param1, param2, param3...], 
    fetcher
  )
  return {
    data,
    isLoading: isValidating,
    error,
  };
};

마찬가지로 key값을 따로 지정할 필요 없이 param1값이 key값과 동시에 매개변수가 되고, 나머지부분도 자연스럽게 매개변수로 들어가게 된다.

사실 fetcher를 빼는 것은 취향 차이인 것 같지만 fetcher를 api폴더안에 넣고, useXXX를 hooks폴더에 넣는다면 너무너무 관리하기 쉬울 것 같다는 개인적인 의견이 든다. 앞으로 많이 애용할 예정!

4. 기타 옵션


1. revalidateOnFocus

useSWR에는 독특한 옵션이 많다. 그 중 브라우저 창을 다른 곳으로 욞겼다가 포커싱할 때 다시 데이터를 불러와진다는 것이다.

https://swr.vercel.app/ko/docs/revalidation

근데 이 부분을 원치 않아서 옵션을 살펴보던 중, revalidateOnFocus을 사용하면 해당 부분을 막을 수 있었다.

export const useXXX = () => {
  const { data, isValidating, error } = useSWR<ReturnDataType>(
    ['key', param1], 
    fetcher,
  	// 포커싱 옵션
  	{ revalidateOnFocus: false },
  )
  return {
    data,
    isLoading: isValidating,
    error,
  };
};

2. 조건부 랜더링

마지막 옵션으로, useSWR을 어떤 버튼을 클릭했을 때나 특정 시점에 발생시킬 순 없을까? 에 대한 고민을 하던 중 이 부분 역시 공식문서에 잘 나와 있었다.

export const useXXX = (isTrigger: boolean) => {
  const { data, isValidating, error } = useSWR<ReturnDataType>(
    ['key', param1], 
    // 조건부 랜더링
    isTrigger ? fetcher : null,
  )
  return {
    data,
    isLoading: isValidating,
    error,
  };
};

위와 같이 코드를 작성하면 isTrigger 변수값에 따라 swr을 실행시킬 수 있다. 원하는 시점에 swr를 실행시키는 방법도 이렇게 배워볼 수 있었다.



비동기 통신 관련하여 redux saga를 기존에 사용해왔었는데, redux saga에 심신이 지쳐서 라이브러리를 탐구해보던 중 괜찮은 라이브러리를 발견하게 된 것 같다. 모두 기존에 사용하던 기술도 좋지만, 개발에 번아웃이 올 때 새로운 라이브러리를 탐구해보며 리프레쉬 시간을 갖는 것도 좋을 듯 하다! 💪

-끝-

profile
오로지 나의 기억력을 위한 일지

1개의 댓글

comment-user-thumbnail
2024년 2월 1일

너무 좋아요

답글 달기