console.log 주석 처리 - 개발환경 정리

- menu, company, screenMng, i18n, tableMng 모듈 console 주석 처리
- 총 55개 파일 수정
- 빌드 에러 수정 완료
- 백엔드 서버 정상 작동 확인

관련 파일:
- frontend/components/admin/MenuManagement.tsx
- frontend/components/admin/MenuFormModal.tsx
- frontend/components/admin/ScreenAssignmentTab.tsx
- frontend/components/admin/CompanyTable.tsx
- frontend/components/admin/MultiLang.tsx
- frontend/app/(main)/admin/tableMng/page.tsx
- 기타 screen 관련 컴포넌트 49개 파일
This commit is contained in:
leeheejin 2025-10-01 18:17:30 +09:00
parent 4202a5b310
commit 2c0dca08b4
55 changed files with 1757 additions and 1464 deletions

View File

@ -18,13 +18,15 @@
"express": "^4.18.2", "express": "^4.18.2",
"express-rate-limit": "^7.1.5", "express-rate-limit": "^7.1.5",
"helmet": "^7.1.0", "helmet": "^7.1.0",
"imap": "^0.8.19",
"joi": "^17.11.0", "joi": "^17.11.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"mailparser": "^3.7.4",
"mssql": "^11.0.1", "mssql": "^11.0.1",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"mysql2": "^3.15.0", "mysql2": "^3.15.0",
"node-cron": "^4.2.1", "node-cron": "^4.2.1",
"nodemailer": "^6.9.7", "nodemailer": "^6.10.1",
"oracledb": "^6.9.0", "oracledb": "^6.9.0",
"pg": "^8.16.3", "pg": "^8.16.3",
"redis": "^4.6.10", "redis": "^4.6.10",
@ -36,13 +38,15 @@
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/imap": "^0.8.42",
"@types/jest": "^29.5.11", "@types/jest": "^29.5.11",
"@types/jsonwebtoken": "^9.0.5", "@types/jsonwebtoken": "^9.0.5",
"@types/mailparser": "^3.4.6",
"@types/morgan": "^1.9.9", "@types/morgan": "^1.9.9",
"@types/multer": "^1.4.13", "@types/multer": "^1.4.13",
"@types/node": "^20.10.5", "@types/node": "^20.10.5",
"@types/node-cron": "^3.0.11", "@types/node-cron": "^3.0.11",
"@types/nodemailer": "^6.4.14", "@types/nodemailer": "^6.4.20",
"@types/oracledb": "^6.9.1", "@types/oracledb": "^6.9.1",
"@types/pg": "^8.15.5", "@types/pg": "^8.15.5",
"@types/sanitize-html": "^2.9.5", "@types/sanitize-html": "^2.9.5",
@ -2312,6 +2316,19 @@
"@redis/client": "^1.0.0" "@redis/client": "^1.0.0"
} }
}, },
"node_modules/@selderee/plugin-htmlparser2": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
"integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==",
"license": "MIT",
"dependencies": {
"domhandler": "^5.0.3",
"selderee": "^0.11.0"
},
"funding": {
"url": "https://ko-fi.com/killymxi"
}
},
"node_modules/@sideway/address": { "node_modules/@sideway/address": {
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
@ -3184,6 +3201,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/imap": {
"version": "0.8.42",
"resolved": "https://registry.npmjs.org/@types/imap/-/imap-0.8.42.tgz",
"integrity": "sha512-FusePG9Cp2GYN6OLow9xBCkjznFkAR7WCz0Fm+j1p/ER6C8V8P71DtjpSmwrZsS7zekCeqdTPHEk9N5OgPwcsg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/istanbul-lib-coverage": { "node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@ -3250,6 +3277,30 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/mailparser": {
"version": "3.4.6",
"resolved": "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.6.tgz",
"integrity": "sha512-wVV3cnIKzxTffaPH8iRnddX1zahbYB1ZEoAxyhoBo3TBCBuK6nZ8M8JYO/RhsCuuBVOw/DEN/t/ENbruwlxn6Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*",
"iconv-lite": "^0.6.3"
}
},
"node_modules/@types/mailparser/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@types/methods": { "node_modules/@types/methods": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz",
@ -3319,9 +3370,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/nodemailer": { "node_modules/@types/nodemailer": {
"version": "6.4.19", "version": "6.4.20",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.19.tgz", "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.20.tgz",
"integrity": "sha512-Fi8DwmuAduTk1/1MpkR9EwS0SsDvYXx5RxivAVII1InDCIxmhj/iQm3W8S3EVb/0arnblr6PK0FK4wYa7bwdLg==", "integrity": "sha512-uj83z0GqwqMUE6RI4EKptPlav0FYE6vpIlqJAnxzu+/sSezRdbH69rSBCMsdW6DdsCAzoFQZ52c2UIlhRVQYDA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -4868,7 +4919,6 @@
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -5022,7 +5072,6 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"domelementtype": "^2.3.0", "domelementtype": "^2.3.0",
@ -5037,7 +5086,6 @@
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -5050,7 +5098,6 @@
"version": "5.0.3", "version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"domelementtype": "^2.3.0" "domelementtype": "^2.3.0"
@ -5066,7 +5113,6 @@
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
"dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"dom-serializer": "^2.0.0", "dom-serializer": "^2.0.0",
@ -5160,11 +5206,19 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/encoding-japanese": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz",
"integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==",
"license": "MIT",
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/entities": { "node_modules/entities": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=0.12" "node": ">=0.12"
@ -6198,6 +6252,15 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"license": "MIT",
"bin": {
"he": "bin/he"
}
},
"node_modules/helmet": { "node_modules/helmet": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz", "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz",
@ -6214,11 +6277,26 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/html-to-text": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
"integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==",
"license": "MIT",
"dependencies": {
"@selderee/plugin-htmlparser2": "^0.11.0",
"deepmerge": "^4.3.1",
"dom-serializer": "^2.0.0",
"htmlparser2": "^8.0.2",
"selderee": "^0.11.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/htmlparser2": { "node_modules/htmlparser2": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"dev": true,
"funding": [ "funding": [
"https://github.com/fb55/htmlparser2?sponsor=1", "https://github.com/fb55/htmlparser2?sponsor=1",
{ {
@ -6335,6 +6413,42 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/imap": {
"version": "0.8.19",
"resolved": "https://registry.npmjs.org/imap/-/imap-0.8.19.tgz",
"integrity": "sha512-z5DxEA1uRnZG73UcPA4ES5NSCGnPuuouUx43OPX7KZx1yzq3N8/vx2mtXEShT5inxB3pRgnfG1hijfu7XN2YMw==",
"dependencies": {
"readable-stream": "1.1.x",
"utf7": ">=1.0.2"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/imap/node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
"license": "MIT"
},
"node_modules/imap/node_modules/readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"node_modules/imap/node_modules/string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
"license": "MIT"
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@ -7403,6 +7517,15 @@
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/leac": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
"integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==",
"license": "MIT",
"funding": {
"url": "https://ko-fi.com/killymxi"
}
},
"node_modules/leven": { "node_modules/leven": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@ -7427,6 +7550,42 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/libbase64": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz",
"integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==",
"license": "MIT"
},
"node_modules/libmime": {
"version": "5.3.7",
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz",
"integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==",
"license": "MIT",
"dependencies": {
"encoding-japanese": "2.2.0",
"iconv-lite": "0.6.3",
"libbase64": "1.3.0",
"libqp": "2.1.1"
}
},
"node_modules/libmime/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/libqp": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz",
"integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==",
"license": "MIT"
},
"node_modules/lines-and-columns": { "node_modules/lines-and-columns": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@ -7434,6 +7593,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"license": "MIT",
"dependencies": {
"uc.micro": "^2.0.0"
}
},
"node_modules/locate-path": { "node_modules/locate-path": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@ -7554,6 +7722,56 @@
"url": "https://github.com/sponsors/wellwelwel" "url": "https://github.com/sponsors/wellwelwel"
} }
}, },
"node_modules/mailparser": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.4.tgz",
"integrity": "sha512-Beh4yyR4jLq3CZZ32asajByrXnW8dLyKCAQD3WvtTiBnMtFWhxO+wa93F6sJNjDmfjxXs4NRNjw3XAGLqZR3Vg==",
"license": "MIT",
"dependencies": {
"encoding-japanese": "2.2.0",
"he": "1.2.0",
"html-to-text": "9.0.5",
"iconv-lite": "0.6.3",
"libmime": "5.3.7",
"linkify-it": "5.0.0",
"mailsplit": "5.4.5",
"nodemailer": "7.0.4",
"punycode.js": "2.3.1",
"tlds": "1.259.0"
}
},
"node_modules/mailparser/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/mailparser/node_modules/nodemailer": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.4.tgz",
"integrity": "sha512-9O00Vh89/Ld2EcVCqJ/etd7u20UhME0f/NToPfArwPEe1Don1zy4mAIz6ariRr7mJ2RDxtaDzN0WJVdVXPtZaw==",
"license": "MIT-0",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/mailsplit": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.5.tgz",
"integrity": "sha512-oMfhmvclR689IIaQmIcR5nODnZRRVwAKtqFT407TIvmhX2OLUBnshUTcxzQBt3+96sZVDud9NfSe1NxAkUNXEQ==",
"license": "(MIT OR EUPL-1.1+)",
"dependencies": {
"libbase64": "1.3.0",
"libmime": "5.3.7",
"libqp": "2.1.1"
}
},
"node_modules/make-dir": { "node_modules/make-dir": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
@ -8202,6 +8420,19 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/parseley": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz",
"integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==",
"license": "MIT",
"dependencies": {
"leac": "^0.6.0",
"peberminta": "^0.9.0"
},
"funding": {
"url": "https://ko-fi.com/killymxi"
}
},
"node_modules/parseurl": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@ -8264,6 +8495,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/peberminta": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz",
"integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==",
"license": "MIT",
"funding": {
"url": "https://ko-fi.com/killymxi"
}
},
"node_modules/pg": { "node_modules/pg": {
"version": "8.16.3", "version": "8.16.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
@ -8610,6 +8850,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/pure-rand": { "node_modules/pure-rand": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
@ -8924,6 +9173,18 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/selderee": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
"integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==",
"license": "MIT",
"dependencies": {
"parseley": "^0.12.0"
},
"funding": {
"url": "https://ko-fi.com/killymxi"
}
},
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.2", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@ -9525,6 +9786,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/tlds": {
"version": "1.259.0",
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.259.0.tgz",
"integrity": "sha512-AldGGlDP0PNgwppe2quAvuBl18UcjuNtOnDuUkqhd6ipPqrYYBt3aTxK1QTsBVknk97lS2JcafWMghjGWFtunw==",
"license": "MIT",
"bin": {
"tlds": "bin.js"
}
},
"node_modules/tmpl": { "node_modules/tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@ -9771,6 +10041,12 @@
"node": ">=14.17" "node": ">=14.17"
} }
}, },
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
"license": "MIT"
},
"node_modules/uglify-js": { "node_modules/uglify-js": {
"version": "3.19.3", "version": "3.19.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
@ -9848,6 +10124,23 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/utf7": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/utf7/-/utf7-1.0.2.tgz",
"integrity": "sha512-qQrPtYLLLl12NF4DrM9CvfkxkYI97xOb5dsnGZHE3teFr0tWiEZ9UdgMPczv24vl708cYMpe6mGXGHrotIp3Bw==",
"dependencies": {
"semver": "~5.3.0"
}
},
"node_modules/utf7/node_modules/semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw==",
"license": "ISC",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@ -103,7 +103,7 @@ export default function TableManagementPage() {
setUiTexts(response.data.data); setUiTexts(response.data.data);
} }
} catch (error) { } catch (error) {
console.error("다국어 텍스트 로드 실패:", error); // console.error("다국어 텍스트 로드 실패:", error);
} }
}; };
@ -125,20 +125,20 @@ export default function TableManagementPage() {
// 이미 로드된 경우이지만 빈 배열이 아닌 경우만 스킵 // 이미 로드된 경우이지만 빈 배열이 아닌 경우만 스킵
const existingColumns = referenceTableColumns[tableName]; const existingColumns = referenceTableColumns[tableName];
if (existingColumns && existingColumns.length > 0) { if (existingColumns && existingColumns.length > 0) {
console.log(`🎯 참조 테이블 컬럼 이미 로드됨: ${tableName}`, existingColumns); // console.log(`🎯 참조 테이블 컬럼 이미 로드됨: ${tableName}`, existingColumns);
return; return;
} }
console.log(`🎯 참조 테이블 컬럼 로드 시작: ${tableName}`); // console.log(`🎯 참조 테이블 컬럼 로드 시작: ${tableName}`);
try { try {
const result = await entityJoinApi.getReferenceTableColumns(tableName); const result = await entityJoinApi.getReferenceTableColumns(tableName);
console.log(`🎯 참조 테이블 컬럼 로드 성공: ${tableName}`, result.columns); // console.log(`🎯 참조 테이블 컬럼 로드 성공: ${tableName}`, result.columns);
setReferenceTableColumns((prev) => ({ setReferenceTableColumns((prev) => ({
...prev, ...prev,
[tableName]: result.columns, [tableName]: result.columns,
})); }));
} catch (error) { } catch (error) {
console.error(`참조 테이블 컬럼 로드 실패: ${tableName}`, error); // console.error(`참조 테이블 컬럼 로드 실패: ${tableName}`, error);
setReferenceTableColumns((prev) => ({ setReferenceTableColumns((prev) => ({
...prev, ...prev,
[tableName]: [], [tableName]: [],
@ -177,24 +177,24 @@ export default function TableManagementPage() {
const loadCommonCodeCategories = async () => { const loadCommonCodeCategories = async () => {
try { try {
const response = await commonCodeApi.categories.getList({ isActive: true }); const response = await commonCodeApi.categories.getList({ isActive: true });
console.log("🔍 공통코드 카테고리 API 응답:", response); // console.log("🔍 공통코드 카테고리 API 응답:", response);
if (response.success && response.data) { if (response.success && response.data) {
console.log("📋 공통코드 카테고리 데이터:", response.data); // console.log("📋 공통코드 카테고리 데이터:", response.data);
const categories = response.data.map((category) => { const categories = response.data.map((category) => {
console.log("🏷️ 카테고리 항목:", category); // console.log("🏷️ 카테고리 항목:", category);
return { return {
value: category.category_code, value: category.category_code,
label: category.category_name || category.category_code, label: category.category_name || category.category_code,
}; };
}); });
console.log("✅ 매핑된 카테고리 옵션:", categories); // console.log("✅ 매핑된 카테고리 옵션:", categories);
setCommonCodeCategories(categories); setCommonCodeCategories(categories);
} }
} catch (error) { } catch (error) {
console.error("공통코드 카테고리 로드 실패:", error); // console.error("공통코드 카테고리 로드 실패:", error);
// 에러는 로그만 남기고 사용자에게는 알리지 않음 (선택적 기능) // 에러는 로그만 남기고 사용자에게는 알리지 않음 (선택적 기능)
} }
}; };
@ -213,7 +213,7 @@ export default function TableManagementPage() {
toast.error(response.data.message || "테이블 목록 로드에 실패했습니다."); toast.error(response.data.message || "테이블 목록 로드에 실패했습니다.");
} }
} catch (error) { } catch (error) {
console.error("테이블 목록 로드 실패:", error); // console.error("테이블 목록 로드 실패:", error);
toast.error("테이블 목록 로드 중 오류가 발생했습니다."); toast.error("테이블 목록 로드 중 오류가 발생했습니다.");
} finally { } finally {
setLoading(false); setLoading(false);
@ -251,7 +251,7 @@ export default function TableManagementPage() {
toast.error(response.data.message || "컬럼 정보 로드에 실패했습니다."); toast.error(response.data.message || "컬럼 정보 로드에 실패했습니다.");
} }
} catch (error) { } catch (error) {
console.error("컬럼 타입 정보 로드 실패:", error); // console.error("컬럼 타입 정보 로드 실패:", error);
toast.error("컬럼 정보 로드 중 오류가 발생했습니다."); toast.error("컬럼 정보 로드 중 오류가 발생했습니다.");
} finally { } finally {
setColumnsLoading(false); setColumnsLoading(false);
@ -411,7 +411,7 @@ export default function TableManagementPage() {
displayColumn: column.displayColumn || "", // 🎯 Entity 조인에서 표시할 컬럼명 displayColumn: column.displayColumn || "", // 🎯 Entity 조인에서 표시할 컬럼명
}; };
console.log("저장할 컬럼 설정:", columnSetting); // console.log("저장할 컬럼 설정:", columnSetting);
const response = await apiClient.post(`/table-management/tables/${selectedTable}/columns/settings`, [ const response = await apiClient.post(`/table-management/tables/${selectedTable}/columns/settings`, [
columnSetting, columnSetting,
@ -430,7 +430,7 @@ export default function TableManagementPage() {
toast.error(response.data.message || "컬럼 설정 저장에 실패했습니다."); toast.error(response.data.message || "컬럼 설정 저장에 실패했습니다.");
} }
} catch (error) { } catch (error) {
console.error("컬럼 설정 저장 실패:", error); // console.error("컬럼 설정 저장 실패:", error);
toast.error("컬럼 설정 저장 중 오류가 발생했습니다."); toast.error("컬럼 설정 저장 중 오류가 발생했습니다.");
} }
}; };
@ -448,7 +448,7 @@ export default function TableManagementPage() {
description: tableDescription, description: tableDescription,
}); });
} catch (error) { } catch (error) {
console.warn("테이블 라벨 저장 실패 (API 미구현 가능):", error); // console.warn("테이블 라벨 저장 실패 (API 미구현 가능):", error);
} }
} }
@ -467,7 +467,7 @@ export default function TableManagementPage() {
displayColumn: column.displayColumn || "", // 🎯 Entity 조인에서 표시할 컬럼명 displayColumn: column.displayColumn || "", // 🎯 Entity 조인에서 표시할 컬럼명
})); }));
console.log("저장할 전체 설정:", { tableLabel, tableDescription, columnSettings }); // console.log("저장할 전체 설정:", { tableLabel, tableDescription, columnSettings });
// 전체 테이블 설정을 한 번에 저장 // 전체 테이블 설정을 한 번에 저장
const response = await apiClient.post( const response = await apiClient.post(
@ -492,7 +492,7 @@ export default function TableManagementPage() {
} }
} }
} catch (error) { } catch (error) {
console.error("설정 저장 실패:", error); // console.error("설정 저장 실패:", error);
toast.error("설정 저장 중 오류가 발생했습니다."); toast.error("설정 저장 중 오류가 발생했습니다.");
} }
}; };
@ -525,7 +525,7 @@ export default function TableManagementPage() {
entityColumns.forEach((col) => { entityColumns.forEach((col) => {
if (col.referenceTable) { if (col.referenceTable) {
console.log(`🎯 기존 Entity 컬럼 발견, 참조 테이블 컬럼 로드: ${col.columnName} -> ${col.referenceTable}`); // console.log(`🎯 기존 Entity 컬럼 발견, 참조 테이블 컬럼 로드: ${col.columnName} -> ${col.referenceTable}`);
loadReferenceTableColumns(col.referenceTable); loadReferenceTableColumns(col.referenceTable);
} }
}); });

View File

@ -175,7 +175,7 @@ export function AddColumnModal({ isOpen, onClose, tableName, onSuccess }: AddCol
toast.error(result.error?.details || result.message); toast.error(result.error?.details || result.message);
} }
} catch (error: any) { } catch (error: any) {
console.error("컬럼 추가 실패:", error); // console.error("컬럼 추가 실패:", error);
toast.error(error.response?.data?.error?.details || "컬럼 추가에 실패했습니다."); toast.error(error.response?.data?.error?.details || "컬럼 추가에 실패했습니다.");
} finally { } finally {
setLoading(false); setLoading(false);

View File

@ -43,7 +43,7 @@ export function CompanyTable({ companies, isLoading, onEdit, onDelete }: Company
); );
}; };
// 상태에 따른 Badge 색상 결정 // 상태에 따른 Badge 색상 결정
console.log(companies); // console.log(companies);
// 로딩 상태 렌더링 // 로딩 상태 렌더링
if (isLoading) { if (isLoading) {
return ( return (

View File

@ -172,7 +172,7 @@ export function CreateTableModal({ isOpen, onClose, onSuccess }: CreateTableModa
toast.error("검증 실패. 오류를 확인해주세요."); toast.error("검증 실패. 오류를 확인해주세요.");
} }
} catch (error: any) { } catch (error: any) {
console.error("테이블 검증 실패:", error); // console.error("테이블 검증 실패:", error);
toast.error("검증 중 오류가 발생했습니다."); toast.error("검증 중 오류가 발생했습니다.");
} finally { } finally {
setValidating(false); setValidating(false);
@ -210,7 +210,7 @@ export function CreateTableModal({ isOpen, onClose, onSuccess }: CreateTableModa
toast.error(result.error?.details || result.message); toast.error(result.error?.details || result.message);
} }
} catch (error: any) { } catch (error: any) {
console.error("테이블 생성 실패:", error); // console.error("테이블 생성 실패:", error);
toast.error(error.response?.data?.error?.details || "테이블 생성에 실패했습니다."); toast.error(error.response?.data?.error?.details || "테이블 생성에 실패했습니다.");
} finally { } finally {
setLoading(false); setLoading(false);

View File

@ -64,7 +64,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) {
setLogs(logsResult.logs); setLogs(logsResult.logs);
setStatistics(statsResult); setStatistics(statsResult);
} catch (error) { } catch (error) {
console.error("DDL 로그 로드 실패:", error); // console.error("DDL 로그 로드 실패:", error);
toast.error("DDL 로그를 불러오는데 실패했습니다."); toast.error("DDL 로그를 불러오는데 실패했습니다.");
} finally { } finally {
if (showLoading) setLoading(false); if (showLoading) setLoading(false);
@ -101,7 +101,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) {
toast.success(`${result.deletedCount}개의 오래된 로그가 삭제되었습니다.`); toast.success(`${result.deletedCount}개의 오래된 로그가 삭제되었습니다.`);
loadData(false); loadData(false);
} catch (error) { } catch (error) {
console.error("로그 정리 실패:", error); // console.error("로그 정리 실패:", error);
toast.error("로그 정리에 실패했습니다."); toast.error("로그 정리에 실패했습니다.");
} }
}; };

View File

@ -46,21 +46,21 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
parentCompanyCode, parentCompanyCode,
uiTexts, uiTexts,
}) => { }) => {
console.log("🎯 MenuFormModal 렌더링 - Props:", { // console.log("🎯 MenuFormModal 렌더링 - Props:", {
isOpen, // isOpen,
menuId, // menuId,
parentId, // parentId,
menuType, // menuType,
level, // level,
parentCompanyCode, // parentCompanyCode,
}); // });
// 다국어 텍스트 가져오기 함수 // 다국어 텍스트 가져오기 함수
const getText = (key: string, fallback?: string): string => { const getText = (key: string, fallback?: string): string => {
return uiTexts[key] || fallback || key; return uiTexts[key] || fallback || key;
}; };
console.log("🔍 MenuFormModal 컴포넌트 마운트됨"); // console.log("🔍 MenuFormModal 컴포넌트 마운트됨");
const [formData, setFormData] = useState<MenuFormData>({ const [formData, setFormData] = useState<MenuFormData>({
parentObjId: parentId || "0", parentObjId: parentId || "0",
@ -93,20 +93,20 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
try { try {
const response = await screenApi.getScreens({ size: 1000 }); // 모든 화면 가져오기 const response = await screenApi.getScreens({ size: 1000 }); // 모든 화면 가져오기
console.log("🔍 화면 목록 로드 디버깅:", { // console.log("🔍 화면 목록 로드 디버깅:", {
totalScreens: response.data.length, // totalScreens: response.data.length,
firstScreen: response.data[0], // firstScreen: response.data[0],
firstScreenFields: response.data[0] ? Object.keys(response.data[0]) : [], // firstScreenFields: response.data[0] ? Object.keys(response.data[0]) : [],
firstScreenValues: response.data[0] ? Object.values(response.data[0]) : [], // firstScreenValues: response.data[0] ? Object.values(response.data[0]) : [],
allScreenIds: response.data // allScreenIds: response.data
.map((s) => ({ // .map((s) => ({
screenId: s.screenId, // screenId: s.screenId,
legacyId: s.id, // legacyId: s.id,
name: s.screenName, // name: s.screenName,
code: s.screenCode, // code: s.screenCode,
})) // }))
.slice(0, 5), // 처음 5개만 출력 // .slice(0, 5), // 처음 5개만 출력
}); // });
setScreens(response.data); setScreens(response.data);
console.log("✅ 화면 목록 로드 완료:", response.data.length); console.log("✅ 화면 목록 로드 완료:", response.data.length);
@ -118,14 +118,14 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
// 화면 선택 시 URL 자동 설정 // 화면 선택 시 URL 자동 설정
const handleScreenSelect = (screen: ScreenDefinition) => { const handleScreenSelect = (screen: ScreenDefinition) => {
console.log("🖥️ 화면 선택 디버깅:", { // console.log("🖥️ 화면 선택 디버깅:", {
screen, // screen,
screenId: screen.screenId, // screenId: screen.screenId,
screenIdType: typeof screen.screenId, // screenIdType: typeof screen.screenId,
legacyId: screen.id, // legacyId: screen.id,
allFields: Object.keys(screen), // allFields: Object.keys(screen),
screenValues: Object.values(screen), // screenValues: Object.values(screen),
}); // });
// ScreenDefinition에서는 screenId 필드를 사용 // ScreenDefinition에서는 screenId 필드를 사용
const actualScreenId = screen.screenId || screen.id; const actualScreenId = screen.screenId || screen.id;
@ -154,26 +154,26 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
menuUrl: screenUrl, menuUrl: screenUrl,
})); }));
console.log("🖥️ 화면 선택 완료:", { // console.log("🖥️ 화면 선택 완료:", {
screenId: screen.screenId, // screenId: screen.screenId,
legacyId: screen.id, // legacyId: screen.id,
actualScreenId, // actualScreenId,
screenName: screen.screenName, // screenName: screen.screenName,
menuType: menuType, // menuType: menuType,
formDataMenuType: formData.menuType, // formDataMenuType: formData.menuType,
isAdminMenu, // isAdminMenu,
generatedUrl: screenUrl, // generatedUrl: screenUrl,
}); // });
}; };
// URL 타입 변경 시 처리 // URL 타입 변경 시 처리
const handleUrlTypeChange = (type: "direct" | "screen") => { const handleUrlTypeChange = (type: "direct" | "screen") => {
console.log("🔄 URL 타입 변경:", { // console.log("🔄 URL 타입 변경:", {
from: urlType, // from: urlType,
to: type, // to: type,
currentSelectedScreen: selectedScreen?.screenName, // currentSelectedScreen: selectedScreen?.screenName,
currentUrl: formData.menuUrl, // currentUrl: formData.menuUrl,
}); // });
setUrlType(type); setUrlType(type);
@ -225,7 +225,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
try { try {
setLoading(true); setLoading(true);
console.log("API 호출 시작 - menuId:", menuId); console.log("API 호출 시작 - menuId:", menuId);
console.log("API URL:", `/admin/menus/${menuId}`); // console.log("API URL:", `/admin/menus/${menuId}`);
const response = await menuApi.getMenuInfo(menuId); const response = await menuApi.getMenuInfo(menuId);
console.log("메뉴 정보 조회 응답:", response); console.log("메뉴 정보 조회 응답:", response);
@ -285,29 +285,29 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
// "/screens/123" 또는 "/screens/123?mode=admin" 형태에서 ID 추출 // "/screens/123" 또는 "/screens/123?mode=admin" 형태에서 ID 추출
const screenId = menuUrl.match(/\/screens\/(\d+)/)?.[1]; const screenId = menuUrl.match(/\/screens\/(\d+)/)?.[1];
if (screenId) { if (screenId) {
console.log("🔍 기존 메뉴에서 화면 ID 추출:", { // console.log("🔍 기존 메뉴에서 화면 ID 추출:", {
menuUrl, // menuUrl,
screenId, // screenId,
hasAdminParam: menuUrl.includes("mode=admin"), // hasAdminParam: menuUrl.includes("mode=admin"),
currentScreensCount: screens.length, // currentScreensCount: screens.length,
}); // });
// 화면 설정 함수 // 화면 설정 함수
const setScreenFromId = () => { const setScreenFromId = () => {
const screen = screens.find((s) => s.screenId.toString() === screenId || s.id?.toString() === screenId); const screen = screens.find((s) => s.screenId.toString() === screenId || s.id?.toString() === screenId);
if (screen) { if (screen) {
setSelectedScreen(screen); setSelectedScreen(screen);
console.log("🖥️ 기존 메뉴의 할당된 화면 설정:", { // console.log("🖥️ 기존 메뉴의 할당된 화면 설정:", {
screen, // screen,
originalUrl: menuUrl, // originalUrl: menuUrl,
hasAdminParam: menuUrl.includes("mode=admin"), // hasAdminParam: menuUrl.includes("mode=admin"),
}); // });
return true; return true;
} else { } else {
console.warn("⚠️ 해당 ID의 화면을 찾을 수 없음:", { // console.warn("⚠️ 해당 ID의 화면을 찾을 수 없음:", {
screenId, // screenId,
availableScreens: screens.map((s) => ({ screenId: s.screenId, id: s.id, name: s.screenName })), // availableScreens: screens.map((s) => ({ screenId: s.screenId, id: s.id, name: s.screenName })),
}); // });
return false; return false;
} }
}; };
@ -330,26 +330,26 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
setSelectedScreen(null); setSelectedScreen(null);
} }
console.log("설정된 폼 데이터:", { // console.log("설정된 폼 데이터:", {
objid: menu.objid || menu.OBJID, // objid: menu.objid || menu.OBJID,
parentObjId: menu.parent_obj_id || menu.PARENT_OBJ_ID || "0", // parentObjId: menu.parent_obj_id || menu.PARENT_OBJ_ID || "0",
menuNameKor: menu.menu_name_kor || menu.MENU_NAME_KOR || "", // menuNameKor: menu.menu_name_kor || menu.MENU_NAME_KOR || "",
menuUrl: menu.menu_url || menu.MENU_URL || "", // menuUrl: menu.menu_url || menu.MENU_URL || "",
menuDesc: menu.menu_desc || menu.MENU_DESC || "", // menuDesc: menu.menu_desc || menu.MENU_DESC || "",
seq: menu.seq || menu.SEQ || 1, // seq: menu.seq || menu.SEQ || 1,
menuType: convertedMenuType, // menuType: convertedMenuType,
status: convertedStatus, // status: convertedStatus,
companyCode: companyCode, // companyCode: companyCode,
langKey: langKey, // langKey: langKey,
}); // });
} }
} catch (error: any) { } catch (error: any) {
console.error("메뉴 정보 로딩 오류:", error); console.error("메뉴 정보 로딩 오류:", error);
console.error("오류 상세 정보:", { // console.error("오류 상세 정보:", {
message: error?.message, // message: error?.message,
stack: error?.stack, // stack: error?.stack,
response: error?.response, // response: error?.response,
}); // });
toast.error(getText(MENU_MANAGEMENT_KEYS.MESSAGE_ERROR_LOAD_MENU_INFO)); toast.error(getText(MENU_MANAGEMENT_KEYS.MESSAGE_ERROR_LOAD_MENU_INFO));
} finally { } finally {
setLoading(false); setLoading(false);
@ -390,13 +390,13 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
langKey: "", // 다국어 키 초기화 langKey: "", // 다국어 키 초기화
}); });
console.log("메뉴 등록 기본값 설정:", { // console.log("메뉴 등록 기본값 설정:", {
parentObjId: parentId || "0", // parentObjId: parentId || "0",
menuType: defaultMenuType, // menuType: defaultMenuType,
status: "ACTIVE", // status: "ACTIVE",
companyCode: "", // companyCode: "",
langKey: "", // langKey: "",
}); // });
} }
}, [menuId, parentId, menuType]); }, [menuId, parentId, menuType]);
@ -448,11 +448,11 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
const screen = screens.find((s) => s.screenId.toString() === screenId || s.id?.toString() === screenId); const screen = screens.find((s) => s.screenId.toString() === screenId || s.id?.toString() === screenId);
if (screen) { if (screen) {
setSelectedScreen(screen); setSelectedScreen(screen);
console.log("✅ 기존 메뉴의 할당된 화면 자동 설정 완료:", { // console.log("✅ 기존 메뉴의 할당된 화면 자동 설정 완료:", {
screenId, // screenId,
screenName: screen.screenName, // screenName: screen.screenName,
menuUrl, // menuUrl,
}); // });
} }
} }
} }

View File

@ -195,7 +195,7 @@ export const MenuManagement: React.FC = () => {
defaultTexts[key] = defaultText; defaultTexts[key] = defaultText;
}); });
setUiTexts(defaultTexts); setUiTexts(defaultTexts);
console.log("🌐 초기 기본 텍스트 설정 완료:", Object.keys(defaultTexts).length); // console.log("🌐 초기 기본 텍스트 설정 완료:", Object.keys(defaultTexts).length);
}; };
// 기본 텍스트 반환 함수 // 기본 텍스트 반환 함수
@ -303,20 +303,20 @@ export const MenuManagement: React.FC = () => {
// uiTexts 상태 변경 감지 // uiTexts 상태 변경 감지
useEffect(() => { useEffect(() => {
console.log("🔄 uiTexts 상태 변경됨:", { // console.log("🔄 uiTexts 상태 변경됨:", {
count: Object.keys(uiTexts).length, // count: Object.keys(uiTexts).length,
sampleKeys: Object.keys(uiTexts).slice(0, 5), // sampleKeys: Object.keys(uiTexts).slice(0, 5),
sampleValues: Object.entries(uiTexts) // sampleValues: Object.entries(uiTexts)
.slice(0, 3) // .slice(0, 3)
.map(([k, v]) => `${k}: ${v}`), // .map(([k, v]) => `${k}: ${v}`),
}); // });
}, [uiTexts]); }, [uiTexts]);
// 컴포넌트 마운트 후 다국어 텍스트 강제 로드 (userLang이 아직 설정되지 않았을 수 있음) // 컴포넌트 마운트 후 다국어 텍스트 강제 로드 (userLang이 아직 설정되지 않았을 수 있음)
useEffect(() => { useEffect(() => {
const timer = setTimeout(() => { const timer = setTimeout(() => {
if (userLang && !uiTextsLoading) { if (userLang && !uiTextsLoading) {
console.log("🔄 컴포넌트 마운트 후 다국어 텍스트 강제 로드"); // console.log("🔄 컴포넌트 마운트 후 다국어 텍스트 강제 로드");
loadUITexts(); loadUITexts();
} }
}, 300); // 300ms 후 실행 }, 300); // 300ms 후 실행
@ -328,7 +328,7 @@ export const MenuManagement: React.FC = () => {
useEffect(() => { useEffect(() => {
const fallbackTimer = setTimeout(() => { const fallbackTimer = setTimeout(() => {
if (!uiTextsLoading && Object.keys(uiTexts).length === 0) { if (!uiTextsLoading && Object.keys(uiTexts).length === 0) {
console.log("🔄 안전장치: 컴포넌트 마운트 후 강제 다국어 텍스트 로드"); // console.log("🔄 안전장치: 컴포넌트 마운트 후 강제 다국어 텍스트 로드");
// 사용자 언어가 설정되지 않았을 때만 기본 텍스트 설정 // 사용자 언어가 설정되지 않았을 때만 기본 텍스트 설정
if (!userLang) { if (!userLang) {
initializeDefaultTexts(); initializeDefaultTexts();
@ -378,15 +378,15 @@ export const MenuManagement: React.FC = () => {
}, [isCompanyDropdownOpen]); }, [isCompanyDropdownOpen]);
const loadMenus = async (showLoading = true) => { const loadMenus = async (showLoading = true) => {
console.log(`📋 메뉴 목록 조회 시작 (showLoading: ${showLoading})`); // console.log(`📋 메뉴 목록 조회 시작 (showLoading: ${showLoading})`);
try { try {
if (showLoading) { if (showLoading) {
setLoading(true); setLoading(true);
} }
await refreshMenus(); await refreshMenus();
console.log("📋 메뉴 목록 조회 성공"); // console.log("📋 메뉴 목록 조회 성공");
} catch (error) { } catch (error) {
console.error("❌ 메뉴 목록 조회 실패:", error); // console.error("❌ 메뉴 목록 조회 실패:", error);
toast.error(getUITextSync("message.error.load.menu.list")); toast.error(getUITextSync("message.error.load.menu.list"));
} finally { } finally {
if (showLoading) { if (showLoading) {
@ -397,21 +397,21 @@ export const MenuManagement: React.FC = () => {
// 회사 목록 조회 // 회사 목록 조회
const loadCompanies = async () => { const loadCompanies = async () => {
console.log("🏢 회사 목록 조회 시작"); // console.log("🏢 회사 목록 조회 시작");
try { try {
const response = await apiClient.get("/admin/companies"); const response = await apiClient.get("/admin/companies");
if (response.data.success) { if (response.data.success) {
console.log("🏢 회사 목록 응답:", response.data); // console.log("🏢 회사 목록 응답:", response.data);
const companyList = response.data.data.map((company: any) => ({ const companyList = response.data.data.map((company: any) => ({
code: company.company_code || company.companyCode, code: company.company_code || company.companyCode,
name: company.company_name || company.companyName, name: company.company_name || company.companyName,
})); }));
console.log("🏢 변환된 회사 목록:", companyList); // console.log("🏢 변환된 회사 목록:", companyList);
setCompanies(companyList); setCompanies(companyList);
} }
} catch (error) { } catch (error) {
console.error("❌ 회사 목록 조회 실패:", error); // console.error("❌ 회사 목록 조회 실패:", error);
} }
}; };
@ -421,7 +421,7 @@ export const MenuManagement: React.FC = () => {
// userLang이 설정되지 않았으면 기본값 설정 // userLang이 설정되지 않았으면 기본값 설정
if (!userLang) { if (!userLang) {
console.log("🌐 사용자 언어가 설정되지 않음, 기본값 설정"); // console.log("🌐 사용자 언어가 설정되지 않음, 기본값 설정");
const defaultTexts: Record<string, string> = {}; const defaultTexts: Record<string, string> = {};
MENU_MANAGEMENT_LANG_KEYS.forEach((key) => { MENU_MANAGEMENT_LANG_KEYS.forEach((key) => {
defaultTexts[key] = getDefaultText(key); // 기본 한국어 텍스트 사용 defaultTexts[key] = getDefaultText(key); // 기본 한국어 텍스트 사용
@ -432,7 +432,7 @@ export const MenuManagement: React.FC = () => {
// 사용자 언어가 설정된 경우, 기존 uiTexts가 비어있으면 기본 텍스트로 초기화 // 사용자 언어가 설정된 경우, 기존 uiTexts가 비어있으면 기본 텍스트로 초기화
if (Object.keys(uiTexts).length === 0) { if (Object.keys(uiTexts).length === 0) {
console.log("🌐 기존 uiTexts가 비어있음, 기본 텍스트로 초기화"); // console.log("🌐 기존 uiTexts가 비어있음, 기본 텍스트로 초기화");
const defaultTexts: Record<string, string> = {}; const defaultTexts: Record<string, string> = {};
MENU_MANAGEMENT_LANG_KEYS.forEach((key) => { MENU_MANAGEMENT_LANG_KEYS.forEach((key) => {
defaultTexts[key] = getDefaultText(key); defaultTexts[key] = getDefaultText(key);
@ -440,14 +440,14 @@ export const MenuManagement: React.FC = () => {
setUiTexts(defaultTexts); setUiTexts(defaultTexts);
} }
console.log("🌐 UI 다국어 텍스트 로드 시작", { // console.log("🌐 UI 다국어 텍스트 로드 시작", {
userLang, // userLang,
apiParams: { // apiParams: {
companyCode: "*", // companyCode: "*",
menuCode: "menu.management", // menuCode: "menu.management",
userLang: userLang, // userLang: userLang,
}, // },
}); // });
setUiTextsLoading(true); setUiTextsLoading(true);
try { try {
@ -467,28 +467,28 @@ export const MenuManagement: React.FC = () => {
if (response.data.success) { if (response.data.success) {
const translations = response.data.data; const translations = response.data.data;
console.log("🌐 배치 다국어 텍스트 응답:", translations); // console.log("🌐 배치 다국어 텍스트 응답:", translations);
// 번역 결과를 상태에 저장 (기존 uiTexts와 병합) // 번역 결과를 상태에 저장 (기존 uiTexts와 병합)
const mergedTranslations = { ...uiTexts, ...translations }; const mergedTranslations = { ...uiTexts, ...translations };
console.log("🔧 setUiTexts 호출 전:", { // console.log("🔧 setUiTexts 호출 전:", {
translationsCount: Object.keys(translations).length, // translationsCount: Object.keys(translations).length,
mergedCount: Object.keys(mergedTranslations).length, // mergedCount: Object.keys(mergedTranslations).length,
}); // });
setUiTexts(mergedTranslations); setUiTexts(mergedTranslations);
console.log("🔧 setUiTexts 호출 후 - mergedTranslations:", mergedTranslations); // console.log("🔧 setUiTexts 호출 후 - mergedTranslations:", mergedTranslations);
// 번역 캐시에 저장 (다른 컴포넌트에서도 사용할 수 있도록) // 번역 캐시에 저장 (다른 컴포넌트에서도 사용할 수 있도록)
setTranslationCache(userLang, mergedTranslations); setTranslationCache(userLang, mergedTranslations);
} else { } else {
console.error("❌ 다국어 텍스트 배치 조회 실패:", response.data.message); // console.error("❌ 다국어 텍스트 배치 조회 실패:", response.data.message);
// API 실패 시에도 기존 uiTexts는 유지 // API 실패 시에도 기존 uiTexts는 유지
console.log("🔄 API 실패로 인해 기존 uiTexts 유지"); // console.log("🔄 API 실패로 인해 기존 uiTexts 유지");
} }
} catch (error) { } catch (error) {
console.error("❌ UI 다국어 텍스트 로드 실패:", error); // console.error("❌ UI 다국어 텍스트 로드 실패:", error);
// API 실패 시에도 기존 uiTexts는 유지 // API 실패 시에도 기존 uiTexts는 유지
console.log("🔄 API 실패로 인해 기존 uiTexts 유지"); // console.log("🔄 API 실패로 인해 기존 uiTexts 유지");
} finally { } finally {
setUiTextsLoading(false); setUiTextsLoading(false);
} }
@ -519,12 +519,12 @@ export const MenuManagement: React.FC = () => {
// 다국어 API 테스트 함수 (getUITextSync 사용) // 다국어 API 테스트 함수 (getUITextSync 사용)
const testMultiLangAPI = async () => { const testMultiLangAPI = async () => {
console.log("🧪 다국어 API 테스트 시작"); // console.log("🧪 다국어 API 테스트 시작");
try { try {
const text = getUITextSync("menu.management.admin"); const text = getUITextSync("menu.management.admin");
console.log("🧪 다국어 API 테스트 결과:", text); // console.log("🧪 다국어 API 테스트 결과:", text);
} catch (error) { } catch (error) {
console.error("❌ 다국어 API 테스트 실패:", error); // console.error("❌ 다국어 API 테스트 실패:", error);
} }
}; };
@ -576,14 +576,14 @@ export const MenuManagement: React.FC = () => {
}; };
const handleEditMenu = (menuId: string) => { const handleEditMenu = (menuId: string) => {
console.log("🔧 메뉴 수정 시작 - menuId:", menuId); // console.log("🔧 메뉴 수정 시작 - menuId:", menuId);
// 현재 메뉴 정보 찾기 // 현재 메뉴 정보 찾기
const currentMenus = selectedMenuType === "admin" ? adminMenus : userMenus; const currentMenus = selectedMenuType === "admin" ? adminMenus : userMenus;
const menuToEdit = currentMenus.find((menu) => (menu.objid || menu.OBJID) === menuId); const menuToEdit = currentMenus.find((menu) => (menu.objid || menu.OBJID) === menuId);
if (menuToEdit) { if (menuToEdit) {
console.log("수정할 메뉴 정보:", menuToEdit); // console.log("수정할 메뉴 정보:", menuToEdit);
setFormData({ setFormData({
menuId: menuId, menuId: menuId,
@ -593,15 +593,15 @@ export const MenuManagement: React.FC = () => {
parentCompanyCode: menuToEdit.company_code || menuToEdit.COMPANY_CODE || "", parentCompanyCode: menuToEdit.company_code || menuToEdit.COMPANY_CODE || "",
}); });
console.log("설정된 formData:", { // console.log("설정된 formData:", {
menuId: menuId, // menuId: menuId,
parentId: menuToEdit.parent_obj_id || menuToEdit.PARENT_OBJ_ID || "", // parentId: menuToEdit.parent_obj_id || menuToEdit.PARENT_OBJ_ID || "",
menuType: selectedMenuType, // menuType: selectedMenuType,
level: 0, // level: 0,
parentCompanyCode: menuToEdit.company_code || menuToEdit.COMPANY_CODE || "", // parentCompanyCode: menuToEdit.company_code || menuToEdit.COMPANY_CODE || "",
}); // });
} else { } else {
console.error("수정할 메뉴를 찾을 수 없음:", menuId); // console.error("수정할 메뉴를 찾을 수 없음:", menuId);
} }
setFormModalOpen(true); setFormModalOpen(true);
@ -640,31 +640,31 @@ export const MenuManagement: React.FC = () => {
setDeleting(true); setDeleting(true);
try { try {
const menuIds = Array.from(selectedMenus); const menuIds = Array.from(selectedMenus);
console.log("삭제할 메뉴 IDs:", menuIds); // console.log("삭제할 메뉴 IDs:", menuIds);
toast.info(getUITextSync("message.menu.delete.processing")); toast.info(getUITextSync("message.menu.delete.processing"));
const response = await menuApi.deleteMenusBatch(menuIds); const response = await menuApi.deleteMenusBatch(menuIds);
console.log("삭제 API 응답:", response); // console.log("삭제 API 응답:", response);
console.log("응답 구조:", { // console.log("응답 구조:", {
success: response.success, // success: response.success,
data: response.data, // data: response.data,
message: response.message, // message: response.message,
}); // });
if (response.success && response.data) { if (response.success && response.data) {
const { deletedCount, failedCount } = response.data; const { deletedCount, failedCount } = response.data;
console.log("삭제 결과:", { deletedCount, failedCount }); // console.log("삭제 결과:", { deletedCount, failedCount });
// 선택된 메뉴 초기화 // 선택된 메뉴 초기화
setSelectedMenus(new Set()); setSelectedMenus(new Set());
// 메뉴 목록 즉시 새로고침 (로딩 상태 없이) // 메뉴 목록 즉시 새로고침 (로딩 상태 없이)
console.log("메뉴 목록 새로고침 시작"); // console.log("메뉴 목록 새로고침 시작");
await loadMenus(false); await loadMenus(false);
// 전역 메뉴 상태도 업데이트 // 전역 메뉴 상태도 업데이트
await refreshMenus(); await refreshMenus();
console.log("메뉴 목록 새로고침 완료"); // console.log("메뉴 목록 새로고침 완료");
// 삭제 결과 메시지 // 삭제 결과 메시지
if (failedCount === 0) { if (failedCount === 0) {
@ -678,11 +678,11 @@ export const MenuManagement: React.FC = () => {
); );
} }
} else { } else {
console.error("삭제 실패:", response); // console.error("삭제 실패:", response);
toast.error(response.message || "메뉴 삭제에 실패했습니다."); toast.error(response.message || "메뉴 삭제에 실패했습니다.");
} }
} catch (error) { } catch (error) {
console.error("메뉴 삭제 중 오류:", error); // console.error("메뉴 삭제 중 오류:", error);
toast.error(getUITextSync("message.menu.delete.failed")); toast.error(getUITextSync("message.menu.delete.failed"));
} finally { } finally {
setDeleting(false); setDeleting(false);
@ -718,7 +718,7 @@ export const MenuManagement: React.FC = () => {
toast.error(response.message); toast.error(response.message);
} }
} catch (error) { } catch (error) {
console.error("메뉴 상태 토글 오류:", error); // console.error("메뉴 상태 토글 오류:", error);
toast.error(getUITextSync("message.menu.status.toggle.failed")); toast.error(getUITextSync("message.menu.status.toggle.failed"));
} }
}; };
@ -785,15 +785,15 @@ export const MenuManagement: React.FC = () => {
const userMenusCount = useMemo(() => userMenus?.length || 0, [userMenus]); const userMenusCount = useMemo(() => userMenus?.length || 0, [userMenus]);
// 디버깅을 위한 간단한 상태 표시 // 디버깅을 위한 간단한 상태 표시
console.log("🔍 MenuManagement 렌더링 상태:", { // console.log("🔍 MenuManagement 렌더링 상태:", {
loading, // loading,
uiTextsLoading, // uiTextsLoading,
uiTextsCount, // uiTextsCount,
adminMenusCount, // adminMenusCount,
userMenusCount, // userMenusCount,
selectedMenuType, // selectedMenuType,
userLang, // userLang,
}); // });
if (loading) { if (loading) {
return ( return (

View File

@ -64,9 +64,9 @@ export default function MultiLangPage() {
// 회사 목록 조회 // 회사 목록 조회
const fetchCompanies = async () => { const fetchCompanies = async () => {
try { try {
console.log("회사 목록 조회 시작"); // console.log("회사 목록 조회 시작");
const response = await apiClient.get("/admin/companies"); const response = await apiClient.get("/admin/companies");
console.log("회사 목록 응답 데이터:", response.data); // console.log("회사 목록 응답 데이터:", response.data);
const data = response.data; const data = response.data;
if (data.success) { if (data.success) {
@ -74,13 +74,13 @@ export default function MultiLangPage() {
code: company.company_code, code: company.company_code,
name: company.company_name, name: company.company_name,
})); }));
console.log("변환된 회사 목록:", companyList); // console.log("변환된 회사 목록:", companyList);
setCompanies(companyList); setCompanies(companyList);
} else { } else {
console.error("회사 목록 조회 실패:", data.message); // console.error("회사 목록 조회 실패:", data.message);
} }
} catch (error) { } catch (error) {
console.error("회사 목록 조회 실패:", error); // console.error("회사 목록 조회 실패:", error);
} }
}; };
@ -93,7 +93,7 @@ export default function MultiLangPage() {
setLanguages(data.data); setLanguages(data.data);
} }
} catch (error) { } catch (error) {
console.error("언어 목록 조회 실패:", error); // console.error("언어 목록 조회 실패:", error);
} }
}; };
@ -103,13 +103,13 @@ export default function MultiLangPage() {
const response = await apiClient.get("/multilang/keys"); const response = await apiClient.get("/multilang/keys");
const data = response.data; const data = response.data;
if (data.success) { if (data.success) {
console.log("✅ 전체 키 목록 로드:", data.data.length, "개"); // console.log("✅ 전체 키 목록 로드:", data.data.length, "개");
setLangKeys(data.data); setLangKeys(data.data);
} else { } else {
console.error("❌ 키 목록 로드 실패:", data.message); // console.error("❌ 키 목록 로드 실패:", data.message);
} }
} catch (error) { } catch (error) {
console.error("다국어 키 목록 조회 실패:", error); // console.error("다국어 키 목록 조회 실패:", error);
} }
}; };
@ -146,25 +146,25 @@ export default function MultiLangPage() {
// 선택된 키의 다국어 텍스트 조회 // 선택된 키의 다국어 텍스트 조회
const fetchLangTexts = async (keyId: number) => { const fetchLangTexts = async (keyId: number) => {
try { try {
console.log("다국어 텍스트 조회 시작: keyId =", keyId); // console.log("다국어 텍스트 조회 시작: keyId =", keyId);
const response = await apiClient.get(`/multilang/keys/${keyId}/texts`); const response = await apiClient.get(`/multilang/keys/${keyId}/texts`);
const data = response.data; const data = response.data;
console.log("다국어 텍스트 조회 응답:", data); // console.log("다국어 텍스트 조회 응답:", data);
if (data.success) { if (data.success) {
setLangTexts(data.data); setLangTexts(data.data);
// 편집용 텍스트 초기화 // 편집용 텍스트 초기화
const editingData = data.data.map((text: LangText) => ({ ...text })); const editingData = data.data.map((text: LangText) => ({ ...text }));
setEditingTexts(editingData); setEditingTexts(editingData);
console.log("편집용 텍스트 설정:", editingData); // console.log("편집용 텍스트 설정:", editingData);
} }
} catch (error) { } catch (error) {
console.error("다국어 텍스트 조회 실패:", error); // console.error("다국어 텍스트 조회 실패:", error);
} }
}; };
// 언어 키 선택 처리 // 언어 키 선택 처리
const handleKeySelect = (key: LangKey) => { const handleKeySelect = (key: LangKey) => {
console.log("언어 키 선택:", key); // console.log("언어 키 선택:", key);
setSelectedKey(key); setSelectedKey(key);
fetchLangTexts(key.keyId); fetchLangTexts(key.keyId);
}; };
@ -172,9 +172,9 @@ export default function MultiLangPage() {
// 디버깅용 useEffect // 디버깅용 useEffect
useEffect(() => { useEffect(() => {
if (selectedKey) { if (selectedKey) {
console.log("선택된 키 변경:", selectedKey); // console.log("선택된 키 변경:", selectedKey);
console.log("언어 목록:", languages); // console.log("언어 목록:", languages);
console.log("편집 텍스트:", editingTexts); // console.log("편집 텍스트:", editingTexts);
} }
}, [selectedKey, languages, editingTexts]); }, [selectedKey, languages, editingTexts]);
@ -222,7 +222,7 @@ export default function MultiLangPage() {
fetchLangTexts(selectedKey.keyId); fetchLangTexts(selectedKey.keyId);
} }
} catch (error) { } catch (error) {
console.error("텍스트 저장 실패:", error); // console.error("텍스트 저장 실패:", error);
alert("저장에 실패했습니다."); alert("저장에 실패했습니다.");
} }
}; };
@ -271,7 +271,7 @@ export default function MultiLangPage() {
alert(`오류: ${result.message}`); alert(`오류: ${result.message}`);
} }
} catch (error) { } catch (error) {
console.error("언어 저장 중 오류:", error); // console.error("언어 저장 중 오류:", error);
alert("언어 저장 중 오류가 발생했습니다."); alert("언어 저장 중 오류가 발생했습니다.");
} }
}; };
@ -307,7 +307,7 @@ export default function MultiLangPage() {
alert(`${failedDeletes.length}개의 언어 삭제에 실패했습니다.`); alert(`${failedDeletes.length}개의 언어 삭제에 실패했습니다.`);
} }
} catch (error) { } catch (error) {
console.error("언어 삭제 중 오류:", error); // console.error("언어 삭제 중 오류:", error);
alert("언어 삭제 중 오류가 발생했습니다."); alert("언어 삭제 중 오류가 발생했습니다.");
} }
}; };
@ -369,7 +369,7 @@ export default function MultiLangPage() {
} }
} }
} catch (error) { } catch (error) {
console.error("언어 키 저장 실패:", error); // console.error("언어 키 저장 실패:", error);
alert("언어 키 저장에 실패했습니다."); alert("언어 키 저장에 실패했습니다.");
} }
}; };
@ -397,7 +397,7 @@ export default function MultiLangPage() {
alert("상태 변경 중 오류가 발생했습니다."); alert("상태 변경 중 오류가 발생했습니다.");
} }
} catch (error) { } catch (error) {
console.error("키 상태 토글 실패:", error); // console.error("키 상태 토글 실패:", error);
alert("키 상태 변경 중 오류가 발생했습니다."); alert("키 상태 변경 중 오류가 발생했습니다.");
} }
}; };
@ -414,7 +414,7 @@ export default function MultiLangPage() {
alert("언어 상태 변경 중 오류가 발생했습니다."); alert("언어 상태 변경 중 오류가 발생했습니다.");
} }
} catch (error) { } catch (error) {
console.error("언어 상태 토글 실패:", error); // console.error("언어 상태 토글 실패:", error);
alert("언어 상태 변경 중 오류가 발생했습니다."); alert("언어 상태 변경 중 오류가 발생했습니다.");
} }
}; };
@ -463,7 +463,7 @@ export default function MultiLangPage() {
alert("일부 키 삭제에 실패했습니다."); alert("일부 키 삭제에 실패했습니다.");
} }
} catch (error) { } catch (error) {
console.error("선택된 키 삭제 실패:", error); // console.error("선택된 키 삭제 실패:", error);
alert("선택된 키 삭제에 실패했습니다."); alert("선택된 키 삭제에 실패했습니다.");
} }
}; };
@ -485,7 +485,7 @@ export default function MultiLangPage() {
} }
} }
} catch (error) { } catch (error) {
console.error("언어 키 삭제 실패:", error); // console.error("언어 키 삭제 실패:", error);
alert("언어 키 삭제에 실패했습니다."); alert("언어 키 삭제에 실패했습니다.");
} }
}; };

View File

@ -40,15 +40,15 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
const [selectedScreen, setSelectedScreen] = useState<ScreenDefinition | null>(null); const [selectedScreen, setSelectedScreen] = useState<ScreenDefinition | null>(null);
// 디버그: 전달받은 메뉴 데이터 확인 // 디버그: 전달받은 메뉴 데이터 확인
console.log("ScreenAssignmentTab - 전달받은 메뉴 데이터:", { // console.log("ScreenAssignmentTab - 전달받은 메뉴 데이터:", {
total: menus.length, // total: menus.length,
sample: menus.slice(0, 3), // sample: menus.slice(0, 3),
keys: menus.length > 0 ? Object.keys(menus[0]) : [], // keys: menus.length > 0 ? Object.keys(menus[0]) : [],
}); // });
// 메뉴 선택 // 메뉴 선택
const handleMenuSelect = async (menuId: string) => { const handleMenuSelect = async (menuId: string) => {
console.log("메뉴 선택:", menuId); // console.log("메뉴 선택:", menuId);
setSelectedMenuId(menuId); setSelectedMenuId(menuId);
// 다양한 형식의 objid 대응 // 다양한 형식의 objid 대응
@ -57,7 +57,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
return objid?.toString() === menuId; return objid?.toString() === menuId;
}); });
console.log("선택된 메뉴:", menu); // console.log("선택된 메뉴:", menu);
setSelectedMenu(menu || null); setSelectedMenu(menu || null);
if (menu) { if (menu) {
@ -75,7 +75,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
const screens = await menuScreenApi.getScreensByMenu(menuObjid); const screens = await menuScreenApi.getScreensByMenu(menuObjid);
setAssignedScreens(screens); setAssignedScreens(screens);
} catch (error) { } catch (error) {
console.error("할당된 화면 로드 실패:", error); // console.error("할당된 화면 로드 실패:", error);
toast.error("할당된 화면 목록을 불러오는데 실패했습니다."); toast.error("할당된 화면 목록을 불러오는데 실패했습니다.");
} finally { } finally {
setLoading(false); setLoading(false);
@ -94,7 +94,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
setAvailableScreens(available); setAvailableScreens(available);
} catch (error) { } catch (error) {
console.error("사용 가능한 화면 로드 실패:", error); // console.error("사용 가능한 화면 로드 실패:", error);
toast.error("사용 가능한 화면 목록을 불러오는데 실패했습니다."); toast.error("사용 가능한 화면 목록을 불러오는데 실패했습니다.");
} }
}; };
@ -118,7 +118,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
setShowAssignDialog(false); setShowAssignDialog(false);
setSelectedScreen(null); setSelectedScreen(null);
} catch (error) { } catch (error) {
console.error("화면 할당 실패:", error); // console.error("화면 할당 실패:", error);
toast.error("화면 할당에 실패했습니다."); toast.error("화면 할당에 실패했습니다.");
} }
}; };
@ -142,7 +142,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
setShowUnassignDialog(false); setShowUnassignDialog(false);
setSelectedScreen(null); setSelectedScreen(null);
} catch (error) { } catch (error) {
console.error("화면 할당 해제 실패:", error); // console.error("화면 할당 해제 실패:", error);
toast.error("화면 할당 해제에 실패했습니다."); toast.error("화면 할당 해제에 실패했습니다.");
} }
}; };
@ -162,14 +162,14 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
// 단순화된 메뉴 옵션 생성 (모든 메뉴를 평면적으로 표시) // 단순화된 메뉴 옵션 생성 (모든 메뉴를 평면적으로 표시)
const getMenuOptions = (menuList: MenuItem[]): JSX.Element[] => { const getMenuOptions = (menuList: MenuItem[]): JSX.Element[] => {
console.log("메뉴 옵션 생성:", { // console.log("메뉴 옵션 생성:", {
total: menuList.length, // total: menuList.length,
sample: menuList.slice(0, 3).map((m) => ({ // sample: menuList.slice(0, 3).map((m) => ({
objid: m.objid || m.OBJID || (m as any).objid, // objid: m.objid || m.OBJID || (m as any).objid,
name: m.menu_name_kor || m.MENU_NAME_KOR || (m as any).menu_name_kor, // name: m.menu_name_kor || m.MENU_NAME_KOR || (m as any).menu_name_kor,
parent: m.parent_obj_id || m.PARENT_OBJ_ID || (m as any).parent_obj_id, // parent: m.parent_obj_id || m.PARENT_OBJ_ID || (m as any).parent_obj_id,
})), // })),
}); // });
if (!menuList || menuList.length === 0) { if (!menuList || menuList.length === 0) {
return [ return [
@ -188,7 +188,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
// 들여쓰기 (레벨에 따라) // 들여쓰기 (레벨에 따라)
const indent = " ".repeat(Math.max(0, lev)); const indent = " ".repeat(Math.max(0, lev));
console.log("메뉴 항목:", { index, menuObjid, menuName, lev }); // console.log("메뉴 항목:", { index, menuObjid, menuName, lev });
return ( return (
<SelectItem key={menuObjid?.toString() || `menu-${index}`} value={menuObjid?.toString() || `menu-${index}`}> <SelectItem key={menuObjid?.toString() || `menu-${index}`} value={menuObjid?.toString() || `menu-${index}`}>

View File

@ -50,7 +50,7 @@ export default function CopyScreenModal({ isOpen, onClose, sourceScreen, onCopyS
const newCode = await screenApi.generateScreenCode(sourceScreen.companyCode); const newCode = await screenApi.generateScreenCode(sourceScreen.companyCode);
setScreenCode(newCode); setScreenCode(newCode);
} catch (error) { } catch (error) {
console.error("화면 코드 생성 실패:", error); // console.error("화면 코드 생성 실패:", error);
toast.error("화면 코드 생성에 실패했습니다."); toast.error("화면 코드 생성에 실패했습니다.");
} }
}; };
@ -84,7 +84,7 @@ export default function CopyScreenModal({ isOpen, onClose, sourceScreen, onCopyS
onCopySuccess(); onCopySuccess();
handleClose(); handleClose();
} catch (error: any) { } catch (error: any) {
console.error("화면 복사 실패:", error); // console.error("화면 복사 실패:", error);
const errorMessage = error.response?.data?.message || "화면 복사에 실패했습니다."; const errorMessage = error.response?.data?.message || "화면 복사에 실패했습니다.";
toast.error(errorMessage); toast.error(errorMessage);
} finally { } finally {

View File

@ -31,7 +31,7 @@ export default function CreateScreenModal({ open, onOpenChange, onCreated }: Cre
const generatedCode = await screenApi.generateScreenCode(companyCode); const generatedCode = await screenApi.generateScreenCode(companyCode);
setScreenCode(generatedCode); setScreenCode(generatedCode);
} catch (e) { } catch (e) {
console.error("화면 코드 생성 실패", e); // console.error("화면 코드 생성 실패", e);
} }
}; };
@ -44,7 +44,7 @@ export default function CreateScreenModal({ open, onOpenChange, onCreated }: Cre
if (abort) return; if (abort) return;
setTables(list.map((t) => ({ tableName: t.tableName, displayName: t.displayName || t.tableName }))); setTables(list.map((t) => ({ tableName: t.tableName, displayName: t.displayName || t.tableName })));
} catch (e) { } catch (e) {
console.error("테이블 목록 조회 실패", e); // console.error("테이블 목록 조회 실패", e);
setTables([]); setTables([]);
} }
}; };
@ -93,7 +93,7 @@ export default function CreateScreenModal({ open, onOpenChange, onCreated }: Cre
setTableName(""); setTableName("");
setDescription(""); setDescription("");
} catch (e) { } catch (e) {
console.error("화면 생성 실패", e); // console.error("화면 생성 실패", e);
// 필요 시 토스트 추가 가능 // 필요 시 토스트 추가 가능
} finally { } finally {
setSubmitting(false); setSubmitting(false);

View File

@ -292,7 +292,7 @@ export const EnhancedInteractiveScreenViewer: React.FC<EnhancedInteractiveScreen
return applyStyles(dynamicElement); return applyStyles(dynamicElement);
} catch (error) { } catch (error) {
console.warn(`DynamicWebTypeRenderer 오류 (${widgetType}):`, error); // console.warn(`DynamicWebTypeRenderer 오류 (${widgetType}):`, error);
// 폴백: 기본 input // 폴백: 기본 input
const fallbackElement = ( const fallbackElement = (

View File

@ -205,7 +205,7 @@ export const FileAttachmentDetailModal: React.FC<FileAttachmentDetailModalProps>
throw new Error(response.message || '파일 업로드에 실패했습니다.'); throw new Error(response.message || '파일 업로드에 실패했습니다.');
} }
} catch (error) { } catch (error) {
console.error('파일 업로드 오류:', error); // console.error('파일 업로드 오류:', error);
toast.dismiss(); toast.dismiss();
toast.error('파일 업로드에 실패했습니다.'); toast.error('파일 업로드에 실패했습니다.');
} finally { } finally {
@ -243,7 +243,7 @@ export const FileAttachmentDetailModal: React.FC<FileAttachmentDetailModalProps>
toast.dismiss(); toast.dismiss();
toast.success(`${file.realFileName} 다운로드가 완료되었습니다.`); toast.success(`${file.realFileName} 다운로드가 완료되었습니다.`);
} catch (error) { } catch (error) {
console.error('파일 다운로드 오류:', error); // console.error('파일 다운로드 오류:', error);
toast.dismiss(); toast.dismiss();
toast.error('파일 다운로드에 실패했습니다.'); toast.error('파일 다운로드에 실패했습니다.');
} }
@ -275,7 +275,7 @@ export const FileAttachmentDetailModal: React.FC<FileAttachmentDetailModalProps>
toast.dismiss(); toast.dismiss();
toast.success(`${file.realFileName}이 삭제되었습니다.`); toast.success(`${file.realFileName}이 삭제되었습니다.`);
} catch (error) { } catch (error) {
console.error('파일 삭제 오류:', error); // console.error('파일 삭제 오류:', error);
toast.dismiss(); toast.dismiss();
toast.error('파일 삭제에 실패했습니다.'); toast.error('파일 삭제에 실패했습니다.');
} }

View File

@ -113,13 +113,13 @@ export const FloatingPanel: React.FC<FloatingPanelProps> = ({
const newHeight = Math.min(Math.max(minHeight, contentHeight + headerHeight + padding), maxHeight); const newHeight = Math.min(Math.max(minHeight, contentHeight + headerHeight + padding), maxHeight);
console.log(`🔧 패널 높이 자동 조정:`, { // console.log(`🔧 패널 높이 자동 조정:`, {
panelId: id, // panelId: id,
contentHeight, // contentHeight,
calculatedHeight: newHeight, // calculatedHeight: newHeight,
currentHeight: panelSize.height, // currentHeight: panelSize.height,
willUpdate: Math.abs(panelSize.height - newHeight) > 10, // willUpdate: Math.abs(panelSize.height - newHeight) > 10,
}); // });
// 현재 높이와 다르면 업데이트 // 현재 높이와 다르면 업데이트
if (Math.abs(panelSize.height - newHeight) > 10) { if (Math.abs(panelSize.height - newHeight) > 10) {

View File

@ -150,7 +150,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
return options; return options;
} }
} catch (error) { } catch (error) {
console.error(`공통코드 옵션 로드 실패: ${categoryCode}`, error); // console.error(`공통코드 옵션 로드 실패: ${categoryCode}`, error);
} }
return []; return [];
@ -176,7 +176,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
return { hasFiles, fileCount, files: response.files || [] }; return { hasFiles, fileCount, files: response.files || [] };
} catch (error) { } catch (error) {
console.error("파일 상태 확인 오류:", error); // console.error("파일 상태 확인 오류:", error);
return { hasFiles: false, fileCount: 0, files: [] }; return { hasFiles: false, fileCount: 0, files: [] };
} }
}, },
@ -235,7 +235,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
return { hasFiles, fileCount, files, targetObjid }; return { hasFiles, fileCount, files, targetObjid };
} catch (error) { } catch (error) {
console.error("컬럼별 파일 상태 확인 오류:", error); // console.error("컬럼별 파일 상태 확인 오류:", error);
return { hasFiles: false, fileCount: 0, files: [], targetObjid: null }; return { hasFiles: false, fileCount: 0, files: [], targetObjid: null };
} }
}, },
@ -301,13 +301,13 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
// 이미지 로딩 실패 시 대체 URL 시도 // 이미지 로딩 실패 시 대체 URL 시도
const handleImageError = useCallback(() => { const handleImageError = useCallback(() => {
if (!imageLoadError && previewImage) { if (!imageLoadError && previewImage) {
console.error("이미지 로딩 실패:", previewImage); // console.error("이미지 로딩 실패:", previewImage);
setImageLoadError(true); setImageLoadError(true);
// 대체 URL 생성 (직접 파일 경로 사용) // 대체 URL 생성 (직접 파일 경로 사용)
if (previewImage.path) { if (previewImage.path) {
const altUrl = getDirectFileUrl(previewImage.path); const altUrl = getDirectFileUrl(previewImage.path);
console.log("대체 URL 시도:", altUrl); // console.log("대체 URL 시도:", altUrl);
setAlternativeImageUrl(altUrl); setAlternativeImageUrl(altUrl);
} else { } else {
toast.error("이미지를 불러올 수 없습니다."); toast.error("이미지를 불러올 수 없습니다.");
@ -365,7 +365,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
try { try {
return tableColumn?.detailSettings ? JSON.parse(tableColumn.detailSettings) : {}; return tableColumn?.detailSettings ? JSON.parse(tableColumn.detailSettings) : {};
} catch { } catch {
console.warn("상세 설정 파싱 실패:", tableColumn?.detailSettings); // console.warn("상세 설정 파싱 실패:", tableColumn?.detailSettings);
return {}; return {};
} }
}, },
@ -483,7 +483,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
setFileStatusMap(statusMap); setFileStatusMap(statusMap);
}); });
} catch (error) { } catch (error) {
console.error("❌ 테이블 데이터 조회 실패:", error); // console.error("❌ 테이블 데이터 조회 실패:", error);
setData([]); setData([]);
setTotal(0); setTotal(0);
setTotalPages(1); setTotalPages(1);
@ -503,7 +503,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
setCurrentUser(response.data); setCurrentUser(response.data);
} }
} catch (error) { } catch (error) {
console.error("현재 사용자 정보 로드 실패:", error); // console.error("현재 사용자 정보 로드 실패:", error);
} }
}; };
@ -515,14 +515,14 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
const handleRefreshFileStatus = async (event: CustomEvent) => { const handleRefreshFileStatus = async (event: CustomEvent) => {
const { tableName, recordId, columnName, targetObjid, fileCount } = event.detail; const { tableName, recordId, columnName, targetObjid, fileCount } = event.detail;
console.log("🔄 InteractiveDataTable 파일 상태 새로고침 이벤트 수신:", { // console.log("🔄 InteractiveDataTable 파일 상태 새로고침 이벤트 수신:", {
tableName, // tableName,
recordId, // recordId,
columnName, // columnName,
targetObjid, // targetObjid,
fileCount, // fileCount,
currentTableName: component.tableName // currentTableName: component.tableName
}); // });
// 현재 테이블과 일치하는지 확인 // 현재 테이블과 일치하는지 확인
if (tableName === component.tableName) { if (tableName === component.tableName) {
@ -534,12 +534,12 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
[columnKey]: { hasFiles: fileCount > 0, fileCount } [columnKey]: { hasFiles: fileCount > 0, fileCount }
})); }));
console.log("✅ 파일 상태 업데이트 완료:", { // console.log("✅ 파일 상태 업데이트 완료:", {
recordId, // recordId,
columnKey, // columnKey,
hasFiles: fileCount > 0, // hasFiles: fileCount > 0,
fileCount // fileCount
}); // });
} }
}; };
@ -559,7 +559,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
const columns = await tableTypeApi.getColumns(component.tableName); const columns = await tableTypeApi.getColumns(component.tableName);
setTableColumns(columns); setTableColumns(columns);
} catch (error) { } catch (error) {
console.error("테이블 컬럼 정보 로드 실패:", error); // console.error("테이블 컬럼 정보 로드 실패:", error);
} }
}; };
@ -820,7 +820,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
handleAddFormChange(columnName, fileNames); handleAddFormChange(columnName, fileNames);
} }
} catch (error) { } catch (error) {
console.error("파일 업로드 실패:", error); // console.error("파일 업로드 실패:", error);
alert("파일 업로드에 실패했습니다."); alert("파일 업로드에 실패했습니다.");
} finally { } finally {
setUploadingFiles((prev) => ({ ...prev, [columnName]: false })); setUploadingFiles((prev) => ({ ...prev, [columnName]: false }));
@ -898,7 +898,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
setIsAdding(true); setIsAdding(true);
// 실제 API 호출로 데이터 추가 // 실제 API 호출로 데이터 추가
console.log("🔥 추가할 데이터:", addFormData); // console.log("🔥 추가할 데이터:", addFormData);
await tableTypeApi.addTableData(component.tableName, addFormData); await tableTypeApi.addTableData(component.tableName, addFormData);
// 모달 닫기 및 폼 초기화 // 모달 닫기 및 폼 초기화
@ -908,7 +908,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
// 첫 페이지로 이동하여 새 데이터 확인 // 첫 페이지로 이동하여 새 데이터 확인
loadData(1, searchValues); loadData(1, searchValues);
} catch (error) { } catch (error) {
console.error("데이터 추가 실패:", error); // console.error("데이터 추가 실패:", error);
alert("데이터 추가에 실패했습니다."); alert("데이터 추가에 실패했습니다.");
} finally { } finally {
setIsAdding(false); setIsAdding(false);
@ -921,8 +921,8 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
setIsEditing(true); setIsEditing(true);
// 실제 API 호출로 데이터 수정 // 실제 API 호출로 데이터 수정
console.log("🔥 수정할 데이터:", editFormData); // console.log("🔥 수정할 데이터:", editFormData);
console.log("🔥 원본 데이터:", editingRowData); // console.log("🔥 원본 데이터:", editingRowData);
if (editingRowData) { if (editingRowData) {
await tableTypeApi.editTableData(component.tableName, editingRowData, editFormData); await tableTypeApi.editTableData(component.tableName, editingRowData, editFormData);
@ -937,7 +937,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
loadData(currentPage, searchValues); loadData(currentPage, searchValues);
} }
} catch (error) { } catch (error) {
console.error("데이터 수정 실패:", error); // console.error("데이터 수정 실패:", error);
alert("데이터 수정에 실패했습니다."); alert("데이터 수정에 실패했습니다.");
} finally { } finally {
setIsEditing(false); setIsEditing(false);
@ -971,7 +971,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
const selectedData = Array.from(selectedRows).map((index) => data[index]); const selectedData = Array.from(selectedRows).map((index) => data[index]);
// 실제 삭제 API 호출 // 실제 삭제 API 호출
console.log("🗑️ 삭제할 데이터:", selectedData); // console.log("🗑️ 삭제할 데이터:", selectedData);
await tableTypeApi.deleteTableData(component.tableName, selectedData); await tableTypeApi.deleteTableData(component.tableName, selectedData);
// 선택 해제 및 다이얼로그 닫기 // 선택 해제 및 다이얼로그 닫기
@ -981,7 +981,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
// 데이터 새로고침 // 데이터 새로고침
loadData(currentPage, searchValues); loadData(currentPage, searchValues);
} catch (error) { } catch (error) {
console.error("데이터 삭제 실패:", error); // console.error("데이터 삭제 실패:", error);
alert("데이터 삭제에 실패했습니다."); alert("데이터 삭제에 실패했습니다.");
} finally { } finally {
setIsDeleting(false); setIsDeleting(false);
@ -1544,7 +1544,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
// File 객체 유효성 검사 // File 객체 유효성 검사
if (!(file instanceof File) && !(file instanceof Blob)) { if (!(file instanceof File) && !(file instanceof Blob)) {
console.error("❌ 잘못된 파일 객체:", file); // console.error("❌ 잘못된 파일 객체:", file);
toast.error("파일 객체가 손상되었습니다. 파일을 다시 업로드해주세요."); toast.error("파일 객체가 손상되었습니다. 파일을 다시 업로드해주세요.");
return; return;
} }
@ -1560,7 +1560,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
toast.success(`${fileInfo.name} 다운로드가 완료되었습니다.`); toast.success(`${fileInfo.name} 다운로드가 완료되었습니다.`);
return; return;
} catch (error) { } catch (error) {
console.error("❌ 로컬 파일 다운로드 오류:", error); // console.error("❌ 로컬 파일 다운로드 오류:", error);
toast.error("로컬 파일 다운로드에 실패했습니다. 파일을 다시 업로드해주세요."); toast.error("로컬 파일 다운로드에 실패했습니다. 파일을 다시 업로드해주세요.");
return; return;
} }
@ -1580,7 +1580,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
toast.success(`${fileInfo.name} 다운로드가 완료되었습니다.`); toast.success(`${fileInfo.name} 다운로드가 완료되었습니다.`);
} catch (error) { } catch (error) {
console.error("파일 다운로드 오류:", error); // console.error("파일 다운로드 오류:", error);
toast.error(`${fileInfo.name} 다운로드에 실패했습니다.`); toast.error(`${fileInfo.name} 다운로드에 실패했습니다.`);
} }
}, []); }, []);
@ -1589,7 +1589,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
const handleDeleteLinkedFile = useCallback( const handleDeleteLinkedFile = useCallback(
async (fileId: string, fileName: string) => { async (fileId: string, fileName: string) => {
try { try {
console.log("🗑️ 파일 삭제 시작:", { fileId, fileName }); // console.log("🗑️ 파일 삭제 시작:", { fileId, fileName });
// 삭제 확인 다이얼로그 // 삭제 확인 다이얼로그
if (!confirm(`"${fileName}" 파일을 삭제하시겠습니까?`)) { if (!confirm(`"${fileName}" 파일을 삭제하시겠습니까?`)) {
@ -1605,7 +1605,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
}); });
const result = response.data; const result = response.data;
console.log("📡 파일 삭제 API 응답:", result); // console.log("📡 파일 삭제 API 응답:", result);
if (!result.success) { if (!result.success) {
throw new Error(result.message || "파일 삭제 실패"); throw new Error(result.message || "파일 삭제 실패");
@ -1622,15 +1622,15 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
try { try {
const response = await getLinkedFiles(component.tableName, recordId); const response = await getLinkedFiles(component.tableName, recordId);
setLinkedFiles(response.files || []); setLinkedFiles(response.files || []);
console.log("📁 파일 목록 새로고침 완료:", response.files?.length || 0); // console.log("📁 파일 목록 새로고침 완료:", response.files?.length || 0);
} catch (error) { } catch (error) {
console.error("파일 목록 새로고침 실패:", error); // console.error("파일 목록 새로고침 실패:", error);
} }
} }
console.log("✅ 파일 삭제 완료:", fileName); // console.log("✅ 파일 삭제 완료:", fileName);
} catch (error) { } catch (error) {
console.error("❌ 파일 삭제 실패:", error); // console.error("❌ 파일 삭제 실패:", error);
toast.error(`"${fileName}" 파일 삭제에 실패했습니다.`); toast.error(`"${fileName}" 파일 삭제에 실패했습니다.`);
} }
}, },

View File

@ -153,7 +153,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
const loadPopupLayout = async () => { const loadPopupLayout = async () => {
try { try {
setPopupLoading(true); setPopupLoading(true);
console.log("🔍 팝업 화면 로드 시작:", popupScreen); // console.log("🔍 팝업 화면 로드 시작:", popupScreen);
// 화면 레이아웃과 화면 정보를 병렬로 가져오기 // 화면 레이아웃과 화면 정보를 병렬로 가져오기
const [layout, screen] = await Promise.all([ const [layout, screen] = await Promise.all([
@ -180,7 +180,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
// 팝업 formData 초기화 // 팝업 formData 초기화
setPopupFormData({}); setPopupFormData({});
} catch (error) { } catch (error) {
console.error("❌ 팝업 화면 로드 실패:", error); // console.error("❌ 팝업 화면 로드 실패:", error);
setPopupLayout([]); setPopupLayout([]);
setPopupScreenInfo(null); setPopupScreenInfo(null);
} finally { } finally {
@ -203,27 +203,27 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
// 폼 데이터 업데이트 // 폼 데이터 업데이트
const updateFormData = (fieldName: string, value: any) => { const updateFormData = (fieldName: string, value: any) => {
console.log(`🔄 updateFormData: ${fieldName} = "${value}" (외부콜백: ${!!onFormDataChange})`); // console.log(`🔄 updateFormData: ${fieldName} = "${value}" (외부콜백: ${!!onFormDataChange})`);
// 항상 로컬 상태도 업데이트 // 항상 로컬 상태도 업데이트
setLocalFormData((prev) => ({ setLocalFormData((prev) => ({
...prev, ...prev,
[fieldName]: value, [fieldName]: value,
})); }));
console.log(`💾 로컬 상태 업데이트: ${fieldName} = "${value}"`); // console.log(`💾 로컬 상태 업데이트: ${fieldName} = "${value}"`);
// 외부 콜백이 있는 경우에도 전달 (개별 필드 단위로) // 외부 콜백이 있는 경우에도 전달 (개별 필드 단위로)
if (onFormDataChange) { if (onFormDataChange) {
onFormDataChange(fieldName, value); onFormDataChange(fieldName, value);
console.log(`📤 외부 콜백으로 전달: ${fieldName} = "${value}"`); // console.log(`📤 외부 콜백으로 전달: ${fieldName} = "${value}"`);
} }
}; };
// 자동입력 필드들의 값을 formData에 초기 설정 // 자동입력 필드들의 값을 formData에 초기 설정
React.useEffect(() => { React.useEffect(() => {
console.log("🚀 자동입력 초기화 useEffect 실행 - allComponents 개수:", allComponents.length); // console.log("🚀 자동입력 초기화 useEffect 실행 - allComponents 개수:", allComponents.length);
const initAutoInputFields = () => { const initAutoInputFields = () => {
console.log("🔧 initAutoInputFields 실행 시작"); // console.log("🔧 initAutoInputFields 실행 시작");
allComponents.forEach(comp => { allComponents.forEach(comp => {
if (comp.type === 'widget') { if (comp.type === 'widget') {
const widget = comp as WidgetComponent; const widget = comp as WidgetComponent;
@ -258,7 +258,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
updateFormData(fieldName, autoValue); updateFormData(fieldName, autoValue);
} else { } else {
console.log(`⏭️ 자동입력 건너뜀 (값 있음): ${fieldName} = "${currentValue}"`); // console.log(`⏭️ 자동입력 건너뜀 (값 있음): ${fieldName} = "${currentValue}"`);
} }
} }
} }
@ -378,7 +378,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
// 입력 검증 함수 // 입력 검증 함수
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value; const value = e.target.value;
console.log(`📝 입력 변경: ${fieldName} = "${value}"`); // console.log(`📝 입력 변경: ${fieldName} = "${value}"`);
// 형식별 실시간 검증 // 형식별 실시간 검증
if (config?.format && config.format !== "none") { if (config?.format && config.format !== "none") {
@ -386,7 +386,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
if (pattern) { if (pattern) {
const regex = new RegExp(`^${pattern}$`); const regex = new RegExp(`^${pattern}$`);
if (value && !regex.test(value)) { if (value && !regex.test(value)) {
console.log(`❌ 형식 검증 실패: ${fieldName} = "${value}"`); // console.log(`❌ 형식 검증 실패: ${fieldName} = "${value}"`);
return; // 유효하지 않은 입력 차단 return; // 유효하지 않은 입력 차단
} }
} }
@ -394,11 +394,11 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
// 길이 제한 검증 // 길이 제한 검증
if (config?.maxLength && value.length > config.maxLength) { if (config?.maxLength && value.length > config.maxLength) {
console.log(`❌ 길이 제한 초과: ${fieldName} = "${value}" (최대: ${config.maxLength})`); // console.log(`❌ 길이 제한 초과: ${fieldName} = "${value}" (최대: ${config.maxLength})`);
return; // 최대 길이 초과 차단 return; // 최대 길이 초과 차단
} }
console.log(`✅ updateFormData 호출: ${fieldName} = "${value}"`); // console.log(`✅ updateFormData 호출: ${fieldName} = "${value}"`);
updateFormData(fieldName, value); updateFormData(fieldName, value);
}; };
@ -797,16 +797,16 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
// 파일 선택을 취소한 경우 (files가 null이거나 길이가 0) // 파일 선택을 취소한 경우 (files가 null이거나 길이가 0)
if (!files || files.length === 0) { if (!files || files.length === 0) {
console.log("📁 파일 선택 취소됨 - 기존 파일 유지"); // console.log("📁 파일 선택 취소됨 - 기존 파일 유지");
// 현재 저장된 파일이 있는지 확인 // 현재 저장된 파일이 있는지 확인
const currentStoredValue = externalFormData?.[fieldName] || localFormData[fieldName]; const currentStoredValue = externalFormData?.[fieldName] || localFormData[fieldName];
if (currentStoredValue) { if (currentStoredValue) {
console.log("📁 기존 파일 있음 - 유지:", currentStoredValue); // console.log("📁 기존 파일 있음 - 유지:", currentStoredValue);
// 기존 파일이 있으면 그대로 유지 (아무것도 하지 않음) // 기존 파일이 있으면 그대로 유지 (아무것도 하지 않음)
return; return;
} else { } else {
console.log("📁 기존 파일 없음 - 빈 상태 유지"); // console.log("📁 기존 파일 없음 - 빈 상태 유지");
// 기존 파일이 없으면 빈 상태 유지 // 기존 파일이 없으면 빈 상태 유지
return; return;
} }
@ -831,7 +831,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
const uploadResult = await uploadFilesAndCreateData(files); const uploadResult = await uploadFilesAndCreateData(files);
if (uploadResult.success) { if (uploadResult.success) {
console.log("📁 업로드 완료된 파일 데이터:", uploadResult.data); // console.log("📁 업로드 완료된 파일 데이터:", uploadResult.data);
setLocalFormData(prev => ({ ...prev, [fieldName]: uploadResult.data })); setLocalFormData(prev => ({ ...prev, [fieldName]: uploadResult.data }));
@ -845,7 +845,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
throw new Error("파일 업로드에 실패했습니다."); throw new Error("파일 업로드에 실패했습니다.");
} }
} catch (error) { } catch (error) {
console.error("파일 업로드 오류:", error); // console.error("파일 업로드 오류:", error);
toast.error("파일 업로드에 실패했습니다."); toast.error("파일 업로드에 실패했습니다.");
// 파일 입력 초기화 // 파일 입력 초기화
@ -1023,12 +1023,12 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
isCodeType: true, // 코드 타입임을 명시 isCodeType: true, // 코드 타입임을 명시
}} }}
onEvent={(event: string, data: any) => { onEvent={(event: string, data: any) => {
console.log(`Code widget event: ${event}`, data); // console.log(`Code widget event: ${event}`, data);
}} }}
/> />
); );
} catch (error) { } catch (error) {
console.error("DynamicWebTypeRenderer 로딩 실패, 기본 Select 사용:", error); // console.error("DynamicWebTypeRenderer 로딩 실패, 기본 Select 사용:", error);
// 폴백: 기본 Select 컴포넌트 사용 // 폴백: 기본 Select 컴포넌트 사용
return applyStyles( return applyStyles(
@ -1148,21 +1148,21 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
await handleCustomAction(); await handleCustomAction();
break; break;
default: default:
console.log(`알 수 없는 액션 타입: ${actionType}`); // console.log(`알 수 없는 액션 타입: ${actionType}`);
} }
} catch (error) { } catch (error) {
console.error(`버튼 액션 실행 오류 (${actionType}):`, error); // console.error(`버튼 액션 실행 오류 (${actionType}):`, error);
alert(`작업 중 오류가 발생했습니다: ${error.message}`); alert(`작업 중 오류가 발생했습니다: ${error.message}`);
} }
}; };
// 저장 액션 (개선된 버전) // 저장 액션 (개선된 버전)
const handleSaveAction = async () => { const handleSaveAction = async () => {
console.log("💾 저장 시작"); // console.log("💾 저장 시작");
// 개선된 검증 시스템이 활성화된 경우 // 개선된 검증 시스템이 활성화된 경우
if (enhancedValidation) { if (enhancedValidation) {
console.log("🔍 개선된 검증 시스템 사용"); // console.log("🔍 개선된 검증 시스템 사용");
const success = await enhancedValidation.saveForm(); const success = await enhancedValidation.saveForm();
if (success) { if (success) {
toast.success("데이터가 성공적으로 저장되었습니다!"); toast.success("데이터가 성공적으로 저장되었습니다!");
@ -1172,7 +1172,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
// 기존 방식 (레거시 지원) // 기존 방식 (레거시 지원)
const currentFormData = { ...localFormData, ...externalFormData }; const currentFormData = { ...localFormData, ...externalFormData };
console.log("💾 기존 방식으로 저장 - currentFormData:", currentFormData); // console.log("💾 기존 방식으로 저장 - currentFormData:", currentFormData);
// formData 유효성 체크를 완화 (빈 객체라도 위젯이 있으면 저장 진행) // formData 유효성 체크를 완화 (빈 객체라도 위젯이 있으면 저장 진행)
const hasWidgets = allComponents.some(comp => comp.type === 'widget'); const hasWidgets = allComponents.some(comp => comp.type === 'widget');
@ -1249,7 +1249,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
existingValue: value existingValue: value
}); });
} else if (!isAutoInput) { } else if (!isAutoInput) {
console.log(`📝 일반 입력 필드: ${fieldName} = "${value}"`); // console.log(`📝 일반 입력 필드: ${fieldName} = "${value}"`);
} }
} }
@ -1260,7 +1260,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
mappedData[saveKey] = value; mappedData[saveKey] = value;
} else if (widget.columnName) { } else if (widget.columnName) {
// 값이 없지만 columnName이 있는 경우, 빈 문자열로 저장 // 값이 없지만 columnName이 있는 경우, 빈 문자열로 저장
console.log(`⚠️ ${widget.columnName} 필드에 값이 없어 빈 문자열로 저장`); // console.log(`⚠️ ${widget.columnName} 필드에 값이 없어 빈 문자열로 저장`);
mappedData[widget.columnName] = ""; mappedData[widget.columnName] = "";
} }
} }
@ -1275,20 +1275,20 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
}); });
// 각 컴포넌트의 상세 정보 로그 // 각 컴포넌트의 상세 정보 로그
console.log("🔍 컴포넌트별 데이터 수집 상세:"); // console.log("🔍 컴포넌트별 데이터 수집 상세:");
allComponents.forEach(comp => { allComponents.forEach(comp => {
if (comp.type === 'widget') { if (comp.type === 'widget') {
const widget = comp as WidgetComponent; const widget = comp as WidgetComponent;
const fieldName = widget.columnName || widget.id; const fieldName = widget.columnName || widget.id;
const value = currentFormData[fieldName]; const value = currentFormData[fieldName];
const hasValue = value !== undefined && value !== null && value !== ''; const hasValue = value !== undefined && value !== null && value !== '';
console.log(` - ${fieldName} (${widget.widgetType}): "${value}" (값있음: ${hasValue}, 컬럼명: ${widget.columnName})`); // console.log(` - ${fieldName} (${widget.widgetType}): "${value}" (값있음: ${hasValue}, 컬럼명: ${widget.columnName})`);
} }
}); });
// 매핑된 데이터가 비어있으면 경고 // 매핑된 데이터가 비어있으면 경고
if (Object.keys(mappedData).length === 0) { if (Object.keys(mappedData).length === 0) {
console.warn("⚠️ 매핑된 데이터가 없습니다. 빈 데이터로 저장됩니다."); // console.warn("⚠️ 매핑된 데이터가 없습니다. 빈 데이터로 저장됩니다.");
} }
// 테이블명 결정 (화면 정보에서 가져오거나 첫 번째 컴포넌트의 테이블명 사용) // 테이블명 결정 (화면 정보에서 가져오거나 첫 번째 컴포넌트의 테이블명 사용)
@ -1302,13 +1302,13 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
data: mappedData, data: mappedData,
}; };
console.log("🚀 API 저장 요청:", saveData); // console.log("🚀 API 저장 요청:", saveData);
const result = await dynamicFormApi.saveFormData(saveData); const result = await dynamicFormApi.saveFormData(saveData);
if (result.success) { if (result.success) {
alert("저장되었습니다."); alert("저장되었습니다.");
console.log("✅ 저장 성공:", result.data); // console.log("✅ 저장 성공:", result.data);
// 저장 후 데이터 초기화 (선택사항) // 저장 후 데이터 초기화 (선택사항)
if (onFormDataChange) { if (onFormDataChange) {
@ -1322,7 +1322,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
throw new Error(result.message || "저장에 실패했습니다."); throw new Error(result.message || "저장에 실패했습니다.");
} }
} catch (error: any) { } catch (error: any) {
console.error("❌ 저장 실패:", error); // console.error("❌ 저장 실패:", error);
alert(`저장 중 오류가 발생했습니다: ${error.message}`); alert(`저장 중 오류가 발생했습니다: ${error.message}`);
} }
}; };
@ -1355,13 +1355,13 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
} }
try { try {
console.log("🗑️ 삭제 실행:", { recordId, tableName, formData }); // console.log("🗑️ 삭제 실행:", { recordId, tableName, formData });
const result = await dynamicFormApi.deleteFormDataFromTable(recordId, tableName); const result = await dynamicFormApi.deleteFormDataFromTable(recordId, tableName);
if (result.success) { if (result.success) {
alert("삭제되었습니다."); alert("삭제되었습니다.");
console.log("✅ 삭제 성공"); // console.log("✅ 삭제 성공");
// 삭제 후 폼 초기화 // 삭제 후 폼 초기화
if (onFormDataChange) { if (onFormDataChange) {
@ -1375,28 +1375,28 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
throw new Error(result.message || "삭제에 실패했습니다."); throw new Error(result.message || "삭제에 실패했습니다.");
} }
} catch (error: any) { } catch (error: any) {
console.error("❌ 삭제 실패:", error); // console.error("❌ 삭제 실패:", error);
alert(`삭제 중 오류가 발생했습니다: ${error.message}`); alert(`삭제 중 오류가 발생했습니다: ${error.message}`);
} }
}; };
// 편집 액션 // 편집 액션
const handleEditAction = () => { const handleEditAction = () => {
console.log("✏️ 편집 모드 활성화"); // console.log("✏️ 편집 모드 활성화");
// 읽기 전용 모드를 편집 모드로 전환 // 읽기 전용 모드를 편집 모드로 전환
alert("편집 모드로 전환되었습니다."); alert("편집 모드로 전환되었습니다.");
}; };
// 추가 액션 // 추가 액션
const handleAddAction = () => { const handleAddAction = () => {
console.log(" 새 항목 추가"); // console.log(" 새 항목 추가");
// 새 항목 추가 로직 // 새 항목 추가 로직
alert("새 항목을 추가할 수 있습니다."); alert("새 항목을 추가할 수 있습니다.");
}; };
// 검색 액션 // 검색 액션
const handleSearchAction = () => { const handleSearchAction = () => {
console.log("🔍 검색 실행:", formData); // console.log("🔍 검색 실행:", formData);
// 검색 로직 // 검색 로직
const searchTerms = Object.values(formData).filter(v => v && v.toString().trim()); const searchTerms = Object.values(formData).filter(v => v && v.toString().trim());
if (searchTerms.length === 0) { if (searchTerms.length === 0) {
@ -1416,21 +1416,21 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
}); });
onFormDataChange(resetData); onFormDataChange(resetData);
} }
console.log("🔄 폼 초기화 완료"); // console.log("🔄 폼 초기화 완료");
alert("입력이 초기화되었습니다."); alert("입력이 초기화되었습니다.");
} }
}; };
// 제출 액션 // 제출 액션
const handleSubmitAction = async () => { const handleSubmitAction = async () => {
console.log("📤 폼 제출:", formData); // console.log("📤 폼 제출:", formData);
// 제출 로직 // 제출 로직
alert("제출되었습니다."); alert("제출되었습니다.");
}; };
// 닫기 액션 // 닫기 액션
const handleCloseAction = () => { const handleCloseAction = () => {
console.log("❌ 닫기 액션 실행"); // console.log("❌ 닫기 액션 실행");
// 모달 내부에서 실행되는지 확인 // 모달 내부에서 실행되는지 확인
const isInModal = document.querySelector('[role="dialog"]') !== null; const isInModal = document.querySelector('[role="dialog"]') !== null;
@ -1438,7 +1438,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
if (isInModal) { if (isInModal) {
// 모달 내부인 경우: 모달의 닫기 버튼 클릭하거나 모달 닫기 이벤트 발생 // 모달 내부인 경우: 모달의 닫기 버튼 클릭하거나 모달 닫기 이벤트 발생
console.log("🔄 모달 내부에서 닫기 - 모달 닫기 시도"); // console.log("🔄 모달 내부에서 닫기 - 모달 닫기 시도");
// 모달의 닫기 버튼을 찾아서 클릭 // 모달의 닫기 버튼을 찾아서 클릭
const modalCloseButton = document.querySelector('[role="dialog"] button[aria-label*="Close"], [role="dialog"] button[data-dismiss="modal"], [role="dialog"] .dialog-close'); const modalCloseButton = document.querySelector('[role="dialog"] button[aria-label*="Close"], [role="dialog"] button[data-dismiss="modal"], [role="dialog"] .dialog-close');
@ -1451,18 +1451,18 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
} }
} else if (isInPopup) { } else if (isInPopup) {
// 팝업 창인 경우 // 팝업 창인 경우
console.log("🔄 팝업 창 닫기"); // console.log("🔄 팝업 창 닫기");
window.close(); window.close();
} else { } else {
// 일반 페이지인 경우 - 이전 페이지로 이동하지 않고 아무것도 하지 않음 // 일반 페이지인 경우 - 이전 페이지로 이동하지 않고 아무것도 하지 않음
console.log("🔄 일반 페이지에서 닫기 - 아무 동작 하지 않음"); // console.log("🔄 일반 페이지에서 닫기 - 아무 동작 하지 않음");
alert("닫기 버튼이 클릭되었습니다."); alert("닫기 버튼이 클릭되었습니다.");
} }
}; };
// 팝업 액션 // 팝업 액션
const handlePopupAction = () => { const handlePopupAction = () => {
console.log("🎯 팝업 액션 실행:", { popupScreenId: config?.popupScreenId }); // console.log("🎯 팝업 액션 실행:", { popupScreenId: config?.popupScreenId });
if (config?.popupScreenId) { if (config?.popupScreenId) {
// 화면 모달 열기 // 화면 모달 열기
@ -1528,12 +1528,12 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
if (result instanceof Promise) { if (result instanceof Promise) {
await result; await result;
} }
console.log("⚡ 커스텀 액션 실행 완료"); // console.log("⚡ 커스텀 액션 실행 완료");
} catch (error) { } catch (error) {
throw new Error(`커스텀 액션 실행 실패: ${error.message}`); throw new Error(`커스텀 액션 실행 실패: ${error.message}`);
} }
} else { } else {
console.log("⚡ 커스텀 액션이 설정되지 않았습니다."); // console.log("⚡ 커스텀 액션이 설정되지 않았습니다.");
} }
}; };
@ -1619,7 +1619,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
company_code: file.companyCode company_code: file.companyCode
})); }));
console.log("💾 attach_file_info 형태로 변환된 데이터:", fileInfoForDB); // console.log("💾 attach_file_info 형태로 변환된 데이터:", fileInfoForDB);
// FormData에는 파일 연결 정보만 저장 (간단한 형태) // FormData에는 파일 연결 정보만 저장 (간단한 형태)
const formDataValue = { const formDataValue = {
@ -1633,7 +1633,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
})) }))
}; };
console.log("📝 FormData 저장값:", { fieldName, formDataValue }); // console.log("📝 FormData 저장값:", { fieldName, formDataValue });
onFormDataChange(fieldName, formDataValue); onFormDataChange(fieldName, formDataValue);
// TODO: 실제 API 연동 시 attach_file_info 테이블에 저장 // TODO: 실제 API 연동 시 attach_file_info 테이블에 저장

View File

@ -119,7 +119,7 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
setPopupScreen(null); setPopupScreen(null);
} }
} catch (error) { } catch (error) {
console.error("팝업 화면 로드 오류:", error); // console.error("팝업 화면 로드 오류:", error);
toast.error("팝업 화면 로드 중 오류가 발생했습니다."); toast.error("팝업 화면 로드 중 오류가 발생했습니다.");
setPopupScreen(null); setPopupScreen(null);
} finally { } finally {
@ -129,14 +129,14 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
// 폼 데이터 변경 핸들러 // 폼 데이터 변경 핸들러
const handleFormDataChange = (fieldName: string, value: any) => { const handleFormDataChange = (fieldName: string, value: any) => {
console.log(`🎯 InteractiveScreenViewerDynamic handleFormDataChange 호출: ${fieldName} = "${value}"`); // console.log(`🎯 InteractiveScreenViewerDynamic handleFormDataChange 호출: ${fieldName} = "${value}"`);
console.log(`📋 onFormDataChange 존재 여부:`, !!onFormDataChange); // console.log(`📋 onFormDataChange 존재 여부:`, !!onFormDataChange);
if (onFormDataChange) { if (onFormDataChange) {
console.log(`📤 InteractiveScreenViewerDynamic -> onFormDataChange 호출: ${fieldName} = "${value}"`); // console.log(`📤 InteractiveScreenViewerDynamic -> onFormDataChange 호출: ${fieldName} = "${value}"`);
onFormDataChange(fieldName, value); onFormDataChange(fieldName, value);
} else { } else {
console.log(`💾 InteractiveScreenViewerDynamic 로컬 상태 업데이트: ${fieldName} = "${value}"`); // console.log(`💾 InteractiveScreenViewerDynamic 로컬 상태 업데이트: ${fieldName} = "${value}"`);
setLocalFormData((prev) => ({ ...prev, [fieldName]: value })); setLocalFormData((prev) => ({ ...prev, [fieldName]: value }));
} }
}; };
@ -240,14 +240,14 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
config={widget.webTypeConfig} config={widget.webTypeConfig}
onEvent={(event: string, data: any) => { onEvent={(event: string, data: any) => {
// 이벤트 처리 // 이벤트 처리
console.log(`Widget event: ${event}`, data); // console.log(`Widget event: ${event}`, data);
}} }}
/> />
); );
return applyStyles(dynamicElement); return applyStyles(dynamicElement);
} catch (error) { } catch (error) {
console.error(`웹타입 "${widgetType}" 대화형 렌더링 실패:`, error); // console.error(`웹타입 "${widgetType}" 대화형 렌더링 실패:`, error);
// 오류 발생 시 폴백으로 기본 input 렌더링 // 오류 발생 시 폴백으로 기본 input 렌더링
const fallbackElement = ( const fallbackElement = (
<Input <Input
@ -297,7 +297,7 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
data: formData, data: formData,
}; };
console.log("💾 저장 액션 실행:", saveData); // console.log("💾 저장 액션 실행:", saveData);
const response = await dynamicFormApi.saveData(saveData); const response = await dynamicFormApi.saveData(saveData);
if (response.success) { if (response.success) {
@ -306,14 +306,14 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
toast.error(response.message || "저장에 실패했습니다."); toast.error(response.message || "저장에 실패했습니다.");
} }
} catch (error) { } catch (error) {
console.error("저장 오류:", error); // console.error("저장 오류:", error);
toast.error("저장 중 오류가 발생했습니다."); toast.error("저장 중 오류가 발생했습니다.");
} }
}; };
const handleDeleteAction = async () => { const handleDeleteAction = async () => {
if (!config?.confirmationEnabled || window.confirm(config.confirmationMessage || "정말 삭제하시겠습니까?")) { if (!config?.confirmationEnabled || window.confirm(config.confirmationMessage || "정말 삭제하시겠습니까?")) {
console.log("🗑️ 삭제 액션 실행"); // console.log("🗑️ 삭제 액션 실행");
toast.success("삭제가 완료되었습니다."); toast.success("삭제가 완료되었습니다.");
} }
}; };
@ -355,7 +355,7 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
if (result instanceof Promise) { if (result instanceof Promise) {
await result; await result;
} }
console.log("⚡ 커스텀 액션 실행 완료"); // console.log("⚡ 커스텀 액션 실행 완료");
} catch (error) { } catch (error) {
throw new Error(`커스텀 액션 실행 실패: ${error.message}`); throw new Error(`커스텀 액션 실행 실패: ${error.message}`);
} }
@ -383,10 +383,10 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
await handleCustomAction(); await handleCustomAction();
break; break;
default: default:
console.log("🔘 기본 버튼 클릭"); // console.log("🔘 기본 버튼 클릭");
} }
} catch (error) { } catch (error) {
console.error("버튼 액션 오류:", error); // console.error("버튼 액션 오류:", error);
toast.error(error.message || "액션 실행 중 오류가 발생했습니다."); toast.error(error.message || "액션 실행 중 오류가 발생했습니다.");
} }
}; };
@ -451,7 +451,7 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
...formData ...formData
}} }}
onFormDataChange={(data) => { onFormDataChange={(data) => {
console.log("📝 실제 화면 파일 업로드 완료:", data); // console.log("📝 실제 화면 파일 업로드 완료:", data);
if (onFormDataChange) { if (onFormDataChange) {
Object.entries(data).forEach(([key, value]) => { Object.entries(data).forEach(([key, value]) => {
onFormDataChange(key, value); onFormDataChange(key, value);
@ -486,25 +486,25 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
source: 'realScreen' // 실제 화면에서 온 이벤트임을 표시 source: 'realScreen' // 실제 화면에서 온 이벤트임을 표시
}; };
console.log("🚀🚀🚀 실제 화면 파일 변경 이벤트 발생:", eventDetail); // console.log("🚀🚀🚀 실제 화면 파일 변경 이벤트 발생:", eventDetail);
const event = new CustomEvent('globalFileStateChanged', { const event = new CustomEvent('globalFileStateChanged', {
detail: eventDetail detail: eventDetail
}); });
window.dispatchEvent(event); window.dispatchEvent(event);
console.log("✅✅✅ 실제 화면 → 화면설계 모드 동기화 이벤트 발생 완료"); // console.log("✅✅✅ 실제 화면 → 화면설계 모드 동기화 이벤트 발생 완료");
// 추가 지연 이벤트들 (화면설계 모드가 열려있을 때를 대비) // 추가 지연 이벤트들 (화면설계 모드가 열려있을 때를 대비)
setTimeout(() => { setTimeout(() => {
console.log("🔄 실제 화면 추가 이벤트 발생 (지연 100ms)"); // console.log("🔄 실제 화면 추가 이벤트 발생 (지연 100ms)");
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
detail: { ...eventDetail, delayed: true } detail: { ...eventDetail, delayed: true }
})); }));
}, 100); }, 100);
setTimeout(() => { setTimeout(() => {
console.log("🔄 실제 화면 추가 이벤트 발생 (지연 500ms)"); // console.log("🔄 실제 화면 추가 이벤트 발생 (지연 500ms)");
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
detail: { ...eventDetail, delayed: true, attempt: 2 } detail: { ...eventDetail, delayed: true, attempt: 2 }
})); }));

View File

@ -71,13 +71,13 @@ export const MenuAssignmentModal: React.FC<MenuAssignmentModalProps> = ({
company_name: menu.company_name || menu.COMPANY_NAME, company_name: menu.company_name || menu.COMPANY_NAME,
})); }));
console.log("로드된 관리자 메뉴 목록:", { // console.log("로드된 관리자 메뉴 목록:", {
total: normalizedAdminMenus.length, // total: normalizedAdminMenus.length,
sample: normalizedAdminMenus.slice(0, 3), // sample: normalizedAdminMenus.slice(0, 3),
}); // });
setMenus(normalizedAdminMenus); setMenus(normalizedAdminMenus);
} catch (error) { } catch (error) {
console.error("메뉴 목록 로드 실패:", error); // console.error("메뉴 목록 로드 실패:", error);
toast.error("메뉴 목록을 불러오는데 실패했습니다."); toast.error("메뉴 목록을 불러오는데 실패했습니다.");
} finally { } finally {
setLoading(false); setLoading(false);
@ -117,10 +117,10 @@ export const MenuAssignmentModal: React.FC<MenuAssignmentModalProps> = ({
if (menuObjid > 0) { if (menuObjid > 0) {
const screens = await menuScreenApi.getScreensByMenu(menuObjid); const screens = await menuScreenApi.getScreensByMenu(menuObjid);
setExistingScreens(screens); setExistingScreens(screens);
console.log(`메뉴 "${menu.menu_name_kor}"에 할당된 화면:`, screens); // console.log(`메뉴 "${menu.menu_name_kor}"에 할당된 화면:`, screens);
} }
} catch (error) { } catch (error) {
console.error("할당된 화면 조회 실패:", error); // console.error("할당된 화면 조회 실패:", error);
setExistingScreens([]); setExistingScreens([]);
} }
} }
@ -166,13 +166,13 @@ export const MenuAssignmentModal: React.FC<MenuAssignmentModalProps> = ({
// 기존 화면 교체인 경우 기존 화면들 먼저 제거 // 기존 화면 교체인 경우 기존 화면들 먼저 제거
if (replaceExisting && existingScreens.length > 0) { if (replaceExisting && existingScreens.length > 0) {
console.log("기존 화면들 제거 중...", existingScreens); // console.log("기존 화면들 제거 중...", existingScreens);
for (const existingScreen of existingScreens) { for (const existingScreen of existingScreens) {
try { try {
await menuScreenApi.unassignScreenFromMenu(existingScreen.screenId, menuObjid); await menuScreenApi.unassignScreenFromMenu(existingScreen.screenId, menuObjid);
console.log(`기존 화면 "${existingScreen.screenName}" 제거 완료`); // console.log(`기존 화면 "${existingScreen.screenName}" 제거 완료`);
} catch (error) { } catch (error) {
console.error(`기존 화면 "${existingScreen.screenName}" 제거 실패:`, error); // console.error(`기존 화면 "${existingScreen.screenName}" 제거 실패:`, error);
} }
} }
} }
@ -202,7 +202,7 @@ export const MenuAssignmentModal: React.FC<MenuAssignmentModalProps> = ({
} }
}, 3000); }, 3000);
} catch (error: any) { } catch (error: any) {
console.error("화면 할당 실패:", error); // console.error("화면 할당 실패:", error);
const errorMessage = error.response?.data?.message || "화면 할당에 실패했습니다."; const errorMessage = error.response?.data?.message || "화면 할당에 실패했습니다.";
toast.error(errorMessage); toast.error(errorMessage);
} finally { } finally {

View File

@ -62,7 +62,7 @@ export const OptimizedButtonComponent: React.FC<OptimizedButtonProps> = ({
const startTime = performance.now(); const startTime = performance.now();
try { try {
console.log(`🔘 Button clicked: ${component.id} (${config?.actionType})`); // console.log(`🔘 Button clicked: ${component.id} (${config?.actionType})`);
// 🔥 확장된 컨텍스트 데이터 수집 // 🔥 확장된 컨텍스트 데이터 수집
const contextData = { const contextData = {
@ -88,14 +88,14 @@ export const OptimizedButtonComponent: React.FC<OptimizedButtonProps> = ({
// 🔥 제어 전용 액션인지 확인 // 🔥 제어 전용 액션인지 확인
const isControlOnlyAction = config?.actionType === "control"; const isControlOnlyAction = config?.actionType === "control";
console.log("🎯 OptimizedButtonComponent 실행:", { // console.log("🎯 OptimizedButtonComponent 실행:", {
actionType: config?.actionType, // actionType: config?.actionType,
isControlOnlyAction, // isControlOnlyAction,
enableDataflowControl: config?.enableDataflowControl, // enableDataflowControl: config?.enableDataflowControl,
hasDataflowConfig: !!config?.dataflowConfig, // hasDataflowConfig: !!config?.dataflowConfig,
selectedRows, // selectedRows,
selectedRowsData, // selectedRowsData,
}); // });
if (config?.enableDataflowControl && config?.dataflowConfig) { if (config?.enableDataflowControl && config?.dataflowConfig) {
// 🔥 확장된 제어 검증 먼저 실행 // 🔥 확장된 제어 검증 먼저 실행
@ -131,7 +131,7 @@ export const OptimizedButtonComponent: React.FC<OptimizedButtonProps> = ({
await executeOriginalAction(config?.actionType || "save", contextData); await executeOriginalAction(config?.actionType || "save", contextData);
} }
} catch (error) { } catch (error) {
console.error("Button execution failed:", error); // console.error("Button execution failed:", error);
toast.error("버튼 실행 중 오류가 발생했습니다."); toast.error("버튼 실행 중 오류가 발생했습니다.");
setLastResult({ success: false, error: error.message }); setLastResult({ success: false, error: error.message });
} finally { } finally {
@ -142,9 +142,9 @@ export const OptimizedButtonComponent: React.FC<OptimizedButtonProps> = ({
// 성능 로깅 // 성능 로깅
if (totalTime > 200) { if (totalTime > 200) {
console.warn(`🐌 Slow button execution: ${totalTime.toFixed(2)}ms`); // console.warn(`🐌 Slow button execution: ${totalTime.toFixed(2)}ms`);
} else { } else {
console.log(`⚡ Button execution: ${totalTime.toFixed(2)}ms`); // console.log(`⚡ Button execution: ${totalTime.toFixed(2)}ms`);
} }
} }
}, [isExecuting, disabled, component.id, config?.actionType, config?.enableDataflowControl, formData, clickCount]); }, [isExecuting, disabled, component.id, config?.actionType, config?.enableDataflowControl, formData, clickCount]);
@ -212,22 +212,22 @@ export const OptimizedButtonComponent: React.FC<OptimizedButtonProps> = ({
switch (actionType) { switch (actionType) {
case "save": case "save":
console.log("💾 Save action completed:", result); // console.log("💾 Save action completed:", result);
break; break;
case "delete": case "delete":
console.log("🗑️ Delete action completed:", result); // console.log("🗑️ Delete action completed:", result);
break; break;
case "search": case "search":
console.log("🔍 Search action completed:", result); // console.log("🔍 Search action completed:", result);
break; break;
case "add": case "add":
console.log(" Add action completed:", result); // console.log(" Add action completed:", result);
break; break;
case "edit": case "edit":
console.log("✏️ Edit action completed:", result); // console.log("✏️ Edit action completed:", result);
break; break;
default: default:
console.log(`${actionType} action completed:`, result); // console.log(`✅ ${actionType} action completed:`, result);
} }
}; };
@ -289,7 +289,7 @@ export const OptimizedButtonComponent: React.FC<OptimizedButtonProps> = ({
return newSet; return newSet;
}); });
console.error("Background job failed:", status.result); // console.error("Background job failed:", status.result);
toast.error("백그라운드 처리 중 오류가 발생했습니다.", { duration: 3000 }); toast.error("백그라운드 처리 중 오류가 발생했습니다.", { duration: 3000 });
return; return;
} }
@ -298,7 +298,7 @@ export const OptimizedButtonComponent: React.FC<OptimizedButtonProps> = ({
if (pollCount < maxPolls && (status.status === "pending" || status.status === "processing")) { if (pollCount < maxPolls && (status.status === "pending" || status.status === "processing")) {
setTimeout(pollJobStatus, pollInterval); setTimeout(pollJobStatus, pollInterval);
} else if (pollCount >= maxPolls) { } else if (pollCount >= maxPolls) {
console.warn(`Background job polling timeout: ${jobId}`); // console.warn(`Background job polling timeout: ${jobId}`);
setBackgroundJobs((prev) => { setBackgroundJobs((prev) => {
const newSet = new Set(prev); const newSet = new Set(prev);
newSet.delete(jobId); newSet.delete(jobId);
@ -306,7 +306,7 @@ export const OptimizedButtonComponent: React.FC<OptimizedButtonProps> = ({
}); });
} }
} catch (error) { } catch (error) {
console.error("Failed to check job status:", error); // console.error("Failed to check job status:", error);
setBackgroundJobs((prev) => { setBackgroundJobs((prev) => {
const newSet = new Set(prev); const newSet = new Set(prev);
newSet.delete(jobId); newSet.delete(jobId);
@ -388,7 +388,7 @@ export const OptimizedButtonComponent: React.FC<OptimizedButtonProps> = ({
): Promise<any> => { ): Promise<any> => {
// 🔥 제어 액션은 여기서 처리하지 않음 (이미 위에서 처리됨) // 🔥 제어 액션은 여기서 처리하지 않음 (이미 위에서 처리됨)
if (actionType === "control") { if (actionType === "control") {
console.warn("제어 액션은 executeOriginalAction에서 처리되지 않아야 합니다."); // console.warn("제어 액션은 executeOriginalAction에서 처리되지 않아야 합니다.");
return; return;
} }

View File

@ -112,7 +112,7 @@ const WidgetRenderer: React.FC<{ component: ComponentData }> = ({ component }) =
const { widgetType, label, placeholder, required, readonly, columnName, style } = widget; const { widgetType, label, placeholder, required, readonly, columnName, style } = widget;
// 디버깅: 실제 widgetType 값 확인 // 디버깅: 실제 widgetType 값 확인
console.log("RealtimePreviewDynamic - widgetType:", widgetType, "columnName:", columnName); // console.log("RealtimePreviewDynamic - widgetType:", widgetType, "columnName:", columnName);
// 사용자가 테두리를 설정했는지 확인 // 사용자가 테두리를 설정했는지 확인
const hasCustomBorder = style && (style.borderWidth || style.borderStyle || style.borderColor || style.border); const hasCustomBorder = style && (style.borderWidth || style.borderStyle || style.borderColor || style.border);
@ -129,11 +129,11 @@ const WidgetRenderer: React.FC<{ component: ComponentData }> = ({ component }) =
// 파일 컴포넌트는 별도 로직에서 처리하므로 여기서는 제외 // 파일 컴포넌트는 별도 로직에서 처리하므로 여기서는 제외
if (isFileComponent(widget)) { if (isFileComponent(widget)) {
console.log("🎯 RealtimePreview - 파일 컴포넌트 감지 (별도 처리):", { // console.log("🎯 RealtimePreview - 파일 컴포넌트 감지 (별도 처리):", {
componentId: widget.id, // componentId: widget.id,
widgetType: widgetType, // widgetType: widgetType,
isFileComponent: true // isFileComponent: true
}); // });
return <div className="text-xs text-gray-500 p-2"> ( )</div>; return <div className="text-xs text-gray-500 p-2"> ( )</div>;
} }
@ -154,7 +154,7 @@ const WidgetRenderer: React.FC<{ component: ComponentData }> = ({ component }) =
/> />
); );
} catch (error) { } catch (error) {
console.error(`웹타입 "${widgetType}" 렌더링 실패:`, error); // console.error(`웹타입 "${widgetType}" 렌더링 실패:`, error);
// 오류 발생 시 폴백으로 기본 input 렌더링 // 오류 발생 시 폴백으로 기본 input 렌더링
return <Input type="text" {...commonProps} placeholder={`${widgetType} (렌더링 오류)`} />; return <Input type="text" {...commonProps} placeholder={`${widgetType} (렌더링 오류)`} />;
} }
@ -226,66 +226,66 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
// 전역 파일 상태 변경 감지 (해당 컴포넌트만) // 전역 파일 상태 변경 감지 (해당 컴포넌트만)
useEffect(() => { useEffect(() => {
const handleGlobalFileStateChange = (event: CustomEvent) => { const handleGlobalFileStateChange = (event: CustomEvent) => {
console.log("🎯🎯🎯 RealtimePreview 이벤트 수신:", { // console.log("🎯🎯🎯 RealtimePreview 이벤트 수신:", {
eventComponentId: event.detail.componentId, // eventComponentId: event.detail.componentId,
currentComponentId: component.id, // currentComponentId: component.id,
isMatch: event.detail.componentId === component.id, // isMatch: event.detail.componentId === component.id,
filesCount: event.detail.files?.length || 0, // filesCount: event.detail.files?.length || 0,
action: event.detail.action, // action: event.detail.action,
delayed: event.detail.delayed || false, // delayed: event.detail.delayed || false,
attempt: event.detail.attempt || 1, // attempt: event.detail.attempt || 1,
eventDetail: event.detail // eventDetail: event.detail
}); // });
if (event.detail.componentId === component.id) { if (event.detail.componentId === component.id) {
console.log("✅✅✅ RealtimePreview 파일 상태 변경 감지 - 리렌더링 시작:", { // console.log("✅✅✅ RealtimePreview 파일 상태 변경 감지 - 리렌더링 시작:", {
componentId: component.id, // componentId: component.id,
filesCount: event.detail.files?.length || 0, // filesCount: event.detail.files?.length || 0,
action: event.detail.action, // action: event.detail.action,
oldTrigger: fileUpdateTrigger, // oldTrigger: fileUpdateTrigger,
delayed: event.detail.delayed || false, // delayed: event.detail.delayed || false,
attempt: event.detail.attempt || 1 // attempt: event.detail.attempt || 1
}); // });
setFileUpdateTrigger(prev => { setFileUpdateTrigger(prev => {
const newTrigger = prev + 1; const newTrigger = prev + 1;
console.log("🔄🔄🔄 fileUpdateTrigger 업데이트:", { // console.log("🔄🔄🔄 fileUpdateTrigger 업데이트:", {
old: prev, // old: prev,
new: newTrigger, // new: newTrigger,
componentId: component.id, // componentId: component.id,
attempt: event.detail.attempt || 1 // attempt: event.detail.attempt || 1
}); // });
return newTrigger; return newTrigger;
}); });
} else { } else {
console.log("❌ 컴포넌트 ID 불일치:", { // console.log("❌ 컴포넌트 ID 불일치:", {
eventComponentId: event.detail.componentId, // eventComponentId: event.detail.componentId,
currentComponentId: component.id // currentComponentId: component.id
}); // });
} }
}; };
// 강제 업데이트 함수 등록 // 강제 업데이트 함수 등록
const forceUpdate = (componentId: string, files: any[]) => { const forceUpdate = (componentId: string, files: any[]) => {
console.log("🔥🔥🔥 RealtimePreview 강제 업데이트 호출:", { // console.log("🔥🔥🔥 RealtimePreview 강제 업데이트 호출:", {
targetComponentId: componentId, // targetComponentId: componentId,
currentComponentId: component.id, // currentComponentId: component.id,
isMatch: componentId === component.id, // isMatch: componentId === component.id,
filesCount: files.length // filesCount: files.length
}); // });
if (componentId === component.id) { if (componentId === component.id) {
console.log("✅✅✅ RealtimePreview 강제 업데이트 적용:", { // console.log("✅✅✅ RealtimePreview 강제 업데이트 적용:", {
componentId: component.id, // componentId: component.id,
filesCount: files.length, // filesCount: files.length,
oldTrigger: fileUpdateTrigger // oldTrigger: fileUpdateTrigger
}); // });
setFileUpdateTrigger(prev => { setFileUpdateTrigger(prev => {
const newTrigger = prev + 1; const newTrigger = prev + 1;
console.log("🔄🔄🔄 강제 fileUpdateTrigger 업데이트:", { // console.log("🔄🔄🔄 강제 fileUpdateTrigger 업데이트:", {
old: prev, // old: prev,
new: newTrigger, // new: newTrigger,
componentId: component.id // componentId: component.id
}); // });
return newTrigger; return newTrigger;
}); });
} }
@ -300,14 +300,14 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
(window as any).forceRealtimePreviewUpdate = forceUpdate; (window as any).forceRealtimePreviewUpdate = forceUpdate;
} }
} catch (error) { } catch (error) {
console.warn("RealtimePreview 이벤트 리스너 등록 실패:", error); // console.warn("RealtimePreview 이벤트 리스너 등록 실패:", error);
} }
return () => { return () => {
try { try {
window.removeEventListener('globalFileStateChanged', handleGlobalFileStateChange as EventListener); window.removeEventListener('globalFileStateChanged', handleGlobalFileStateChange as EventListener);
} catch (error) { } catch (error) {
console.warn("RealtimePreview 이벤트 리스너 제거 실패:", error); // console.warn("RealtimePreview 이벤트 리스너 제거 실패:", error);
} }
}; };
} }
@ -423,16 +423,16 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
// 최신 파일 정보 사용 (전역 상태 > 컴포넌트 속성) // 최신 파일 정보 사용 (전역 상태 > 컴포넌트 속성)
const currentFiles = globalFiles.length > 0 ? globalFiles : uploadedFiles; const currentFiles = globalFiles.length > 0 ? globalFiles : uploadedFiles;
console.log("🔍 RealtimePreview 파일 컴포넌트 렌더링:", { // console.log("🔍 RealtimePreview 파일 컴포넌트 렌더링:", {
componentId: component.id, // componentId: component.id,
uploadedFilesCount: uploadedFiles.length, // uploadedFilesCount: uploadedFiles.length,
globalFilesCount: globalFiles.length, // globalFilesCount: globalFiles.length,
currentFilesCount: currentFiles.length, // currentFilesCount: currentFiles.length,
currentFiles: currentFiles.map((f: any) => ({ objid: f.objid, name: f.realFileName || f.name })), // currentFiles: currentFiles.map((f: any) => ({ objid: f.objid, name: f.realFileName || f.name })),
componentType: component.type, // componentType: component.type,
fileUpdateTrigger: fileUpdateTrigger, // fileUpdateTrigger: fileUpdateTrigger,
timestamp: new Date().toISOString() // timestamp: new Date().toISOString()
}); // });
return ( return (
<div key={`file-component-${component.id}-${fileUpdateTrigger}`} className="flex h-full flex-col"> <div key={`file-component-${component.id}-${fileUpdateTrigger}`} className="flex h-full flex-col">

View File

@ -201,14 +201,14 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
const restoreFileComponentsData = useCallback(async (components: ComponentData[]) => { const restoreFileComponentsData = useCallback(async (components: ComponentData[]) => {
if (!selectedScreen?.screenId) return; if (!selectedScreen?.screenId) return;
console.log("🔄 파일 컴포넌트 데이터 복원 시작:", components.length); // console.log("🔄 파일 컴포넌트 데이터 복원 시작:", components.length);
try { try {
// 실제 DB에서 화면의 모든 파일 정보 조회 // 실제 DB에서 화면의 모든 파일 정보 조회
const fileResponse = await ScreenFileAPI.getScreenComponentFiles(selectedScreen.screenId); const fileResponse = await ScreenFileAPI.getScreenComponentFiles(selectedScreen.screenId);
if (!fileResponse.success) { if (!fileResponse.success) {
console.warn("⚠️ 파일 정보 조회 실패:", fileResponse); // console.warn("⚠️ 파일 정보 조회 실패:", fileResponse);
return; return;
} }
@ -271,7 +271,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
} }
} }
} catch (error) { } catch (error) {
console.error("❌ 파일 컴포넌트 데이터 복원 실패:", error); // console.error("❌ 파일 컴포넌트 데이터 복원 실패:", error);
toast.error("파일 데이터 복원 중 오류가 발생했습니다."); toast.error("파일 데이터 복원 중 오류가 발생했습니다.");
} }
}, [selectedScreen?.screenId]); }, [selectedScreen?.screenId]);
@ -723,11 +723,11 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
useEffect(() => { useEffect(() => {
const initComponents = async () => { const initComponents = async () => {
try { try {
console.log("🚀 컴포넌트 시스템 초기화 시작..."); // console.log("🚀 컴포넌트 시스템 초기화 시작...");
await initializeComponents(); await initializeComponents();
console.log("✅ 컴포넌트 시스템 초기화 완료"); // console.log("✅ 컴포넌트 시스템 초기화 완료");
} catch (error) { } catch (error) {
console.error("❌ 컴포넌트 시스템 초기화 실패:", error); // console.error("❌ 컴포넌트 시스템 초기화 실패:", error);
} }
}; };
@ -746,13 +746,13 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
if (!selectedScreen?.screenId) return; if (!selectedScreen?.screenId) return;
try { try {
console.log("🔄 화면 파일 복원 시작:", selectedScreen.screenId); // console.log("🔄 화면 파일 복원 시작:", selectedScreen.screenId);
// 해당 화면의 모든 파일 조회 // 해당 화면의 모든 파일 조회
const response = await ScreenFileAPI.getScreenComponentFiles(selectedScreen.screenId); const response = await ScreenFileAPI.getScreenComponentFiles(selectedScreen.screenId);
if (response.success && response.componentFiles) { if (response.success && response.componentFiles) {
console.log("📁 복원할 파일 데이터:", response.componentFiles); // console.log("📁 복원할 파일 데이터:", response.componentFiles);
// 각 컴포넌트별로 파일 복원 (전역 상태와 localStorage 우선 적용) // 각 컴포넌트별로 파일 복원 (전역 상태와 localStorage 우선 적용)
Object.entries(response.componentFiles).forEach(([componentId, serverFiles]) => { Object.entries(response.componentFiles).forEach(([componentId, serverFiles]) => {
@ -769,7 +769,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
currentLocalStorageFiles = JSON.parse(storedFiles); currentLocalStorageFiles = JSON.parse(storedFiles);
} }
} catch (e) { } catch (e) {
console.warn("localStorage 파일 파싱 실패:", e); // console.warn("localStorage 파일 파싱 실패:", e);
} }
} }
@ -777,12 +777,12 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
let finalFiles = serverFiles; let finalFiles = serverFiles;
if (currentGlobalFiles.length > 0) { if (currentGlobalFiles.length > 0) {
finalFiles = currentGlobalFiles; finalFiles = currentGlobalFiles;
console.log(`📂 컴포넌트 ${componentId} 전역 상태 우선 적용:`, finalFiles.length, "개"); // console.log(`📂 컴포넌트 ${componentId} 전역 상태 우선 적용:`, finalFiles.length, "개");
} else if (currentLocalStorageFiles.length > 0) { } else if (currentLocalStorageFiles.length > 0) {
finalFiles = currentLocalStorageFiles; finalFiles = currentLocalStorageFiles;
console.log(`📂 컴포넌트 ${componentId} localStorage 우선 적용:`, finalFiles.length, "개"); // console.log(`📂 컴포넌트 ${componentId} localStorage 우선 적용:`, finalFiles.length, "개");
} else { } else {
console.log(`📂 컴포넌트 ${componentId} 서버 데이터 적용:`, finalFiles.length, "개"); // console.log(`📂 컴포넌트 ${componentId} 서버 데이터 적용:`, finalFiles.length, "개");
} }
// 전역 상태에 파일 저장 // 전역 상태에 파일 저장
@ -821,17 +821,17 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}; };
}); });
console.log("✅ 화면 파일 복원 완료"); // console.log("✅ 화면 파일 복원 완료");
} }
} catch (error) { } catch (error) {
console.error("❌ 화면 파일 복원 오류:", error); // console.error("❌ 화면 파일 복원 오류:", error);
} }
}, [selectedScreen?.screenId]); }, [selectedScreen?.screenId]);
// 전역 파일 상태 변경 이벤트 리스너 // 전역 파일 상태 변경 이벤트 리스너
useEffect(() => { useEffect(() => {
const handleGlobalFileStateChange = (event: CustomEvent) => { const handleGlobalFileStateChange = (event: CustomEvent) => {
console.log("🔄 ScreenDesigner: 전역 파일 상태 변경 감지", event.detail); // console.log("🔄 ScreenDesigner: 전역 파일 상태 변경 감지", event.detail);
setForceRenderTrigger(prev => prev + 1); setForceRenderTrigger(prev => prev + 1);
}; };
@ -881,7 +881,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}; };
setTables([tableInfo]); // 단일 테이블 정보만 설정 setTables([tableInfo]); // 단일 테이블 정보만 설정
} catch (error) { } catch (error) {
console.error("테이블 정보 로드 실패:", error); // console.error("테이블 정보 로드 실패:", error);
toast.error(`테이블 '${selectedScreen.tableName}' 정보를 불러오는데 실패했습니다.`); toast.error(`테이블 '${selectedScreen.tableName}' 정보를 불러오는데 실패했습니다.`);
} }
}; };
@ -923,13 +923,13 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 저장된 해상도 정보가 있으면 적용, 없으면 기본값 사용 // 저장된 해상도 정보가 있으면 적용, 없으면 기본값 사용
if (response.screenResolution) { if (response.screenResolution) {
setScreenResolution(response.screenResolution); setScreenResolution(response.screenResolution);
console.log("💾 저장된 해상도 불러옴:", response.screenResolution); // console.log("💾 저장된 해상도 불러옴:", response.screenResolution);
} else { } else {
// 기본 해상도 (Full HD) // 기본 해상도 (Full HD)
const defaultResolution = const defaultResolution =
SCREEN_RESOLUTIONS.find((r) => r.name === "Full HD (1920×1080)") || SCREEN_RESOLUTIONS[0]; SCREEN_RESOLUTIONS.find((r) => r.name === "Full HD (1920×1080)") || SCREEN_RESOLUTIONS[0];
setScreenResolution(defaultResolution); setScreenResolution(defaultResolution);
console.log("🔧 기본 해상도 적용:", defaultResolution); // console.log("🔧 기본 해상도 적용:", defaultResolution);
} }
setLayout(layoutWithDefaultGrid); setLayout(layoutWithDefaultGrid);
@ -940,7 +940,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
restoreFileComponentsData(layoutWithDefaultGrid.components); restoreFileComponentsData(layoutWithDefaultGrid.components);
} }
} catch (error) { } catch (error) {
console.error("레이아웃 로드 실패:", error); // console.error("레이아웃 로드 실패:", error);
toast.error("화면 레이아웃을 불러오는데 실패했습니다."); toast.error("화면 레이아웃을 불러오는데 실패했습니다.");
} }
}; };
@ -989,8 +989,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}); });
newLayout.components = adjustedComponents; newLayout.components = adjustedComponents;
console.log("격자 설정 변경으로 컴포넌트 위치 및 크기 자동 조정:", adjustedComponents.length, "개"); // console.log("격자 설정 변경으로 컴포넌트 위치 및 크기 자동 조정:", adjustedComponents.length, "개");
console.log("새로운 격자 정보:", newGridInfo); // console.log("새로운 격자 정보:", newGridInfo);
} }
setLayout(newLayout); setLayout(newLayout);
@ -1033,7 +1033,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 강제 격자 재조정 핸들러 (해상도 변경 후 수동 격자 맞춤용) // 강제 격자 재조정 핸들러 (해상도 변경 후 수동 격자 맞춤용)
const handleForceGridUpdate = useCallback(() => { const handleForceGridUpdate = useCallback(() => {
if (!layout.gridSettings?.snapToGrid || layout.components.length === 0) { if (!layout.gridSettings?.snapToGrid || layout.components.length === 0) {
console.log("격자 재조정 생략: 스냅 비활성화 또는 컴포넌트 없음"); // console.log("격자 재조정 생략: 스냅 비활성화 또는 컴포넌트 없음");
return; return;
} }
@ -1114,7 +1114,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 저장 성공 후 메뉴 할당 모달 열기 // 저장 성공 후 메뉴 할당 모달 열기
setShowMenuAssignmentModal(true); setShowMenuAssignmentModal(true);
} catch (error) { } catch (error) {
console.error("저장 실패:", error); // console.error("저장 실패:", error);
toast.error("저장 중 오류가 발생했습니다."); toast.error("저장 중 오류가 발생했습니다.");
} finally { } finally {
setIsSaving(false); setIsSaving(false);
@ -1581,7 +1581,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 존 클릭 핸들러 // 존 클릭 핸들러
const handleZoneClick = useCallback((zoneId: string) => { const handleZoneClick = useCallback((zoneId: string) => {
console.log("🎯 존 클릭:", zoneId); // console.log("🎯 존 클릭:", zoneId);
// 필요시 존 선택 로직 추가 // 필요시 존 선택 로직 추가
}, []); }, []);
@ -1648,7 +1648,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
return; return;
} }
} catch (error) { } catch (error) {
console.error("드래그 데이터 파싱 오류:", error); // console.error("드래그 데이터 파싱 오류:", error);
return; return;
} }
} }
@ -1810,15 +1810,15 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
e.preventDefault(); e.preventDefault();
const dragData = e.dataTransfer.getData("application/json"); const dragData = e.dataTransfer.getData("application/json");
console.log("🎯 드롭 이벤트:", { dragData }); // console.log("🎯 드롭 이벤트:", { dragData });
if (!dragData) { if (!dragData) {
console.log("❌ 드래그 데이터가 없습니다"); // console.log("❌ 드래그 데이터가 없습니다");
return; return;
} }
try { try {
const parsedData = JSON.parse(dragData); const parsedData = JSON.parse(dragData);
console.log("📋 파싱된 데이터:", parsedData); // console.log("📋 파싱된 데이터:", parsedData);
// 템플릿 드래그인 경우 // 템플릿 드래그인 경우
if (parsedData.type === "template") { if (parsedData.type === "template") {
@ -1871,7 +1871,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}, },
}; };
} else if (type === "column") { } else if (type === "column") {
console.log("🔄 컬럼 드롭 처리:", { webType: column.widgetType, columnName: column.columnName }); // console.log("🔄 컬럼 드롭 처리:", { webType: column.widgetType, columnName: column.columnName });
// 현재 해상도에 맞는 격자 정보로 기본 크기 계산 // 현재 해상도에 맞는 격자 정보로 기본 크기 계산
const currentGridInfo = layout.gridSettings const currentGridInfo = layout.gridSettings
? calculateGridInfo(screenResolution.width, screenResolution.height, { ? calculateGridInfo(screenResolution.width, screenResolution.height, {
@ -2052,7 +2052,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 웹타입을 새로운 컴포넌트 ID로 매핑 // 웹타입을 새로운 컴포넌트 ID로 매핑
const componentId = getComponentIdFromWebType(column.widgetType); const componentId = getComponentIdFromWebType(column.widgetType);
console.log(`🔄 폼 컨테이너 드롭: ${column.widgetType}${componentId}`); // console.log(`🔄 폼 컨테이너 드롭: ${column.widgetType} → ${componentId}`);
newComponent = { newComponent = {
id: generateComponentId(), id: generateComponentId(),
@ -2096,7 +2096,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
} else { } else {
// 일반 캔버스에 드롭한 경우 - 새로운 컴포넌트 시스템 사용 // 일반 캔버스에 드롭한 경우 - 새로운 컴포넌트 시스템 사용
const componentId = getComponentIdFromWebType(column.widgetType); const componentId = getComponentIdFromWebType(column.widgetType);
console.log(`🔄 캔버스 드롭: ${column.widgetType}${componentId}`); // console.log(`🔄 캔버스 드롭: ${column.widgetType} → ${componentId}`);
newComponent = { newComponent = {
id: generateComponentId(), id: generateComponentId(),
@ -2186,7 +2186,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 속성 패널 자동 열기 // 속성 패널 자동 열기
openPanel("properties"); openPanel("properties");
} catch (error) { } catch (error) {
console.error("드롭 처리 실패:", error); // console.error("드롭 처리 실패:", error);
} }
}, },
[layout, gridInfo, saveToHistory, openPanel], [layout, gridInfo, saveToHistory, openPanel],
@ -2237,7 +2237,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
} }
// 다른 컴포넌트 타입의 더블클릭 처리는 여기에 추가 // 다른 컴포넌트 타입의 더블클릭 처리는 여기에 추가
console.log("더블클릭된 컴포넌트:", component.type, component.id); // console.log("더블클릭된 컴포넌트:", component.type, component.id);
}, },
[], [],
); );
@ -2339,7 +2339,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
componentsToMove = [...componentsToMove, ...additionalComponents]; componentsToMove = [...componentsToMove, ...additionalComponents];
} }
console.log("드래그 시작:", component.id, "이동할 컴포넌트 수:", componentsToMove.length); // console.log("드래그 시작:", component.id, "이동할 컴포넌트 수:", componentsToMove.length);
console.log("마우스 위치:", { console.log("마우스 위치:", {
clientX: event.clientX, clientX: event.clientX,
clientY: event.clientY, clientY: event.clientY,
@ -2706,7 +2706,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
const deleteComponent = useCallback(() => { const deleteComponent = useCallback(() => {
// 다중 선택된 컴포넌트가 있는 경우 // 다중 선택된 컴포넌트가 있는 경우
if (groupState.selectedComponents.length > 0) { if (groupState.selectedComponents.length > 0) {
console.log("🗑️ 다중 컴포넌트 삭제:", groupState.selectedComponents.length, "개"); // console.log("🗑️ 다중 컴포넌트 삭제:", groupState.selectedComponents.length, "개");
let newComponents = [...layout.components]; let newComponents = [...layout.components];
@ -2751,7 +2751,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 단일 선택된 컴포넌트 삭제 // 단일 선택된 컴포넌트 삭제
if (!selectedComponent) return; if (!selectedComponent) return;
console.log("🗑️ 단일 컴포넌트 삭제:", selectedComponent.id); // console.log("🗑️ 단일 컴포넌트 삭제:", selectedComponent.id);
let newComponents; let newComponents;
@ -2789,12 +2789,12 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 다중 선택된 컴포넌트들 복사 // 다중 선택된 컴포넌트들 복사
const componentsToCopy = layout.components.filter((comp) => groupState.selectedComponents.includes(comp.id)); const componentsToCopy = layout.components.filter((comp) => groupState.selectedComponents.includes(comp.id));
setClipboard(componentsToCopy); setClipboard(componentsToCopy);
console.log("다중 컴포넌트 복사:", componentsToCopy.length, "개"); // console.log("다중 컴포넌트 복사:", componentsToCopy.length, "개");
toast.success(`${componentsToCopy.length}개 컴포넌트가 복사되었습니다.`); toast.success(`${componentsToCopy.length}개 컴포넌트가 복사되었습니다.`);
} else if (selectedComponent) { } else if (selectedComponent) {
// 단일 컴포넌트 복사 // 단일 컴포넌트 복사
setClipboard([selectedComponent]); setClipboard([selectedComponent]);
console.log("단일 컴포넌트 복사:", selectedComponent.id); // console.log("단일 컴포넌트 복사:", selectedComponent.id);
toast.success("컴포넌트가 복사되었습니다."); toast.success("컴포넌트가 복사되었습니다.");
} }
}, [selectedComponent, groupState.selectedComponents, layout.components]); }, [selectedComponent, groupState.selectedComponents, layout.components]);
@ -2837,14 +2837,14 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
selectedComponents: newComponents.map((comp) => comp.id), selectedComponents: newComponents.map((comp) => comp.id),
})); }));
console.log("컴포넌트 붙여넣기 완료:", newComponents.length, "개"); // console.log("컴포넌트 붙여넣기 완료:", newComponents.length, "개");
toast.success(`${newComponents.length}개 컴포넌트가 붙여넣어졌습니다.`); toast.success(`${newComponents.length}개 컴포넌트가 붙여넣어졌습니다.`);
}, [clipboard, layout, saveToHistory]); }, [clipboard, layout, saveToHistory]);
// 그룹 생성 (임시 비활성화) // 그룹 생성 (임시 비활성화)
const handleGroupCreate = useCallback( const handleGroupCreate = useCallback(
(componentIds: string[], title: string, style?: any) => { (componentIds: string[], title: string, style?: any) => {
console.log("그룹 생성 기능이 임시 비활성화되었습니다."); // console.log("그룹 생성 기능이 임시 비활성화되었습니다.");
toast.info("그룹 기능이 임시 비활성화되었습니다."); toast.info("그룹 기능이 임시 비활성화되었습니다.");
return; return;
@ -3047,13 +3047,13 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
return; return;
} }
console.log("🔄 그룹 생성 다이얼로그 표시"); // console.log("🔄 그룹 생성 다이얼로그 표시");
setShowGroupCreateDialog(true); setShowGroupCreateDialog(true);
}, [groupState.selectedComponents]); }, [groupState.selectedComponents]);
// 그룹 해제 함수 (임시 비활성화) // 그룹 해제 함수 (임시 비활성화)
const ungroupComponents = useCallback(() => { const ungroupComponents = useCallback(() => {
console.log("그룹 해제 기능이 임시 비활성화되었습니다."); // console.log("그룹 해제 기능이 임시 비활성화되었습니다.");
toast.info("그룹 해제 기능이 임시 비활성화되었습니다."); toast.info("그룹 해제 기능이 임시 비활성화되었습니다.");
return; return;
@ -3166,7 +3166,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 키보드 이벤트 처리 (브라우저 기본 기능 완전 차단) // 키보드 이벤트 처리 (브라우저 기본 기능 완전 차단)
useEffect(() => { useEffect(() => {
const handleKeyDown = async (e: KeyboardEvent) => { const handleKeyDown = async (e: KeyboardEvent) => {
console.log("🎯 키 입력 감지:", { key: e.key, ctrlKey: e.ctrlKey, shiftKey: e.shiftKey, metaKey: e.metaKey }); // console.log("🎯 키 입력 감지:", { key: e.key, ctrlKey: e.ctrlKey, shiftKey: e.shiftKey, metaKey: e.metaKey });
// 🚫 브라우저 기본 단축키 완전 차단 목록 // 🚫 브라우저 기본 단축키 완전 차단 목록
const browserShortcuts = [ const browserShortcuts = [
@ -3219,7 +3219,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}); });
if (isBrowserShortcut) { if (isBrowserShortcut) {
console.log("🚫 브라우저 기본 단축키 차단:", e.key); // console.log("🚫 브라우저 기본 단축키 차단:", e.key);
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@ -3229,38 +3229,38 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 1. 그룹 관련 단축키 // 1. 그룹 관련 단축키
if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "g" && !e.shiftKey) { if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "g" && !e.shiftKey) {
console.log("🔄 그룹 생성 단축키"); // console.log("🔄 그룹 생성 단축키");
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
if (groupState.selectedComponents.length >= 2) { if (groupState.selectedComponents.length >= 2) {
console.log("✅ 그룹 생성 실행"); // console.log("✅ 그룹 생성 실행");
createGroup(); createGroup();
} else { } else {
console.log("⚠️ 선택된 컴포넌트가 부족함 (2개 이상 필요)"); // console.log("⚠️ 선택된 컴포넌트가 부족함 (2개 이상 필요)");
} }
return false; return false;
} }
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key?.toLowerCase() === "g") { if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key?.toLowerCase() === "g") {
console.log("🔄 그룹 해제 단축키"); // console.log("🔄 그룹 해제 단축키");
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
if (selectedComponent && selectedComponent.type === "group") { if (selectedComponent && selectedComponent.type === "group") {
console.log("✅ 그룹 해제 실행"); // console.log("✅ 그룹 해제 실행");
ungroupComponents(); ungroupComponents();
} else { } else {
console.log("⚠️ 선택된 그룹이 없음"); // console.log("⚠️ 선택된 그룹이 없음");
} }
return false; return false;
} }
// 2. 전체 선택 (애플리케이션 내에서만) // 2. 전체 선택 (애플리케이션 내에서만)
if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "a") { if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "a") {
console.log("🔄 전체 선택 (애플리케이션 내)"); // console.log("🔄 전체 선택 (애플리케이션 내)");
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@ -3271,7 +3271,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 3. 실행취소/다시실행 // 3. 실행취소/다시실행
if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "z" && !e.shiftKey) { if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "z" && !e.shiftKey) {
console.log("🔄 실행취소"); // console.log("🔄 실행취소");
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@ -3283,7 +3283,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "y") || ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "y") ||
((e.ctrlKey || e.metaKey) && e.shiftKey && e.key?.toLowerCase() === "z") ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key?.toLowerCase() === "z")
) { ) {
console.log("🔄 다시실행"); // console.log("🔄 다시실행");
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@ -3293,7 +3293,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 4. 복사 (컴포넌트 복사) // 4. 복사 (컴포넌트 복사)
if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "c") { if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "c") {
console.log("🔄 컴포넌트 복사"); // console.log("🔄 컴포넌트 복사");
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@ -3303,7 +3303,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 5. 붙여넣기 (컴포넌트 붙여넣기) // 5. 붙여넣기 (컴포넌트 붙여넣기)
if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "v") { if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "v") {
console.log("🔄 컴포넌트 붙여넣기"); // console.log("🔄 컴포넌트 붙여넣기");
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@ -3313,7 +3313,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 6. 삭제 (단일/다중 선택 지원) // 6. 삭제 (단일/다중 선택 지원)
if (e.key === "Delete" && (selectedComponent || groupState.selectedComponents.length > 0)) { if (e.key === "Delete" && (selectedComponent || groupState.selectedComponents.length > 0)) {
console.log("🗑️ 컴포넌트 삭제 (단축키)"); // console.log("🗑️ 컴포넌트 삭제 (단축키)");
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@ -3323,7 +3323,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 7. 선택 해제 // 7. 선택 해제
if (e.key === "Escape") { if (e.key === "Escape") {
console.log("🔄 선택 해제"); // console.log("🔄 선택 해제");
setSelectedComponent(null); setSelectedComponent(null);
setGroupState((prev) => ({ ...prev, selectedComponents: [], isGrouping: false })); setGroupState((prev) => ({ ...prev, selectedComponents: [], isGrouping: false }));
return false; return false;
@ -3331,7 +3331,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 8. 저장 (Ctrl+S는 레이아웃 저장용으로 사용) // 8. 저장 (Ctrl+S는 레이아웃 저장용으로 사용)
if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "s") { if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "s") {
console.log("💾 레이아웃 저장"); // console.log("💾 레이아웃 저장");
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@ -3353,13 +3353,13 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
await screenApi.saveLayout(selectedScreen.screenId, layoutWithResolution); await screenApi.saveLayout(selectedScreen.screenId, layoutWithResolution);
toast.success("레이아웃이 저장되었습니다."); toast.success("레이아웃이 저장되었습니다.");
} catch (error) { } catch (error) {
console.error("레이아웃 저장 실패:", error); // console.error("레이아웃 저장 실패:", error);
toast.error("레이아웃 저장에 실패했습니다."); toast.error("레이아웃 저장에 실패했습니다.");
} finally { } finally {
setIsSaving(false); setIsSaving(false);
} }
} else { } else {
console.log("⚠️ 저장할 컴포넌트가 없습니다"); // console.log("⚠️ 저장할 컴포넌트가 없습니다");
toast.warning("저장할 컴포넌트가 없습니다."); toast.warning("저장할 컴포넌트가 없습니다.");
} }
return false; return false;
@ -3455,7 +3455,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}} }}
onDrop={(e) => { onDrop={(e) => {
e.preventDefault(); e.preventDefault();
console.log("🎯 캔버스 드롭 이벤트 발생"); // console.log("🎯 캔버스 드롭 이벤트 발생");
handleDrop(e); handleDrop(e);
}} }}
> >
@ -3555,7 +3555,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
onZoneClick={handleZoneClick} onZoneClick={handleZoneClick}
// 설정 변경 핸들러 (테이블 페이지 크기 등 설정을 상세설정에 반영) // 설정 변경 핸들러 (테이블 페이지 크기 등 설정을 상세설정에 반영)
onConfigChange={(config) => { onConfigChange={(config) => {
console.log("📤 테이블 설정 변경을 상세설정에 반영:", config); // console.log("📤 테이블 설정 변경을 상세설정에 반영:", config);
// 컴포넌트의 componentConfig 업데이트 // 컴포넌트의 componentConfig 업데이트
const updatedComponents = layout.components.map(comp => { const updatedComponents = layout.components.map(comp => {
@ -3667,7 +3667,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
onZoneClick={handleZoneClick} onZoneClick={handleZoneClick}
// 설정 변경 핸들러 (자식 컴포넌트용) // 설정 변경 핸들러 (자식 컴포넌트용)
onConfigChange={(config) => { onConfigChange={(config) => {
console.log("📤 자식 컴포넌트 설정 변경을 상세설정에 알림:", config); // console.log("📤 자식 컴포넌트 설정 변경을 상세설정에 알림:", config);
// TODO: 실제 구현은 DetailSettingsPanel과의 연동 필요 // TODO: 실제 구현은 DetailSettingsPanel과의 연동 필요
}} }}
/> />
@ -3732,13 +3732,13 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
searchTerm={searchTerm} searchTerm={searchTerm}
onSearchChange={setSearchTerm} onSearchChange={setSearchTerm}
onDragStart={(e, table, column) => { onDragStart={(e, table, column) => {
console.log("🚀 드래그 시작:", { table: table.tableName, column: column?.columnName }); // console.log("🚀 드래그 시작:", { table: table.tableName, column: column?.columnName });
const dragData = { const dragData = {
type: column ? "column" : "table", type: column ? "column" : "table",
table, table,
column, column,
}; };
console.log("📦 드래그 데이터:", dragData); // console.log("📦 드래그 데이터:", dragData);
e.dataTransfer.setData("application/json", JSON.stringify(dragData)); e.dataTransfer.setData("application/json", JSON.stringify(dragData));
}} }}
selectedTableName={selectedScreen.tableName} selectedTableName={selectedScreen.tableName}
@ -3984,7 +3984,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
onClose={() => setShowMenuAssignmentModal(false)} onClose={() => setShowMenuAssignmentModal(false)}
screenInfo={selectedScreen} screenInfo={selectedScreen}
onAssignmentComplete={() => { onAssignmentComplete={() => {
console.log("메뉴 할당 완료"); // console.log("메뉴 할당 완료");
// 필요시 추가 작업 수행 // 필요시 추가 작업 수행
}} }}
onBackToList={onBackToList} onBackToList={onBackToList}

View File

@ -223,7 +223,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
const response = await screenApi.getTableInfo([selectedScreen.tableName]); const response = await screenApi.getTableInfo([selectedScreen.tableName]);
setTables(response.data || []); setTables(response.data || []);
} catch (error) { } catch (error) {
console.error("테이블 정보 로드 실패:", error); // console.error("테이블 정보 로드 실패:", error);
toast.error("테이블 정보를 불러오는데 실패했습니다."); toast.error("테이블 정보를 불러오는데 실패했습니다.");
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@ -247,7 +247,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
setHasUnsavedChanges(false); setHasUnsavedChanges(false);
} }
} catch (error) { } catch (error) {
console.error("레이아웃 로드 실패:", error); // console.error("레이아웃 로드 실패:", error);
toast.error("화면 레이아웃을 불러오는데 실패했습니다."); toast.error("화면 레이아웃을 불러오는데 실패했습니다.");
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@ -271,7 +271,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
toast.error("저장에 실패했습니다."); toast.error("저장에 실패했습니다.");
} }
} catch (error) { } catch (error) {
console.error("저장 실패:", error); // console.error("저장 실패:", error);
toast.error("저장 중 오류가 발생했습니다."); toast.error("저장 중 오류가 발생했습니다.");
} finally { } finally {
setIsSaving(false); setIsSaving(false);
@ -346,7 +346,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 속성 패널 자동 열기 // 속성 패널 자동 열기
openPanel("properties"); openPanel("properties");
} catch (error) { } catch (error) {
console.error("드롭 처리 실패:", error); // console.error("드롭 처리 실패:", error);
} }
}, },
[layout, gridInfo, saveToHistory, openPanel], [layout, gridInfo, saveToHistory, openPanel],

View File

@ -410,7 +410,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
} }
try { try {
console.log(`=== 테이블 정보 조회 시작: ${selectedScreen.tableName} ===`); // console.log(`=== 테이블 정보 조회 시작: ${selectedScreen.tableName} ===`);
const startTime = performance.now(); const startTime = performance.now();
// 최적화된 단일 테이블 조회 API 사용 // 최적화된 단일 테이블 조회 API 사용
@ -421,29 +421,29 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}); });
const endTime = performance.now(); const endTime = performance.now();
console.log(`테이블 조회 완료: ${(endTime - startTime).toFixed(2)}ms`); // console.log(`테이블 조회 완료: ${(endTime - startTime).toFixed(2)}ms`);
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
if (data.success && data.data) { if (data.success && data.data) {
setTables([data.data]); setTables([data.data]);
console.log(`테이블 ${selectedScreen.tableName} 로드 완료, 컬럼 ${data.data.columns.length}`); // console.log(`테이블 ${selectedScreen.tableName} 로드 완료, 컬럼 ${data.data.columns.length}개`);
} else { } else {
console.error("테이블 조회 실패:", data.message); // console.error("테이블 조회 실패:", data.message);
// 선택된 화면의 테이블에 대한 임시 데이터 생성 // 선택된 화면의 테이블에 대한 임시 데이터 생성
setTables([createMockTableForScreen(selectedScreen.tableName)]); setTables([createMockTableForScreen(selectedScreen.tableName)]);
} }
} else if (response.status === 404) { } else if (response.status === 404) {
console.warn(`테이블 ${selectedScreen.tableName}을 찾을 수 없습니다.`); // console.warn(`테이블 ${selectedScreen.tableName}을 찾을 수 없습니다.`);
// 테이블이 존재하지 않는 경우 임시 데이터 생성 // 테이블이 존재하지 않는 경우 임시 데이터 생성
setTables([createMockTableForScreen(selectedScreen.tableName)]); setTables([createMockTableForScreen(selectedScreen.tableName)]);
} else { } else {
console.error("테이블 조회 실패:", response.status); // console.error("테이블 조회 실패:", response.status);
// 선택된 화면의 테이블에 대한 임시 데이터 생성 // 선택된 화면의 테이블에 대한 임시 데이터 생성
setTables([createMockTableForScreen(selectedScreen.tableName)]); setTables([createMockTableForScreen(selectedScreen.tableName)]);
} }
} catch (error) { } catch (error) {
console.error("테이블 조회 중 오류:", error); // console.error("테이블 조회 중 오류:", error);
// 선택된 화면의 테이블에 대한 임시 데이터 생성 // 선택된 화면의 테이블에 대한 임시 데이터 생성
setTables([createMockTableForScreen(selectedScreen.tableName)]); setTables([createMockTableForScreen(selectedScreen.tableName)]);
} }
@ -562,7 +562,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 웹타입에 따른 위젯 타입 매핑 // 웹타입에 따른 위젯 타입 매핑
const getWidgetTypeFromWebType = useCallback((webType: string): string => { const getWidgetTypeFromWebType = useCallback((webType: string): string => {
console.log("getWidgetTypeFromWebType - input webType:", webType); // console.log("getWidgetTypeFromWebType - input webType:", webType);
switch (webType) { switch (webType) {
case "text": case "text":
return "text"; return "text";
@ -599,7 +599,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
case "file": case "file":
return "file"; return "file";
default: default:
console.log("getWidgetTypeFromWebType - default case, returning text for:", webType); // console.log("getWidgetTypeFromWebType - default case, returning text for:", webType);
return "text"; return "text";
} }
}, []); }, []);
@ -986,7 +986,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
setHasUnsavedChanges(false); // 저장 완료 시 변경사항 플래그 해제 setHasUnsavedChanges(false); // 저장 완료 시 변경사항 플래그 해제
toast.success("레이아웃이 성공적으로 저장되었습니다."); toast.success("레이아웃이 성공적으로 저장되었습니다.");
} catch (error) { } catch (error) {
console.error("레이아웃 저장 실패:", error); // console.error("레이아웃 저장 실패:", error);
toast.error("레이아웃 저장에 실패했습니다."); toast.error("레이아웃 저장에 실패했습니다.");
} finally { } finally {
setIsSaving(false); setIsSaving(false);
@ -1027,7 +1027,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
setHasUnsavedChanges(false); setHasUnsavedChanges(false);
} }
} catch (error) { } catch (error) {
console.error("레이아웃 로드 실패:", error); // console.error("레이아웃 로드 실패:", error);
// 에러 시에도 기본 레이아웃으로 초기화 // 에러 시에도 기본 레이아웃으로 초기화
const defaultLayout = { const defaultLayout = {
components: [], components: [],
@ -1282,7 +1282,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
saveToHistory(newLayout); saveToHistory(newLayout);
} }
} catch (error) { } catch (error) {
console.error("드롭 처리 중 오류:", error); // console.error("드롭 처리 중 오류:", error);
} }
setDragState({ setDragState({
@ -1686,9 +1686,9 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
className="flex cursor-pointer items-center space-x-2 p-2 pl-6 hover:bg-gray-100" className="flex cursor-pointer items-center space-x-2 p-2 pl-6 hover:bg-gray-100"
draggable draggable
onDragStart={(e) => { onDragStart={(e) => {
console.log("Drag start - column:", column.columnName, "webType:", column.webType); // console.log("Drag start - column:", column.columnName, "webType:", column.webType);
const widgetType = getWidgetTypeFromWebType(column.webType || "text"); const widgetType = getWidgetTypeFromWebType(column.webType || "text");
console.log("Drag start - widgetType:", widgetType); // console.log("Drag start - widgetType:", widgetType);
startDrag( startDrag(
{ {
type: "widget", type: "widget",

View File

@ -100,7 +100,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
setTotalPages(Math.max(1, Math.ceil((resp.total || 0) / 20))); setTotalPages(Math.max(1, Math.ceil((resp.total || 0) / 20)));
} }
} catch (e) { } catch (e) {
console.error("화면 목록 조회 실패", e); // console.error("화면 목록 조회 실패", e);
if (activeTab === "active") { if (activeTab === "active") {
setScreens([]); setScreens([]);
} else { } else {
@ -127,7 +127,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
setScreens(resp.data || []); setScreens(resp.data || []);
setTotalPages(Math.max(1, Math.ceil((resp.total || 0) / 20))); setTotalPages(Math.max(1, Math.ceil((resp.total || 0) / 20)));
} catch (e) { } catch (e) {
console.error("화면 목록 조회 실패", e); // console.error("화면 목록 조회 실패", e);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@ -139,7 +139,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
const handleEdit = (screen: ScreenDefinition) => { const handleEdit = (screen: ScreenDefinition) => {
// 편집 모달 열기 // 편집 모달 열기
console.log("편집:", screen); // console.log("편집:", screen);
}; };
const handleDelete = async (screen: ScreenDefinition) => { const handleDelete = async (screen: ScreenDefinition) => {
@ -157,7 +157,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
setDeleteDialogOpen(true); setDeleteDialogOpen(true);
} }
} catch (error) { } catch (error) {
console.error("의존성 체크 실패:", error); // console.error("의존성 체크 실패:", error);
// 의존성 체크 실패 시에도 삭제 다이얼로그는 열어줌 // 의존성 체크 실패 시에도 삭제 다이얼로그는 열어줌
setDeleteDialogOpen(true); setDeleteDialogOpen(true);
} finally { } finally {
@ -177,7 +177,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
setDeleteReason(""); setDeleteReason("");
setDependencies([]); setDependencies([]);
} catch (error: any) { } catch (error: any) {
console.error("화면 삭제 실패:", error); // console.error("화면 삭제 실패:", error);
// 의존성 오류인 경우 경고창 표시 // 의존성 오류인 경우 경고창 표시
if (error.response?.status === 409 && error.response?.data?.code === "SCREEN_HAS_DEPENDENCIES") { if (error.response?.status === 409 && error.response?.data?.code === "SCREEN_HAS_DEPENDENCIES") {
@ -208,7 +208,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
setActiveTab("active"); setActiveTab("active");
reloadScreens(); reloadScreens();
} catch (error) { } catch (error) {
console.error("화면 복원 실패:", error); // console.error("화면 복원 실패:", error);
alert("화면 복원에 실패했습니다."); alert("화면 복원에 실패했습니다.");
} }
}; };
@ -227,7 +227,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
setPermanentDeleteDialogOpen(false); setPermanentDeleteDialogOpen(false);
setScreenToPermanentDelete(null); setScreenToPermanentDelete(null);
} catch (error) { } catch (error) {
console.error("화면 영구 삭제 실패:", error); // console.error("화면 영구 삭제 실패:", error);
alert("화면 영구 삭제에 실패했습니다."); alert("화면 영구 삭제에 실패했습니다.");
} }
}; };
@ -283,7 +283,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
alert(message); alert(message);
} catch (error) { } catch (error) {
console.error("일괄 삭제 실패:", error); // console.error("일괄 삭제 실패:", error);
alert("일괄 삭제에 실패했습니다."); alert("일괄 삭제에 실패했습니다.");
} finally { } finally {
setBulkDeleting(false); setBulkDeleting(false);
@ -297,7 +297,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
const handleView = (screen: ScreenDefinition) => { const handleView = (screen: ScreenDefinition) => {
// 미리보기 모달 열기 // 미리보기 모달 열기
console.log("미리보기:", screen); // console.log("미리보기:", screen);
}; };
const handleCopySuccess = () => { const handleCopySuccess = () => {

View File

@ -127,7 +127,7 @@ export default function ScreenPreview({ layout, screenName, className }: ScreenP
// 폼 데이터 출력 // 폼 데이터 출력
const logFormData = () => { const logFormData = () => {
console.log("폼 데이터:", formData); // console.log("폼 데이터:", formData);
}; };
return ( return (

View File

@ -52,7 +52,7 @@ export default function SimpleScreenDesigner({ selectedScreen, onBackToList }: S
await screenApi.saveLayout(selectedScreen.screenId, layoutWithResolution); await screenApi.saveLayout(selectedScreen.screenId, layoutWithResolution);
toast.success("화면이 저장되었습니다."); toast.success("화면이 저장되었습니다.");
} catch (error) { } catch (error) {
console.error("저장 실패:", error); // console.error("저장 실패:", error);
toast.error("저장 중 오류가 발생했습니다."); toast.error("저장 중 오류가 발생했습니다.");
} finally { } finally {
setIsSaving(false); setIsSaving(false);

View File

@ -42,7 +42,7 @@ export default function TableTypeSelector({
const tableList = await tableTypeApi.getTables(); const tableList = await tableTypeApi.getTables();
setTables(tableList); setTables(tableList);
} catch (error) { } catch (error) {
console.error("테이블 목록 조회 실패:", error); // console.error("테이블 목록 조회 실패:", error);
// API 호출 실패 시 기본 테이블 목록 사용 // API 호출 실패 시 기본 테이블 목록 사용
const fallbackTables = [ const fallbackTables = [
{ tableName: "user_info", displayName: "사용자 정보", description: "사용자 기본 정보", columnCount: "25" }, { tableName: "user_info", displayName: "사용자 정보", description: "사용자 기본 정보", columnCount: "25" },
@ -89,7 +89,7 @@ export default function TableTypeSelector({
setColumns(formattedColumns); setColumns(formattedColumns);
} catch (error) { } catch (error) {
console.error("컬럼 정보 조회 실패:", error); // console.error("컬럼 정보 조회 실패:", error);
// API 호출 실패 시 기본 컬럼 정보 사용 // API 호출 실패 시 기본 컬럼 정보 사용
const fallbackColumns: ColumnInfo[] = [ const fallbackColumns: ColumnInfo[] = [
{ {
@ -167,9 +167,9 @@ export default function TableTypeSelector({
// 로컬 상태 업데이트 // 로컬 상태 업데이트
setColumns((prev) => prev.map((col) => (col.columnName === columnName ? { ...col, webType } : col))); setColumns((prev) => prev.map((col) => (col.columnName === columnName ? { ...col, webType } : col)));
console.log(`컬럼 ${columnName}의 웹 타입을 ${webType}로 변경했습니다.`); // console.log(`컬럼 ${columnName}의 웹 타입을 ${webType}로 변경했습니다.`);
} catch (error) { } catch (error) {
console.error("웹 타입 설정 실패:", error); // console.error("웹 타입 설정 실패:", error);
alert("웹 타입 설정에 실패했습니다. 다시 시도해주세요."); alert("웹 타입 설정에 실패했습니다. 다시 시도해주세요.");
} }
}; };
@ -193,9 +193,9 @@ export default function TableTypeSelector({
// 로컬 상태 업데이트 // 로컬 상태 업데이트
setColumns((prev) => prev.map((col) => (col.columnName === columnName ? { ...col, inputType } : col))); setColumns((prev) => prev.map((col) => (col.columnName === columnName ? { ...col, inputType } : col)));
console.log(`컬럼 ${columnName}의 입력 타입을 ${inputType}로 변경했습니다.`); // console.log(`컬럼 ${columnName}의 입력 타입을 ${inputType}로 변경했습니다.`);
} catch (error) { } catch (error) {
console.error("입력 타입 변경 실패:", error); // console.error("입력 타입 변경 실패:", error);
alert("입력 타입 설정에 실패했습니다. 다시 시도해주세요."); alert("입력 타입 설정에 실패했습니다. 다시 시도해주세요.");
} }
}; };

View File

@ -44,7 +44,7 @@ export default function TemplateManager({
}); });
setTemplates(templateList); setTemplates(templateList);
} catch (error) { } catch (error) {
console.error("템플릿 목록 조회 실패:", error); // console.error("템플릿 목록 조회 실패:", error);
// API 호출 실패 시 기본 템플릿 목록 사용 // API 호출 실패 시 기본 템플릿 목록 사용
const fallbackTemplates: ScreenTemplate[] = [ const fallbackTemplates: ScreenTemplate[] = [
{ {
@ -196,7 +196,7 @@ export default function TemplateManager({
} }
alert("템플릿이 삭제되었습니다."); alert("템플릿이 삭제되었습니다.");
} catch (error) { } catch (error) {
console.error("템플릿 삭제 실패:", error); // console.error("템플릿 삭제 실패:", error);
alert("템플릿 삭제에 실패했습니다. 다시 시도해주세요."); alert("템플릿 삭제에 실패했습니다. 다시 시도해주세요.");
} }
}; };
@ -204,7 +204,7 @@ export default function TemplateManager({
// 새 템플릿 생성 // 새 템플릿 생성
const handleCreateTemplate = () => { const handleCreateTemplate = () => {
// TODO: 새 템플릿 생성 모달 또는 페이지로 이동 // TODO: 새 템플릿 생성 모달 또는 페이지로 이동
console.log("새 템플릿 생성"); // console.log("새 템플릿 생성");
}; };
// 템플릿 내보내기 // 템플릿 내보내기

View File

@ -39,9 +39,9 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({ component,
const fetchScreens = async () => { const fetchScreens = async () => {
try { try {
setScreensLoading(true); setScreensLoading(true);
console.log("🔍 화면 목록 API 호출 시작"); // console.log("🔍 화면 목록 API 호출 시작");
const response = await apiClient.get("/screen-management/screens"); const response = await apiClient.get("/screen-management/screens");
console.log("✅ 화면 목록 API 응답:", response.data); // console.log("✅ 화면 목록 API 응답:", response.data);
if (response.data.success && Array.isArray(response.data.data)) { if (response.data.success && Array.isArray(response.data.data)) {
const screenList = response.data.data.map((screen: any) => ({ const screenList = response.data.data.map((screen: any) => ({
@ -50,10 +50,10 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({ component,
description: screen.description, description: screen.description,
})); }));
setScreens(screenList); setScreens(screenList);
console.log("✅ 화면 목록 설정 완료:", screenList.length, "개"); // console.log("✅ 화면 목록 설정 완료:", screenList.length, "개");
} }
} catch (error) { } catch (error) {
console.error("❌ 화면 목록 로딩 실패:", error); // console.error("❌ 화면 목록 로딩 실패:", error);
} finally { } finally {
setScreensLoading(false); setScreensLoading(false);
} }

View File

@ -77,7 +77,7 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
const loadDiagrams = async () => { const loadDiagrams = async () => {
try { try {
setDiagramsLoading(true); setDiagramsLoading(true);
console.log("🔍 데이터플로우 관계 목록 로딩..."); // console.log("🔍 데이터플로우 관계 목록 로딩...");
const response = await apiClient.get("/test-button-dataflow/diagrams"); const response = await apiClient.get("/test-button-dataflow/diagrams");
@ -90,10 +90,10 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
})); }));
setDiagrams(diagramList); setDiagrams(diagramList);
console.log(`✅ 관계 ${diagramList.length}개 로딩 완료`); // console.log(`✅ 관계 ${diagramList.length}개 로딩 완료`);
} }
} catch (error) { } catch (error) {
console.error("❌ 관계 목록 로딩 실패:", error); // console.error("❌ 관계 목록 로딩 실패:", error);
setDiagrams([]); setDiagrams([]);
} finally { } finally {
setDiagramsLoading(false); setDiagramsLoading(false);
@ -106,15 +106,15 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
const loadRelationships = async (diagramId: number) => { const loadRelationships = async (diagramId: number) => {
try { try {
setRelationshipsLoading(true); setRelationshipsLoading(true);
console.log(`🔍 관계 ${diagramId} 관계 목록 로딩...`); // console.log(`🔍 관계 ${diagramId} 관계 목록 로딩...`);
const response = await apiClient.get(`/test-button-dataflow/diagrams/${diagramId}/relationships`); const response = await apiClient.get(`/test-button-dataflow/diagrams/${diagramId}/relationships`);
if (response.data.success && Array.isArray(response.data.data)) { if (response.data.success && Array.isArray(response.data.data)) {
console.log("🔍 백엔드에서 받은 관계 데이터:", response.data.data); // console.log("🔍 백엔드에서 받은 관계 데이터:", response.data.data);
const relationshipList = response.data.data.map((rel: any) => { const relationshipList = response.data.data.map((rel: any) => {
console.log("🔍 개별 관계 데이터:", rel); // console.log("🔍 개별 관계 데이터:", rel);
// 여러 가지 가능한 필드명 시도 (백엔드 로그 기준으로 수정) // 여러 가지 가능한 필드명 시도 (백엔드 로그 기준으로 수정)
const relationshipName = const relationshipName =
@ -137,15 +137,15 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
category: rel.category || rel.type || "data-save", category: rel.category || rel.type || "data-save",
}; };
console.log("🔍 매핑된 관계 데이터:", mappedRel); // console.log("🔍 매핑된 관계 데이터:", mappedRel);
return mappedRel; return mappedRel;
}); });
setRelationships(relationshipList); setRelationships(relationshipList);
console.log(`✅ 관계 ${relationshipList.length}개 로딩 완료:`, relationshipList); // console.log(`✅ 관계 ${relationshipList.length}개 로딩 완료:`, relationshipList);
} }
} catch (error) { } catch (error) {
console.error("❌ 관계 목록 로딩 실패:", error); // console.error("❌ 관계 목록 로딩 실패:", error);
setRelationships([]); setRelationships([]);
} finally { } finally {
setRelationshipsLoading(false); setRelationshipsLoading(false);
@ -169,7 +169,7 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
setPreviewData(response.data.data); setPreviewData(response.data.data);
} }
} catch (error) { } catch (error) {
console.error("❌ 관계 미리보기 로딩 실패:", error); // console.error("❌ 관계 미리보기 로딩 실패:", error);
} }
}; };

View File

@ -63,7 +63,7 @@ export const ImprovedButtonControlConfigPanel: React.FC<ImprovedButtonControlCon
const loadRelationships = async () => { const loadRelationships = async () => {
try { try {
setLoading(true); setLoading(true);
console.log("🔍 전체 관계 목록 로딩..."); // console.log("🔍 전체 관계 목록 로딩...");
const response = await apiClient.get("/test-button-dataflow/relationships/all"); const response = await apiClient.get("/test-button-dataflow/relationships/all");
@ -77,10 +77,10 @@ export const ImprovedButtonControlConfigPanel: React.FC<ImprovedButtonControlCon
})); }));
setRelationships(relationshipList); setRelationships(relationshipList);
console.log(`✅ 관계 ${relationshipList.length}개 로딩 완료`); // console.log(`✅ 관계 ${relationshipList.length}개 로딩 완료`);
} }
} catch (error) { } catch (error) {
console.error("❌ 관계 목록 로딩 실패:", error); // console.error("❌ 관계 목록 로딩 실패:", error);
setRelationships([]); setRelationships([]);
} finally { } finally {
setLoading(false); setLoading(false);

View File

@ -108,7 +108,7 @@ export const AdvancedSearchFilters: React.FC<AdvancedSearchFiltersProps> = ({
setCodeOptions((prev) => ({ ...prev, [codeCategory]: options })); setCodeOptions((prev) => ({ ...prev, [codeCategory]: options }));
} catch (error) { } catch (error) {
console.error(`코드 카테고리 ${codeCategory} 로드 실패:`, error); // console.error(`코드 카테고리 ${codeCategory} 로드 실패:`, error);
setCodeOptions((prev) => ({ ...prev, [codeCategory]: [] })); setCodeOptions((prev) => ({ ...prev, [codeCategory]: [] }));
} finally { } finally {
setLoadingStates((prev) => ({ ...prev, [codeCategory]: false })); setLoadingStates((prev) => ({ ...prev, [codeCategory]: false }));
@ -134,7 +134,7 @@ export const AdvancedSearchFilters: React.FC<AdvancedSearchFiltersProps> = ({
setEntityOptions((prev) => ({ ...prev, [key]: options })); setEntityOptions((prev) => ({ ...prev, [key]: options }));
} catch (error) { } catch (error) {
console.error(`엔티티 ${tableName}.${columnName} 로드 실패:`, error); // console.error(`엔티티 ${tableName}.${columnName} 로드 실패:`, error);
setEntityOptions((prev) => ({ ...prev, [key]: [] })); setEntityOptions((prev) => ({ ...prev, [key]: [] }));
} finally { } finally {
setLoadingStates((prev) => ({ ...prev, [key]: false })); setLoadingStates((prev) => ({ ...prev, [key]: false }));

View File

@ -21,12 +21,12 @@ export function ComponentsPanel({ className }: ComponentsPanelProps) {
// 레지스트리에서 모든 컴포넌트 조회 // 레지스트리에서 모든 컴포넌트 조회
const allComponents = useMemo(() => { const allComponents = useMemo(() => {
const components = ComponentRegistry.getAllComponents(); const components = ComponentRegistry.getAllComponents();
console.log("🔍 ComponentsPanel - 로드된 컴포넌트:", components.map(c => ({ id: c.id, name: c.name, category: c.category }))); // console.log("🔍 ComponentsPanel - 로드된 컴포넌트:", components.map(c => ({ id: c.id, name: c.name, category: c.category })));
// 수동으로 table-list 컴포넌트 추가 (임시) // 수동으로 table-list 컴포넌트 추가 (임시)
const hasTableList = components.some(c => c.id === 'table-list'); const hasTableList = components.some(c => c.id === 'table-list');
if (!hasTableList) { if (!hasTableList) {
console.log("⚠️ table-list 컴포넌트가 없어서 수동 추가"); // console.log("⚠️ table-list 컴포넌트가 없어서 수동 추가");
components.push({ components.push({
id: "table-list", id: "table-list",
name: "테이블 리스트", name: "테이블 리스트",
@ -92,7 +92,7 @@ export function ComponentsPanel({ className }: ComponentsPanelProps) {
type: "component", type: "component",
component: component, component: component,
}; };
console.log("🚀 컴포넌트 드래그 시작:", component.name, dragData); // console.log("🚀 컴포넌트 드래그 시작:", component.name, dragData);
e.dataTransfer.setData("application/json", JSON.stringify(dragData)); e.dataTransfer.setData("application/json", JSON.stringify(dragData));
e.dataTransfer.effectAllowed = "copy"; e.dataTransfer.effectAllowed = "copy";
}; };

View File

@ -106,47 +106,47 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
// 컴포넌트 변경 시 로컬 값 동기화 // 컴포넌트 변경 시 로컬 값 동기화
useEffect(() => { useEffect(() => {
console.log("🔄 DataTableConfig: 컴포넌트 변경 감지", { // console.log("🔄 DataTableConfig: 컴포넌트 변경 감지", {
componentId: component.id, // componentId: component.id,
title: component.title, // title: component.title,
searchButtonText: component.searchButtonText, // searchButtonText: component.searchButtonText,
columnsCount: component.columns.length, // columnsCount: component.columns.length,
filtersCount: component.filters.length, // filtersCount: component.filters.length,
columnIds: component.columns.map((col) => col.id), // columnIds: component.columns.map((col) => col.id),
filterColumnNames: component.filters.map((filter) => filter.columnName), // filterColumnNames: component.filters.map((filter) => filter.columnName),
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
}); // });
// 컬럼과 필터 상세 정보 로그 // 컬럼과 필터 상세 정보 로그
if (component.columns.length > 0) { if (component.columns.length > 0) {
console.log( // console.log(
"📋 현재 컬럼 목록:", // "📋 현재 컬럼 목록:",
component.columns.map((col) => ({ // component.columns.map((col) => ({
id: col.id, // id: col.id,
columnName: col.columnName, // columnName: col.columnName,
label: col.label, // label: col.label,
visible: col.visible, // visible: col.visible,
gridColumns: col.gridColumns, // gridColumns: col.gridColumns,
})), // })),
); // );
} }
// 로컬 상태 정보 로그 // 로컬 상태 정보 로그
console.log("🔧 로컬 상태 정보:", { // console.log("🔧 로컬 상태 정보:", {
localColumnInputsCount: Object.keys(localColumnInputs).length, // localColumnInputsCount: Object.keys(localColumnInputs).length,
localColumnCheckboxesCount: Object.keys(localColumnCheckboxes).length, // localColumnCheckboxesCount: Object.keys(localColumnCheckboxes).length,
localColumnGridColumnsCount: Object.keys(localColumnGridColumns).length, // localColumnGridColumnsCount: Object.keys(localColumnGridColumns).length,
}); // });
if (component.filters.length > 0) { if (component.filters.length > 0) {
console.log( // console.log(
"🔍 현재 필터 목록:", // "🔍 현재 필터 목록:",
component.filters.map((filter) => ({ // component.filters.map((filter) => ({
columnName: filter.columnName, // columnName: filter.columnName,
widgetType: filter.widgetType, // widgetType: filter.widgetType,
label: filter.label, // label: filter.label,
})), // })),
); // );
} }
setLocalValues({ setLocalValues({
@ -252,11 +252,11 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
const filterKey = `${filter.columnName}-${index}`; const filterKey = `${filter.columnName}-${index}`;
if (!(filterKey in newFilterInputs)) { if (!(filterKey in newFilterInputs)) {
newFilterInputs[filterKey] = filter.label || filter.columnName; newFilterInputs[filterKey] = filter.label || filter.columnName;
console.log("🆕 새 필터 로컬 상태 추가:", { // console.log("🆕 새 필터 로컬 상태 추가:", {
filterKey, // filterKey,
label: filter.label, // label: filter.label,
columnName: filter.columnName, // columnName: filter.columnName,
}); // });
} }
}); });
@ -266,16 +266,16 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
); );
Object.keys(newFilterInputs).forEach((key) => { Object.keys(newFilterInputs).forEach((key) => {
if (!currentFilterKeys.has(key)) { if (!currentFilterKeys.has(key)) {
console.log("🗑️ 삭제된 필터 로컬 상태 제거:", { filterKey: key }); // console.log("🗑️ 삭제된 필터 로컬 상태 제거:", { filterKey: key });
delete newFilterInputs[key]; delete newFilterInputs[key];
} }
}); });
console.log("📝 필터 로컬 상태 동기화 완료:", { // console.log("📝 필터 로컬 상태 동기화 완료:", {
prevCount: Object.keys(prev).length, // prevCount: Object.keys(prev).length,
newCount: Object.keys(newFilterInputs).length, // newCount: Object.keys(newFilterInputs).length,
newKeys: Object.keys(newFilterInputs), // newKeys: Object.keys(newFilterInputs),
}); // });
return newFilterInputs; return newFilterInputs;
}); });
@ -313,20 +313,20 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
const table = tables.find((t) => t.tableName === tableName); const table = tables.find((t) => t.tableName === tableName);
if (!table) return; if (!table) return;
console.log("🔄 테이블 변경:", { // console.log("🔄 테이블 변경:", {
tableName, // tableName,
currentTableName: localValues.tableName, // currentTableName: localValues.tableName,
table, // table,
columnsCount: table.columns.length, // columnsCount: table.columns.length,
}); // });
// 테이블 변경 시 컬럼을 자동으로 추가하지 않음 (사용자가 수동으로 추가해야 함) // 테이블 변경 시 컬럼을 자동으로 추가하지 않음 (사용자가 수동으로 추가해야 함)
const defaultColumns: DataTableColumn[] = []; const defaultColumns: DataTableColumn[] = [];
console.log("✅ 생성된 컬럼 설정:", { // console.log("✅ 생성된 컬럼 설정:", {
defaultColumnsCount: defaultColumns.length, // defaultColumnsCount: defaultColumns.length,
visibleColumns: defaultColumns.filter((col) => col.visible).length, // visibleColumns: defaultColumns.filter((col) => col.visible).length,
}); // });
// 상태 업데이트를 한 번에 처리 // 상태 업데이트를 한 번에 처리
setTimeout(() => { setTimeout(() => {
@ -363,7 +363,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
async (columnId: string, webTypeConfig: any) => { async (columnId: string, webTypeConfig: any) => {
// 1. 먼저 화면 컴포넌트의 컬럼 설정 업데이트 // 1. 먼저 화면 컴포넌트의 컬럼 설정 업데이트
const updatedColumns = component.columns.map((col) => (col.id === columnId ? { ...col, webTypeConfig } : col)); const updatedColumns = component.columns.map((col) => (col.id === columnId ? { ...col, webTypeConfig } : col));
console.log("🔄 컬럼 상세 설정 업데이트:", { columnId, webTypeConfig, updatedColumns }); // console.log("🔄 컬럼 상세 설정 업데이트:", { columnId, webTypeConfig, updatedColumns });
onUpdateComponent({ columns: updatedColumns }); onUpdateComponent({ columns: updatedColumns });
// 2. 테이블 타입 관리에도 반영 (라디오 타입인 경우) // 2. 테이블 타입 관리에도 반영 (라디오 타입인 경우)
@ -371,14 +371,14 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
if (targetColumn && targetColumn.widgetType === "radio" && selectedTable) { if (targetColumn && targetColumn.widgetType === "radio" && selectedTable) {
try { try {
// TODO: 테이블 타입 관리 API 호출하여 웹 타입과 상세 설정 업데이트 // TODO: 테이블 타입 관리 API 호출하여 웹 타입과 상세 설정 업데이트
console.log("📡 테이블 타입 관리 업데이트 필요:", { // console.log("📡 테이블 타입 관리 업데이트 필요:", {
tableName: component.tableName, // tableName: component.tableName,
columnName: targetColumn.columnName, // columnName: targetColumn.columnName,
webType: "radio", // webType: "radio",
detailSettings: JSON.stringify(webTypeConfig), // detailSettings: JSON.stringify(webTypeConfig),
}); // });
} catch (error) { } catch (error) {
console.error("테이블 타입 관리 업데이트 실패:", error); // console.error("테이블 타입 관리 업데이트 실패:", error);
} }
} }
}, },
@ -731,11 +731,11 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
return newGridColumns; return newGridColumns;
}); });
console.log("🗑️ 컬럼 삭제:", { // console.log("🗑️ 컬럼 삭제:", {
columnId, // columnId,
columnName: columnToRemove?.columnName, // columnName: columnToRemove?.columnName,
remainingColumns: updatedColumns.length, // remainingColumns: updatedColumns.length,
}); // });
onUpdateComponent({ onUpdateComponent({
columns: updatedColumns, columns: updatedColumns,
@ -748,7 +748,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
const updateFilter = useCallback( const updateFilter = useCallback(
(index: number, updates: Partial<DataTableFilter>) => { (index: number, updates: Partial<DataTableFilter>) => {
const updatedFilters = component.filters.map((filter, i) => (i === index ? { ...filter, ...updates } : filter)); const updatedFilters = component.filters.map((filter, i) => (i === index ? { ...filter, ...updates } : filter));
console.log("🔄 필터 업데이트:", { index, updates, updatedFilters }); // console.log("🔄 필터 업데이트:", { index, updates, updatedFilters });
onUpdateComponent({ filters: updatedFilters }); onUpdateComponent({ filters: updatedFilters });
}, },
[component.filters, onUpdateComponent], [component.filters, onUpdateComponent],
@ -782,30 +782,30 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
displayColumn: targetColumn.displayColumn, displayColumn: targetColumn.displayColumn,
}; };
console.log(" 필터 추가 시작:", { // console.log(" 필터 추가 시작:", {
targetColumnName: targetColumn.columnName, // targetColumnName: targetColumn.columnName,
targetColumnLabel: targetColumn.columnLabel, // targetColumnLabel: targetColumn.columnLabel,
inferredWidgetType: widgetType, // inferredWidgetType: widgetType,
currentFiltersCount: component.filters.length, // currentFiltersCount: component.filters.length,
}); // });
console.log(" 생성된 새 필터:", { // console.log(" 생성된 새 필터:", {
columnName: newFilter.columnName, // columnName: newFilter.columnName,
widgetType: newFilter.widgetType, // widgetType: newFilter.widgetType,
label: newFilter.label, // label: newFilter.label,
gridColumns: newFilter.gridColumns, // gridColumns: newFilter.gridColumns,
}); // });
const updatedFilters = [...component.filters, newFilter]; const updatedFilters = [...component.filters, newFilter];
console.log("🔄 필터 업데이트 호출:", { // console.log("🔄 필터 업데이트 호출:", {
filtersToAdd: 1, // filtersToAdd: 1,
totalFiltersAfter: updatedFilters.length, // totalFiltersAfter: updatedFilters.length,
updatedFilters: updatedFilters.map((filter) => ({ // updatedFilters: updatedFilters.map((filter) => ({
columnName: filter.columnName, // columnName: filter.columnName,
widgetType: filter.widgetType, // widgetType: filter.widgetType,
label: filter.label, // label: filter.label,
})), // })),
}); // });
// 먼저 로컬 상태를 업데이트하고 // 먼저 로컬 상태를 업데이트하고
const filterKey = `${newFilter.columnName}-${component.filters.length}`; const filterKey = `${newFilter.columnName}-${component.filters.length}`;
@ -814,12 +814,12 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
...prev, ...prev,
[filterKey]: newFilter.label, [filterKey]: newFilter.label,
}; };
console.log("📝 필터 로컬 상태 업데이트:", { // console.log("📝 필터 로컬 상태 업데이트:", {
filterKey, // filterKey,
newLabel: newFilter.label, // newLabel: newFilter.label,
prevState: prev, // prevState: prev,
newState, // newState,
}); // });
return newState; return newState;
}); });
@ -829,10 +829,10 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
// 필터 추가 후 필터 탭으로 자동 이동 // 필터 추가 후 필터 탭으로 자동 이동
setActiveTab("filters"); setActiveTab("filters");
console.log("🔍 필터 추가 후 탭 이동:", { // console.log("🔍 필터 추가 후 탭 이동:", {
activeTab: "filters", // activeTab: "filters",
isExternalControl: !!onTabChange, // isExternalControl: !!onTabChange,
}); // });
// 강제로 리렌더링을 트리거하기 위해 여러 방법 사용 // 강제로 리렌더링을 트리거하기 위해 여러 방법 사용
setTimeout(() => { setTimeout(() => {
@ -840,23 +840,23 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
...prev, ...prev,
[filterKey]: newFilter.label, [filterKey]: newFilter.label,
})); }));
console.log("🔄 setTimeout에서 강제 로컬 상태 업데이트:", { filterKey, label: newFilter.label }); // console.log("🔄 setTimeout에서 강제 로컬 상태 업데이트:", { filterKey, label: newFilter.label });
}, 0); }, 0);
// 추가적인 강제 업데이트 // 추가적인 강제 업데이트
setTimeout(() => { setTimeout(() => {
setLocalFilterInputs((prev) => { setLocalFilterInputs((prev) => {
const updated = { ...prev, [filterKey]: newFilter.label }; const updated = { ...prev, [filterKey]: newFilter.label };
console.log("🔄 두 번째 강제 업데이트:", { updated }); // console.log("🔄 두 번째 강제 업데이트:", { updated });
return updated; return updated;
}); });
}, 100); }, 100);
console.log("✅ 필터 추가 완료 - 로컬 상태와 컴포넌트 모두 업데이트됨", { // console.log("✅ 필터 추가 완료 - 로컬 상태와 컴포넌트 모두 업데이트됨", {
filterKey, // filterKey,
newFilterLabel: newFilter.label, // newFilterLabel: newFilter.label,
switchedToTab: "filters", // switchedToTab: "filters",
}); // });
}, [selectedTable, component.filters, onUpdateComponent]); }, [selectedTable, component.filters, onUpdateComponent]);
// 필터 삭제 // 필터 삭제
@ -916,24 +916,24 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
// 필터는 자동으로 추가하지 않음 (사용자가 수동으로 추가) // 필터는 자동으로 추가하지 않음 (사용자가 수동으로 추가)
console.log(" 컬럼 추가 시작:", { // console.log(" 컬럼 추가 시작:", {
targetColumnName: targetColumn.columnName, // targetColumnName: targetColumn.columnName,
targetColumnLabel: targetColumn.columnLabel, // targetColumnLabel: targetColumn.columnLabel,
inferredWidgetType: widgetType, // inferredWidgetType: widgetType,
currentColumnsCount: component.columns.length, // currentColumnsCount: component.columns.length,
currentFiltersCount: component.filters.length, // currentFiltersCount: component.filters.length,
}); // });
console.log(" 생성된 새 컬럼:", { // console.log(" 생성된 새 컬럼:", {
id: newColumn.id, // id: newColumn.id,
columnName: newColumn.columnName, // columnName: newColumn.columnName,
label: newColumn.label, // label: newColumn.label,
widgetType: newColumn.widgetType, // widgetType: newColumn.widgetType,
filterable: newColumn.filterable, // filterable: newColumn.filterable,
visible: newColumn.visible, // visible: newColumn.visible,
sortable: newColumn.sortable, // sortable: newColumn.sortable,
searchable: newColumn.searchable, // searchable: newColumn.searchable,
}); // });
// 필터는 수동으로만 추가 // 필터는 수동으로만 추가
@ -943,11 +943,11 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
...prev, ...prev,
[newColumn.id]: newColumn.label, [newColumn.id]: newColumn.label,
}; };
console.log("🔄 로컬 컬럼 상태 업데이트:", { // console.log("🔄 로컬 컬럼 상태 업데이트:", {
newColumnId: newColumn.id, // newColumnId: newColumn.id,
newLabel: newColumn.label, // newLabel: newColumn.label,
totalLocalInputs: Object.keys(newInputs).length, // totalLocalInputs: Object.keys(newInputs).length,
}); // });
return newInputs; return newInputs;
}); });
@ -972,28 +972,28 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
columns: [...component.columns, newColumn], columns: [...component.columns, newColumn],
}; };
console.log("🔄 컴포넌트 업데이트 호출:", { // console.log("🔄 컴포넌트 업데이트 호출:", {
columnsToAdd: 1, // columnsToAdd: 1,
totalColumnsAfter: updates.columns?.length, // totalColumnsAfter: updates.columns?.length,
hasColumns: !!updates.columns, // hasColumns: !!updates.columns,
updateKeys: Object.keys(updates), // updateKeys: Object.keys(updates),
}); // });
console.log("🔄 업데이트 상세 내용:", { // console.log("🔄 업데이트 상세 내용:", {
columns: updates.columns?.map((col) => ({ id: col.id, columnName: col.columnName, label: col.label })), // columns: updates.columns?.map((col) => ({ id: col.id, columnName: col.columnName, label: col.label })),
}); // });
onUpdateComponent(updates); onUpdateComponent(updates);
// 컬럼 추가 후 컬럼 탭으로 자동 이동 // 컬럼 추가 후 컬럼 탭으로 자동 이동
setActiveTab("columns"); setActiveTab("columns");
console.log("📋 컬럼 추가 후 탭 이동:", { // console.log("📋 컬럼 추가 후 탭 이동:", {
activeTab: "columns", // activeTab: "columns",
isExternalControl: !!onTabChange, // isExternalControl: !!onTabChange,
}); // });
console.log("✅ 컬럼 추가 완료 - onUpdateComponent 호출됨"); // console.log("✅ 컬럼 추가 완료 - onUpdateComponent 호출됨");
}, },
[selectedTable, component.columns, component.filters, onUpdateComponent], [selectedTable, component.columns, component.filters, onUpdateComponent],
); );
@ -1022,11 +1022,11 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
}, },
}; };
console.log("📁 가상 파일 컬럼 추가:", { // console.log("📁 가상 파일 컬럼 추가:", {
columnName: newColumn.columnName, // columnName: newColumn.columnName,
label: newColumn.label, // label: newColumn.label,
isVirtualFileColumn: newColumn.isVirtualFileColumn, // isVirtualFileColumn: newColumn.isVirtualFileColumn,
}); // });
// 로컬 상태에 새 컬럼 입력값 추가 // 로컬 상태에 새 컬럼 입력값 추가
setLocalColumnInputs((prev) => ({ setLocalColumnInputs((prev) => ({
@ -1060,7 +1060,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
// 컬럼 추가 후 컬럼 탭으로 자동 이동 // 컬럼 추가 후 컬럼 탭으로 자동 이동
setActiveTab("columns"); setActiveTab("columns");
console.log("✅ 가상 파일 컬럼 추가 완료"); // console.log("✅ 가상 파일 컬럼 추가 완료");
}, [component.columns, onUpdateComponent]); }, [component.columns, onUpdateComponent]);
return ( return (
@ -1386,7 +1386,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
value={localValues.gridColumns.toString()} value={localValues.gridColumns.toString()}
onChange={(e) => { onChange={(e) => {
const gridColumns = parseInt(e.target.value, 10); const gridColumns = parseInt(e.target.value, 10);
console.log("🔄 테이블 그리드 컬럼 수 변경:", gridColumns); // console.log("🔄 테이블 그리드 컬럼 수 변경:", gridColumns);
setLocalValues((prev) => ({ ...prev, gridColumns })); setLocalValues((prev) => ({ ...prev, gridColumns }));
onUpdateComponent({ gridColumns }); onUpdateComponent({ gridColumns });
}} }}
@ -1406,7 +1406,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
id="show-search-button" id="show-search-button"
checked={localValues.showSearchButton} checked={localValues.showSearchButton}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
console.log("🔄 검색 버튼 표시 변경:", checked); // console.log("🔄 검색 버튼 표시 변경:", checked);
setLocalValues((prev) => ({ ...prev, showSearchButton: checked as boolean })); setLocalValues((prev) => ({ ...prev, showSearchButton: checked as boolean }));
onUpdateComponent({ showSearchButton: checked as boolean }); onUpdateComponent({ showSearchButton: checked as boolean });
}} }}
@ -1421,7 +1421,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
id="enable-export" id="enable-export"
checked={localValues.enableExport} checked={localValues.enableExport}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
console.log("🔄 내보내기 기능 변경:", checked); // console.log("🔄 내보내기 기능 변경:", checked);
setLocalValues((prev) => ({ ...prev, enableExport: checked as boolean })); setLocalValues((prev) => ({ ...prev, enableExport: checked as boolean }));
onUpdateComponent({ enableExport: checked as boolean }); onUpdateComponent({ enableExport: checked as boolean });
}} }}
@ -1501,7 +1501,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
<Checkbox <Checkbox
checked={localColumnCheckboxes[column.id]?.visible ?? column.visible} checked={localColumnCheckboxes[column.id]?.visible ?? column.visible}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
console.log("🔄 컬럼 표시 변경:", { columnId: column.id, checked }); // console.log("🔄 컬럼 표시 변경:", { columnId: column.id, checked });
setLocalColumnCheckboxes((prev) => ({ setLocalColumnCheckboxes((prev) => ({
...prev, ...prev,
[column.id]: { ...prev[column.id], visible: checked as boolean }, [column.id]: { ...prev[column.id], visible: checked as boolean },
@ -1582,7 +1582,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
value={(localColumnGridColumns[column.id] ?? column.gridColumns).toString()} value={(localColumnGridColumns[column.id] ?? column.gridColumns).toString()}
onValueChange={(value) => { onValueChange={(value) => {
const newGridColumns = parseInt(value); const newGridColumns = parseInt(value);
console.log("🔄 컬럼 그리드 컬럼 변경:", { columnId: column.id, newGridColumns }); // console.log("🔄 컬럼 그리드 컬럼 변경:", { columnId: column.id, newGridColumns });
setLocalColumnGridColumns((prev) => ({ setLocalColumnGridColumns((prev) => ({
...prev, ...prev,
[column.id]: newGridColumns, [column.id]: newGridColumns,
@ -1609,7 +1609,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
id={`sortable-${column.id}`} id={`sortable-${column.id}`}
checked={localColumnCheckboxes[column.id]?.sortable ?? column.sortable} checked={localColumnCheckboxes[column.id]?.sortable ?? column.sortable}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
console.log("🔄 컬럼 정렬 가능 변경:", { columnId: column.id, checked }); // console.log("🔄 컬럼 정렬 가능 변경:", { columnId: column.id, checked });
setLocalColumnCheckboxes((prev) => ({ setLocalColumnCheckboxes((prev) => ({
...prev, ...prev,
[column.id]: { ...prev[column.id], sortable: checked as boolean }, [column.id]: { ...prev[column.id], sortable: checked as boolean },
@ -1627,7 +1627,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
id={`searchable-${column.id}`} id={`searchable-${column.id}`}
checked={localColumnCheckboxes[column.id]?.searchable ?? column.searchable} checked={localColumnCheckboxes[column.id]?.searchable ?? column.searchable}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
console.log("🔄 컬럼 검색 가능 변경:", { columnId: column.id, checked }); // console.log("🔄 컬럼 검색 가능 변경:", { columnId: column.id, checked });
setLocalColumnCheckboxes((prev) => ({ setLocalColumnCheckboxes((prev) => ({
...prev, ...prev,
[column.id]: { ...prev[column.id], searchable: checked as boolean }, [column.id]: { ...prev[column.id], searchable: checked as boolean },
@ -1967,13 +1967,13 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
const filterKey = `${filter.columnName}-${index}`; const filterKey = `${filter.columnName}-${index}`;
const localValue = localFilterInputs[filterKey]; const localValue = localFilterInputs[filterKey];
const finalValue = localValue !== undefined ? localValue : filter.label; const finalValue = localValue !== undefined ? localValue : filter.label;
console.log("🎯 필터 입력 값 결정:", { // console.log("🎯 필터 입력 값 결정:", {
filterKey, // filterKey,
localValue, // localValue,
filterLabel: filter.label, // filterLabel: filter.label,
finalValue, // finalValue,
allLocalInputs: Object.keys(localFilterInputs), // allLocalInputs: Object.keys(localFilterInputs),
}); // });
return finalValue; return finalValue;
})()} })()}
onChange={(e) => { onChange={(e) => {
@ -2109,7 +2109,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
id="pagination-enabled" id="pagination-enabled"
checked={localValues.paginationEnabled} checked={localValues.paginationEnabled}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
console.log("🔄 페이지네이션 사용 변경:", checked); // console.log("🔄 페이지네이션 사용 변경:", checked);
setLocalValues((prev) => ({ ...prev, paginationEnabled: checked as boolean })); setLocalValues((prev) => ({ ...prev, paginationEnabled: checked as boolean }));
onUpdateComponent({ onUpdateComponent({
pagination: { ...component.pagination, enabled: checked as boolean }, pagination: { ...component.pagination, enabled: checked as boolean },
@ -2151,7 +2151,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
id="show-page-size-selector" id="show-page-size-selector"
checked={localValues.showPageSizeSelector} checked={localValues.showPageSizeSelector}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
console.log("🔄 페이지 크기 선택기 표시 변경:", checked); // console.log("🔄 페이지 크기 선택기 표시 변경:", checked);
setLocalValues((prev) => ({ ...prev, showPageSizeSelector: checked as boolean })); setLocalValues((prev) => ({ ...prev, showPageSizeSelector: checked as boolean }));
onUpdateComponent({ onUpdateComponent({
pagination: { pagination: {
@ -2171,7 +2171,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
id="show-page-info" id="show-page-info"
checked={localValues.showPageInfo} checked={localValues.showPageInfo}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
console.log("🔄 페이지 정보 표시 변경:", checked); // console.log("🔄 페이지 정보 표시 변경:", checked);
setLocalValues((prev) => ({ ...prev, showPageInfo: checked as boolean })); setLocalValues((prev) => ({ ...prev, showPageInfo: checked as boolean }));
onUpdateComponent({ onUpdateComponent({
pagination: { pagination: {
@ -2191,7 +2191,7 @@ const DataTableConfigPanelComponent: React.FC<DataTableConfigPanelProps> = ({
id="show-first-last" id="show-first-last"
checked={localValues.showFirstLast} checked={localValues.showFirstLast}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
console.log("🔄 처음/마지막 버튼 표시 변경:", checked); // console.log("🔄 처음/마지막 버튼 표시 변경:", checked);
setLocalValues((prev) => ({ ...prev, showFirstLast: checked as boolean })); setLocalValues((prev) => ({ ...prev, showFirstLast: checked as boolean }));
onUpdateComponent({ onUpdateComponent({
pagination: { pagination: {

View File

@ -46,17 +46,17 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
// 데이터베이스에서 입력 가능한 웹타입들을 동적으로 가져오기 // 데이터베이스에서 입력 가능한 웹타입들을 동적으로 가져오기
const { webTypes } = useWebTypes({ active: "Y" }); const { webTypes } = useWebTypes({ active: "Y" });
console.log(`🔍 DetailSettingsPanel props:`, { // console.log(`🔍 DetailSettingsPanel props:`, {
selectedComponent: selectedComponent?.id, // selectedComponent: selectedComponent?.id,
componentType: selectedComponent?.type, // componentType: selectedComponent?.type,
currentTableName, // currentTableName,
currentTable: currentTable?.tableName, // currentTable: currentTable?.tableName,
selectedComponentTableName: selectedComponent?.tableName, // selectedComponentTableName: selectedComponent?.tableName,
}); // });
console.log(`🔍 DetailSettingsPanel webTypes 로드됨:`, webTypes?.length, "개"); // console.log(`🔍 DetailSettingsPanel webTypes 로드됨:`, webTypes?.length, "개");
console.log(`🔍 webTypes:`, webTypes); // console.log(`🔍 webTypes:`, webTypes);
console.log(`🔍 DetailSettingsPanel selectedComponent:`, selectedComponent); // console.log(`🔍 DetailSettingsPanel selectedComponent:`, selectedComponent);
console.log(`🔍 DetailSettingsPanel selectedComponent.widgetType:`, selectedComponent?.widgetType); // console.log(`🔍 DetailSettingsPanel selectedComponent.widgetType:`, selectedComponent?.widgetType);
const inputableWebTypes = webTypes.map((wt) => wt.web_type); const inputableWebTypes = webTypes.map((wt) => wt.web_type);
// 레이아웃 컴포넌트 설정 렌더링 함수 // 레이아웃 컴포넌트 설정 렌더링 함수
@ -198,7 +198,7 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
value={layoutComponent.layoutConfig?.flexbox?.direction || "row"} value={layoutComponent.layoutConfig?.flexbox?.direction || "row"}
onChange={(e) => { onChange={(e) => {
const newDirection = e.target.value; const newDirection = e.target.value;
console.log("🔄 플렉스박스 방향 변경:", newDirection); // console.log("🔄 플렉스박스 방향 변경:", newDirection);
// 방향 설정 업데이트 // 방향 설정 업데이트
onUpdateProperty(layoutComponent.id, "layoutConfig.flexbox.direction", newDirection); onUpdateProperty(layoutComponent.id, "layoutConfig.flexbox.direction", newDirection);
@ -217,11 +217,11 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
}, },
})); }));
console.log("🔄 존 크기 자동 조정:", { // console.log("🔄 존 크기 자동 조정:", {
direction: newDirection, // direction: newDirection,
zoneCount, // zoneCount,
updatedZones: updatedZones.map((z) => ({ id: z.id, size: z.size })), // updatedZones: updatedZones.map((z) => ({ id: z.id, size: z.size })),
}); // });
onUpdateProperty(layoutComponent.id, "zones", updatedZones); onUpdateProperty(layoutComponent.id, "zones", updatedZones);
} }
@ -698,27 +698,27 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
const renderWebTypeConfig = (widget: WidgetComponent) => { const renderWebTypeConfig = (widget: WidgetComponent) => {
const currentConfig = widget.webTypeConfig || {}; const currentConfig = widget.webTypeConfig || {};
console.log("🎨 DetailSettingsPanel renderWebTypeConfig 호출:", { // console.log("🎨 DetailSettingsPanel renderWebTypeConfig 호출:", {
componentId: widget.id, // componentId: widget.id,
widgetType: widget.widgetType, // widgetType: widget.widgetType,
currentConfig, // currentConfig,
configExists: !!currentConfig, // configExists: !!currentConfig,
configKeys: Object.keys(currentConfig), // configKeys: Object.keys(currentConfig),
configStringified: JSON.stringify(currentConfig), // configStringified: JSON.stringify(currentConfig),
widgetWebTypeConfig: widget.webTypeConfig, // widgetWebTypeConfig: widget.webTypeConfig,
widgetWebTypeConfigExists: !!widget.webTypeConfig, // widgetWebTypeConfigExists: !!widget.webTypeConfig,
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
}); // });
console.log("🎨 selectedComponent 전체:", selectedComponent); // console.log("🎨 selectedComponent 전체:", selectedComponent);
const handleConfigChange = (newConfig: WebTypeConfig) => { const handleConfigChange = (newConfig: WebTypeConfig) => {
console.log("🔧 WebTypeConfig 업데이트:", { // console.log("🔧 WebTypeConfig 업데이트:", {
widgetType: widget.widgetType, // widgetType: widget.widgetType,
oldConfig: currentConfig, // oldConfig: currentConfig,
newConfig, // newConfig,
componentId: widget.id, // componentId: widget.id,
isEqual: JSON.stringify(currentConfig) === JSON.stringify(newConfig), // isEqual: JSON.stringify(currentConfig) === JSON.stringify(newConfig),
}); // });
// 강제 새 객체 생성으로 React 변경 감지 보장 // 강제 새 객체 생성으로 React 변경 감지 보장
const freshConfig = { ...newConfig }; const freshConfig = { ...newConfig };
@ -727,18 +727,18 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
// 1순위: DB에서 지정된 설정 패널 사용 // 1순위: DB에서 지정된 설정 패널 사용
const dbWebType = webTypes.find((wt) => wt.web_type === widget.widgetType); const dbWebType = webTypes.find((wt) => wt.web_type === widget.widgetType);
console.log(`🎨 웹타입 "${widget.widgetType}" DB 조회 결과:`, dbWebType); // console.log(`🎨 웹타입 "${widget.widgetType}" DB 조회 결과:`, dbWebType);
if (dbWebType?.config_panel) { if (dbWebType?.config_panel) {
console.log(`🎨 웹타입 "${widget.widgetType}" → DB 지정 설정 패널 "${dbWebType.config_panel}" 사용`); // console.log(`🎨 웹타입 "${widget.widgetType}" → DB 지정 설정 패널 "${dbWebType.config_panel}" 사용`);
const ConfigPanelComponent = getConfigPanelComponent(dbWebType.config_panel); const ConfigPanelComponent = getConfigPanelComponent(dbWebType.config_panel);
console.log(`🎨 getConfigPanelComponent 결과:`, ConfigPanelComponent); // console.log(`🎨 getConfigPanelComponent 결과:`, ConfigPanelComponent);
if (ConfigPanelComponent) { if (ConfigPanelComponent) {
console.log(`🎨 ✅ ConfigPanelComponent 렌더링 시작`); // console.log(`🎨 ✅ ConfigPanelComponent 렌더링 시작`);
return <ConfigPanelComponent config={currentConfig} onConfigChange={handleConfigChange} />; return <ConfigPanelComponent config={currentConfig} onConfigChange={handleConfigChange} />;
} else { } else {
console.log(`🎨 ❌ ConfigPanelComponent가 null - 기본 설정 표시`); // console.log(`🎨 ❌ ConfigPanelComponent가 null - 기본 설정 표시`);
return ( return (
<div className="py-8 text-center text-gray-500"> <div className="py-8 text-center text-gray-500">
@ -748,7 +748,7 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
); );
} }
} else { } else {
console.log(`🎨 config_panel이 없음 - 기본 설정 표시`); // console.log(`🎨 config_panel이 없음 - 기본 설정 표시`);
return ( return (
<div className="py-8 text-center text-gray-500"> <div className="py-8 text-center text-gray-500">
@ -771,10 +771,10 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
// 컴포넌트 타입별 설정 패널 렌더링 // 컴포넌트 타입별 설정 패널 렌더링
const renderComponentConfigPanel = () => { const renderComponentConfigPanel = () => {
console.log("🔍 renderComponentConfigPanel - selectedComponent:", selectedComponent); // console.log("🔍 renderComponentConfigPanel - selectedComponent:", selectedComponent);
if (!selectedComponent) { if (!selectedComponent) {
console.error("❌ selectedComponent가 undefined입니다!"); // console.error("❌ selectedComponent가 undefined입니다!");
return ( return (
<div className="flex h-full flex-col items-center justify-center p-6 text-center"> <div className="flex h-full flex-col items-center justify-center p-6 text-center">
<Settings className="mb-4 h-12 w-12 text-red-400" /> <Settings className="mb-4 h-12 w-12 text-red-400" />
@ -835,11 +835,11 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
// 새로운 컴포넌트 타입들에 대한 설정 패널 확인 // 새로운 컴포넌트 타입들에 대한 설정 패널 확인
const componentType = selectedComponent?.componentConfig?.type || selectedComponent?.type; const componentType = selectedComponent?.componentConfig?.type || selectedComponent?.type;
console.log("🔍 DetailSettingsPanel componentType 확인:", { // console.log("🔍 DetailSettingsPanel componentType 확인:", {
selectedComponentType: selectedComponent?.type, // selectedComponentType: selectedComponent?.type,
componentConfigType: selectedComponent?.componentConfig?.type, // componentConfigType: selectedComponent?.componentConfig?.type,
finalComponentType: componentType, // finalComponentType: componentType,
}); // });
const hasNewConfigPanel = const hasNewConfigPanel =
componentType && componentType &&
@ -861,7 +861,7 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
"badge-status", "badge-status",
].includes(componentType); ].includes(componentType);
console.log("🔍 hasNewConfigPanel:", hasNewConfigPanel); // console.log("🔍 hasNewConfigPanel:", hasNewConfigPanel);
if (hasNewConfigPanel) { if (hasNewConfigPanel) {
return ( return (
@ -944,7 +944,7 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
// 레거시 버튼을 새로운 컴포넌트 시스템으로 강제 변환 // 레거시 버튼을 새로운 컴포넌트 시스템으로 강제 변환
if (selectedComponent.type === "button") { if (selectedComponent.type === "button") {
console.log("🔄 레거시 버튼을 새로운 컴포넌트 시스템으로 변환:", selectedComponent); // console.log("🔄 레거시 버튼을 새로운 컴포넌트 시스템으로 변환:", selectedComponent);
// 레거시 버튼을 새로운 시스템으로 변환 // 레거시 버튼을 새로운 시스템으로 변환
const convertedComponent = { const convertedComponent = {
@ -970,7 +970,7 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
const componentId = (selectedComponent as any).componentType || selectedComponent.componentConfig?.type; const componentId = (selectedComponent as any).componentType || selectedComponent.componentConfig?.type;
const webType = selectedComponent.componentConfig?.webType; const webType = selectedComponent.componentConfig?.webType;
console.log("🔧 새로운 컴포넌트 시스템 설정 패널:", { componentId, webType }); // console.log("🔧 새로운 컴포넌트 시스템 설정 패널:", { componentId, webType });
if (!componentId) { if (!componentId) {
return ( return (
@ -1011,23 +1011,23 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
componentId={componentId} componentId={componentId}
config={(() => { config={(() => {
const config = selectedComponent.componentConfig || {}; const config = selectedComponent.componentConfig || {};
console.log("🔍 DetailSettingsPanel에서 전달하는 config:", config); // console.log("🔍 DetailSettingsPanel에서 전달하는 config:", config);
console.log("🔍 selectedComponent 전체:", selectedComponent); // console.log("🔍 selectedComponent 전체:", selectedComponent);
return config; return config;
})()} })()}
screenTableName={selectedComponent.tableName || currentTable?.tableName || currentTableName} screenTableName={selectedComponent.tableName || currentTable?.tableName || currentTableName}
tableColumns={(() => { tableColumns={(() => {
console.log("🔍 DetailSettingsPanel tableColumns 전달:", { // console.log("🔍 DetailSettingsPanel tableColumns 전달:", {
currentTable, // currentTable,
columns: currentTable?.columns, // columns: currentTable?.columns,
columnsLength: currentTable?.columns?.length, // columnsLength: currentTable?.columns?.length,
sampleColumn: currentTable?.columns?.[0], // sampleColumn: currentTable?.columns?.[0],
deptCodeColumn: currentTable?.columns?.find((col) => col.columnName === "dept_code"), // deptCodeColumn: currentTable?.columns?.find((col) => col.columnName === "dept_code"),
}); // });
return currentTable?.columns || []; return currentTable?.columns || [];
})()} })()}
onChange={(newConfig) => { onChange={(newConfig) => {
console.log("🔧 컴포넌트 설정 변경:", newConfig); // console.log("🔧 컴포넌트 설정 변경:", newConfig);
// 개별 속성별로 업데이트하여 다른 속성과의 충돌 방지 // 개별 속성별로 업데이트하여 다른 속성과의 충돌 방지
Object.entries(newConfig).forEach(([key, value]) => { Object.entries(newConfig).forEach(([key, value]) => {
onUpdateProperty(selectedComponent.id, `componentConfig.${key}`, value); onUpdateProperty(selectedComponent.id, `componentConfig.${key}`, value);

View File

@ -28,13 +28,13 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
currentTable, currentTable,
currentTableName, currentTableName,
}) => { }) => {
console.log("🎨🎨🎨 FileComponentConfigPanel 렌더링:", { // console.log("🎨🎨🎨 FileComponentConfigPanel 렌더링:", {
componentId: component?.id, // componentId: component?.id,
componentType: component?.type, // componentType: component?.type,
hasOnUpdateProperty: !!onUpdateProperty, // hasOnUpdateProperty: !!onUpdateProperty,
currentTable, // currentTable,
currentTableName // currentTableName
}); // });
// fileConfig가 없는 경우 초기화 // fileConfig가 없는 경우 초기화
React.useEffect(() => { React.useEffect(() => {
if (!component.fileConfig) { if (!component.fileConfig) {
@ -90,11 +90,11 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
const currentState = getGlobalFileState(); const currentState = getGlobalFileState();
const newState = updater(currentState); const newState = updater(currentState);
(window as any).globalFileState = newState; (window as any).globalFileState = newState;
console.log("🌐 전역 파일 상태 업데이트:", { // console.log("🌐 전역 파일 상태 업데이트:", {
componentId: component.id, // componentId: component.id,
newFileCount: newState[component.id]?.length || 0, // newFileCount: newState[component.id]?.length || 0,
totalComponents: Object.keys(newState).length // totalComponents: Object.keys(newState).length
}); // });
// 강제 리렌더링을 위한 이벤트 발생 // 강제 리렌더링을 위한 이벤트 발생
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
@ -103,13 +103,13 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
// 디버깅용 전역 함수 등록 // 디버깅용 전역 함수 등록
(window as any).debugFileState = () => { (window as any).debugFileState = () => {
console.log("🔍 전역 파일 상태 디버깅:", { // console.log("🔍 전역 파일 상태 디버깅:", {
globalState: (window as any).globalFileState, // globalState: (window as any).globalFileState,
localStorage: Object.keys(localStorage).filter(key => key.startsWith('fileComponent_')).map(key => ({ // localStorage: Object.keys(localStorage).filter(key => key.startsWith('fileComponent_')).map(key => ({
key, // key,
data: JSON.parse(localStorage.getItem(key) || '[]') // data: JSON.parse(localStorage.getItem(key) || '[]')
})) // }))
}); // });
}; };
} }
}; };
@ -136,7 +136,7 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
try { try {
parsedBackupFiles = JSON.parse(backupFiles); parsedBackupFiles = JSON.parse(backupFiles);
} catch (error) { } catch (error) {
console.error("백업 파일 파싱 실패:", error); // console.error("백업 파일 파싱 실패:", error);
} }
} }
@ -144,7 +144,7 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
try { try {
parsedTempFiles = JSON.parse(tempBackupFiles); parsedTempFiles = JSON.parse(tempBackupFiles);
} catch (error) { } catch (error) {
console.error("임시 파일 파싱 실패:", error); // console.error("임시 파일 파싱 실패:", error);
} }
} }
@ -153,7 +153,7 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
try { try {
parsedFileUploadFiles = JSON.parse(fileUploadBackupFiles); parsedFileUploadFiles = JSON.parse(fileUploadBackupFiles);
} catch (error) { } catch (error) {
console.error("FileUploadComponent 백업 파일 파싱 실패:", error); // console.error("FileUploadComponent 백업 파일 파싱 실패:", error);
} }
} }
@ -164,19 +164,19 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
parsedTempFiles.length > 0 ? parsedTempFiles : parsedTempFiles.length > 0 ? parsedTempFiles :
componentFiles; componentFiles;
console.log("🚀 FileComponentConfigPanel 초기화:", { // console.log("🚀 FileComponentConfigPanel 초기화:", {
componentId: component.id, // componentId: component.id,
componentFiles: componentFiles.length, // componentFiles: componentFiles.length,
globalFiles: globalFiles.length, // globalFiles: globalFiles.length,
backupFiles: parsedBackupFiles.length, // backupFiles: parsedBackupFiles.length,
tempFiles: parsedTempFiles.length, // tempFiles: parsedTempFiles.length,
fileUploadFiles: parsedFileUploadFiles.length, // 🎯 실제 화면 파일 수 // fileUploadFiles: parsedFileUploadFiles.length, // 🎯 실제 화면 파일 수
finalFiles: finalFiles.length, // finalFiles: finalFiles.length,
source: globalFiles.length > 0 ? 'global' : // source: globalFiles.length > 0 ? 'global' :
parsedFileUploadFiles.length > 0 ? 'fileUploadComponent' : // 🎯 실제 화면 소스 // parsedFileUploadFiles.length > 0 ? 'fileUploadComponent' : // 🎯 실제 화면 소스
parsedBackupFiles.length > 0 ? 'localStorage' : // parsedBackupFiles.length > 0 ? 'localStorage' :
parsedTempFiles.length > 0 ? 'temp' : 'component' // parsedTempFiles.length > 0 ? 'temp' : 'component'
}); // });
return finalFiles; return finalFiles;
}; };
@ -188,10 +188,10 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
setTimeout(() => { setTimeout(() => {
onUpdateProperty(component.id, "uploadedFiles", initialFiles); onUpdateProperty(component.id, "uploadedFiles", initialFiles);
onUpdateProperty(component.id, "lastFileUpdate", Date.now()); onUpdateProperty(component.id, "lastFileUpdate", Date.now());
console.log("🔄 초기화 시 컴포넌트 속성 동기화:", { // console.log("🔄 초기화 시 컴포넌트 속성 동기화:", {
componentId: component.id, // componentId: component.id,
fileCount: initialFiles.length // fileCount: initialFiles.length
}); // });
}, 0); }, 0);
} }
return initialFiles; return initialFiles;
@ -216,15 +216,15 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
// 파일 업로드 처리 // 파일 업로드 처리
const handleFileUpload = useCallback(async (files: FileList | File[]) => { const handleFileUpload = useCallback(async (files: FileList | File[]) => {
console.log("🚀🚀🚀 FileComponentConfigPanel 파일 업로드 시작:", { // console.log("🚀🚀🚀 FileComponentConfigPanel 파일 업로드 시작:", {
filesCount: files?.length || 0, // filesCount: files?.length || 0,
componentId: component?.id, // componentId: component?.id,
componentType: component?.type, // componentType: component?.type,
hasOnUpdateProperty: !!onUpdateProperty // hasOnUpdateProperty: !!onUpdateProperty
}); // });
if (!files || files.length === 0) { if (!files || files.length === 0) {
console.log("❌ 파일이 없음"); // console.log("❌ 파일이 없음");
return; return;
} }
@ -251,23 +251,23 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
if (!isAllowed) { if (!isAllowed) {
toast.error(`${file.name}: 허용되지 않는 파일 형식입니다. (허용: ${acceptTypes.join(', ')})`); toast.error(`${file.name}: 허용되지 않는 파일 형식입니다. (허용: ${acceptTypes.join(', ')})`);
console.log(`파일 검증 실패:`, { // console.log(`파일 검증 실패:`, {
fileName: file.name, // fileName: file.name,
fileType: file.type, // fileType: file.type,
fileExt, // fileExt,
acceptTypes, // acceptTypes,
isAllowed // isAllowed
}); // });
continue; continue;
} }
} }
console.log(`파일 검증 성공:`, { // console.log(`파일 검증 성공:`, {
fileName: file.name, // fileName: file.name,
fileType: file.type, // fileType: file.type,
fileSize: file.size, // fileSize: file.size,
acceptTypesCount: acceptTypes.length // acceptTypesCount: acceptTypes.length
}); // });
validFiles.push(file); validFiles.push(file);
} }
@ -280,27 +280,27 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
const duplicates: string[] = []; const duplicates: string[] = [];
const uniqueFiles: File[] = []; const uniqueFiles: File[] = [];
console.log("🔍 중복 파일 체크:", { // console.log("🔍 중복 파일 체크:", {
uploadedFiles: existingFiles.length, // uploadedFiles: existingFiles.length,
existingFileNames: existingFileNames, // existingFileNames: existingFileNames,
newFiles: validFiles.map(f => f.name.toLowerCase()) // newFiles: validFiles.map(f => f.name.toLowerCase())
}); // });
validFiles.forEach(file => { validFiles.forEach(file => {
const fileName = file.name.toLowerCase(); const fileName = file.name.toLowerCase();
if (existingFileNames.includes(fileName)) { if (existingFileNames.includes(fileName)) {
duplicates.push(file.name); duplicates.push(file.name);
console.log("❌ 중복 파일 발견:", file.name); // console.log("❌ 중복 파일 발견:", file.name);
} else { } else {
uniqueFiles.push(file); uniqueFiles.push(file);
console.log("✅ 새로운 파일:", file.name); // console.log("✅ 새로운 파일:", file.name);
} }
}); });
console.log("🔍 중복 체크 결과:", { // console.log("🔍 중복 체크 결과:", {
duplicates: duplicates, // duplicates: duplicates,
uniqueFiles: uniqueFiles.map(f => f.name) // uniqueFiles: uniqueFiles.map(f => f.name)
}); // });
if (duplicates.length > 0) { if (duplicates.length > 0) {
toast.error(`중복된 파일이 있습니다: ${duplicates.join(', ')}`, { toast.error(`중복된 파일이 있습니다: ${duplicates.join(', ')}`, {
@ -319,11 +319,11 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
const filesToUpload = uniqueFiles.length > 0 ? uniqueFiles : validFiles; const filesToUpload = uniqueFiles.length > 0 ? uniqueFiles : validFiles;
try { try {
console.log("🔄 파일 업로드 시작:", { // console.log("🔄 파일 업로드 시작:", {
originalFiles: validFiles.length, // originalFiles: validFiles.length,
filesToUpload: filesToUpload.length, // filesToUpload: filesToUpload.length,
uploading // uploading
}); // });
setUploading(true); setUploading(true);
toast.loading(`${filesToUpload.length}개 파일 업로드 중...`); toast.loading(`${filesToUpload.length}개 파일 업로드 중...`);
@ -344,21 +344,21 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
// 3차: 기본값 설정 // 3차: 기본값 설정
if (!screenId) { if (!screenId) {
screenId = 40; // 기본 화면 ID (디자인 모드용) screenId = 40; // 기본 화면 ID (디자인 모드용)
console.warn("⚠️ screenId를 찾을 수 없어 기본값(40) 사용"); // console.warn("⚠️ screenId를 찾을 수 없어 기본값(40) 사용");
} }
const componentId = component.id; const componentId = component.id;
const fieldName = component.columnName || component.id || 'file_attachment'; const fieldName = component.columnName || component.id || 'file_attachment';
console.log("📋 파일 업로드 기본 정보:", { // console.log("📋 파일 업로드 기본 정보:", {
screenId, // screenId,
screenIdSource: (window as any).__CURRENT_SCREEN_ID__ ? 'global' : 'url_or_default', // screenIdSource: (window as any).__CURRENT_SCREEN_ID__ ? 'global' : 'url_or_default',
componentId, // componentId,
fieldName, // fieldName,
docType: localInputs.docType, // docType: localInputs.docType,
docTypeName: localInputs.docTypeName, // docTypeName: localInputs.docTypeName,
currentPath: typeof window !== 'undefined' ? window.location.pathname : 'unknown' // currentPath: typeof window !== 'undefined' ? window.location.pathname : 'unknown'
}); // });
const response = await uploadFiles({ const response = await uploadFiles({
files: filesToUpload, files: filesToUpload,
@ -372,11 +372,11 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
docTypeName: localInputs.docTypeName, docTypeName: localInputs.docTypeName,
}); });
console.log("📤 파일 업로드 응답:", response); // console.log("📤 파일 업로드 응답:", response);
if (response.success && (response.data || response.files)) { if (response.success && (response.data || response.files)) {
const filesData = response.data || response.files; const filesData = response.data || response.files;
console.log("📁 업로드된 파일 데이터:", filesData); // console.log("📁 업로드된 파일 데이터:", filesData);
const newFiles: FileInfo[] = filesData.map((file: any) => ({ const newFiles: FileInfo[] = filesData.map((file: any) => ({
objid: file.objid || `temp_${Date.now()}_${Math.random()}`, objid: file.objid || `temp_${Date.now()}_${Math.random()}`,
savedFileName: file.saved_file_name || file.savedFileName, savedFileName: file.saved_file_name || file.savedFileName,
@ -431,10 +431,10 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
source: 'designMode' // 🎯 화면설계 모드에서 온 이벤트임을 표시 source: 'designMode' // 🎯 화면설계 모드에서 온 이벤트임을 표시
}; };
console.log("🚀🚀🚀 FileComponentConfigPanel 이벤트 발생:", eventDetail); // console.log("🚀🚀🚀 FileComponentConfigPanel 이벤트 발생:", eventDetail);
console.log("🔍 현재 컴포넌트 ID:", component.id); // console.log("🔍 현재 컴포넌트 ID:", component.id);
console.log("🔍 업로드된 파일 수:", updatedFiles.length); // console.log("🔍 업로드된 파일 수:", updatedFiles.length);
console.log("🔍 파일 목록:", updatedFiles.map(f => f.name)); // console.log("🔍 파일 목록:", updatedFiles.map(f => f.name));
const event = new CustomEvent('globalFileStateChanged', { const event = new CustomEvent('globalFileStateChanged', {
detail: eventDetail detail: eventDetail
@ -444,49 +444,49 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
const listenerCount = window.getEventListeners ? const listenerCount = window.getEventListeners ?
window.getEventListeners(window)?.globalFileStateChanged?.length || 0 : window.getEventListeners(window)?.globalFileStateChanged?.length || 0 :
'unknown'; 'unknown';
console.log("🔍 globalFileStateChanged 리스너 수:", listenerCount); // console.log("🔍 globalFileStateChanged 리스너 수:", listenerCount);
window.dispatchEvent(event); window.dispatchEvent(event);
console.log("✅✅✅ globalFileStateChanged 이벤트 발생 완료"); // console.log("✅✅✅ globalFileStateChanged 이벤트 발생 완료");
// 강제로 모든 RealtimePreview 컴포넌트에게 알림 (여러 번) // 강제로 모든 RealtimePreview 컴포넌트에게 알림 (여러 번)
setTimeout(() => { setTimeout(() => {
console.log("🔄 추가 이벤트 발생 (지연 100ms)"); // console.log("🔄 추가 이벤트 발생 (지연 100ms)");
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
detail: { ...eventDetail, delayed: true } detail: { ...eventDetail, delayed: true }
})); }));
}, 100); }, 100);
setTimeout(() => { setTimeout(() => {
console.log("🔄 추가 이벤트 발생 (지연 300ms)"); // console.log("🔄 추가 이벤트 발생 (지연 300ms)");
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
detail: { ...eventDetail, delayed: true, attempt: 2 } detail: { ...eventDetail, delayed: true, attempt: 2 }
})); }));
}, 300); }, 300);
setTimeout(() => { setTimeout(() => {
console.log("🔄 추가 이벤트 발생 (지연 500ms)"); // console.log("🔄 추가 이벤트 발생 (지연 500ms)");
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
detail: { ...eventDetail, delayed: true, attempt: 3 } detail: { ...eventDetail, delayed: true, attempt: 3 }
})); }));
}, 500); }, 500);
// 직접 전역 상태 강제 업데이트 // 직접 전역 상태 강제 업데이트
console.log("🔄 전역 상태 강제 업데이트 시도"); // console.log("🔄 전역 상태 강제 업데이트 시도");
if ((window as any).forceRealtimePreviewUpdate) { if ((window as any).forceRealtimePreviewUpdate) {
(window as any).forceRealtimePreviewUpdate(component.id, updatedFiles); (window as any).forceRealtimePreviewUpdate(component.id, updatedFiles);
} }
} }
console.log("🔄 FileComponentConfigPanel 자동 저장:", { // console.log("🔄 FileComponentConfigPanel 자동 저장:", {
componentId: component.id, // componentId: component.id,
uploadedFiles: updatedFiles.length, // uploadedFiles: updatedFiles.length,
status: "자동 영구 저장됨", // status: "자동 영구 저장됨",
onUpdatePropertyExists: typeof onUpdateProperty === 'function', // onUpdatePropertyExists: typeof onUpdateProperty === 'function',
globalFileStateUpdated: getGlobalFileState()[component.id]?.length || 0, // globalFileStateUpdated: getGlobalFileState()[component.id]?.length || 0,
localStorageBackup: localStorage.getItem(`fileComponent_${component.id}_files`) ? 'saved' : 'not saved' // localStorageBackup: localStorage.getItem(`fileComponent_${component.id}_files`) ? 'saved' : 'not saved'
}); // });
// 그리드 파일 상태 새로고침 이벤트 발생 // 그리드 파일 상태 새로고침 이벤트 발생
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@ -505,40 +505,40 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
} }
}); });
window.dispatchEvent(refreshEvent); window.dispatchEvent(refreshEvent);
console.log("🔄 FileComponentConfigPanel 그리드 새로고침 이벤트 발생:", { // console.log("🔄 FileComponentConfigPanel 그리드 새로고침 이벤트 발생:", {
tableName, // tableName,
recordId, // recordId,
columnName, // columnName,
targetObjid, // targetObjid,
fileCount: updatedFiles.length // fileCount: updatedFiles.length
}); // });
} }
toast.dismiss(); toast.dismiss();
toast.success(`${validFiles.length}개 파일이 성공적으로 업로드되었습니다.`); toast.success(`${validFiles.length}개 파일이 성공적으로 업로드되었습니다.`);
console.log("✅ 파일 업로드 성공:", { // console.log("✅ 파일 업로드 성공:", {
newFilesCount: newFiles.length, // newFilesCount: newFiles.length,
totalFiles: updatedFiles.length, // totalFiles: updatedFiles.length,
componentId: component.id, // componentId: component.id,
updatedFiles: updatedFiles.map(f => ({ objid: f.objid, name: f.realFileName })) // updatedFiles: updatedFiles.map(f => ({ objid: f.objid, name: f.realFileName }))
}); // });
} else { } else {
throw new Error(response.message || '파일 업로드에 실패했습니다.'); throw new Error(response.message || '파일 업로드에 실패했습니다.');
} }
} catch (error: any) { } catch (error: any) {
console.error('❌ 파일 업로드 오류:', { // console.error('❌ 파일 업로드 오류:', {
error, // error,
errorMessage: error?.message, // errorMessage: error?.message,
errorResponse: error?.response?.data, // errorResponse: error?.response?.data,
errorStatus: error?.response?.status, // errorStatus: error?.response?.status,
componentId: component?.id, // componentId: component?.id,
screenId, // screenId,
fieldName // fieldName
}); // });
toast.dismiss(); toast.dismiss();
toast.error(`파일 업로드에 실패했습니다: ${error?.message || '알 수 없는 오류'}`); toast.error(`파일 업로드에 실패했습니다: ${error?.message || '알 수 없는 오류'}`);
} finally { } finally {
console.log("🏁 파일 업로드 완료, 로딩 상태 해제"); // console.log("🏁 파일 업로드 완료, 로딩 상태 해제");
setUploading(false); setUploading(false);
} }
}, [localInputs, localValues, uploadedFiles, onUpdateProperty, currentTableName, component, acceptTypes]); }, [localInputs, localValues, uploadedFiles, onUpdateProperty, currentTableName, component, acceptTypes]);
@ -553,24 +553,24 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
}); });
toast.success(`${file.realFileName || file.name} 다운로드가 완료되었습니다.`); toast.success(`${file.realFileName || file.name} 다운로드가 완료되었습니다.`);
} catch (error) { } catch (error) {
console.error('파일 다운로드 오류:', error); // console.error('파일 다운로드 오류:', error);
toast.error('파일 다운로드에 실패했습니다.'); toast.error('파일 다운로드에 실패했습니다.');
} }
}, []); }, []);
// 파일 삭제 처리 // 파일 삭제 처리
const handleFileDelete = useCallback(async (fileId: string) => { const handleFileDelete = useCallback(async (fileId: string) => {
console.log("🗑️🗑️🗑️ FileComponentConfigPanel 파일 삭제 시작:", { // console.log("🗑️🗑️🗑️ FileComponentConfigPanel 파일 삭제 시작:", {
fileId, // fileId,
componentId: component?.id, // componentId: component?.id,
currentFilesCount: uploadedFiles.length, // currentFilesCount: uploadedFiles.length,
hasOnUpdateProperty: !!onUpdateProperty // hasOnUpdateProperty: !!onUpdateProperty
}); // });
try { try {
console.log("📡 deleteFile API 호출 시작..."); // console.log("📡 deleteFile API 호출 시작...");
await deleteFile(fileId, 'temp_record'); await deleteFile(fileId, 'temp_record');
console.log("✅ deleteFile API 호출 성공"); // console.log("✅ deleteFile API 호출 성공");
const updatedFiles = uploadedFiles.filter(file => file.objid !== fileId && file.id !== fileId); const updatedFiles = uploadedFiles.filter(file => file.objid !== fileId && file.id !== fileId);
setUploadedFiles(updatedFiles); setUploadedFiles(updatedFiles);
@ -591,12 +591,12 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
localStorage.setItem(backupKey, JSON.stringify(updatedFiles)); localStorage.setItem(backupKey, JSON.stringify(updatedFiles));
localStorage.setItem(tempBackupKey, JSON.stringify(updatedFiles)); localStorage.setItem(tempBackupKey, JSON.stringify(updatedFiles));
console.log("🗑️ FileComponentConfigPanel 파일 삭제:", { // console.log("🗑️ FileComponentConfigPanel 파일 삭제:", {
componentId: component.id, // componentId: component.id,
deletedFileId: fileId, // deletedFileId: fileId,
remainingFiles: updatedFiles.length, // remainingFiles: updatedFiles.length,
timestamp: timestamp // timestamp: timestamp
}); // });
// 🎯 RealtimePreview 동기화를 위한 전역 이벤트 발생 // 🎯 RealtimePreview 동기화를 위한 전역 이벤트 발생
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@ -610,39 +610,39 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
source: 'designMode' // 🎯 화면설계 모드에서 온 이벤트임을 표시 source: 'designMode' // 🎯 화면설계 모드에서 온 이벤트임을 표시
}; };
console.log("🚀🚀🚀 FileComponentConfigPanel 삭제 이벤트 발생:", eventDetail); // console.log("🚀🚀🚀 FileComponentConfigPanel 삭제 이벤트 발생:", eventDetail);
const event = new CustomEvent('globalFileStateChanged', { const event = new CustomEvent('globalFileStateChanged', {
detail: eventDetail detail: eventDetail
}); });
window.dispatchEvent(event); window.dispatchEvent(event);
console.log("✅✅✅ globalFileStateChanged 삭제 이벤트 발생 완료"); // console.log("✅✅✅ globalFileStateChanged 삭제 이벤트 발생 완료");
// 추가 지연 이벤트들 // 추가 지연 이벤트들
setTimeout(() => { setTimeout(() => {
try { try {
console.log("🔄 추가 삭제 이벤트 발생 (지연 100ms)"); // console.log("🔄 추가 삭제 이벤트 발생 (지연 100ms)");
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
detail: { ...eventDetail, delayed: true } detail: { ...eventDetail, delayed: true }
})); }));
} catch (error) { } catch (error) {
console.warn("FileComponentConfigPanel 지연 이벤트 발생 실패:", error); // console.warn("FileComponentConfigPanel 지연 이벤트 발생 실패:", error);
} }
}, 100); }, 100);
setTimeout(() => { setTimeout(() => {
try { try {
console.log("🔄 추가 삭제 이벤트 발생 (지연 300ms)"); // console.log("🔄 추가 삭제 이벤트 발생 (지연 300ms)");
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
detail: { ...eventDetail, delayed: true, attempt: 2 } detail: { ...eventDetail, delayed: true, attempt: 2 }
})); }));
} catch (error) { } catch (error) {
console.warn("FileComponentConfigPanel 지연 이벤트 발생 실패:", error); // console.warn("FileComponentConfigPanel 지연 이벤트 발생 실패:", error);
} }
}, 300); }, 300);
} catch (error) { } catch (error) {
console.warn("FileComponentConfigPanel 이벤트 발생 실패:", error); // console.warn("FileComponentConfigPanel 이벤트 발생 실패:", error);
} }
// 그리드 파일 상태 새로고침 이벤트도 유지 // 그리드 파일 상태 새로고침 이벤트도 유지
@ -663,20 +663,20 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
}); });
window.dispatchEvent(refreshEvent); window.dispatchEvent(refreshEvent);
} catch (error) { } catch (error) {
console.warn("FileComponentConfigPanel refreshFileStatus 이벤트 발생 실패:", error); // console.warn("FileComponentConfigPanel refreshFileStatus 이벤트 발생 실패:", error);
} }
console.log("🔄 FileComponentConfigPanel 파일 삭제 후 그리드 새로고침:", { // console.log("🔄 FileComponentConfigPanel 파일 삭제 후 그리드 새로고침:", {
tableName, // tableName,
recordId, // recordId,
columnName, // columnName,
targetObjid, // targetObjid,
fileCount: updatedFiles.length // fileCount: updatedFiles.length
}); // });
} }
toast.success('파일이 삭제되었습니다.'); toast.success('파일이 삭제되었습니다.');
} catch (error) { } catch (error) {
console.error('파일 삭제 오류:', error); // console.error('파일 삭제 오류:', error);
toast.error('파일 삭제에 실패했습니다.'); toast.error('파일 삭제에 실패했습니다.');
} }
}, [uploadedFiles, onUpdateProperty, component.id]); }, [uploadedFiles, onUpdateProperty, component.id]);
@ -703,16 +703,16 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
const tempBackupKey = `fileComponent_${component.id}_files_temp`; const tempBackupKey = `fileComponent_${component.id}_files_temp`;
localStorage.removeItem(tempBackupKey); localStorage.removeItem(tempBackupKey);
console.log("💾 파일 저장 완료:", { // console.log("💾 파일 저장 완료:", {
componentId: component.id, // componentId: component.id,
fileCount: uploadedFiles.length, // fileCount: uploadedFiles.length,
timestamp: timestamp, // timestamp: timestamp,
files: uploadedFiles.map(f => ({ objid: f.objid, name: f.realFileName })) // files: uploadedFiles.map(f => ({ objid: f.objid, name: f.realFileName }))
}); // });
toast.success(`${uploadedFiles.length}개 파일이 영구 저장되었습니다.`); toast.success(`${uploadedFiles.length}개 파일이 영구 저장되었습니다.`);
} catch (error) { } catch (error) {
console.error('파일 저장 오류:', error); // console.error('파일 저장 오류:', error);
toast.error('파일 저장에 실패했습니다.'); toast.error('파일 저장에 실패했습니다.');
} }
}, [uploadedFiles, onUpdateProperty, component.id, setGlobalFileState]); }, [uploadedFiles, onUpdateProperty, component.id, setGlobalFileState]);
@ -732,21 +732,21 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
e.preventDefault(); e.preventDefault();
setDragOver(false); setDragOver(false);
const files = e.dataTransfer.files; const files = e.dataTransfer.files;
console.log("📂 드래그앤드롭 이벤트:", { // console.log("📂 드래그앤드롭 이벤트:", {
filesCount: files.length, // filesCount: files.length,
files: files.length > 0 ? Array.from(files).map(f => f.name) : [], // files: files.length > 0 ? Array.from(files).map(f => f.name) : [],
componentId: component?.id // componentId: component?.id
}); // });
if (files.length > 0) { if (files.length > 0) {
handleFileUpload(files); handleFileUpload(files);
} }
}, [handleFileUpload, component?.id]); }, [handleFileUpload, component?.id]);
const handleFileSelect = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { const handleFileSelect = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
console.log("📁 파일 선택 이벤트:", { // console.log("📁 파일 선택 이벤트:", {
filesCount: e.target.files?.length || 0, // filesCount: e.target.files?.length || 0,
files: e.target.files ? Array.from(e.target.files).map(f => f.name) : [] // files: e.target.files ? Array.from(e.target.files).map(f => f.name) : []
}); // });
const files = e.target.files; const files = e.target.files;
if (files && files.length > 0) { if (files && files.length > 0) {
@ -782,27 +782,27 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
if (prevComponentIdRef.current !== component.id) { if (prevComponentIdRef.current !== component.id) {
// 새로운 컴포넌트로 변경된 경우 // 새로운 컴포넌트로 변경된 경우
console.log("🔄 FileComponentConfigPanel 새 컴포넌트 선택:", { // console.log("🔄 FileComponentConfigPanel 새 컴포넌트 선택:", {
prevComponentId: prevComponentIdRef.current, // prevComponentId: prevComponentIdRef.current,
newComponentId: component.id, // newComponentId: component.id,
componentFiles: componentFiles.length, // componentFiles: componentFiles.length,
action: "새 컴포넌트 → 상태 초기화", // action: "새 컴포넌트 → 상태 초기화",
globalFileStateExists: !!getGlobalFileState()[component.id], // globalFileStateExists: !!getGlobalFileState()[component.id],
globalFileStateLength: getGlobalFileState()[component.id]?.length || 0, // globalFileStateLength: getGlobalFileState()[component.id]?.length || 0,
localStorageExists: !!localStorage.getItem(`fileComponent_${component.id}_files`), // localStorageExists: !!localStorage.getItem(`fileComponent_${component.id}_files`),
onUpdatePropertyExists: typeof onUpdateProperty === 'function' // onUpdatePropertyExists: typeof onUpdateProperty === 'function'
}); // });
// 1순위: 전역 상태에서 파일 복원 // 1순위: 전역 상태에서 파일 복원
const globalFileState = getGlobalFileState(); const globalFileState = getGlobalFileState();
const globalFiles = globalFileState[component.id]; const globalFiles = globalFileState[component.id];
if (globalFiles && globalFiles.length > 0) { if (globalFiles && globalFiles.length > 0) {
console.log("🌐 전역 상태에서 파일 복원:", { // console.log("🌐 전역 상태에서 파일 복원:", {
componentId: component.id, // componentId: component.id,
globalFiles: globalFiles.length, // globalFiles: globalFiles.length,
action: "전역 상태 → 상태 복원" // action: "전역 상태 → 상태 복원"
}); // });
setUploadedFiles(globalFiles); setUploadedFiles(globalFiles);
onUpdateProperty(component.id, "uploadedFiles", globalFiles); onUpdateProperty(component.id, "uploadedFiles", globalFiles);
} }
@ -814,11 +814,11 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
if (backupFiles && componentFiles.length === 0) { if (backupFiles && componentFiles.length === 0) {
try { try {
const parsedBackupFiles = JSON.parse(backupFiles); const parsedBackupFiles = JSON.parse(backupFiles);
console.log("📂 localStorage에서 파일 복원:", { // console.log("📂 localStorage에서 파일 복원:", {
componentId: component.id, // componentId: component.id,
backupFiles: parsedBackupFiles.length, // backupFiles: parsedBackupFiles.length,
action: "백업 → 상태 복원" // action: "백업 → 상태 복원"
}); // });
setUploadedFiles(parsedBackupFiles); setUploadedFiles(parsedBackupFiles);
// 전역 상태에도 저장 // 전역 상태에도 저장
setGlobalFileState(prev => ({ setGlobalFileState(prev => ({
@ -828,7 +828,7 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
// 컴포넌트 속성에도 복원 // 컴포넌트 속성에도 복원
onUpdateProperty(component.id, "uploadedFiles", parsedBackupFiles); onUpdateProperty(component.id, "uploadedFiles", parsedBackupFiles);
} catch (error) { } catch (error) {
console.error("백업 파일 복원 실패:", error); // console.error("백업 파일 복원 실패:", error);
setUploadedFiles(componentFiles); setUploadedFiles(componentFiles);
} }
} else { } else {
@ -839,12 +839,12 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
prevComponentIdRef.current = component.id; prevComponentIdRef.current = component.id;
} else if (componentFiles.length > 0 && JSON.stringify(componentFiles) !== JSON.stringify(uploadedFiles)) { } else if (componentFiles.length > 0 && JSON.stringify(componentFiles) !== JSON.stringify(uploadedFiles)) {
// 같은 컴포넌트에서 파일이 업데이트된 경우 // 같은 컴포넌트에서 파일이 업데이트된 경우
console.log("🔄 FileComponentConfigPanel 파일 동기화:", { // console.log("🔄 FileComponentConfigPanel 파일 동기화:", {
componentId: component.id, // componentId: component.id,
componentFiles: componentFiles.length, // componentFiles: componentFiles.length,
currentFiles: uploadedFiles.length, // currentFiles: uploadedFiles.length,
action: "컴포넌트 → 상태 동기화" // action: "컴포넌트 → 상태 동기화"
}); // });
setUploadedFiles(componentFiles); setUploadedFiles(componentFiles);
} }
}, [component.id]); // 컴포넌트 ID가 변경될 때만 초기화 }, [component.id]); // 컴포넌트 ID가 변경될 때만 초기화
@ -855,24 +855,24 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
const { componentId, files, fileCount, isRestore, source } = event.detail; const { componentId, files, fileCount, isRestore, source } = event.detail;
if (componentId === component.id) { if (componentId === component.id) {
console.log("🌐 FileComponentConfigPanel 전역 상태 변경 감지:", { // console.log("🌐 FileComponentConfigPanel 전역 상태 변경 감지:", {
componentId, // componentId,
fileCount, // fileCount,
isRestore: !!isRestore, // isRestore: !!isRestore,
source: source || 'unknown', // source: source || 'unknown',
files: files?.map((f: any) => ({ objid: f.objid, name: f.realFileName })) // files: files?.map((f: any) => ({ objid: f.objid, name: f.realFileName }))
}); // });
if (files && Array.isArray(files)) { if (files && Array.isArray(files)) {
setUploadedFiles(files); setUploadedFiles(files);
// 🎯 실제 화면에서 온 이벤트이거나 화면 복원인 경우 컴포넌트 속성도 업데이트 // 🎯 실제 화면에서 온 이벤트이거나 화면 복원인 경우 컴포넌트 속성도 업데이트
if (isRestore || source === 'realScreen') { if (isRestore || source === 'realScreen') {
console.log("✅✅✅ 실제 화면 → 화면설계 모드 동기화 적용:", { // console.log("✅✅✅ 실제 화면 → 화면설계 모드 동기화 적용:", {
componentId, // componentId,
fileCount: files.length, // fileCount: files.length,
source: source || 'restore' // source: source || 'restore'
}); // });
onUpdateProperty(component.id, "uploadedFiles", files); onUpdateProperty(component.id, "uploadedFiles", files);
onUpdateProperty(component.id, "lastFileUpdate", Date.now()); onUpdateProperty(component.id, "lastFileUpdate", Date.now());
@ -881,12 +881,12 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
try { try {
const backupKey = `fileComponent_${component.id}_files`; const backupKey = `fileComponent_${component.id}_files`;
localStorage.setItem(backupKey, JSON.stringify(files)); localStorage.setItem(backupKey, JSON.stringify(files));
console.log("💾 실제 화면 동기화 후 localStorage 백업 업데이트:", { // console.log("💾 실제 화면 동기화 후 localStorage 백업 업데이트:", {
componentId: component.id, // componentId: component.id,
fileCount: files.length // fileCount: files.length
}); // });
} catch (e) { } catch (e) {
console.warn("localStorage 백업 업데이트 실패:", e); // console.warn("localStorage 백업 업데이트 실패:", e);
} }
// 전역 상태 업데이트 // 전역 상태 업데이트
@ -895,10 +895,10 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
[component.id]: files [component.id]: files
})); }));
} else if (isRestore) { } else if (isRestore) {
console.log("✅ 파일 컴포넌트 설정 패널 데이터 복원 완료:", { // console.log("✅ 파일 컴포넌트 설정 패널 데이터 복원 완료:", {
componentId, // componentId,
restoredFileCount: files.length // restoredFileCount: files.length
}); // });
} }
} }
} }
@ -1119,18 +1119,18 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
onDragLeave={handleDragLeave} onDragLeave={handleDragLeave}
onDrop={handleDrop} onDrop={handleDrop}
onClick={() => { onClick={() => {
console.log("🖱️ 파일 업로드 영역 클릭:", { // console.log("🖱️ 파일 업로드 영역 클릭:", {
uploading, // uploading,
inputElement: document.getElementById('file-input-config'), // inputElement: document.getElementById('file-input-config'),
componentId: component?.id // componentId: component?.id
}); // });
if (!uploading) { if (!uploading) {
const input = document.getElementById('file-input-config'); const input = document.getElementById('file-input-config');
if (input) { if (input) {
console.log("✅ 파일 input 클릭 실행"); // console.log("✅ 파일 input 클릭 실행");
input.click(); input.click();
} else { } else {
console.log("❌ 파일 input 요소를 찾을 수 없음"); // console.log("❌ 파일 input 요소를 찾을 수 없음");
} }
} }
}} }}

View File

@ -108,14 +108,14 @@ export default function LayoutsPanel({
height: 400, // 높이는 고정 height: 400, // 높이는 고정
}; };
console.log("🎯 카드 레이아웃 동적 크기 계산:", { // console.log("🎯 카드 레이아웃 동적 크기 계산:", {
gridColumns: 8, // gridColumns: 8,
screenResolution, // screenResolution,
gridSettings, // gridSettings,
gridInfo, // gridInfo,
calculatedWidth, // calculatedWidth,
finalSize: calculatedSize, // finalSize: calculatedSize,
}); // });
} }
// 새 레이아웃 컴포넌트 데이터 생성 // 새 레이아웃 컴포넌트 데이터 생성

View File

@ -34,7 +34,7 @@ const DataTableConfigPanelWrapper: React.FC<{
// 안정화된 업데이트 핸들러 // 안정화된 업데이트 핸들러
const handleUpdateComponent = React.useCallback( const handleUpdateComponent = React.useCallback(
(updates: Partial<DataTableComponent>) => { (updates: Partial<DataTableComponent>) => {
console.log("🔄 DataTable 래퍼 컴포넌트 업데이트:", updates); // console.log("🔄 DataTable 래퍼 컴포넌트 업데이트:", updates);
// 변경사항이 있는지 확인 (간단한 비교로 성능 향상) // 변경사항이 있는지 확인 (간단한 비교로 성능 향상)
const hasChanges = Object.entries(updates).some(([key, value]) => { const hasChanges = Object.entries(updates).some(([key, value]) => {
@ -48,7 +48,7 @@ const DataTableConfigPanelWrapper: React.FC<{
}); });
if (!hasChanges) { if (!hasChanges) {
console.log("⏭️ 래퍼: 변경사항 없음, 업데이트 스킵"); // console.log("⏭️ 래퍼: 변경사항 없음, 업데이트 스킵");
return; return;
} }
@ -123,18 +123,18 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
canUngroup = false, canUngroup = false,
}) => { }) => {
// 🔍 디버깅: PropertiesPanel 렌더링 및 dragState 전달 확인 // 🔍 디버깅: PropertiesPanel 렌더링 및 dragState 전달 확인
console.log("📍 PropertiesPanel 렌더링:", { // console.log("📍 PropertiesPanel 렌더링:", {
renderTime: Date.now(), // renderTime: Date.now(),
selectedComponentId: selectedComponent?.id, // selectedComponentId: selectedComponent?.id,
dragState: dragState // dragState: dragState
? { // ? {
isDragging: dragState.isDragging, // isDragging: dragState.isDragging,
draggedComponentId: dragState.draggedComponent?.id, // draggedComponentId: dragState.draggedComponent?.id,
currentPosition: dragState.currentPosition, // currentPosition: dragState.currentPosition,
dragStateRef: dragState, // 객체 참조 확인 // dragStateRef: dragState, // 객체 참조 확인
} // }
: "null", // : "null",
}); // });
// 동적 웹타입 목록 가져오기 - API에서 직접 조회 // 동적 웹타입 목록 가져오기 - API에서 직접 조회
const { webTypes, isLoading: isWebTypesLoading } = useWebTypes({ active: "Y" }); const { webTypes, isLoading: isWebTypesLoading } = useWebTypes({ active: "Y" });
@ -160,11 +160,11 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
// 실시간 위치 계산 (드래그 중일 때는 dragState.currentPosition 사용) // 실시간 위치 계산 (드래그 중일 때는 dragState.currentPosition 사용)
const getCurrentPosition = () => { const getCurrentPosition = () => {
if (dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id) { if (dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id) {
console.log("🎯 드래그 중 실시간 위치:", { // console.log("🎯 드래그 중 실시간 위치:", {
draggedId: dragState.draggedComponent?.id, // draggedId: dragState.draggedComponent?.id,
selectedId: selectedComponent?.id, // selectedId: selectedComponent?.id,
currentPosition: dragState.currentPosition, // currentPosition: dragState.currentPosition,
}); // });
return { return {
x: Math.round(dragState.currentPosition.x), x: Math.round(dragState.currentPosition.x),
y: Math.round(dragState.currentPosition.y), y: Math.round(dragState.currentPosition.y),
@ -225,22 +225,22 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
const group = selectedComponent.type === "group" ? (selectedComponent as GroupComponent) : null; const group = selectedComponent.type === "group" ? (selectedComponent as GroupComponent) : null;
const area = selectedComponent.type === "area" ? (selectedComponent as AreaComponent) : null; const area = selectedComponent.type === "area" ? (selectedComponent as AreaComponent) : null;
console.log("🔄 PropertiesPanel: 컴포넌트 변경 감지", { // console.log("🔄 PropertiesPanel: 컴포넌트 변경 감지", {
componentId: selectedComponent.id, // componentId: selectedComponent.id,
componentType: selectedComponent.type, // componentType: selectedComponent.type,
isDragging: dragState?.isDragging, // isDragging: dragState?.isDragging,
justFinishedDrag: dragState?.justFinishedDrag, // justFinishedDrag: dragState?.justFinishedDrag,
currentValues: { // currentValues: {
placeholder: widget?.placeholder, // placeholder: widget?.placeholder,
title: group?.title || area?.title, // title: group?.title || area?.title,
description: area?.description, // description: area?.description,
actualPositionX: selectedComponent.position.x, // actualPositionX: selectedComponent.position.x,
actualPositionY: selectedComponent.position.y, // actualPositionY: selectedComponent.position.y,
dragPositionX: dragState?.currentPosition.x, // dragPositionX: dragState?.currentPosition.x,
dragPositionY: dragState?.currentPosition.y, // dragPositionY: dragState?.currentPosition.y,
}, // },
getCurrentPosResult: getCurrentPosition(), // getCurrentPosResult: getCurrentPosition(),
}); // });
// 드래그 중이 아닐 때만 localInputs 업데이트 (드래그 완료 후 최종 위치 반영) // 드래그 중이 아닐 때만 localInputs 업데이트 (드래그 완료 후 최종 위치 반영)
if (!dragState?.isDragging || dragState.draggedComponent?.id !== selectedComponent.id) { if (!dragState?.isDragging || dragState.draggedComponent?.id !== selectedComponent.id) {
@ -270,10 +270,10 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
widgetType: widget?.widgetType || "text", widgetType: widget?.widgetType || "text",
}); });
console.log("✅ localInputs 업데이트 완료:", { // console.log("✅ localInputs 업데이트 완료:", {
positionX: currentPos.x.toString(), // positionX: currentPos.x.toString(),
positionY: currentPos.y.toString(), // positionY: currentPos.y.toString(),
}); // });
} }
} }
}, [ }, [
@ -307,24 +307,24 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
}; };
// 🔍 디버깅: 컴포넌트 구조 확인 // 🔍 디버깅: 컴포넌트 구조 확인
console.log("🔍 PropertiesPanel 삭제 액션 디버깅:", { // console.log("🔍 PropertiesPanel 삭제 액션 디버깅:", {
componentType: selectedComponent.type, // componentType: selectedComponent.type,
componentId: selectedComponent.id, // componentId: selectedComponent.id,
componentConfig: selectedComponent.componentConfig, // componentConfig: selectedComponent.componentConfig,
config: selectedComponent.config, // config: selectedComponent.config,
webTypeConfig: selectedComponent.webTypeConfig, // webTypeConfig: selectedComponent.webTypeConfig,
actionType1: selectedComponent.componentConfig?.action?.type, // actionType1: selectedComponent.componentConfig?.action?.type,
actionType2: selectedComponent.config?.action?.type, // actionType2: selectedComponent.config?.action?.type,
actionType3: selectedComponent.webTypeConfig?.actionType, // actionType3: selectedComponent.webTypeConfig?.actionType,
isDeleteAction: isDeleteAction(), // isDeleteAction: isDeleteAction(),
currentLabelColor: selectedComponent.style?.labelColor, // currentLabelColor: selectedComponent.style?.labelColor,
}); // });
// 액션에 따른 라벨 색상 자동 설정 // 액션에 따른 라벨 색상 자동 설정
if (isDeleteAction()) { if (isDeleteAction()) {
// 삭제 액션일 때 빨간색으로 설정 (이미 빨간색이 아닌 경우에만) // 삭제 액션일 때 빨간색으로 설정 (이미 빨간색이 아닌 경우에만)
if (selectedComponent.style?.labelColor !== '#ef4444') { if (selectedComponent.style?.labelColor !== '#ef4444') {
console.log("🔴 삭제 액션 감지: 라벨 색상을 빨간색으로 자동 설정"); // console.log("🔴 삭제 액션 감지: 라벨 색상을 빨간색으로 자동 설정");
onUpdateProperty("style", { onUpdateProperty("style", {
...selectedComponent.style, ...selectedComponent.style,
labelColor: '#ef4444' labelColor: '#ef4444'
@ -339,7 +339,7 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
} else { } else {
// 다른 액션일 때 기본 파란색으로 리셋 (현재 빨간색인 경우에만) // 다른 액션일 때 기본 파란색으로 리셋 (현재 빨간색인 경우에만)
if (selectedComponent.style?.labelColor === '#ef4444') { if (selectedComponent.style?.labelColor === '#ef4444') {
console.log("🔵 일반 액션 감지: 라벨 색상을 기본 파란색으로 리셋"); // console.log("🔵 일반 액션 감지: 라벨 색상을 기본 파란색으로 리셋");
onUpdateProperty("style", { onUpdateProperty("style", {
...selectedComponent.style, ...selectedComponent.style,
labelColor: '#3b83f6' labelColor: '#3b83f6'
@ -365,12 +365,12 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
// 렌더링 시마다 실행되는 직접적인 드래그 상태 체크 // 렌더링 시마다 실행되는 직접적인 드래그 상태 체크
if (dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id) { if (dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id) {
console.log("🎯 렌더링 중 드래그 상태 감지:", { // console.log("🎯 렌더링 중 드래그 상태 감지:", {
isDragging: dragState.isDragging, // isDragging: dragState.isDragging,
draggedId: dragState.draggedComponent?.id, // draggedId: dragState.draggedComponent?.id,
selectedId: selectedComponent?.id, // selectedId: selectedComponent?.id,
currentPosition: dragState.currentPosition, // currentPosition: dragState.currentPosition,
}); // });
const newPosition = { const newPosition = {
x: dragState.currentPosition.x, x: dragState.currentPosition.x,
@ -379,10 +379,10 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
// 위치가 변경되었는지 확인 // 위치가 변경되었는지 확인
if (lastDragPosition.x !== newPosition.x || lastDragPosition.y !== newPosition.y) { if (lastDragPosition.x !== newPosition.x || lastDragPosition.y !== newPosition.y) {
console.log("🔄 위치 변경 감지됨:", { // console.log("🔄 위치 변경 감지됨:", {
oldPosition: lastDragPosition, // oldPosition: lastDragPosition,
newPosition: newPosition, // newPosition: newPosition,
}); // });
// 다음 렌더링 사이클에서 업데이트 // 다음 렌더링 사이클에서 업데이트
setTimeout(() => { setTimeout(() => {
setLastDragPosition(newPosition); setLastDragPosition(newPosition);
@ -542,7 +542,7 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
value={localInputs.placeholder} value={localInputs.placeholder}
onChange={(e) => { onChange={(e) => {
const newValue = e.target.value; const newValue = e.target.value;
console.log("🔄 placeholder 변경:", newValue); // console.log("🔄 placeholder 변경:", newValue);
setLocalInputs((prev) => ({ ...prev, placeholder: newValue })); setLocalInputs((prev) => ({ ...prev, placeholder: newValue }));
onUpdateProperty("placeholder", newValue); onUpdateProperty("placeholder", newValue);
}} }}
@ -610,7 +610,7 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
const isDragging = dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id; const isDragging = dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id;
if (isDragging) { if (isDragging) {
const realTimeX = Math.round(dragState.currentPosition.x); const realTimeX = Math.round(dragState.currentPosition.x);
console.log("🔥 실시간 X 렌더링:", realTimeX, "forceRender:", forceRender); // console.log("🔥 실시간 X 렌더링:", realTimeX, "forceRender:", forceRender);
return realTimeX.toString(); return realTimeX.toString();
} }
return localInputs.positionX; return localInputs.positionX;
@ -640,7 +640,7 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
const isDragging = dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id; const isDragging = dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id;
if (isDragging) { if (isDragging) {
const realTimeY = Math.round(dragState.currentPosition.y); const realTimeY = Math.round(dragState.currentPosition.y);
console.log("🔥 실시간 Y 렌더링:", realTimeY, "forceRender:", forceRender); // console.log("🔥 실시간 Y 렌더링:", realTimeY, "forceRender:", forceRender);
return realTimeY.toString(); return realTimeY.toString();
} }
return localInputs.positionY; return localInputs.positionY;
@ -770,7 +770,7 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
id="labelDisplay" id="labelDisplay"
checked={localInputs.labelDisplay} checked={localInputs.labelDisplay}
onChange={(e) => { onChange={(e) => {
console.log("🔄 라벨 표시 변경:", e.target.checked); // console.log("🔄 라벨 표시 변경:", e.target.checked);
setLocalInputs((prev) => ({ ...prev, labelDisplay: e.target.checked })); setLocalInputs((prev) => ({ ...prev, labelDisplay: e.target.checked }));
onUpdateProperty("style.labelDisplay", e.target.checked); onUpdateProperty("style.labelDisplay", e.target.checked);
}} }}
@ -788,7 +788,7 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
value={localInputs.labelText} value={localInputs.labelText}
onChange={(e) => { onChange={(e) => {
const newValue = e.target.value; const newValue = e.target.value;
console.log("🔄 라벨 텍스트 변경:", newValue); // console.log("🔄 라벨 텍스트 변경:", newValue);
setLocalInputs((prev) => ({ ...prev, labelText: newValue })); setLocalInputs((prev) => ({ ...prev, labelText: newValue }));
// 기본 라벨과 스타일 라벨을 모두 업데이트 // 기본 라벨과 스타일 라벨을 모두 업데이트
onUpdateProperty("label", newValue); onUpdateProperty("label", newValue);

View File

@ -446,7 +446,7 @@ export const TemplatesPanel: React.FC<TemplatesPanelProps> = ({ onDragStart }) =
const dynamicTemplates = React.useMemo(() => { const dynamicTemplates = React.useMemo(() => {
if (error || !dbTemplates) { if (error || !dbTemplates) {
// 오류 발생 시 폴백 템플릿 사용 // 오류 발생 시 폴백 템플릿 사용
console.warn("템플릿 로딩 실패, 폴백 템플릿 사용:", error); // console.warn("템플릿 로딩 실패, 폴백 템플릿 사용:", error);
return fallbackTemplates; return fallbackTemplates;
} }

View File

@ -89,13 +89,13 @@ export const CheckboxTypeConfigPanel: React.FC<CheckboxTypeConfigPanelProps> = (
}; };
const newConfig = JSON.parse(JSON.stringify(currentValues)); const newConfig = JSON.parse(JSON.stringify(currentValues));
console.log("☑️ CheckboxTypeConfig 업데이트:", { // console.log("☑️ CheckboxTypeConfig 업데이트:", {
key, // key,
value, // value,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
localValues, // localValues,
}); // });
setTimeout(() => { setTimeout(() => {
onConfigChange(newConfig); onConfigChange(newConfig);

View File

@ -104,12 +104,12 @@ export const CodeTypeConfigPanel: React.FC<CodeTypeConfigPanelProps> = ({ config
// 실제 config 업데이트 // 실제 config 업데이트
const newConfig = { ...safeConfig, [key]: value }; const newConfig = { ...safeConfig, [key]: value };
console.log("💻 CodeTypeConfig 업데이트:", { // console.log("💻 CodeTypeConfig 업데이트:", {
key, // key,
value, // value,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
}); // });
onConfigChange(newConfig); onConfigChange(newConfig);
}; };

View File

@ -26,10 +26,10 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
// 로컬 상태로 실시간 입력 관리 // 로컬 상태로 실시간 입력 관리
const [localValues, setLocalValues] = useState(() => { const [localValues, setLocalValues] = useState(() => {
console.log("📅 DateTypeConfigPanel 초기 상태 설정:", { // console.log("📅 DateTypeConfigPanel 초기 상태 설정:", {
config, // config,
safeConfig, // safeConfig,
}); // });
return { return {
format: safeConfig.format, format: safeConfig.format,
@ -46,23 +46,23 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
// config가 실제로 존재하고 의미있는 데이터가 있을 때만 업데이트 // config가 실제로 존재하고 의미있는 데이터가 있을 때만 업데이트
const hasValidConfig = config && Object.keys(config).length > 0; const hasValidConfig = config && Object.keys(config).length > 0;
console.log("📅 DateTypeConfigPanel config 변경 감지:", { // console.log("📅 DateTypeConfigPanel config 변경 감지:", {
config, // config,
configExists: !!config, // configExists: !!config,
configKeys: config ? Object.keys(config) : [], // configKeys: config ? Object.keys(config) : [],
hasValidConfig, // hasValidConfig,
safeConfig, // safeConfig,
safeConfigKeys: Object.keys(safeConfig), // safeConfigKeys: Object.keys(safeConfig),
currentLocalValues: localValues, // currentLocalValues: localValues,
configStringified: JSON.stringify(config), // configStringified: JSON.stringify(config),
safeConfigStringified: JSON.stringify(safeConfig), // safeConfigStringified: JSON.stringify(safeConfig),
willUpdateLocalValues: hasValidConfig, // willUpdateLocalValues: hasValidConfig,
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
}); // });
// config가 없거나 비어있으면 로컬 상태를 유지 // config가 없거나 비어있으면 로컬 상태를 유지
if (!hasValidConfig) { if (!hasValidConfig) {
console.log("⚠️ config가 없거나 비어있음 - 로컬 상태 유지"); // console.log("⚠️ config가 없거나 비어있음 - 로컬 상태 유지");
return; return;
} }
@ -84,25 +84,25 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
localValues.minDate !== newLocalValues.minDate || localValues.minDate !== newLocalValues.minDate ||
localValues.maxDate !== newLocalValues.maxDate; localValues.maxDate !== newLocalValues.maxDate;
console.log("🔄 로컬 상태 업데이트 검사:", { // console.log("🔄 로컬 상태 업데이트 검사:", {
oldLocalValues: localValues, // oldLocalValues: localValues,
newLocalValues, // newLocalValues,
hasChanges, // hasChanges,
changes: { // changes: {
format: localValues.format !== newLocalValues.format, // format: localValues.format !== newLocalValues.format,
showTime: localValues.showTime !== newLocalValues.showTime, // showTime: localValues.showTime !== newLocalValues.showTime,
defaultValue: localValues.defaultValue !== newLocalValues.defaultValue, // defaultValue: localValues.defaultValue !== newLocalValues.defaultValue,
placeholder: localValues.placeholder !== newLocalValues.placeholder, // placeholder: localValues.placeholder !== newLocalValues.placeholder,
minDate: localValues.minDate !== newLocalValues.minDate, // minDate: localValues.minDate !== newLocalValues.minDate,
maxDate: localValues.maxDate !== newLocalValues.maxDate, // maxDate: localValues.maxDate !== newLocalValues.maxDate,
}, // },
}); // });
if (hasChanges) { if (hasChanges) {
console.log("✅ 로컬 상태 업데이트 실행"); // console.log("✅ 로컬 상태 업데이트 실행");
setLocalValues(newLocalValues); setLocalValues(newLocalValues);
} else { } else {
console.log("⏭️ 변경사항 없음 - 로컬 상태 유지"); // console.log("⏭️ 변경사항 없음 - 로컬 상태 유지");
} }
}, [JSON.stringify(config)]); }, [JSON.stringify(config)]);
@ -112,36 +112,36 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
// 실제 config 업데이트 - 현재 로컬 상태를 기반으로 새 객체 생성 (safeConfig 기본값 덮어쓰기 방지) // 실제 config 업데이트 - 현재 로컬 상태를 기반으로 새 객체 생성 (safeConfig 기본값 덮어쓰기 방지)
const newConfig = JSON.parse(JSON.stringify({ ...localValues, [key]: value })); const newConfig = JSON.parse(JSON.stringify({ ...localValues, [key]: value }));
console.log("📅 DateTypeConfig 업데이트:", { // console.log("📅 DateTypeConfig 업데이트:", {
key, // key,
value, // value,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
localValues, // localValues,
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
changes: { // changes: {
format: newConfig.format !== safeConfig.format, // format: newConfig.format !== safeConfig.format,
showTime: newConfig.showTime !== safeConfig.showTime, // showTime: newConfig.showTime !== safeConfig.showTime,
placeholder: newConfig.placeholder !== safeConfig.placeholder, // placeholder: newConfig.placeholder !== safeConfig.placeholder,
minDate: newConfig.minDate !== safeConfig.minDate, // minDate: newConfig.minDate !== safeConfig.minDate,
maxDate: newConfig.maxDate !== safeConfig.maxDate, // maxDate: newConfig.maxDate !== safeConfig.maxDate,
defaultValue: newConfig.defaultValue !== safeConfig.defaultValue, // defaultValue: newConfig.defaultValue !== safeConfig.defaultValue,
}, // },
willCallOnConfigChange: true, // willCallOnConfigChange: true,
}); // });
console.log("🔄 onConfigChange 호출 직전:", { // console.log("🔄 onConfigChange 호출 직전:", {
newConfig, // newConfig,
configStringified: JSON.stringify(newConfig), // configStringified: JSON.stringify(newConfig),
}); // });
// 약간의 지연을 두고 업데이트 (배치 업데이트 방지) // 약간의 지연을 두고 업데이트 (배치 업데이트 방지)
setTimeout(() => { setTimeout(() => {
console.log("✅ onConfigChange 호출 완료:", { // console.log("✅ onConfigChange 호출 완료:", {
key, // key,
newConfig, // newConfig,
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
}); // });
onConfigChange(newConfig); onConfigChange(newConfig);
}, 0); }, 0);
}; };
@ -156,11 +156,11 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
<Select <Select
value={localValues.format} value={localValues.format}
onValueChange={(value) => { onValueChange={(value) => {
console.log("📅 날짜 형식 변경:", { // console.log("📅 날짜 형식 변경:", {
oldFormat: localValues.format, // oldFormat: localValues.format,
newFormat: value, // newFormat: value,
oldShowTime: localValues.showTime, // oldShowTime: localValues.showTime,
}); // });
// format 변경 시 showTime도 자동 동기화 // format 변경 시 showTime도 자동 동기화
const hasTime = value.includes("HH:mm"); const hasTime = value.includes("HH:mm");
@ -174,11 +174,11 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
}), }),
); );
console.log("🔄 format+showTime 동시 업데이트:", { // console.log("🔄 format+showTime 동시 업데이트:", {
newFormat: value, // newFormat: value,
newShowTime: hasTime, // newShowTime: hasTime,
newConfig, // newConfig,
}); // });
// 로컬 상태도 동시 업데이트 // 로컬 상태도 동시 업데이트
setLocalValues((prev) => ({ setLocalValues((prev) => ({
@ -214,11 +214,11 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
checked={localValues.showTime} checked={localValues.showTime}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
const newShowTime = !!checked; const newShowTime = !!checked;
console.log("⏰ 시간 표시 체크박스 변경:", { // console.log("⏰ 시간 표시 체크박스 변경:", {
oldShowTime: localValues.showTime, // oldShowTime: localValues.showTime,
newShowTime, // newShowTime,
currentFormat: localValues.format, // currentFormat: localValues.format,
}); // });
// showTime 변경 시 format도 적절히 조정 // showTime 변경 시 format도 적절히 조정
let newFormat = localValues.format; let newFormat = localValues.format;
@ -230,11 +230,11 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
newFormat = "YYYY-MM-DD"; newFormat = "YYYY-MM-DD";
} }
console.log("🔄 showTime+format 동시 업데이트:", { // console.log("🔄 showTime+format 동시 업데이트:", {
newShowTime, // newShowTime,
oldFormat: localValues.format, // oldFormat: localValues.format,
newFormat, // newFormat,
}); // });
// 한 번에 두 값을 모두 업데이트 - 현재 로컬 상태 기반으로 생성 // 한 번에 두 값을 모두 업데이트 - 현재 로컬 상태 기반으로 생성
const newConfig = JSON.parse( const newConfig = JSON.parse(

View File

@ -91,12 +91,12 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
// 실제 config 업데이트 // 실제 config 업데이트
const newConfig = { ...safeConfig, [key]: value }; const newConfig = { ...safeConfig, [key]: value };
console.log("🏢 EntityTypeConfig 업데이트:", { // console.log("🏢 EntityTypeConfig 업데이트:", {
key, // key,
value, // value,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
}); // });
onConfigChange(newConfig); onConfigChange(newConfig);
}; };

View File

@ -79,12 +79,12 @@ export const FileTypeConfigPanel: React.FC<FileTypeConfigPanelProps> = ({ config
// 실제 config 업데이트 // 실제 config 업데이트
const newConfig = { ...safeConfig, [key]: value }; const newConfig = { ...safeConfig, [key]: value };
console.log("📁 FileTypeConfig 업데이트:", { // console.log("📁 FileTypeConfig 업데이트:", {
key, // key,
value, // value,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
}); // });
onConfigChange(newConfig); onConfigChange(newConfig);
}; };

View File

@ -88,14 +88,14 @@ export const NumberTypeConfigPanel: React.FC<NumberTypeConfigPanelProps> = ({ co
}; };
const newConfig = JSON.parse(JSON.stringify(currentValues)); const newConfig = JSON.parse(JSON.stringify(currentValues));
console.log("🔢 NumberTypeConfig 업데이트:", { // console.log("🔢 NumberTypeConfig 업데이트:", {
key, // key,
value, // value,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
localValues, // localValues,
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
}); // });
// 약간의 지연을 두고 업데이트 (배치 업데이트 방지) // 약간의 지연을 두고 업데이트 (배치 업데이트 방지)
setTimeout(() => { setTimeout(() => {

View File

@ -67,14 +67,14 @@ export const RadioTypeConfigPanel: React.FC<RadioTypeConfigPanelProps> = ({ conf
// 실제 config 업데이트 - 깊은 복사로 새 객체 보장 // 실제 config 업데이트 - 깊은 복사로 새 객체 보장
const newConfig = JSON.parse(JSON.stringify({ ...safeConfig, [key]: processedValue })); const newConfig = JSON.parse(JSON.stringify({ ...safeConfig, [key]: processedValue }));
console.log("📻 RadioTypeConfig 업데이트:", { // console.log("📻 RadioTypeConfig 업데이트:", {
key, // key,
value, // value,
processedValue, // processedValue,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
}); // });
// 약간의 지연을 두고 업데이트 (배치 업데이트 방지) // 약간의 지연을 두고 업데이트 (배치 업데이트 방지)
setTimeout(() => { setTimeout(() => {
@ -87,12 +87,12 @@ export const RadioTypeConfigPanel: React.FC<RadioTypeConfigPanelProps> = ({ conf
const newOptionData = { ...newOption }; const newOptionData = { ...newOption };
const updatedOptions = [...(safeConfig.options || []), newOptionData]; const updatedOptions = [...(safeConfig.options || []), newOptionData];
console.log(" RadioType 옵션 추가:", { // console.log(" RadioType 옵션 추가:", {
newOption: newOptionData, // newOption: newOptionData,
updatedOptions, // updatedOptions,
currentLocalOptions: localOptions, // currentLocalOptions: localOptions,
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
}); // });
// 로컬 상태 즉시 업데이트 // 로컬 상태 즉시 업데이트
setLocalOptions((prev) => { setLocalOptions((prev) => {
@ -103,7 +103,7 @@ export const RadioTypeConfigPanel: React.FC<RadioTypeConfigPanelProps> = ({ conf
value: newOption.value, value: newOption.value,
}, },
]; ];
console.log("📻 RadioType 로컬 옵션 업데이트:", newLocalOptions); // console.log("📻 RadioType 로컬 옵션 업데이트:", newLocalOptions);
return newLocalOptions; return newLocalOptions;
}); });
@ -113,16 +113,16 @@ export const RadioTypeConfigPanel: React.FC<RadioTypeConfigPanelProps> = ({ conf
}; };
const removeOption = (index: number) => { const removeOption = (index: number) => {
console.log(" RadioType 옵션 삭제:", { // console.log(" RadioType 옵션 삭제:", {
removeIndex: index, // removeIndex: index,
currentOptions: safeConfig.options, // currentOptions: safeConfig.options,
currentLocalOptions: localOptions, // currentLocalOptions: localOptions,
}); // });
// 로컬 상태 즉시 업데이트 // 로컬 상태 즉시 업데이트
setLocalOptions((prev) => { setLocalOptions((prev) => {
const newLocalOptions = prev.filter((_, i) => i !== index); const newLocalOptions = prev.filter((_, i) => i !== index);
console.log("📻 RadioType 로컬 옵션 삭제 후:", newLocalOptions); // console.log("📻 RadioType 로컬 옵션 삭제 후:", newLocalOptions);
return newLocalOptions; return newLocalOptions;
}); });

View File

@ -81,13 +81,13 @@ export const SelectTypeConfigPanel: React.FC<SelectTypeConfigPanelProps> = ({ co
// 실제 config 업데이트 - 깊은 복사로 새 객체 보장 // 실제 config 업데이트 - 깊은 복사로 새 객체 보장
const newConfig = JSON.parse(JSON.stringify({ ...safeConfig, [key]: value })); const newConfig = JSON.parse(JSON.stringify({ ...safeConfig, [key]: value }));
console.log("📋 SelectTypeConfig 업데이트:", { // console.log("📋 SelectTypeConfig 업데이트:", {
key, // key,
value, // value,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
}); // });
// 약간의 지연을 두고 업데이트 (배치 업데이트 방지) // 약간의 지연을 두고 업데이트 (배치 업데이트 방지)
setTimeout(() => { setTimeout(() => {
@ -100,12 +100,12 @@ export const SelectTypeConfigPanel: React.FC<SelectTypeConfigPanelProps> = ({ co
const newOptionData = { ...newOption, disabled: false }; const newOptionData = { ...newOption, disabled: false };
const updatedOptions = [...(safeConfig.options || []), newOptionData]; const updatedOptions = [...(safeConfig.options || []), newOptionData];
console.log(" SelectType 옵션 추가:", { // console.log(" SelectType 옵션 추가:", {
newOption: newOptionData, // newOption: newOptionData,
updatedOptions, // updatedOptions,
currentLocalOptions: localOptions, // currentLocalOptions: localOptions,
timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
}); // });
// 로컬 상태 즉시 업데이트 // 로컬 상태 즉시 업데이트
setLocalOptions((prev) => { setLocalOptions((prev) => {
@ -117,7 +117,7 @@ export const SelectTypeConfigPanel: React.FC<SelectTypeConfigPanelProps> = ({ co
disabled: false, disabled: false,
}, },
]; ];
console.log("📋 SelectType 로컬 옵션 업데이트:", newLocalOptions); // console.log("📋 SelectType 로컬 옵션 업데이트:", newLocalOptions);
return newLocalOptions; return newLocalOptions;
}); });
@ -127,16 +127,16 @@ export const SelectTypeConfigPanel: React.FC<SelectTypeConfigPanelProps> = ({ co
}; };
const removeOption = (index: number) => { const removeOption = (index: number) => {
console.log(" SelectType 옵션 삭제:", { // console.log(" SelectType 옵션 삭제:", {
removeIndex: index, // removeIndex: index,
currentOptions: safeConfig.options, // currentOptions: safeConfig.options,
currentLocalOptions: localOptions, // currentLocalOptions: localOptions,
}); // });
// 로컬 상태 즉시 업데이트 // 로컬 상태 즉시 업데이트
setLocalOptions((prev) => { setLocalOptions((prev) => {
const newLocalOptions = prev.filter((_, i) => i !== index); const newLocalOptions = prev.filter((_, i) => i !== index);
console.log("📋 SelectType 로컬 옵션 삭제 후:", newLocalOptions); // console.log("📋 SelectType 로컬 옵션 삭제 후:", newLocalOptions);
return newLocalOptions; return newLocalOptions;
}); });

View File

@ -93,13 +93,13 @@ export const TextTypeConfigPanel: React.FC<TextTypeConfigPanelProps> = ({ config
}; };
const newConfig = JSON.parse(JSON.stringify(currentValues)); const newConfig = JSON.parse(JSON.stringify(currentValues));
console.log("📝 TextTypeConfig 업데이트:", { // console.log("📝 TextTypeConfig 업데이트:", {
key, // key,
value, // value,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
localValues, // localValues,
}); // });
setTimeout(() => { setTimeout(() => {
onConfigChange(newConfig); onConfigChange(newConfig);

View File

@ -78,13 +78,13 @@ export const TextareaTypeConfigPanel: React.FC<TextareaTypeConfigPanelProps> = (
}; };
const newConfig = JSON.parse(JSON.stringify(currentValues)); const newConfig = JSON.parse(JSON.stringify(currentValues));
console.log("📄 TextareaTypeConfig 업데이트:", { // console.log("📄 TextareaTypeConfig 업데이트:", {
key, // key,
value, // value,
oldConfig: safeConfig, // oldConfig: safeConfig,
newConfig, // newConfig,
localValues, // localValues,
}); // });
setTimeout(() => { setTimeout(() => {
onConfigChange(newConfig); onConfigChange(newConfig);

View File

@ -34,10 +34,10 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
const newState = updater(currentState); const newState = updater(currentState);
(window as any).globalFileState = newState; (window as any).globalFileState = newState;
console.log("🌐 FileUpload 전역 파일 상태 업데이트:", { // console.log("🌐 FileUpload 전역 파일 상태 업데이트:", {
componentId: component.id, // componentId: component.id,
newFileCount: newState[component.id]?.length || 0 // newFileCount: newState[component.id]?.length || 0
}); // });
// 강제 리렌더링을 위한 이벤트 발생 // 강제 리렌더링을 위한 이벤트 발생
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
@ -52,12 +52,12 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
const componentFiles = component.uploadedFiles || []; const componentFiles = component.uploadedFiles || [];
const finalFiles = globalFiles.length > 0 ? globalFiles : componentFiles; const finalFiles = globalFiles.length > 0 ? globalFiles : componentFiles;
console.log("🚀 FileUpload 파일 상태 초기화:", { // console.log("🚀 FileUpload 파일 상태 초기화:", {
componentId: component.id, // componentId: component.id,
globalFiles: globalFiles.length, // globalFiles: globalFiles.length,
componentFiles: componentFiles.length, // componentFiles: componentFiles.length,
finalFiles: finalFiles.length // finalFiles: finalFiles.length
}); // });
return finalFiles; return finalFiles;
}; };
@ -70,10 +70,10 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
const handleGlobalFileStateChange = (event: CustomEvent) => { const handleGlobalFileStateChange = (event: CustomEvent) => {
if (event.detail.componentId === component.id) { if (event.detail.componentId === component.id) {
const globalFiles = getGlobalFileState()[component.id] || []; const globalFiles = getGlobalFileState()[component.id] || [];
console.log("🔄 FileUpload 전역 상태 변경 감지:", { // console.log("🔄 FileUpload 전역 상태 변경 감지:", {
componentId: component.id, // componentId: component.id,
newFileCount: globalFiles.length // newFileCount: globalFiles.length
}); // });
setLocalUploadedFiles(globalFiles); setLocalUploadedFiles(globalFiles);
} }
}; };
@ -101,53 +101,53 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
// 사용자 정보 디버깅 // 사용자 정보 디버깅
useEffect(() => { useEffect(() => {
console.log("👤 File 컴포넌트 인증 상태 및 사용자 정보:", { // console.log("👤 File 컴포넌트 인증 상태 및 사용자 정보:", {
isLoading, // isLoading,
isLoggedIn, // isLoggedIn,
hasUser: !!user, // hasUser: !!user,
user: user, // user: user,
userId: user?.userId, // userId: user?.userId,
company_code: user?.company_code, // company_code: user?.company_code,
companyCode: user?.companyCode, // companyCode: user?.companyCode,
userType: typeof user, // userType: typeof user,
userKeys: user ? Object.keys(user) : "no user", // userKeys: user ? Object.keys(user) : "no user",
userValues: user ? Object.entries(user) : "no user", // userValues: user ? Object.entries(user) : "no user",
}); // });
// 사용자 정보가 유효하면 initialUser와 userRef 업데이트 // 사용자 정보가 유효하면 initialUser와 userRef 업데이트
if (user && user.userId) { if (user && user.userId) {
setInitialUser(user); setInitialUser(user);
userRef.current = user; // 🎯 ref에도 최신 정보 저장 userRef.current = user; // 🎯 ref에도 최신 정보 저장
console.log("✅ 초기 사용자 정보 업데이트:", { userId: user.userId, companyCode: user.companyCode }); // console.log("✅ 초기 사용자 정보 업데이트:", { userId: user.userId, companyCode: user.companyCode });
} }
// 회사 관련 필드들 확인 // 회사 관련 필드들 확인
if (user) { if (user) {
console.log("🔍 회사 관련 필드 검색:", { // console.log("🔍 회사 관련 필드 검색:", {
company_code: user.company_code, // company_code: user.company_code,
companyCode: user.companyCode, // companyCode: user.companyCode,
company: user.company, // company: user.company,
deptCode: user.deptCode, // deptCode: user.deptCode,
partnerCd: user.partnerCd, // partnerCd: user.partnerCd,
// 모든 필드에서 company 관련된 것들 찾기 // 모든 필드에서 company 관련된 것들 찾기
allFields: Object.keys(user).filter( // allFields: Object.keys(user).filter(
(key) => // (key) =>
key.toLowerCase().includes("company") || // key.toLowerCase().includes("company") ||
key.toLowerCase().includes("corp") || // key.toLowerCase().includes("corp") ||
key.toLowerCase().includes("code"), // key.toLowerCase().includes("code"),
), // ),
}); // });
} else { } else {
console.warn("⚠️ 사용자 정보가 없습니다. 인증 상태 확인 필요"); // console.warn("⚠️ 사용자 정보가 없습니다. 인증 상태 확인 필요");
} }
}, [user, isLoading, isLoggedIn]); }, [user, isLoading, isLoggedIn]);
// 컴포넌트 props가 변경될 때 로컬 상태 동기화 // 컴포넌트 props가 변경될 때 로컬 상태 동기화
useEffect(() => { useEffect(() => {
console.log("🔄 File 컴포넌트 props 변경:", { // console.log("🔄 File 컴포넌트 props 변경:", {
propsUploadedFiles: component.uploadedFiles?.length || 0, // propsUploadedFiles: component.uploadedFiles?.length || 0,
localUploadedFiles: localUploadedFiles.length, // localUploadedFiles: localUploadedFiles.length,
}); // });
setLocalUploadedFiles(component.uploadedFiles || []); setLocalUploadedFiles(component.uploadedFiles || []);
}, [component.uploadedFiles]); }, [component.uploadedFiles]);
@ -176,23 +176,23 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
const isFileTypeAllowed = (file: File): boolean => { const isFileTypeAllowed = (file: File): boolean => {
const fileName = file.name.toLowerCase(); const fileName = file.name.toLowerCase();
console.log("🔍 파일 타입 검증:", { // console.log("🔍 파일 타입 검증:", {
fileName: file.name, // fileName: file.name,
fileType: file.type, // fileType: file.type,
acceptRules: fileConfig.accept, // acceptRules: fileConfig.accept,
}); // });
const result = fileConfig.accept.some((accept) => { const result = fileConfig.accept.some((accept) => {
// 모든 파일 허용 (와일드카드) // 모든 파일 허용 (와일드카드)
if (accept === "*/*" || accept === "*") { if (accept === "*/*" || accept === "*") {
console.log("✅ 와일드카드 매칭:", accept); // console.log("✅ 와일드카드 매칭:", accept);
return true; return true;
} }
// 확장자 기반 검증 (.jpg, .png 등) // 확장자 기반 검증 (.jpg, .png 등)
if (accept.startsWith(".")) { if (accept.startsWith(".")) {
const matches = fileName.endsWith(accept.toLowerCase()); const matches = fileName.endsWith(accept.toLowerCase());
console.log(`${matches ? "✅" : "❌"} 확장자 검증:`, accept, "→", matches); // console.log(`${matches ? "✅" : "❌"} 확장자 검증:`, accept, "→", matches);
return matches; return matches;
} }
@ -200,97 +200,97 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
if (accept.includes("/*")) { if (accept.includes("/*")) {
const type = accept.split("/")[0]; const type = accept.split("/")[0];
const matches = file.type.startsWith(type); const matches = file.type.startsWith(type);
console.log(`${matches ? "✅" : "❌"} MIME 타입 검증:`, accept, "→", matches); // console.log(`${matches ? "✅" : "❌"} MIME 타입 검증:`, accept, "→", matches);
return matches; return matches;
} }
// 정확한 MIME 타입 매칭 (image/jpeg, application/pdf 등) // 정확한 MIME 타입 매칭 (image/jpeg, application/pdf 등)
const matches = file.type === accept; const matches = file.type === accept;
console.log(`${matches ? "✅" : "❌"} 정확한 MIME 매칭:`, accept, "→", matches); // console.log(`${matches ? "✅" : "❌"} 정확한 MIME 매칭:`, accept, "→", matches);
return matches; return matches;
}); });
console.log(`🎯 최종 검증 결과:`, result); // console.log(`🎯 최종 검증 결과:`, result);
return result; return result;
}; };
// 파일 선택 핸들러 // 파일 선택 핸들러
const handleFileSelect = useCallback( const handleFileSelect = useCallback(
(files: FileList | null) => { (files: FileList | null) => {
console.log("📁 파일 선택됨:", files ? Array.from(files).map((f) => f.name) : "없음"); // console.log("📁 파일 선택됨:", files ? Array.from(files).map((f) => f.name) : "없음");
if (!files) return; if (!files) return;
const fileArray = Array.from(files); const fileArray = Array.from(files);
const validFiles: File[] = []; const validFiles: File[] = [];
const errors: string[] = []; const errors: string[] = [];
console.log("🔍 파일 검증 시작:", { // console.log("🔍 파일 검증 시작:", {
totalFiles: fileArray.length, // totalFiles: fileArray.length,
currentUploadedCount: uploadedFiles.length, // currentUploadedCount: uploadedFiles.length,
maxFiles: fileConfig.maxFiles, // maxFiles: fileConfig.maxFiles,
maxSize: fileConfig.maxSize, // maxSize: fileConfig.maxSize,
allowedTypes: fileConfig.accept, // allowedTypes: fileConfig.accept,
}); // });
// 파일 검증 // 파일 검증
fileArray.forEach((file) => { fileArray.forEach((file) => {
console.log(`📄 파일 검증: ${file.name} (${file.size} bytes, ${file.type})`); // console.log(`📄 파일 검증: ${file.name} (${file.size} bytes, ${file.type})`);
// 파일 타입 검증 // 파일 타입 검증
if (!isFileTypeAllowed(file)) { if (!isFileTypeAllowed(file)) {
errors.push(`${file.name}: 허용되지 않는 파일 타입입니다.`); errors.push(`${file.name}: 허용되지 않는 파일 타입입니다.`);
console.log(`❌ 파일 타입 거부: ${file.name}`); // console.log(`❌ 파일 타입 거부: ${file.name}`);
return; return;
} }
// 파일 크기 검증 // 파일 크기 검증
if (file.size > fileConfig.maxSize * 1024 * 1024) { if (file.size > fileConfig.maxSize * 1024 * 1024) {
errors.push(`${file.name}: 파일 크기가 ${fileConfig.maxSize}MB를 초과합니다.`); errors.push(`${file.name}: 파일 크기가 ${fileConfig.maxSize}MB를 초과합니다.`);
console.log(`❌ 파일 크기 초과: ${file.name} (${file.size} > ${fileConfig.maxSize * 1024 * 1024})`); // console.log(`❌ 파일 크기 초과: ${file.name} (${file.size} > ${fileConfig.maxSize * 1024 * 1024})`);
return; return;
} }
// 최대 파일 수 검증 // 최대 파일 수 검증
if (uploadedFiles.length + validFiles.length >= fileConfig.maxFiles) { if (uploadedFiles.length + validFiles.length >= fileConfig.maxFiles) {
errors.push(`최대 ${fileConfig.maxFiles}개까지만 업로드할 수 있습니다.`); errors.push(`최대 ${fileConfig.maxFiles}개까지만 업로드할 수 있습니다.`);
console.log(`❌ 최대 파일 수 초과`); // console.log(`❌ 최대 파일 수 초과`);
return; return;
} }
validFiles.push(file); validFiles.push(file);
console.log(`✅ 파일 검증 통과: ${file.name}`); // console.log(`✅ 파일 검증 통과: ${file.name}`);
}); });
// 에러가 있으면 알림 // 에러가 있으면 알림
if (errors.length > 0) { if (errors.length > 0) {
console.error("💥 파일 업로드 오류:", errors); // console.error("💥 파일 업로드 오류:", errors);
// TODO: Toast 알림 표시 // TODO: Toast 알림 표시
} }
// 유효한 파일들을 업로드 큐에 추가 // 유효한 파일들을 업로드 큐에 추가
if (validFiles.length > 0) { if (validFiles.length > 0) {
console.log( // console.log(
"✅ 유효한 파일들 업로드 큐에 추가:", // "✅ 유효한 파일들 업로드 큐에 추가:",
validFiles.map((f) => f.name), // validFiles.map((f) => f.name),
); // );
setUploadQueue((prev) => [...prev, ...validFiles]); setUploadQueue((prev) => [...prev, ...validFiles]);
if (fileConfig.autoUpload) { if (fileConfig.autoUpload) {
console.log("🚀 자동 업로드 시작:", { // console.log("🚀 자동 업로드 시작:", {
autoUpload: fileConfig.autoUpload, // autoUpload: fileConfig.autoUpload,
filesCount: validFiles.length, // filesCount: validFiles.length,
fileNames: validFiles.map((f) => f.name), // fileNames: validFiles.map((f) => f.name),
}); // });
// 자동 업로드 실행 // 자동 업로드 실행
validFiles.forEach(uploadFile); validFiles.forEach(uploadFile);
} else { } else {
console.log("⏸️ 자동 업로드 비활성화:", { // console.log("⏸️ 자동 업로드 비활성화:", {
autoUpload: fileConfig.autoUpload, // autoUpload: fileConfig.autoUpload,
filesCount: validFiles.length, // filesCount: validFiles.length,
}); // });
} }
} else { } else {
console.log("❌ 업로드할 유효한 파일이 없음"); // console.log("❌ 업로드할 유효한 파일이 없음");
} }
}, },
[fileConfig, uploadedFiles.length], [fileConfig, uploadedFiles.length],
@ -299,7 +299,7 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
// 파일 업로드 함수 (실시간 상태 조회로 타이밍 문제 해결) // 파일 업로드 함수 (실시간 상태 조회로 타이밍 문제 해결)
const uploadFile = useCallback( const uploadFile = useCallback(
async (file: File) => { async (file: File) => {
console.log("📤 파일 업로드 시작:", file.name); // console.log("📤 파일 업로드 시작:", file.name);
const formData = new FormData(); const formData = new FormData();
formData.append("files", file); formData.append("files", file);
@ -311,31 +311,31 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
const currentUser = userRef.current; const currentUser = userRef.current;
// 실시간 사용자 정보 디버깅 // 실시간 사용자 정보 디버깅
console.log("🔍 FileUpload - uploadFile ref를 통한 실시간 상태:", { // console.log("🔍 FileUpload - uploadFile ref를 통한 실시간 상태:", {
hasCurrentUser: !!currentUser, // hasCurrentUser: !!currentUser,
currentUser: currentUser // currentUser: currentUser
? { // ? {
userId: currentUser.userId, // userId: currentUser.userId,
companyCode: currentUser.companyCode, // companyCode: currentUser.companyCode,
company_code: currentUser.company_code, // company_code: currentUser.company_code,
} // }
: null, // : null,
// 기존 상태와 비교 // 기존 상태와 비교
originalUser: user, // originalUser: user,
originalInitialUser: initialUser, // originalInitialUser: initialUser,
refExists: !!userRef.current, // refExists: !!userRef.current,
}); // });
// 사용자 정보가 로드되지 않은 경우 잠시 대기 // 사용자 정보가 로드되지 않은 경우 잠시 대기
if (isLoading) { if (isLoading) {
console.log("⏳ 사용자 정보 로딩 중... 업로드 대기"); // console.log("⏳ 사용자 정보 로딩 중... 업로드 대기");
setTimeout(() => uploadFile(file), 500); // 500ms 후 재시도 setTimeout(() => uploadFile(file), 500); // 500ms 후 재시도
return; return;
} }
// 사용자 정보가 없는 경우 - 무한루프 방지로 재시도 제한 // 사용자 정보가 없는 경우 - 무한루프 방지로 재시도 제한
if (!user && isLoggedIn) { if (!user && isLoggedIn) {
console.warn("⚠️ 로그인은 되어 있지만 사용자 정보가 없음. DEFAULT로 진행"); // console.warn("⚠️ 로그인은 되어 있지만 사용자 정보가 없음. DEFAULT로 진행");
// 무한루프 방지: 재시도하지 않고 DEFAULT로 진행 // 무한루프 방지: 재시도하지 않고 DEFAULT로 진행
// setTimeout(() => uploadFile(file), 1000); // 1초 후 재시도 // setTimeout(() => uploadFile(file), 1000); // 1초 후 재시도
// return; // return;
@ -348,27 +348,27 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
if (companyCode) { if (companyCode) {
// "*"는 실제 회사코드이므로 그대로 사용 // "*"는 실제 회사코드이므로 그대로 사용
formData.append("companyCode", companyCode); formData.append("companyCode", companyCode);
console.log("✅ 회사코드 추가:", companyCode); // console.log("✅ 회사코드 추가:", companyCode);
} else { } else {
console.warn("⚠️ 회사코드가 없음, DEFAULT 사용. 사용자 정보:", { // console.warn("⚠️ 회사코드가 없음, DEFAULT 사용. 사용자 정보:", {
user: user, // user: user,
initialUser: initialUser, // initialUser: initialUser,
effectiveUser: effectiveUser, // effectiveUser: effectiveUser,
companyCode: effectiveUser?.companyCode, // companyCode: effectiveUser?.companyCode,
company_code: effectiveUser?.company_code, // company_code: effectiveUser?.company_code,
deptCode: effectiveUser?.deptCode, // deptCode: effectiveUser?.deptCode,
isLoading, // isLoading,
isLoggedIn, // isLoggedIn,
allUserKeys: effectiveUser ? Object.keys(effectiveUser) : "no user", // allUserKeys: effectiveUser ? Object.keys(effectiveUser) : "no user",
}); // });
formData.append("companyCode", "DEFAULT"); formData.append("companyCode", "DEFAULT");
} }
if (effectiveUser?.userId) { if (effectiveUser?.userId) {
formData.append("writer", effectiveUser.userId); formData.append("writer", effectiveUser.userId);
console.log("✅ 작성자 추가:", effectiveUser.userId); // console.log("✅ 작성자 추가:", effectiveUser.userId);
} else { } else {
console.warn("⚠️ 사용자ID가 없음, system 사용"); // console.warn("⚠️ 사용자ID가 없음, system 사용");
formData.append("writer", "system"); formData.append("writer", "system");
} }
@ -376,45 +376,45 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
if (fileConfig.accept && fileConfig.accept.length > 0) { if (fileConfig.accept && fileConfig.accept.length > 0) {
const acceptString = fileConfig.accept.join(","); const acceptString = fileConfig.accept.join(",");
formData.append("accept", acceptString); formData.append("accept", acceptString);
console.log("✅ 허용 파일 타입 추가:", acceptString); // console.log("✅ 허용 파일 타입 추가:", acceptString);
} }
// 자동 연결 정보 추가 // 자동 연결 정보 추가
if (fileConfig.autoLink) { if (fileConfig.autoLink) {
formData.append("autoLink", "true"); formData.append("autoLink", "true");
console.log("✅ 자동 연결 활성화: true"); // console.log("✅ 자동 연결 활성화: true");
if (fileConfig.linkedTable) { if (fileConfig.linkedTable) {
formData.append("linkedTable", fileConfig.linkedTable); formData.append("linkedTable", fileConfig.linkedTable);
console.log("✅ 연결 테이블 추가:", fileConfig.linkedTable); // console.log("✅ 연결 테이블 추가:", fileConfig.linkedTable);
} }
if (fileConfig.linkedField) { if (fileConfig.linkedField) {
formData.append("linkedField", fileConfig.linkedField); formData.append("linkedField", fileConfig.linkedField);
console.log("✅ 연결 필드 추가:", fileConfig.linkedField); // console.log("✅ 연결 필드 추가:", fileConfig.linkedField);
} }
if (fileConfig.recordId) { if (fileConfig.recordId) {
formData.append("recordId", fileConfig.recordId); formData.append("recordId", fileConfig.recordId);
console.log("✅ 레코드 ID 추가:", fileConfig.recordId); // console.log("✅ 레코드 ID 추가:", fileConfig.recordId);
} }
// 가상 파일 컬럼 정보 추가 // 가상 파일 컬럼 정보 추가
if (fileConfig.isVirtualFileColumn) { if (fileConfig.isVirtualFileColumn) {
formData.append("isVirtualFileColumn", "true"); formData.append("isVirtualFileColumn", "true");
console.log("✅ 가상 파일 컬럼 활성화: true"); // console.log("✅ 가상 파일 컬럼 활성화: true");
if (fileConfig.columnName) { if (fileConfig.columnName) {
formData.append("columnName", fileConfig.columnName); formData.append("columnName", fileConfig.columnName);
console.log("✅ 컬럼명 추가:", fileConfig.columnName); // console.log("✅ 컬럼명 추가:", fileConfig.columnName);
} }
} }
} }
// FormData 내용 디버깅 // FormData 내용 디버깅
console.log("📋 FormData 내용 확인:"); // console.log("📋 FormData 내용 확인:");
for (const [key, value] of formData.entries()) { for (const [key, value] of formData.entries()) {
console.log(` ${key}:`, value); // console.log(` ${key}:`, value);
} }
try { try {
@ -437,23 +437,23 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
isUploading: true, isUploading: true,
}; };
console.log("📋 임시 파일 정보 생성:", tempFileInfo); // console.log("📋 임시 파일 정보 생성:", tempFileInfo);
const newUploadedFiles = [...uploadedFiles, tempFileInfo]; const newUploadedFiles = [...uploadedFiles, tempFileInfo];
console.log("📊 업데이트 전 파일 목록:", uploadedFiles.length, "개"); // console.log("📊 업데이트 전 파일 목록:", uploadedFiles.length, "개");
console.log("📊 업데이트 후 파일 목록:", newUploadedFiles.length, "개"); // console.log("📊 업데이트 후 파일 목록:", newUploadedFiles.length, "개");
// 로컬 상태 즉시 업데이트 // 로컬 상태 즉시 업데이트
setLocalUploadedFiles(newUploadedFiles); setLocalUploadedFiles(newUploadedFiles);
// 임시 파일 정보를 업로드된 파일 목록에 추가 // 임시 파일 정보를 업로드된 파일 목록에 추가
console.log("🔄 onUpdateComponent 호출 중..."); // console.log("🔄 onUpdateComponent 호출 중...");
onUpdateComponent({ onUpdateComponent({
uploadedFiles: newUploadedFiles, uploadedFiles: newUploadedFiles,
}); });
console.log("✅ onUpdateComponent 호출 완료"); // console.log("✅ onUpdateComponent 호출 완료");
console.log("🚀 API 호출 시작 - /files/upload"); // console.log("🚀 API 호출 시작 - /files/upload");
// 실제 API 호출 (apiClient 사용으로 자동 JWT 토큰 추가) // 실제 API 호출 (apiClient 사용으로 자동 JWT 토큰 추가)
// FormData 사용 시 Content-Type을 삭제하여 boundary가 자동 설정되도록 함 // FormData 사용 시 Content-Type을 삭제하여 boundary가 자동 설정되도록 함
@ -464,7 +464,7 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
}); });
const result = response.data; const result = response.data;
console.log("📡 API 응답 성공:", result); // console.log("📡 API 응답 성공:", result);
if (!result.success || !result.files || result.files.length === 0) { if (!result.success || !result.files || result.files.length === 0) {
throw new Error(result.message || "파일 업로드 실패"); throw new Error(result.message || "파일 업로드 실패");
@ -491,7 +491,7 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
isUploading: false, isUploading: false,
}; };
console.log("✅ 실제 파일 업로드 완료 (attach_file_info 저장됨):", successFileInfo); // console.log("✅ 실제 파일 업로드 완료 (attach_file_info 저장됨):", successFileInfo);
const updatedFiles = uploadedFiles.map((f) => (f.objid === tempFileInfo.objid ? successFileInfo : f)); const updatedFiles = uploadedFiles.map((f) => (f.objid === tempFileInfo.objid ? successFileInfo : f));
@ -514,14 +514,14 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
timestamp: Date.now() timestamp: Date.now()
}; };
console.log("🚀 FileUpload 위젯 이벤트 발생:", eventDetail); // console.log("🚀 FileUpload 위젯 이벤트 발생:", eventDetail);
const event = new CustomEvent('globalFileStateChanged', { const event = new CustomEvent('globalFileStateChanged', {
detail: eventDetail detail: eventDetail
}); });
window.dispatchEvent(event); window.dispatchEvent(event);
console.log("✅ FileUpload globalFileStateChanged 이벤트 발생 완료"); // console.log("✅ FileUpload globalFileStateChanged 이벤트 발생 완료");
} }
// 컴포넌트 업데이트 (옵셔널) // 컴포넌트 업데이트 (옵셔널)
@ -539,21 +539,21 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
// 업로드 큐에서 제거 // 업로드 큐에서 제거
setUploadQueue((prev) => prev.filter((f) => f !== file)); setUploadQueue((prev) => prev.filter((f) => f !== file));
} catch (error) { } catch (error) {
console.error("❌ 파일 업로드 실패:", { // console.error("❌ 파일 업로드 실패:", {
error, // error,
errorMessage: error instanceof Error ? error.message : "알 수 없는 오류", // errorMessage: error instanceof Error ? error.message : "알 수 없는 오류",
errorStack: error instanceof Error ? error.stack : undefined, // errorStack: error instanceof Error ? error.stack : undefined,
user: user ? { userId: user.userId, companyCode: user.companyCode, hasUser: true } : "no user", // user: user ? { userId: user.userId, companyCode: user.companyCode, hasUser: true } : "no user",
authState: { isLoading, isLoggedIn }, // authState: { isLoading, isLoggedIn },
}); // });
// API 응답 에러인 경우 상세 정보 출력 // API 응답 에러인 경우 상세 정보 출력
if ((error as any)?.response) { if ((error as any)?.response) {
console.error("📡 API 응답 에러:", { // console.error("📡 API 응답 에러:", {
status: (error as any).response.status, // status: (error as any).response.status,
statusText: (error as any).response.statusText, // statusText: (error as any).response.statusText,
data: (error as any).response.data, // data: (error as any).response.data,
}); // });
} }
// 에러 상태로 업데이트 // 에러 상태로 업데이트
@ -576,7 +576,7 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
// 파일 삭제 // 파일 삭제
const deleteFile = async (fileInfo: AttachedFileInfo) => { const deleteFile = async (fileInfo: AttachedFileInfo) => {
console.log("🗑️ 파일 삭제:", fileInfo.realFileName); // console.log("🗑️ 파일 삭제:", fileInfo.realFileName);
try { try {
// 실제 API 호출 (논리적 삭제) - apiClient 사용으로 JWT 토큰 자동 추가 // 실제 API 호출 (논리적 삭제) - apiClient 사용으로 JWT 토큰 자동 추가
const response = await apiClient.delete(`/files/${fileInfo.objid}`, { const response = await apiClient.delete(`/files/${fileInfo.objid}`, {
@ -586,7 +586,7 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
}); });
const result = response.data; const result = response.data;
console.log("📡 파일 삭제 API 응답:", result); // console.log("📡 파일 삭제 API 응답:", result);
if (!result.success) { if (!result.success) {
throw new Error(result.message || "파일 삭제 실패"); throw new Error(result.message || "파일 삭제 실패");
@ -615,39 +615,39 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
source: 'realScreen' // 실제 화면에서 온 이벤트임을 표시 source: 'realScreen' // 실제 화면에서 온 이벤트임을 표시
}; };
console.log("🚀🚀🚀 FileUpload 위젯 삭제 이벤트 발생:", eventDetail); // console.log("🚀🚀🚀 FileUpload 위젯 삭제 이벤트 발생:", eventDetail);
const event = new CustomEvent('globalFileStateChanged', { const event = new CustomEvent('globalFileStateChanged', {
detail: eventDetail detail: eventDetail
}); });
window.dispatchEvent(event); window.dispatchEvent(event);
console.log("✅✅✅ FileUpload 위젯 → 화면설계 모드 동기화 이벤트 발생 완료"); // console.log("✅✅✅ FileUpload 위젯 → 화면설계 모드 동기화 이벤트 발생 완료");
// 추가 지연 이벤트들 // 추가 지연 이벤트들
setTimeout(() => { setTimeout(() => {
try { try {
console.log("🔄 FileUpload 위젯 추가 삭제 이벤트 발생 (지연 100ms)"); // console.log("🔄 FileUpload 위젯 추가 삭제 이벤트 발생 (지연 100ms)");
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
detail: { ...eventDetail, delayed: true } detail: { ...eventDetail, delayed: true }
})); }));
} catch (error) { } catch (error) {
console.warn("FileUpload 지연 이벤트 발생 실패:", error); // console.warn("FileUpload 지연 이벤트 발생 실패:", error);
} }
}, 100); }, 100);
setTimeout(() => { setTimeout(() => {
try { try {
console.log("🔄 FileUpload 위젯 추가 삭제 이벤트 발생 (지연 300ms)"); // console.log("🔄 FileUpload 위젯 추가 삭제 이벤트 발생 (지연 300ms)");
window.dispatchEvent(new CustomEvent('globalFileStateChanged', { window.dispatchEvent(new CustomEvent('globalFileStateChanged', {
detail: { ...eventDetail, delayed: true, attempt: 2 } detail: { ...eventDetail, delayed: true, attempt: 2 }
})); }));
} catch (error) { } catch (error) {
console.warn("FileUpload 지연 이벤트 발생 실패:", error); // console.warn("FileUpload 지연 이벤트 발생 실패:", error);
} }
}, 300); }, 300);
} catch (error) { } catch (error) {
console.warn("FileUpload 이벤트 발생 실패:", error); // console.warn("FileUpload 이벤트 발생 실패:", error);
} }
} }
@ -655,9 +655,9 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
uploadedFiles: filteredFiles, uploadedFiles: filteredFiles,
}); });
console.log("✅ 파일 삭제 완료 (attach_file_info.status = DELETED)"); // console.log("✅ 파일 삭제 완료 (attach_file_info.status = DELETED)");
} catch (error) { } catch (error) {
console.error("파일 삭제 실패:", error); // console.error("파일 삭제 실패:", error);
} }
}; };
@ -692,10 +692,10 @@ export function FileUpload({ component, onUpdateComponent, onFileUpload, userInf
if (isImage) { if (isImage) {
// TODO: 이미지 미리보기 모달 열기 // TODO: 이미지 미리보기 모달 열기
console.log("이미지 미리보기:", fileInfo); // console.log("이미지 미리보기:", fileInfo);
} else { } else {
// TODO: 파일 다운로드 // TODO: 파일 다운로드
console.log("파일 다운로드:", fileInfo); // console.log("파일 다운로드:", fileInfo);
} }
}; };

View File

@ -17,7 +17,7 @@ export const ButtonWidget: React.FC<WebTypeComponentProps> = ({
}) => { }) => {
const handleClick = () => { const handleClick = () => {
// 버튼 클릭 시 동작 (추후 버튼 액션 시스템과 연동) // 버튼 클릭 시 동작 (추후 버튼 액션 시스템과 연동)
console.log("Button clicked:", config); // console.log("Button clicked:", config);
// onChange를 통해 클릭 이벤트 전달 // onChange를 통해 클릭 이벤트 전달
if (onChange) { if (onChange) {

View File

@ -54,7 +54,7 @@ export const getWidgetComponentByName = (componentName: string): React.Component
case "RatingWidget": case "RatingWidget":
return RatingWidget; return RatingWidget;
default: default:
console.warn(`알 수 없는 컴포넌트명: ${componentName}, TextWidget으로 폴백`); // console.warn(`알 수 없는 컴포넌트명: ${componentName}, TextWidget으로 폴백`);
return TextWidget; return TextWidget;
} }
}; };

View File

@ -59,9 +59,9 @@ export const useCompanyManagement = () => {
const data = await companyAPI.getList(searchParams); const data = await companyAPI.getList(searchParams);
setCompanies(data); setCompanies(data);
console.log("✅ 실제 DB에서 회사 목록 조회 성공:", data.length, "개"); // console.log("✅ 실제 DB에서 회사 목록 조회 성공:", data.length, "개");
} catch (err) { } catch (err) {
console.error("❌ 회사 목록 조회 실패:", err); // console.error("❌ 회사 목록 조회 실패:", err);
setError(err instanceof Error ? err.message : "회사 목록 조회에 실패했습니다."); setError(err instanceof Error ? err.message : "회사 목록 조회에 실패했습니다.");
setCompanies([]); setCompanies([]);
} finally { } finally {
@ -75,9 +75,9 @@ export const useCompanyManagement = () => {
try { try {
const data = await companyAPI.getAllDiskUsage(); const data = await companyAPI.getAllDiskUsage();
setDiskUsageInfo(data); setDiskUsageInfo(data);
console.log("✅ 디스크 사용량 조회 성공:", data.summary); // console.log("✅ 디스크 사용량 조회 성공:", data.summary);
} catch (err) { } catch (err) {
console.error("❌ 디스크 사용량 조회 실패:", err); // console.error("❌ 디스크 사용량 조회 실패:", err);
// 디스크 사용량 조회 실패는 에러로 처리하지 않음 (선택적 기능) // 디스크 사용량 조회 실패는 에러로 처리하지 않음 (선택적 기능)
} finally { } finally {
setIsDiskUsageLoading(false); setIsDiskUsageLoading(false);

View File

@ -33,19 +33,19 @@ export async function getCompanyList(params?: { company_name?: string; status?:
// 실제 데이터베이스에서 회사 목록 조회하는 엔드포인트 사용 // 실제 데이터베이스에서 회사 목록 조회하는 엔드포인트 사용
const endpoint = `/admin/companies/db${queryString ? `?${queryString}` : ""}`; const endpoint = `/admin/companies/db${queryString ? `?${queryString}` : ""}`;
console.log("🔍 실제 DB에서 회사 목록 조회 API 호출:", endpoint); // console.log("🔍 실제 DB에서 회사 목록 조회 API 호출:", endpoint);
try { try {
const response = await apiClient.get(endpoint); const response = await apiClient.get(endpoint);
if (response.data.success && response.data.data) { if (response.data.success && response.data.data) {
console.log("✅ 실제 DB에서 회사 목록 조회 성공:", response.data.data.length, "개"); // console.log("✅ 실제 DB에서 회사 목록 조회 성공:", response.data.data.length, "개");
return response.data.data; return response.data.data;
} }
throw new Error(response.data.message || "회사 목록 조회에 실패했습니다."); throw new Error(response.data.message || "회사 목록 조회에 실패했습니다.");
} catch (error) { } catch (error) {
console.error("❌ 실제 DB에서 회사 목록 조회 실패:", error); // console.error("❌ 실제 DB에서 회사 목록 조회 실패:", error);
throw error; throw error;
} }
} }
@ -67,16 +67,16 @@ export async function getCompanyInfo(companyCode: string): Promise<Company> {
* *
*/ */
export async function createCompany(formData: CompanyFormData): Promise<Company> { export async function createCompany(formData: CompanyFormData): Promise<Company> {
console.log("회사 등록 요청:", formData); // console.log("회사 등록 요청:", formData);
const response = await apiClient.post("/admin/companies", formData); const response = await apiClient.post("/admin/companies", formData);
if (response.data.success && response.data.data) { if (response.data.success && response.data.data) {
console.log("회사 등록 완료:", { // console.log("회사 등록 완료:", {
code: response.data.data.company_code, // code: response.data.data.company_code,
name: response.data.data.company_name, // name: response.data.data.company_name,
writer: response.data.data.writer, // writer: response.data.data.writer,
}); // });
return response.data.data; return response.data.data;
} }