React-Query & Debounce
웹 어플리케이션에서 데이터를 실시간으로 처리하고 제공하려면 많은 통신이 필요하다. 조회할 데이터가 많아지거나 짧은 시간에 반복적인 API호출을 하게 되면 서버에 과부하가 될 뿐만 아니라, 서비스를 이용하는 사용자도 네트워크에서 데이터를 반복적으로 받아오기 때문에 비 효율적이다. 이러한 상황에서, 효율적인 데이터 조회와 불필요한 API호출을 줄이기 위해 React-Query와 Debounce를 활용하여 어플리케이션을 최적화할 수 있다.
React-Query
React-Query는 React 어플리케이션에서 서버상태를 효과적으로 동기화하고 캐시하는데 사용되는 라이브러리이다. React-Query를 사용하여 데이터의 새로고침, 캐시유지 등을 손쉽게 관리할 수 있다. 주요 기능은 다음과 같다.
- 데이터 캐싱 : API 데이터 결과를 캐싱. 동일한 요청이 재발생하면 캐시된 데이터를 사용.
- 백그라운드 동기화 : 웹이 다시 포커스될때, 네트워크가 복원되었을 때 데이터를 자동으로 동기화.
- 페이지네이션, 무한스크롤 : 데이터 패치 패턴을 다양하게 제공.
그 외 다양한 기능들을 tanstack 홈페이지에서 확인해 볼 수 있다.
React-Query를 다음과 같이 예제 코드로 작성하여 사용할 수 있다.
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Components />
</QueryClientProvider>
)
}
먼저 내 프로젝트를 QueryClientProvider로 감싼다. QueryClientProvider와 QueryClient를 가져와 import후 컴포넌트를 감싸면 된다.
데이터를 가져오는 함수를 react-query를 참고하여 작성하면 된다.
function Example() {
const { isLoading, error, data } = useQuery('repoData', () =>
fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res =>
res.json()
)
)
if (isLoading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
...
)
}
fetch를 사용하면 다음과 같이 작성하면 된다.
위와 같이 작성하면, 해당 쿼리로 변경될때, 이전에 호출했었던 데이터는 캐싱되어 다시 호출되지 않는다. 캐싱된 데이터를 다시 불러오기 때문에 효율적으로 사용할 수 있다.
get-products에 해당하는 api를 쿼리로 작성해주면, 이전에 캐싱된 데이터 api는 호출되지 않고 그 외 get-products-count만 호출되는 것을 확인할 수 있다. 그렇다면 get-projects-count도 쿼리로 작성하여 데이터를 캐싱하고 반복적인 호출을 막아보자.
둘다 useQuery를 사용해서 데이터를 캐싱했다.
처음 패칭되는 데이터의 결과를 캐싱하여 다시 호출하면 데이터 페칭이 되지 않는 것을 확인할 수 있다. 반복적인 호출을 막은 것이다.
Debounce
Debounce는 이벤트를 그룹화하여 발생하는 이벤트를 줄이는 기술이다. 이를 이용하면, 사용자의 연속적인 입력에 따른 이벤트를 즉시 실행시키는 것을 방지하고, 일정 시간동안 이벤트를 그룹화하여 한 번만 실행하여 성능을 최적화할 수 있다.
주로 타이핑이나 스크롤과 같은 연속적인 이벤트를 처리할 때 유용하다. 예를 들어, 사용자가 검색어를 입력할 때 마다 API를 호출한다면 사용자의 원하는 검색어가 시작부터 끝까지 API 호출이 발생한다.
'클루시아'라는 단어를 검색하는 동안 [ㅋ, 크, 클, 클ㄹ, 클루, 클루ㅅ ...] 와 같이 입력값에 따라 여러번 API호출하게 되는 것을 볼 수 있다.
이를 방지하려면 Debounce Hook을 만들어 편리하게 사용할 수 있다.
useDebounce는 제네릭 함수로 주어진 value를 일정 delay시간만큼 지연시킨 후 상태를 업데이트 하는 방식으로 동작한다.
setDebounceValue state를 설정하고, useEffect를 이용하여 debounceValue 변수와 이 상태를 설정하는 setDebounceValue를 선언하고 초기값으로 입력값은 value로 설정한다.
useEffect는 value나 delay가 변경할 때 마다 실행된다. setTimeout으로 delay를 설정하고, delay시간 만큼이 지난 후에 debounce를 입력받은 value로 설정한다.
컴포넌트가 언마운트 되거나 value, delay가 변경되기 전에 clearTimeout으로 timer를 제거한다. 이렇게 설정하면 불필요한 상태 업데이트를 방지할 수 있다.
마지막으로 debounceValue를 반환한다.
useDebounce를 사용할 컴포넌트 내부에 useDebounce를 다음과 같이 작성해준다.
const [searchValue, setSearchValue] = useState<string>("");
const debounceSearchValue = useDebounce<string>(searchValue);
기존에 사용자의 입력값을 담던 searchValue를 debounce처리하여 debounceSearchValue로 처리하도록 했다.
이렇게 하면 사용자의 연속적인 입력은 searchValue에서 담당하여 저장하고, searchValue가 변한 후 일정 시간 delay 뒤에 debounceSearchValue가 업데이트 되는 방식이다.
기존에 작성된 useQuery에 searchValue에서 debounceSearchValue로 수정해준다.
const { data: total } = useQuery(
[
`/api/get-products-count?category=${selectedCategory}&contains=${searchValue}`,
],
() =>
fetch(
`/api/get-products-count?category=${selectedCategory}&contains=${searchValue}`
)
.then((res) => res.json())
.then((data) => Math.ceil(data.items / TAKE))
);
Debounce 적용 후
이제 [ㅋ, 크, 클, 클ㄹ, 클루, 클루ㅅ...]와 같이 API를 호출하지 않고 [클루시아]를 한번에 호출해버린다. delay에 따라 호출 시간을 짧게 가져갈지 길게 가져갈지 결정할 수 있으니, 프로젝트 규모나 서버의 상태에 따라 적절하게 사용하면 될 것 같다.
React-Query와 Debounce를 사용하여 데이터 캐싱 및 호출을 최적화하고 서버와 클라이언트의 동기화를 효과적으로 관리해봤다. 사용자에게는 더 나은 경험을 제공할 수 있고, 개발에서는 더 좋은 성능과 효율적으로 개발할 수 있었다.
'개발기록' 카테고리의 다른 글
[Google OAuth] Google Login API (1) | 2023.09.15 |
---|---|
[React-Query | Next.js | Prisma | Planetscale] Wishlist Mutation 적용 (0) | 2023.09.11 |
[Next.js] Google Map API 구현 (0) | 2023.09.05 |
[Next.js] Next.js + TypeScript + Tailwind CSS 초기세팅 (0) | 2023.08.31 |
[Next.js] context를 이용해보자! + TypeScript (0) | 2023.08.25 |