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>
This commit is contained in:
hansoo
2025-07-12 15:27:54 +09:00
parent 6a208d6b06
commit 9851627ff1
411 changed files with 14458 additions and 8680 deletions

View File

@@ -1,6 +1,6 @@
import { useEffect, useRef } from "react";
import { useEffect, useRef } from 'react';
import { logger } from "@/utils/logger";
/**
* 트랜잭션 이벤트 리스너 훅 - 성능 및 메모리 누수 방지 개선 버전
*/
@@ -11,76 +11,86 @@ export const useTransactionsEvents = (
// 바운싱 방지 및 이벤트 제어를 위한 참조
const isProcessingRef = useRef(false);
const timeoutIdsRef = useRef<number[]>([]);
// 타임아웃 클리어 도우미 함수
const clearAllTimeouts = () => {
timeoutIdsRef.current.forEach(id => window.clearTimeout(id));
timeoutIdsRef.current.forEach((id) => window.clearTimeout(id));
timeoutIdsRef.current = [];
};
useEffect(() => {
console.log('[이벤트] 이벤트 리스너 설정');
logger.info("[이벤트] 이벤트 리스너 설정");
// 이벤트 핸들러 - 부하 조절(throttle) 적용
const handleEvent = (name: string, delay: number = 200) => {
const handleEvent = (name: string, delay = 200) => {
return (e?: any) => {
// 이미 처리 중인 경우 건너뜀
if (isProcessingRef.current) return;
console.log(`[이벤트] ${name} 이벤트 감지:`, e?.detail?.type || '');
if (isProcessingRef.current) {
return;
}
logger.info(`[이벤트] ${name} 이벤트 감지:`, e?.detail?.type || "");
isProcessingRef.current = true;
// 딜레이 적용 (이벤트 폭주 방지)
const timeoutId = window.setTimeout(() => {
loadTransactions();
isProcessingRef.current = false;
// 타임아웃 ID 목록에서 제거
timeoutIdsRef.current = timeoutIdsRef.current.filter(id => id !== timeoutId);
timeoutIdsRef.current = timeoutIdsRef.current.filter(
(id) => id !== timeoutId
);
}, delay);
// 타임아웃 ID 기록 (나중에 정리하기 위함)
timeoutIdsRef.current.push(timeoutId);
};
};
// 각 이벤트별 핸들러 생성
const handleTransactionUpdate = handleEvent('트랜잭션 업데이트', 150);
const handleTransactionDelete = handleEvent('트랜잭션 삭제', 200);
const handleTransactionChange = handleEvent('트랜잭션 변경', 150);
const handleTransactionUpdate = handleEvent("트랜잭션 업데이트", 150);
const handleTransactionDelete = handleEvent("트랜잭션 삭제", 200);
const handleTransactionChange = handleEvent("트랜잭션 변경", 150);
const handleStorageEvent = (e: StorageEvent) => {
if (e.key === 'transactions' || e.key === null) {
handleEvent('스토리지', 150)();
if (e.key === "transactions" || e.key === null) {
handleEvent("스토리지", 150)();
}
};
const handleFocus = handleEvent('포커스', 200);
const handleFocus = handleEvent("포커스", 200);
// 이벤트 리스너 등록
window.addEventListener('transactionUpdated', handleTransactionUpdate);
window.addEventListener('transactionDeleted', handleTransactionDelete);
window.addEventListener('transactionChanged', handleTransactionChange as EventListener);
window.addEventListener('storage', handleStorageEvent);
window.addEventListener('focus', handleFocus);
window.addEventListener("transactionUpdated", handleTransactionUpdate);
window.addEventListener("transactionDeleted", handleTransactionDelete);
window.addEventListener(
"transactionChanged",
handleTransactionChange as EventListener
);
window.addEventListener("storage", handleStorageEvent);
window.addEventListener("focus", handleFocus);
// 초기 데이터 로드
if (!isProcessingRef.current) {
loadTransactions();
}
// 클린업 함수
return () => {
console.log('[이벤트] 이벤트 리스너 정리');
logger.info("[이벤트] 이벤트 리스너 정리");
// 모든 이벤트 리스너 제거
window.removeEventListener('transactionUpdated', handleTransactionUpdate);
window.removeEventListener('transactionDeleted', handleTransactionDelete);
window.removeEventListener('transactionChanged', handleTransactionChange as EventListener);
window.removeEventListener('storage', handleStorageEvent);
window.removeEventListener('focus', handleFocus);
window.removeEventListener("transactionUpdated", handleTransactionUpdate);
window.removeEventListener("transactionDeleted", handleTransactionDelete);
window.removeEventListener(
"transactionChanged",
handleTransactionChange as EventListener
);
window.removeEventListener("storage", handleStorageEvent);
window.removeEventListener("focus", handleFocus);
// 모든 진행 중인 타임아웃 정리
clearAllTimeouts();
// 처리 상태 초기화
isProcessingRef.current = false;
};