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,449 @@
#!/usr/bin/env node
/**
* Linear GitHub Actions 워크플로우 테스터
* GitHub Actions 환경을 시뮬레이션하여 Linear 통합을 테스트
*/
const { execSync } = require("child_process");
const path = require("path");
// Simple command line argument parsing
const args = process.argv.slice(2);
const options = {
linearApiKey: null,
testType: "all",
issueId: "ZEL-1",
prUrl: "https://github.com/zellycloud/zellyy-finance/pull/1",
author: "hansoo",
verbose: args.includes("--verbose") || args.includes("-v"),
help: args.includes("--help") || args.includes("-h"),
};
// Extract options from arguments
const linearApiIndex = args.findIndex((arg) => arg.startsWith("--linear-api="));
if (linearApiIndex !== -1) {
options.linearApiKey = args[linearApiIndex].split("=")[1];
}
const testTypeIndex = args.findIndex((arg) => arg.startsWith("--test-type="));
if (testTypeIndex !== -1) {
options.testType = args[testTypeIndex].split("=")[1];
}
const issueIdIndex = args.findIndex((arg) => arg.startsWith("--issue-id="));
if (issueIdIndex !== -1) {
options.issueId = args[issueIdIndex].split("=")[1];
}
/**
* GitHub Actions 이벤트 시뮬레이션
*/
const mockEvents = {
pullRequest: {
opened: {
event: "pull_request",
action: "opened",
prUrl: options.prUrl,
prAuthor: options.author,
prMerged: "false",
},
closed: {
event: "pull_request",
action: "closed",
prUrl: options.prUrl,
prAuthor: options.author,
prMerged: "true",
},
review: {
event: "pull_request_review",
reviewState: "approved",
reviewer: "reviewer-user",
prUrl: options.prUrl,
},
},
push: {
commit: {
event: "push",
action: "commit",
commitSha: "abc123def456",
commitMessage: `feat: implement feature [${options.issueId}]`,
prAuthor: options.author,
},
},
issue: {
opened: {
event: "issue",
action: "opened",
githubIssueUrl: "https://github.com/zellycloud/zellyy-finance/issues/1",
},
},
};
/**
* 스크립트 실행 함수
*/
async function runScript(scriptPath, args, description) {
console.log(`\n🔧 ${description}`);
console.log(` Script: ${scriptPath}`);
console.log(` Args: ${args.join(" ")}`);
try {
const command = `node ${scriptPath} ${args.join(" ")}`;
const result = execSync(command, {
encoding: "utf8",
cwd: process.cwd(),
env: {
...process.env,
LINEAR_API_KEY: options.linearApiKey,
},
});
if (options.verbose) {
console.log("📄 Output:");
console.log(result);
}
console.log("✅ Success");
return true;
} catch (error) {
console.error("❌ Failed:");
console.error(" Error:", error.message);
if (options.verbose && error.stdout) {
console.error(" Output:", error.stdout);
}
return false;
}
}
/**
* Pull Request 이벤트 테스트
*/
async function testPullRequestFlow() {
console.log("\n🔀 Testing Pull Request Flow");
console.log("===============================");
const results = [];
// PR opened
console.log("\n📝 1. Pull Request Opened");
const openedEvent = mockEvents.pullRequest.opened;
// Sync PR status
let success = await runScript(
"scripts/linear-sync.cjs",
[
`--issue-id=${options.issueId}`,
`--event=${openedEvent.event}`,
`--action=${openedEvent.action}`,
`--pr-url=${openedEvent.prUrl}`,
`--pr-author=${openedEvent.prAuthor}`,
`--pr-merged=${openedEvent.prMerged}`,
],
"Sync PR opened status"
);
results.push({ test: "PR Sync (opened)", success });
// Add PR comment
success = await runScript(
"scripts/linear-comment.cjs",
[
`--issue-id=${options.issueId}`,
`--event=${openedEvent.event}`,
`--action=${openedEvent.action}`,
`--pr-url=${openedEvent.prUrl}`,
`--pr-author=${openedEvent.prAuthor}`,
],
"Add PR comment"
);
results.push({ test: "PR Comment (opened)", success });
// PR review
console.log("\n👁 2. Pull Request Review");
const reviewEvent = mockEvents.pullRequest.review;
success = await runScript(
"scripts/linear-comment.cjs",
[
`--issue-id=${options.issueId}`,
`--event=${reviewEvent.event}`,
`--review-state=${reviewEvent.reviewState}`,
`--reviewer=${reviewEvent.reviewer}`,
`--pr-url=${reviewEvent.prUrl}`,
],
"Add review comment"
);
results.push({ test: "PR Review Comment", success });
// PR merged
console.log("\n✅ 3. Pull Request Merged");
const closedEvent = mockEvents.pullRequest.closed;
success = await runScript(
"scripts/linear-sync.cjs",
[
`--issue-id=${options.issueId}`,
`--event=${closedEvent.event}`,
`--action=${closedEvent.action}`,
`--pr-url=${closedEvent.prUrl}`,
`--pr-author=${closedEvent.prAuthor}`,
`--pr-merged=${closedEvent.prMerged}`,
],
"Sync PR merged status"
);
results.push({ test: "PR Sync (merged)", success });
success = await runScript(
"scripts/linear-comment.cjs",
[
`--issue-id=${options.issueId}`,
`--event=${closedEvent.event}`,
`--action=${closedEvent.action}`,
`--pr-url=${closedEvent.prUrl}`,
`--pr-author=${closedEvent.prAuthor}`,
],
"Add merge comment"
);
results.push({ test: "PR Comment (merged)", success });
return results;
}
/**
* Push 이벤트 테스트
*/
async function testPushFlow() {
console.log("\n🚀 Testing Push Flow");
console.log("====================");
const results = [];
const pushEvent = mockEvents.push.commit;
// Sync commit
let success = await runScript(
"scripts/linear-sync.cjs",
[
`--issue-id=${options.issueId}`,
`--event=${pushEvent.event}`,
`--action=${pushEvent.action}`,
],
"Sync commit status"
);
results.push({ test: "Push Sync", success });
// Add commit comment
success = await runScript(
"scripts/linear-comment.cjs",
[
`--issue-id=${options.issueId}`,
`--event=${pushEvent.event}`,
`--commit-sha=${pushEvent.commitSha}`,
`--commit-message=${pushEvent.commitMessage}`,
`--pr-author=${pushEvent.prAuthor}`,
],
"Add commit comment"
);
results.push({ test: "Push Comment", success });
return results;
}
/**
* Issue 이벤트 테스트
*/
async function testIssueFlow() {
console.log("\n📋 Testing Issue Flow");
console.log("=====================");
const results = [];
const issueEvent = mockEvents.issue.opened;
// Add issue comment
const success = await runScript(
"scripts/linear-comment.cjs",
[
`--issue-id=${options.issueId}`,
`--event=${issueEvent.event}`,
`--action=${issueEvent.action}`,
`--github-issue-url=${issueEvent.githubIssueUrl}`,
],
"Add issue comment"
);
results.push({ test: "Issue Comment", success });
return results;
}
/**
* 릴리즈 플로우 테스트
*/
async function testReleaseFlow() {
console.log("\n🎯 Testing Release Flow");
console.log("=======================");
const results = [];
const version = "1.1.0";
// Release preparation
let success = await runScript(
"scripts/linear-release-prep.cjs",
[version],
"Prepare release"
);
results.push({ test: "Release Prep", success });
// Release completion
success = await runScript(
"scripts/linear-release-complete.cjs",
[version],
"Complete release"
);
results.push({ test: "Release Complete", success });
return results;
}
/**
* 전체 결과 요약
*/
function printSummary(allResults) {
console.log("\n📊 Test Results Summary");
console.log("========================");
let totalTests = 0;
let passedTests = 0;
allResults.forEach((flowResults) => {
flowResults.forEach((result) => {
totalTests++;
if (result.success) passedTests++;
const status = result.success ? "✅ PASS" : "❌ FAIL";
console.log(`${status} ${result.test}`);
});
});
console.log(`\n🎯 Overall: ${passedTests}/${totalTests} tests passed`);
if (passedTests === totalTests) {
console.log("🎉 All workflow tests passed! Linear integration is ready.");
} else {
console.log("⚠️ Some tests failed. Check the configuration and scripts.");
}
return passedTests === totalTests;
}
/**
* 사용법 출력
*/
function printUsage() {
console.log(`
🧪 Linear GitHub Actions 워크플로우 테스터
사용법:
node scripts/linear-workflow-tester.cjs [옵션]
옵션:
--linear-api=KEY Linear API 키 지정
--test-type=TYPE 테스트 타입 (all, pr, push, issue, release)
--issue-id=ID 테스트할 Linear 이슈 ID (기본: ZEL-1)
--verbose, -v 상세 출력
--help, -h 이 도움말 출력
예시:
# 전체 워크플로우 테스트
node scripts/linear-workflow-tester.cjs --linear-api=lin_api_xxx
# PR 플로우만 테스트
node scripts/linear-workflow-tester.cjs --test-type=pr --issue-id=ZEL-123
# 상세 출력과 함께 테스트
node scripts/linear-workflow-tester.cjs --linear-api=lin_api_xxx --verbose
테스트 타입:
- all: 모든 워크플로우 테스트 (기본값)
- pr: Pull Request 플로우만 테스트
- push: Push 이벤트 플로우만 테스트
- issue: Issue 이벤트 플로우만 테스트
- release: 릴리즈 플로우만 테스트
환경 변수:
LINEAR_API_KEY Linear API 키
`);
}
/**
* 메인 함수
*/
async function main() {
if (options.help) {
printUsage();
process.exit(0);
}
console.log("🧪 Linear GitHub Actions 워크플로우 테스터");
console.log("==========================================\n");
// API 키 확인
const apiKey = options.linearApiKey || process.env.LINEAR_API_KEY;
if (!apiKey) {
console.error(
"❌ Linear API 키가 필요합니다. --linear-api 옵션을 사용하거나 LINEAR_API_KEY 환경 변수를 설정하세요."
);
process.exit(1);
}
options.linearApiKey = apiKey;
console.log(`🔑 Linear API Key: ${apiKey.substring(0, 15)}...`);
console.log(`🏷️ Test Issue ID: ${options.issueId}`);
console.log(`📝 Test Type: ${options.testType}`);
const allResults = [];
try {
// 테스트 타입에 따라 실행
switch (options.testType) {
case "pr":
allResults.push(await testPullRequestFlow());
break;
case "push":
allResults.push(await testPushFlow());
break;
case "issue":
allResults.push(await testIssueFlow());
break;
case "release":
allResults.push(await testReleaseFlow());
break;
case "all":
default:
allResults.push(await testPullRequestFlow());
allResults.push(await testPushFlow());
allResults.push(await testIssueFlow());
allResults.push(await testReleaseFlow());
break;
}
// 결과 요약
const allPassed = printSummary(allResults);
process.exit(allPassed ? 0 : 1);
} catch (error) {
console.error("\n❌ 테스트 실행 중 오류 발생:", error.message);
process.exit(1);
}
}
// 실행
if (require.main === module) {
main();
}
module.exports = {
testPullRequestFlow,
testPushFlow,
testIssueFlow,
testReleaseFlow,
};