코딩핑
[ Next.js / React ] debounce 제대로 작동 안하는 문제 해결 본문
현재 프로젝트에서 상품들을 필터링에 따라 쿼리스트링을 짜서 서버에서 데이터를 새로 받아오도록 구현을 하고자 했다.
사용자가 필터를 선택할 때마다 요청이 들어가면 비효율적이므로 사용자의 필터 선택이 끝났다고 판단되는 시점에 데이터를 가져오고자 디바운스를 1초로 설정하려고 했다. 이때 문제가 발생했다!!
디바운스란?
연속적으로 발생하는 이벤트를 마지막 호출만 유효하게 처리하도록 지연시키는 기술이다
처음 작성한 코드는 다음과 같다
디바운스 함수는 wait시간 만큼 func 실행을 지연시키고 createQueryString() 인자로 받아야하므로 인자를 받도록 작성했다
export default function debounce(func, wait) {
let id;
return function (...args) {
clearTimeout(id);
id = setTimeout(() => {
func(...args);
}, wait);
};
}
디바운스로 setQueryString을 넘겨줘서 이벤트가 끝나고 1초 후 실행되도록 하려고 다음과 같이 작성했다
useEffect(() => {
const createQueryString = () => {
const categoryQuery = selectedCategories.join(',');
const priceQuery = selectedPrices.join(',');
const queryParams = [];
if (categoryQuery) queryParams.push(`categories=${categoryQuery}`);
if (priceQuery) queryParams.push(`prices=${priceQuery}`);
return queryParams.join('&');
};
const debounceSetQueryString = debounce(setQueryString, 1000);
debounceSetQueryString(createQueryString());
}, [selectedCategories, selectedPrices]);
위와 같이 해주니 selectedCategories와 selectedPrices가 useState로 관리 돼서 변경 될 때마다 컴포넌트가 리렌더링이 돼 useEffect가 새로 호출됐다 이 때문에 debounce도 계속 새로 등록되면서 id를 기억하지 못해 cleartimeout이 제대로 동작 하지 않았다
해결
useCallback을 사용하여 debounce를 한번만 등록해 해결할 수 있었다!
useCallback이란?
useCallback은 함수를 메모제이션하여 컴포넌트가 리렌더링 될 때마다 생성되지 않고 의존성 배열이 변경될 때만 새로 생성 되도록 한다
변경되지 않았을 시에는 메모제이션된 동일한 함수를 재사용한다!
const createQueryString = useCallback(() => {
const categoryQuery = selectedCategories.join(',');
const priceQuery = selectedPrices.join(',');
const queryParams = [];
if (categoryQuery) queryParams.push(`categories=${categoryQuery}`);
if (priceQuery) queryParams.push(`prices=${priceQuery}`);
return queryParams.join('&');
}, [selectedCategories, selectedPrices]);
const debounceSetQueryString = useCallback(debounce(setQueryString, 1000), []);
useEffect(() => {
debounceSetQueryString(createQueryString());
}, [debounceSetQueryString, createQueryString]);
useCallback은 훅으로 컴포넌트의 최상위 레벨에서 호출해야 하므로 빼주고 debounce함수는 한번만 생성 되도록 []를 넣어주고, createQueryString은 selectedCategories, selectedPrices가 변경될때만 새로 생성되도록 했다
결과
useCallback을 사용하여 디바운스를 구현함으로써 사용자가 필터를 선택할 때마다 불필요한 요청이 발생하지 않도록 최적화할 수 있었다!
메모제이션을 잘 사용해야 성능이 좋은 코드를 작성할 수 있을 것 같은데 아직은 언제 써야하는지 판단하는 데 어려움이 있는 것 같다ㅜㅡㅜ 능숙해지는 그 날까지 파이팅@-@!
'프로젝트 > keynut' 카테고리의 다른 글
응답 지연 시간에 따른 Skeleton UI 최적화하기 (TanStack-Query, Suspense) + 업데이트 Suspense 제거 (4) | 2024.09.27 |
---|---|
KEYNUT 프로젝트 회고록 ☁️ (0) | 2024.08.20 |
데이터 로딩 중 Skeleton 컴포넌트 사용하기 (0) | 2024.07.19 |
[ Next.js, React-Query ] useInfiniteQuery를 사용해 무한 스크롤 구현하기 (0) | 2024.07.01 |
[ Next.js ] sizes 속성 사용하여 image 최적화 (0) | 2024.06.12 |