Files
zellyy-finance/debug-chunk-error.cjs
hansoo 8343b25439 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>
2025-07-14 10:08:51 +09:00

311 lines
9.6 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Playwright를 사용한 ChunkLoadError 상세 분석 스크립트
*
* 브라우저를 자동화하여 콘솔 로그, 네트워크 요청, 오류를 상세히 캡처합니다.
*/
const { chromium } = require("playwright");
async function analyzeChunkLoadError() {
console.log("🔍 ChunkLoadError 상세 분석 시작...\n");
const browser = await chromium.launch({
headless: false, // 실제 브라우저 창을 열어서 시각적으로 확인
devtools: true, // 개발자 도구 자동 열기
args: [
"--disable-web-security",
"--disable-features=VizDisplayCompositor",
"--no-sandbox",
"--disable-dev-shm-usage",
],
});
const context = await browser.newContext({
viewport: { width: 1280, height: 720 },
// 캐시 비활성화로 항상 최신 리소스 요청
ignoreHTTPSErrors: true,
});
const page = await context.newPage();
// 이벤트 수집 배열
const consoleMessages = [];
const networkRequests = [];
const networkFailures = [];
const errors = [];
const chunkErrors = [];
// 콘솔 메시지 캡처
page.on("console", (msg) => {
const message = {
type: msg.type(),
text: msg.text(),
location: msg.location(),
timestamp: new Date().toISOString(),
};
consoleMessages.push(message);
// 실시간 콘솔 출력
const prefix =
{
error: "❌",
warning: "⚠️ ",
info: " ",
log: "📝",
}[msg.type()] || "📄";
console.log(`${prefix} [CONSOLE] ${msg.text()}`);
});
// 네트워크 요청 모니터링
page.on("request", (request) => {
const requestInfo = {
url: request.url(),
method: request.method(),
headers: request.headers(),
timestamp: new Date().toISOString(),
resourceType: request.resourceType(),
};
networkRequests.push(requestInfo);
// Clerk 관련 요청만 출력
if (request.url().includes("clerk")) {
console.log(`🌐 [REQUEST] ${request.method()} ${request.url()}`);
}
});
// 네트워크 응답 모니터링
page.on("response", (response) => {
const isClerkRelated = response.url().includes("clerk");
const status = response.status();
if (isClerkRelated) {
const statusIcon = status >= 400 ? "🔴" : status >= 300 ? "🟡" : "🟢";
console.log(`${statusIcon} [RESPONSE] ${status} ${response.url()}`);
}
// 실패한 응답 기록
if (status >= 400) {
networkFailures.push({
url: response.url(),
status: status,
statusText: response.statusText(),
headers: response.headers(),
timestamp: new Date().toISOString(),
});
}
});
// 페이지 오류 캡처
page.on("pageerror", (error) => {
const errorInfo = {
name: error.name,
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
};
errors.push(errorInfo);
// ChunkLoadError 특별 처리
if (
error.message.includes("Loading chunk") ||
error.name === "ChunkLoadError"
) {
chunkErrors.push(errorInfo);
console.log(`💥 [CHUNK ERROR] ${error.message}`);
console.log(` Stack: ${error.stack?.split("\n")[0]}`);
} else {
console.log(`❌ [PAGE ERROR] ${error.name}: ${error.message}`);
}
});
// 네트워크 실패 처리
page.on("requestfailed", (request) => {
const failure = {
url: request.url(),
method: request.method(),
failure: request.failure()?.errorText,
timestamp: new Date().toISOString(),
};
networkFailures.push(failure);
if (request.url().includes("clerk")) {
console.log(`💔 [REQUEST FAILED] ${request.url()}`);
console.log(` Error: ${request.failure()?.errorText}`);
}
});
try {
console.log("🚀 페이지 로딩 시작...");
// 무한 새로고침 감지를 위한 네비게이션 카운터
let navigationCount = 0;
page.on("framenavigated", () => {
navigationCount++;
console.log(`🔄 [NAVIGATION] 페이지 네비게이션 ${navigationCount}`);
if (navigationCount > 3) {
console.log("⚠️ 무한 새로고침 감지됨!");
}
});
// 페이지 로드 (타임아웃 30초로 단축)
await page.goto("http://localhost:3002", {
waitUntil: "domcontentloaded", // networkidle 대신 domcontentloaded 사용
timeout: 30000,
});
console.log("✅ 페이지 로드 완료. 10초 대기 중...");
// 무한 새로고침 체크를 위해 10초 대기
await page.waitForTimeout(10000);
console.log(`📊 총 네비게이션 횟수: ${navigationCount}`);
if (navigationCount > 3) {
console.log("❌ 무한 새로고침 문제 발생");
chunkErrors.push({
name: "InfiniteRefresh",
message: `무한 새로고침 감지: ${navigationCount}회 네비게이션`,
timestamp: new Date().toISOString(),
});
}
// JavaScript 실행 상태 확인
const isJSWorking = await page.evaluate(() => {
return (
typeof window.React !== "undefined" ||
document.querySelector("[data-reactroot]") !== null ||
document.querySelector("#root > *") !== null
);
});
console.log(`🔧 JavaScript 실행 상태: ${isJSWorking ? "정상" : "실패"}`);
// Clerk 로딩 상태 확인
const clerkStatus = await page.evaluate(() => {
return {
clerkLoaded: typeof window.Clerk !== "undefined",
clerkProviderExists:
document.querySelector("[data-clerk-provider]") !== null,
clerkErrors: window.sessionStorage?.getItem("chunkLoadErrorMaxRetries"),
skipClerk: window.sessionStorage?.getItem("skipClerk"),
};
});
console.log("🔐 Clerk 상태:", JSON.stringify(clerkStatus, null, 2));
// 페이지 내용 확인
const pageContent = await page.evaluate(() => ({
title: document.title,
hasContent: document.body.children.length > 1,
rootContent: document.getElementById("root")?.children.length || 0,
errorMessages: Array.from(document.querySelectorAll("*"))
.filter(
(el) =>
el.textContent?.includes("ChunkLoadError") ||
el.textContent?.includes("Loading chunk") ||
el.textContent?.includes("오류")
)
.map((el) => el.textContent?.substring(0, 100)),
}));
console.log("📄 페이지 내용 분석:", JSON.stringify(pageContent, null, 2));
// 추가로 10초 더 대기하여 지연된 오류 캡처
console.log("⏳ 추가 10초 대기 중 (지연된 오류 캡처)...");
await page.waitForTimeout(10000);
} catch (error) {
console.log(`💥 페이지 로드 실패: ${error.message}`);
}
// 결과 분석 및 출력
console.log("\n" + "=".repeat(80));
console.log("📊 분석 결과 요약");
console.log("=".repeat(80));
console.log(`\n🔢 통계:`);
console.log(` 콘솔 메시지: ${consoleMessages.length}`);
console.log(` 네트워크 요청: ${networkRequests.length}`);
console.log(` 네트워크 실패: ${networkFailures.length}`);
console.log(` 페이지 오류: ${errors.length}`);
console.log(` 청크 오류: ${chunkErrors.length}`);
if (chunkErrors.length > 0) {
console.log(`\n💥 ChunkLoadError 상세 정보:`);
chunkErrors.forEach((error, index) => {
console.log(` ${index + 1}. ${error.message}`);
console.log(` 시간: ${error.timestamp}`);
if (error.stack) {
console.log(
` 스택: ${error.stack.split("\n").slice(0, 3).join("\n ")}`
);
}
});
}
if (networkFailures.filter((f) => f.url?.includes("clerk")).length > 0) {
console.log(`\n💔 Clerk 관련 네트워크 실패:`);
networkFailures
.filter((f) => f.url?.includes("clerk"))
.forEach((failure, index) => {
console.log(` ${index + 1}. ${failure.url}`);
console.log(` 오류: ${failure.failure || failure.status}`);
console.log(` 시간: ${failure.timestamp}`);
});
}
// Clerk 관련 요청 분석
const clerkRequests = networkRequests.filter((req) =>
req.url.includes("clerk")
);
if (clerkRequests.length > 0) {
console.log(`\n🔐 Clerk 관련 요청 (${clerkRequests.length}개):`);
clerkRequests.forEach((req, index) => {
console.log(` ${index + 1}. ${req.method} ${req.url}`);
});
}
// 오류가 있는 콘솔 메시지
const errorMessages = consoleMessages.filter((msg) => msg.type === "error");
if (errorMessages.length > 0) {
console.log(`\n❌ 콘솔 오류 메시지 (${errorMessages.length}개):`);
errorMessages.forEach((msg, index) => {
console.log(` ${index + 1}. ${msg.text}`);
if (msg.location?.url) {
console.log(
` 위치: ${msg.location.url}:${msg.location.lineNumber}`
);
}
});
}
// 브라우저를 5초 더 열어둔 후 종료
console.log(
"\n🔍 5초 후 브라우저를 닫습니다. 직접 확인하고 싶다면 Ctrl+C로 중단하세요."
);
await page.waitForTimeout(5000);
await browser.close();
console.log("\n✅ 분석 완료!");
// ChunkLoadError 해결 제안
if (chunkErrors.length > 0) {
console.log("\n💡 ChunkLoadError 해결 제안:");
console.log(" 1. 개발 서버 재시작: npm run dev");
console.log(" 2. node_modules/.vite 캐시 삭제");
console.log(" 3. 브라우저 하드 새로고침: Ctrl+Shift+R");
console.log(
" 4. Clerk 설정 확인: .env 파일의 VITE_CLERK_PUBLISHABLE_KEY"
);
}
}
// 스크립트 실행
if (require.main === module) {
analyzeChunkLoadError().catch(console.error);
}
module.exports = { analyzeChunkLoadError };