💡 회사에서 근태 관리 서비스를 만들던 도중, 결재 승인 내역 조회 시, 회사 내 센터 리스트 ID 값과 승인 상태, 신청 구분, 현재 페이지 값이 변할 때 마다 api가 재 호출되어 화면을 갱신해야 했다. 있는 그대로 api를 호출하게끔 작성하고 보니 하나의 api가 여러번 중복 호출되는 현상을 발견하였다. 이를 해결하기 위해 커스텀 훅을 만들었는데 그 과정을 알아보자!
import { useState, useEffect } from 'react';
import { isEqual } from 'lodash';
const useSetTimeout = <T>(value: T, delay: number): T => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
if (value == null) return;
const timer = setTimeout(() => {
if (!isEqual(value, debouncedValue)) {
setDebouncedValue(value);
}
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
};
이 훅은 value라는 입력값이 자주 바뀔 때, 지정한 시간(delay)이 지난 후에만 실제 값(debouncedValue)을 업데이트하도록 만들어진 디바운스(debounce)용 커스텀 훅이다.
변경이 감지되면, 지정한 시간(delay)만큼 기다린 후에 value와 현재 debouncedValue를 lodash.isEqual()로 비교해 값이 다를 때만 setDebouncedValue(value)를 호출함으로써 불필요한 상태 변경을 방지할 수 있다.
마지막으로 cleanup 함수로 이전 타이머를 취소함으로써, value가 빠르게 연달아 바뀌면 타이머가 계속 리셋되고, 최종 입력만 적용된다.
const rawParams = useMemo(() => {
if (!currentTeamId || !category) return undefined;
return {
teamId: currentTeamId,
category,
status,
page: activePage,
};
}, [currentTeamId, category, status, activePage]);
const debouncedParams = useSetTimeout(rawParams, 300);
위처럼 쓰면 queryKey가 사용자의 입력 등으로 자주 바뀌더라도, 마지막 변경 후 일정 시간이 지나야 반영되어 API 호출이 줄어든다!
(커스텀 훅 적용 전 (호출 3번))
(커스텀 훅 적용 후 (호출 1번))
항목 | 변경 전 (3회 호출) | 변경 후 (1회 호출) |
---|---|---|
총 렌더링 시간 | 3,511ms | 2,561ms |
System 처리 시간 | 1,021ms | 689ms |
Scripting 시간 | 537ms | 624ms |
Painting 시간 | 109ms | 60ms |
약 27%의 총 렌더링 시간 감소를 달성하여 페이지 로딩 시의 체감 속도를 변화시켰다! 이번 과정을 통해 불필요한 네트워크 낭비를 줄이고, 최적화를 이루어내서 뿌듯하다!