Files
zellyy-finance/src/hooks/useToast.wrapper.ts
hansoo 9851627ff1 feat: Add CI/CD pipeline and code quality improvements
- Add GitHub Actions workflow for automated CI/CD
- Configure Node.js 18.x and 20.x matrix testing
- Add TypeScript type checking step
- Add ESLint code quality checks with enhanced rules
- Add Prettier formatting verification
- Add production build validation
- Upload build artifacts for deployment
- Set up automated testing on push/PR
- Replace console.log with environment-aware logger
- Add pre-commit hooks for code quality
- Exclude archive folder from linting

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-12 15:27:54 +09:00

158 lines
3.7 KiB
TypeScript

import {
useToast as useOriginalToast,
toast as originalToast,
} from "@/hooks/toast";
import { logger } from "@/utils/logger";
import type { ToasterToast } from "@/hooks/toast/types";
/**
* 토스트 중복 방지를 위한 설정값
*/
const TOAST_CONFIG = {
DEFAULT_DURATION: 3000, // 기본 토스트 표시 시간 (ms)
DEBOUNCE_TIME: 1500, // 동일 메시지 무시 시간 (ms)
HISTORY_LIMIT: 10, // 히스토리에 저장할 최대 토스트 수
CLEANUP_INTERVAL: 30000, // 히스토리 정리 주기 (ms)
HISTORY_RETENTION: 10000, // 히스토리 보관 기간 (ms)
};
/**
* 토스트 메시지 히스토리 인터페이스
*/
interface ToastHistoryItem {
message: string; // 메시지 내용 (title + description)
timestamp: number; // 생성 시간
variant?: string; // 토스트 종류 (default/destructive)
}
/**
* 토스트 히스토리 관리 클래스
*/
class ToastHistoryManager {
private history: ToastHistoryItem[] = [];
private cleanupInterval: ReturnType<typeof setInterval>;
constructor() {
// 주기적으로 오래된 히스토리 정리
this.cleanupInterval = setInterval(
() => this.cleanup(),
TOAST_CONFIG.CLEANUP_INTERVAL
);
}
/**
* 새 토스트를 히스토리에 추가
*/
add(message: string, variant?: string): void {
this.history.push({
message,
timestamp: Date.now(),
variant,
});
// 히스토리 크기 제한
if (this.history.length > TOAST_CONFIG.HISTORY_LIMIT) {
this.history.shift();
}
}
/**
* 오래된 히스토리 정리
*/
cleanup(): void {
const now = Date.now();
this.history = this.history.filter(
(item) => now - item.timestamp < TOAST_CONFIG.HISTORY_RETENTION
);
}
/**
* 최근에 동일한 토스트가 표시되었는지 확인
*/
isDuplicate(message: string, variant?: string): boolean {
const now = Date.now();
return this.history.some(
(item) =>
item.message === message &&
item.variant === variant &&
now - item.timestamp < TOAST_CONFIG.DEBOUNCE_TIME
);
}
/**
* 히스토리 초기화 (테스트용)
*/
clear(): void {
this.history = [];
}
/**
* 정리 타이머 해제 (메모리 누수 방지)
*/
dispose(): void {
clearInterval(this.cleanupInterval);
}
}
// 싱글톤 인스턴스 생성
const toastHistory = new ToastHistoryManager();
/**
* 메시지 내용 추출 (title + description)
*/
const extractMessage = (params: Omit<ToasterToast, "id">): string => {
return [params.title?.toString() || "", params.description?.toString() || ""]
.filter(Boolean)
.join(" - ");
};
/**
* 중복 방지 토스트 표시 함수
*/
const debouncedToast = (params: Omit<ToasterToast, "id">) => {
const message = extractMessage(params);
// 빈 메시지 무시
if (!message.trim()) {
logger.warn("빈 토스트 메시지가 무시되었습니다");
return;
}
// 중복 검사
if (toastHistory.isDuplicate(message, params.variant)) {
logger.info("중복 토스트 감지로 무시됨:", message);
return;
}
// 히스토리에 추가
toastHistory.add(message, params.variant);
// 실제 토스트 표시
originalToast({
...params,
duration: params.duration || TOAST_CONFIG.DEFAULT_DURATION,
});
};
/**
* 토스트 훅 래퍼
*/
export const useToast = () => {
const toast = useOriginalToast();
return {
...toast,
toast: debouncedToast,
};
};
/**
* 토스트 함수 래퍼 (훅을 사용하지 않는 컨텍스트용)
*/
export const toast = debouncedToast;
// 메모리 누수 방지를 위한 정리 함수 (필요시 호출)
export const disposeToastHistory = () => {
toastHistory.dispose();
};