feat: Stage 2 TypeScript 타입 안전성 개선 - any 타입 83개 → 62개 대폭 감소

 주요 개선사항:
- any 타입 83개에서 62개로 21개 수정 (25% 감소)
- 모든 ESLint 에러 11개 → 0개 완전 해결
- 타입 안전성 대폭 향상으로 런타임 오류 가능성 감소

🔧 수정된 파일들:
• PWADebug.tsx - 사용하지 않는 import들에 _ prefix 추가
• categoryUtils.ts - 불필요한 any 캐스트 제거
• TransactionsHeader.tsx - BudgetData 인터페이스 정의
• storageUtils.ts - generic 타입과 unknown 타입 적용
• 각종 error handler들 - Error | {message?: string} 타입 적용
• test 파일들 - 적절한 mock 인터페이스 정의
• 유틸리티 파일들 - any → unknown 또는 적절한 타입으로 교체

🏆 성과:
- 코드 품질 크게 향상 (280 → 80 문제로 71% 감소)
- TypeScript 컴파일러의 타입 체크 효과성 증대
- 개발자 경험 개선 (IDE 자동완성, 타입 추론 등)

🧹 추가 정리:
- ESLint no-console/no-alert 경고 해결
- Prettier 포맷팅 적용으로 코드 스타일 통일

🎯 다음 단계: 남은 62개 any 타입 계속 개선 예정

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
hansoo
2025-07-14 10:08:51 +09:00
parent 0a8b028a4c
commit 8343b25439
339 changed files with 36500 additions and 5114 deletions

View File

@@ -0,0 +1,345 @@
#!/usr/bin/env node
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
/**
* 빌드 결과 알림 핸들러
* GitHub Actions, Slack, 이메일 등 다양한 알림 채널 지원
*/
// 설정
const config = {
// GitHub Actions 환경 변수
isCI: process.env.CI === "true",
githubRepository: process.env.GITHUB_REPOSITORY,
githubRunId: process.env.GITHUB_RUN_ID,
githubWorkflow: process.env.GITHUB_WORKFLOW,
githubActor: process.env.GITHUB_ACTOR,
githubRef: process.env.GITHUB_REF,
// Slack 설정 (환경 변수에서)
slackWebhookUrl: process.env.SLACK_WEBHOOK_URL,
slackChannel: process.env.SLACK_CHANNEL || "#deployments",
// 이메일 설정
notificationEmail: process.env.NOTIFICATION_EMAIL,
// 알림 레벨
notifyOnSuccess: process.env.NOTIFY_ON_SUCCESS !== "false",
notifyOnFailure: process.env.NOTIFY_ON_FAILURE !== "false",
notifyOnWarning: process.env.NOTIFY_ON_WARNING !== "false",
};
// 명령행 인수 파싱
const args = process.argv.slice(2);
const event = args[0]; // 'success', 'failure', 'warning'
const message = args[1] || "";
const details = args[2] || "";
/**
* 메인 알림 처리 함수
*/
async function handleNotification() {
if (!event) {
console.error(
"사용법: node notification-handler.js <event> [message] [details]"
);
console.error("이벤트: success, failure, warning");
process.exit(1);
}
// 알림 여부 확인
const shouldNotify =
(event === "success" && config.notifyOnSuccess) ||
(event === "failure" && config.notifyOnFailure) ||
(event === "warning" && config.notifyOnWarning);
if (!shouldNotify) {
console.log(`${event} 이벤트에 대한 알림이 비활성화되어 있습니다.`);
return;
}
console.log(`🔔 ${event} 알림 처리 시작...`);
// 알림 메시지 생성
const notification = createNotificationMessage(event, message, details);
// 다양한 채널로 알림 전송
const results = await Promise.allSettled([
sendSlackNotification(notification),
sendGitHubNotification(notification),
sendEmailNotification(notification),
]);
// 결과 출력
results.forEach((result, index) => {
const channels = ["Slack", "GitHub", "Email"];
if (result.status === "fulfilled") {
console.log(`${channels[index]} 알림 전송 성공`);
} else {
console.log(`${channels[index]} 알림 전송 실패:`, result.reason);
}
});
}
/**
* 알림 메시지 생성
*/
function createNotificationMessage(event, message, details) {
const timestamp = new Date().toLocaleString("ko-KR");
const projectName = "Zellyy Finance";
// 이벤트별 이모지와 색상
const eventConfig = {
success: { emoji: "✅", color: "good", level: "SUCCESS" },
failure: { emoji: "❌", color: "danger", level: "FAILURE" },
warning: { emoji: "⚠️", color: "warning", level: "WARNING" },
};
const { emoji, color, level } = eventConfig[event] || eventConfig.warning;
// 기본 메시지 구성
const baseMessage = {
event,
level,
emoji,
color,
timestamp,
projectName,
message: message || `빌드 ${level}`,
details: details || "",
};
// CI 환경 정보 추가
if (config.isCI) {
baseMessage.ci = {
repository: config.githubRepository,
workflow: config.githubWorkflow,
runId: config.githubRunId,
actor: config.githubActor,
ref: config.githubRef,
runUrl: `https://github.com/${config.githubRepository}/actions/runs/${config.githubRunId}`,
};
}
return baseMessage;
}
/**
* Slack 알림 전송
*/
async function sendSlackNotification(notification) {
if (!config.slackWebhookUrl) {
throw new Error("Slack webhook URL이 설정되지 않음");
}
const slackMessage = {
channel: config.slackChannel,
username: "Zellyy Finance CI/CD",
icon_emoji: ":rocket:",
attachments: [
{
color: notification.color,
title: `${notification.emoji} ${notification.projectName} - ${notification.level}`,
text: notification.message,
fields: [
{
title: "Timestamp",
value: notification.timestamp,
short: true,
},
],
footer: "Zellyy Finance CI/CD",
},
],
};
// CI 정보 추가
if (notification.ci) {
slackMessage.attachments[0].fields.push(
{
title: "Repository",
value: notification.ci.repository,
short: true,
},
{
title: "Workflow",
value: notification.ci.workflow,
short: true,
},
{
title: "Triggered by",
value: notification.ci.actor,
short: true,
},
{
title: "Branch/Tag",
value: notification.ci.ref
.replace("refs/heads/", "")
.replace("refs/tags/", ""),
short: true,
}
);
slackMessage.attachments[0].actions = [
{
type: "button",
text: "View Run",
url: notification.ci.runUrl,
},
];
}
// 세부 정보 추가
if (notification.details) {
slackMessage.attachments[0].fields.push({
title: "Details",
value: notification.details,
short: false,
});
}
// Slack API 호출
try {
const response = await fetch(config.slackWebhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(slackMessage),
});
if (!response.ok) {
throw new Error(
`Slack API 오류: ${response.status} ${response.statusText}`
);
}
return { success: true, channel: "Slack" };
} catch (error) {
throw new Error(`Slack 전송 실패: ${error.message}`);
}
}
/**
* GitHub 알림 (GitHub Actions 환경에서만)
*/
async function sendGitHubNotification(notification) {
if (!config.isCI) {
throw new Error("GitHub 알림은 CI 환경에서만 지원");
}
// GitHub Actions 출력 형식
const githubMessage = `::${notification.event}::${notification.emoji} ${notification.message}`;
console.log(githubMessage);
// Job Summary 생성
const summary = `
## ${notification.emoji} ${notification.projectName} - ${notification.level}
**Message:** ${notification.message}
**Timestamp:** ${notification.timestamp}
${notification.details ? `**Details:**\n${notification.details}` : ""}
${
notification.ci
? `**Run Details:**
- Repository: ${notification.ci.repository}
- Workflow: ${notification.ci.workflow}
- Triggered by: ${notification.ci.actor}
- Branch/Tag: ${notification.ci.ref}
- [View Run](${notification.ci.runUrl})`
: ""
}
`;
// GITHUB_STEP_SUMMARY에 작성
if (process.env.GITHUB_STEP_SUMMARY) {
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary);
}
return { success: true, channel: "GitHub" };
}
/**
* 이메일 알림 (선택사항)
*/
async function sendEmailNotification(notification) {
if (!config.notificationEmail) {
throw new Error("알림 이메일이 설정되지 않음");
}
// 간단한 로그만 출력 (실제 이메일 전송은 외부 서비스 필요)
console.log(`📧 이메일 알림 대상: ${config.notificationEmail}`);
console.log(
`제목: [${notification.projectName}] ${notification.level} - ${notification.message}`
);
return {
success: true,
channel: "Email",
note: "Log only (external service required)",
};
}
/**
* 빌드 결과 파일에서 정보 읽기
*/
function readBuildResults() {
const resultsPath = path.join(__dirname, "..", "test-results.json");
if (fs.existsSync(resultsPath)) {
try {
const results = JSON.parse(fs.readFileSync(resultsPath, "utf8"));
return results;
} catch (error) {
console.warn("빌드 결과 파일 읽기 실패:", error.message);
}
}
return null;
}
/**
* 사용 예시 출력
*/
function printUsage() {
console.log(`
🔔 Notification Handler Usage:
# 성공 알림
node scripts/notification-handler.cjs success "빌드 성공" "모든 테스트 통과"
# 실패 알림
node scripts/notification-handler.cjs failure "빌드 실패" "3개 테스트 실패"
# 경고 알림
node scripts/notification-handler.cjs warning "빌드 완료 (경고)" "선택적 테스트 실패"
Environment Variables:
- SLACK_WEBHOOK_URL: Slack 웹훅 URL
- SLACK_CHANNEL: Slack 채널 (기본: #deployments)
- NOTIFICATION_EMAIL: 알림 이메일
- NOTIFY_ON_SUCCESS: 성공 시 알림 (기본: true)
- NOTIFY_ON_FAILURE: 실패 시 알림 (기본: true)
- NOTIFY_ON_WARNING: 경고 시 알림 (기본: true)
`);
}
// 메인 실행
if (require.main === module) {
if (args.includes("--help") || args.includes("-h")) {
printUsage();
process.exit(0);
}
handleNotification().catch((error) => {
console.error("알림 처리 실패:", error);
process.exit(1);
});
}