๐งฉ ๋ฌธ์ ์ ์
React์์ ๋๋ฐ์ด์ค ๊ธฐ๋ฅ์ด ํ์ํ ์ํฉ์ ๋์ํ๊ธฐ ์ํด ์ง์ ์ปค์คํ
ํ
useDebounce
๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๊ณ ์์์ต๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก useCallback
๊ณผ useRef
๋ง์ผ๋ก ๊ตฌํํ๊ธฐ ๋๋ฌธ์ ๊ฐ๋จํ๊ณ ์ง๊ด์ ์ด์์ผ๋,
์ฝ๋ฐฑ์ด ์ต์ ์ํ๋ฅผ ๋ฐ์ํ์ง ์๊ฑฐ๋ props๋ก ์ ๋ฌ ์ ๋ฆฌ๋ ๋๋ง์ ์ ๋ฐํ๋ ๋ฑ์ ์จ๊ฒจ์ง ์์ ์ฑ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
์๋ ์ฝ๋
import { useCallback, useRef } from "react";
type Debounce<T extends unknown[]> = (...args: T) => void;
export const useDebounce = <T extends unknown[]>(
func: (...args: T) => void,
delay: number,
): Debounce<T> => {
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
return useCallback(
(...args: T) => {
if (timerRef.current !== null) {
clearTimeout(timerRef.current);
}
timerRef.current = setTimeout(() => {
func(...args);
}, delay);
},
[func, delay],
);
};
๐ ํด๊ฒฐ๊ณผ์
๐ ์คํ์์ค ์ฝ๋ ๋ถ์
Next.js ๊ธฐ๋ฐ ๋์๋ณด๋ ์คํ์์ค ํ๋ก์ ํธ์์useDebouncedCallback
๊ณผ useCallbackRef
๋ผ๋ ์กฐํฉ์ด ๋ ์์ ์ ์ด๊ณ ๋ฒ์ฉ์ ์ธ ํํ๋ก ์ฌ์ฉ๋๊ณ ์๋ ๊ฒ์ ๋ฐ๊ฒฌํ์ต๋๋ค.
๐ ์ฃผ์ ๊ฐ์ ์
- ๋ช
ํํ ๋ค์ด๋ฐ
useDebounce
→useDebouncedCallback
์ผ๋ก ๋ณ๊ฒฝํ์ฌ ์ญํ ์ ๋ณด๋ค ๋ช ํํ ํํfunc
→callback
์ผ๋ก ์๋ฏธ๋ฅผ ๋ช ํํ ๋๋ฌ๋
useCallbackRef
๋์- callback์ ref๋ก ๊ณ ์ ํ์ฌ ์ต์ ์ํ๋ฅผ ์ ์งํ๋ฉด์๋ ๋ถํ์ํ re-render๋ฅผ ๋ง์
- ๋ด๋ถ์ ์ผ๋ก
useMemo
๋ก ํด๋ก์ ๋ฅผ ๊ณ ์ ํ์ฌ prop์ผ๋ก ์ ๋ฌํด๋ ์์ ํจ
- ํ์
์์ ์ฑ ๊ฐํ
<T extends unknown[]>
→<T extends (...args: never[]) => unknown>
๋ณ๊ฒฝ- ์ ๋ฌ๋ฐ๋ ํจ์ ์ ์ฒด๋ฅผ ์ ๋ค๋ฆญ ํ์
์ผ๋ก ์ ํํ์ฌ
Parameters<T>
์ReturnType<T>
๋ฅผ ๋ ์ ํํ ์ฌ์ฉํ ์ ์๊ฒ ํจ
๊ฐ์ ๋ ์ฝ๋
// use-callback-ref.ts
/**
* ์ฝ๋ฐฑ์ ์ฐธ์กฐ๋ก ๋ณํํ์ฌ prop์ผ๋ก ์ ๋ฌ๋ ๋ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ๊ฑฐ๋,
* deps๋ก ์ ๋ฌ๋ ๋ ํจ๊ณผ๋ฅผ ์ฌ์คํํ์ง ์๋๋ก ํ๋ ์ปค์คํ
ํ
*/
export function useCallbackRef<T extends (...args: never[]) => unknown>(
callback: T | undefined
): T {
const callbackRef = useRef(callback);
useEffect(() => {
callbackRef.current = callback;
});
// https://github.com/facebook/react/issues/19240
// useCallback์ด ์๋ useMemo๋ฅผ ์ฐ๋ ์ด์ ๋ ์ ๋งํฌ ์ฐธ๊ณ
return useMemo(
() => ((...args) => callbackRef.current?.(...args)) as T,
[]
);
}
// use-debounced-callback.ts
import { useCallbackRef } from '@/hooks/use-callback-ref';
export function useDebouncedCallback<T extends (...args: never[]) => unknown>(
callback: T,
delay: number
) {
const handleCallback = useCallbackRef(callback);
const debounceTimerRef = useRef(0);
useEffect(() => {
return () => clearTimeout(debounceTimerRef.current);
}, []);
return useCallback((...args: Parameters<T>) => {
clearTimeout(debounceTimerRef.current);
debounceTimerRef.current = setTimeout(
() => handleCallback(...args),
delay
);
}, [handleCallback, delay]);
}
๐ก ๋ฐฐ์ด์ & ์ธ์ฌ์ดํธ
- ๋จ์ํ ๋์ํ๋ ์ฝ๋์ ์์ ํ๊ฒ ๋์ํ๋ ์ฝ๋์ ์ฐจ์ด๋ฅผ ์ค๊ฐํ ์ ์์์ต๋๋ค.
useCallbackRef
์ ๊ฐ์ ์ถ์ํ๋ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์์ ์ฑ์ ๋์์ ๋์ด๋ ์ข์ ํจํด์์ ์๊ฒ ๋์์ต๋๋ค.- ์ ๋ค๋ฆญ ํ์
์ค๊ณ์์
never[]
๋ฅผ ํ์ฉํ ํจ์ ํ์ ์ ์ฝ์ ํ์ ์ถ๋ก ์ ํ๋๋ฅผ ๋์ด๊ณ ์ค์๋ฅผ ๋ฐฉ์งํ ์ ์์์ต๋๋ค. - ์ง์ ๋ง๋ ๊ฐ๋จํ ํ ๋, ์คํ์์ค์ ๊ด์ฉ์ ์ธ ํจํด๊ณผ ๋น๊ตํด๋ณด๋ฉฐ ๊ฐ์ ์ฌ์ง๋ฅผ ํ์ํด๋ณด๋ ์ต๊ด์ด ์ค์ํ๋ค๋ ๊ฒ์ ๋ค์ ํ ๋ฒ ๊นจ๋ฌ์์ต๋๋ค.
'TIL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[250616 TIL] JSON-LD (0) | 2025.06.17 |
---|---|
[250530 TIL] Responses API ์คํธ๋ฆฌ๋ฐ (0) | 2025.05.30 |
[250519 TIL] FSD (1) | 2025.05.19 |
[250512 TIL] use server, use client (1) | 2025.05.12 |
[250505 TIL] polling ์ ์ฉ๊ธฐ (0) | 2025.05.05 |