diff --git a/.gitignore b/.gitignore index 9fd42ff..044699c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* +vitest-report.xml node_modules dist *.local @@ -21,5 +22,3 @@ dist *.njsproj *.sln *.sw? - -vitest-report.xml diff --git a/README.md b/README.md index c63c030..a05ab5b 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ await commonUtil.sleep(1000); // Pauses execution for 1 second const copied = await commonUtil.copyToClipboard("Hello, World!"); // true if successful const encoded = commonUtil.encodeBase64("Hello 한글!"); // Base64 encoded string const decoded = commonUtil.decodeBase64(encoded); // "Hello 한글!" +const debouncedFn = commonUtil.debounce(() => console.log("Called!"), 300); // Debounced function // Search Query utilities const queryParams = searchQueryUtil.getAllQuery(); // { key: ["value1", "value2"], id: "123" } @@ -158,6 +159,7 @@ const cleaned = clearNullProperties({ a: 1, b: null, c: 3 }); - `copyToClipboard(text: string): Promise` - Copies text to the user's clipboard. Uses modern Clipboard API with fallback to legacy execCommand method. Returns true if successful, false if failed. - `encodeBase64(str: string, options?: { convertSpecialChars?: boolean }): string` - Encodes a string to Base64 format with optional special character handling - `decodeBase64(str: string, options?: { convertSpecialChars?: boolean }): string` - Decodes a Base64 string back to original text with optional special character handling +- `debounce(fn: T, delay?: number): (...args: Parameters) => void` - Creates a debounced function that delays execution until after a specified delay (default 300ms) has passed since its last invocation ### SearchQueryUtil diff --git a/package/commonUtil/debounce/index.test.ts b/package/commonUtil/debounce/index.test.ts new file mode 100644 index 0000000..d8983db --- /dev/null +++ b/package/commonUtil/debounce/index.test.ts @@ -0,0 +1,41 @@ +import { describe } from "vitest"; +import { afterEach, expect, test, vi } from "vitest"; +import debounce from "./index"; + +describe("debounce 유틸 함수 테스트", () => { + afterEach(() => { + vi.useRealTimers(); + vi.restoreAllMocks(); + }); + + test("함수가 지정된 시간 (300ms) 후에 실행되어야 한다.", () => { + const fn = vi.fn(); + vi.useFakeTimers(); + const debounceFn = debounce(fn, 300); + debounceFn(); + + expect(fn).not.toHaveBeenCalled(); + + vi.advanceTimersByTime(299); + expect(fn).not.toHaveBeenCalled(); + + vi.advanceTimersByTime(1); + expect(fn).toHaveBeenCalledTimes(1); + }); + + test("여러 번 호출했을 경우에는 일정 시간 후 하나의 이벤트만 실행되어야 한다.", () => { + const fn = vi.fn(); + vi.useFakeTimers(); + const debounced = debounce(fn, 300); + + debounced({ name: "현우" }); + vi.advanceTimersByTime(100); + debounced({ name: "승준" }); + vi.advanceTimersByTime(100); + debounced({ name: "철수" }); + + vi.advanceTimersByTime(300); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith({ name: "철수" }); + }); +}); diff --git a/package/commonUtil/debounce/index.ts b/package/commonUtil/debounce/index.ts new file mode 100644 index 0000000..00b30e0 --- /dev/null +++ b/package/commonUtil/debounce/index.ts @@ -0,0 +1,10 @@ +export default function debounce void>( + fn: T, + delay: number = 300 +) { + let timer: ReturnType; + return (...args: Parameters) => { + clearTimeout(timer); + timer = setTimeout(() => fn(...args), delay); + }; +} diff --git a/package/commonUtil/index.ts b/package/commonUtil/index.ts index a2d3a45..b1d4460 100644 --- a/package/commonUtil/index.ts +++ b/package/commonUtil/index.ts @@ -4,3 +4,4 @@ export { default as sleep } from "./sleep"; export { default as copyToClipboard } from "./copyToClipboard"; export { default as encodeBase64 } from "./encodeBase64"; export { default as decodeBase64 } from "./decodeBase64"; +export { default as debounce } from "./debounce"; diff --git a/vitest-report.xml b/vitest-report.xml deleted file mode 100644 index aa7cfab..0000000 --- a/vitest-report.xml +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -