From 5709342c32d4313cb480d5a4e102585256d9f7b0 Mon Sep 17 00:00:00 2001 From: suzy Date: Thu, 9 Jan 2025 15:19:09 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20main.basic=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 23 + .gitignore | 2 +- .prettierrc | 6 + eslint.config.js | 13 + index.basic.html | 20 +- pnpm-lock.yaml | 1444 +++++++++++++++++++++++++++++++++++++++ src/basic/main.basic.js | 380 +++++------ 7 files changed, 1683 insertions(+), 205 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .prettierrc create mode 100644 eslint.config.js create mode 100644 pnpm-lock.yaml diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..19705f80 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,23 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'prettier', + 'plugin:prettier/recommended', + ], + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 12, + sourceType: 'module', + }, + plugins: ['react', 'prettier'], + rules: { + 'prettier/prettier': 'error', + }, +}; diff --git a/.gitignore b/.gitignore index a547bf36..54f07af5 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,4 @@ dist-ssr *.ntvs* *.njsproj *.sln -*.sw? +*.sw? \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..92f97e75 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5" +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..ac5ec03e --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,13 @@ +import globals from 'globals'; +import pluginJs from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import pluginReact from 'eslint-plugin-react'; + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + { files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'] }, + { languageOptions: { globalns: globals.browser } }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + pluginReact.configs.flat.recommended, +]; diff --git a/index.basic.html b/index.basic.html index 8494659b..0ee95158 100644 --- a/index.basic.html +++ b/index.basic.html @@ -1,13 +1,13 @@ - - - - 장바구니 - - - -
- - + + + + 장바구니 + + + +
+ + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..5a4a3b37 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1444 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@testing-library/jest-dom': + specifier: ^6.5.0 + version: 6.6.3 + '@testing-library/user-event': + specifier: ^14.5.2 + version: 14.5.2(@testing-library/dom@10.4.0) + '@vitest/ui': + specifier: ^2.1.1 + version: 2.1.8(vitest@2.1.8) + jsdom: + specifier: ^25.0.0 + version: 25.0.1 + vite: + specifier: ^5.1.0 + version: 5.4.11 + vitest: + specifier: ^2.1.1 + version: 2.1.8(@vitest/ui@2.1.8)(jsdom@25.0.1) + +packages: + + '@adobe/css-tools@4.4.1': + resolution: {integrity: sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==} + + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@polka/url@1.0.0-next.28': + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + + '@rollup/rollup-android-arm-eabi@4.29.2': + resolution: {integrity: sha512-s/8RiF4bdmGnc/J0N7lHAr5ZFJj+NdJqJ/Hj29K+c4lEdoVlukzvWXB9XpWZCdakVT0YAw8iyIqUP2iFRz5/jA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.29.2': + resolution: {integrity: sha512-mKRlVj1KsKWyEOwR6nwpmzakq6SgZXW4NUHNWlYSiyncJpuXk7wdLzuKdWsRoR1WLbWsZBKvsUCdCTIAqRn9cA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.29.2': + resolution: {integrity: sha512-vJX+vennGwygmutk7N333lvQ/yKVAHnGoBS2xMRQgXWW8tvn46YWuTDOpKroSPR9BEW0Gqdga2DHqz8Pwk6X5w==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.29.2': + resolution: {integrity: sha512-e2rW9ng5O6+Mt3ht8fH0ljfjgSCC6ffmOipiLUgAnlK86CHIaiCdHCzHzmTkMj6vEkqAiRJ7ss6Ibn56B+RE5w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.29.2': + resolution: {integrity: sha512-/xdNwZe+KesG6XJCK043EjEDZTacCtL4yurMZRLESIgHQdvtNyul3iz2Ab03ZJG0pQKbFTu681i+4ETMF9uE/Q==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.29.2': + resolution: {integrity: sha512-eXKvpThGzREuAbc6qxnArHh8l8W4AyTcL8IfEnmx+bcnmaSGgjyAHbzZvHZI2csJ+e0MYddl7DX0X7g3sAuXDQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.29.2': + resolution: {integrity: sha512-h4VgxxmzmtXLLYNDaUcQevCmPYX6zSj4SwKuzY7SR5YlnCBYsmvfYORXgiU8axhkFCDtQF3RW5LIXT8B14Qykg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.29.2': + resolution: {integrity: sha512-EObwZ45eMmWZQ1w4N7qy4+G1lKHm6mcOwDa+P2+61qxWu1PtQJ/lz2CNJ7W3CkfgN0FQ7cBUy2tk6D5yR4KeXw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.29.2': + resolution: {integrity: sha512-Z7zXVHEXg1elbbYiP/29pPwlJtLeXzjrj4241/kCcECds8Zg9fDfURWbZHRIKrEriAPS8wnVtdl4ZJBvZr325w==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.29.2': + resolution: {integrity: sha512-TF4kxkPq+SudS/r4zGPf0G08Bl7+NZcFrUSR3484WwsHgGgJyPQRLCNrQ/R5J6VzxfEeQR9XRpc8m2t7lD6SEQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.29.2': + resolution: {integrity: sha512-kO9Fv5zZuyj2zB2af4KA29QF6t7YSxKrY7sxZXfw8koDQj9bx5Tk5RjH+kWKFKok0wLGTi4bG117h31N+TIBEg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.29.2': + resolution: {integrity: sha512-gIh776X7UCBaetVJGdjXPFurGsdWwHHinwRnC5JlLADU8Yk0EdS/Y+dMO264OjJFo7MXQ5PX4xVFbxrwK8zLqA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.29.2': + resolution: {integrity: sha512-YgikssQ5UNq1GoFKZydMEkhKbjlUq7G3h8j6yWXLBF24KyoA5BcMtaOUAXq5sydPmOPEqB6kCyJpyifSpCfQ0w==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.29.2': + resolution: {integrity: sha512-9ouIR2vFWCyL0Z50dfnon5nOrpDdkTG9lNDs7MRaienQKlTyHcDxplmk3IbhFlutpifBSBr2H4rVILwmMLcaMA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.29.2': + resolution: {integrity: sha512-ckBBNRN/F+NoSUDENDIJ2U9UWmIODgwDB/vEXCPOMcsco1niTkxTXa6D2Y/pvCnpzaidvY2qVxGzLilNs9BSzw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.29.2': + resolution: {integrity: sha512-jycl1wL4AgM2aBFJFlpll/kGvAjhK8GSbEmFT5v3KC3rP/b5xZ1KQmv0vQQ8Bzb2ieFQ0kZFPRMbre/l3Bu9JA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.29.2': + resolution: {integrity: sha512-S2V0LlcOiYkNGlRAWZwwUdNgdZBfvsDHW0wYosYFV3c7aKgEVcbonetZXsHv7jRTTX+oY5nDYT4W6B1oUpMNOg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.29.2': + resolution: {integrity: sha512-pW8kioj9H5f/UujdoX2atFlXNQ9aCfAxFRaa+mhczwcsusm6gGrSo4z0SLvqLF5LwFqFTjiLCCzGkNK/LE0utQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.29.2': + resolution: {integrity: sha512-p6fTArexECPf6KnOHvJXRpAEq0ON1CBtzG/EY4zw08kCHk/kivBc5vUEtnCFNCHOpJZ2ne77fxwRLIKD4wuW2Q==} + cpu: [x64] + os: [win32] + + '@testing-library/dom@10.4.0': + resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.6.3': + resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/user-event@14.5.2': + resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@vitest/expect@2.1.8': + resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} + + '@vitest/mocker@2.1.8': + resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.8': + resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} + + '@vitest/runner@2.1.8': + resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} + + '@vitest/snapshot@2.1.8': + resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} + + '@vitest/spy@2.1.8': + resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} + + '@vitest/ui@2.1.8': + resolution: {integrity: sha512-5zPJ1fs0ixSVSs5+5V2XJjXLmNzjugHRyV11RqxYVR+oMcogZ9qTuSfKW+OcTV0JeFNznI83BNylzH6SSNJ1+w==} + peerDependencies: + vitest: 2.1.8 + + '@vitest/utils@2.1.8': + resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} + + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + chai@5.1.2: + resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} + engines: {node: '>=12'} + + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + cssstyle@4.1.0: + resolution: {integrity: sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==} + engines: {node: '>=18'} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + flatted@3.3.2: + resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + + form-data@4.0.1: + resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + engines: {node: '>= 6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsdom@25.0.1: + resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loupe@3.1.2: + resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nwsapi@2.2.16: + resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} + + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + postcss@8.4.49: + resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + engines: {node: ^10 || ^12 || >=14} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + rollup@4.29.2: + resolution: {integrity: sha512-tJXpsEkzsEzyAKIaB3qv3IuvTVcTN7qBw1jL4SPPXM3vzDrJgiLGFY6+HodgFaUHAJ2RYJ94zV5MKRJCoQzQeA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rrweb-cssom@0.7.1: + resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + sirv@3.0.0: + resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.10: + resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} + engines: {node: '>=12.0.0'} + + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + + tldts-core@6.1.70: + resolution: {integrity: sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==} + + tldts@6.1.70: + resolution: {integrity: sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==} + hasBin: true + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tough-cookie@5.0.0: + resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==} + engines: {node: '>=16'} + + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + + vite-node@2.1.8: + resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.4.11: + resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@2.1.8: + resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.8 + '@vitest/ui': 2.1.8 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.1.0: + resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==} + engines: {node: '>=18'} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + +snapshots: + + '@adobe/css-tools@4.4.1': {} + + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/runtime@7.26.0': + dependencies: + regenerator-runtime: 0.14.1 + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@polka/url@1.0.0-next.28': {} + + '@rollup/rollup-android-arm-eabi@4.29.2': + optional: true + + '@rollup/rollup-android-arm64@4.29.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.29.2': + optional: true + + '@rollup/rollup-darwin-x64@4.29.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.29.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.29.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.29.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.29.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.29.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.29.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.29.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.29.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.29.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.29.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.29.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.29.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.29.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.29.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.29.2': + optional: true + + '@testing-library/dom@10.4.0': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/runtime': 7.26.0 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.6.3': + dependencies: + '@adobe/css-tools': 4.4.1 + aria-query: 5.3.2 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + + '@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)': + dependencies: + '@testing-library/dom': 10.4.0 + + '@types/aria-query@5.0.4': {} + + '@types/estree@1.0.6': {} + + '@vitest/expect@2.1.8': + dependencies: + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.1.2 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.8(vite@5.4.11)': + dependencies: + '@vitest/spy': 2.1.8 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 5.4.11 + + '@vitest/pretty-format@2.1.8': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.8': + dependencies: + '@vitest/utils': 2.1.8 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.8': + dependencies: + '@vitest/pretty-format': 2.1.8 + magic-string: 0.30.17 + pathe: 1.1.2 + + '@vitest/spy@2.1.8': + dependencies: + tinyspy: 3.0.2 + + '@vitest/ui@2.1.8(vitest@2.1.8)': + dependencies: + '@vitest/utils': 2.1.8 + fflate: 0.8.2 + flatted: 3.3.2 + pathe: 1.1.2 + sirv: 3.0.0 + tinyglobby: 0.2.10 + tinyrainbow: 1.2.0 + vitest: 2.1.8(@vitest/ui@2.1.8)(jsdom@25.0.1) + + '@vitest/utils@2.1.8': + dependencies: + '@vitest/pretty-format': 2.1.8 + loupe: 3.1.2 + tinyrainbow: 1.2.0 + + agent-base@7.1.3: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + + assertion-error@2.0.1: {} + + asynckit@0.4.0: {} + + cac@6.7.14: {} + + chai@5.1.2: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.2 + pathval: 2.0.0 + + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + check-error@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + css.escape@1.5.1: {} + + cssstyle@4.1.0: + dependencies: + rrweb-cssom: 0.7.1 + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.0 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + decimal.js@10.4.3: {} + + deep-eql@5.0.2: {} + + delayed-stream@1.0.0: {} + + dequal@2.0.3: {} + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + + entities@4.5.0: {} + + es-module-lexer@1.6.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + + expect-type@1.1.0: {} + + fdir@6.4.2(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fflate@0.8.2: {} + + flatted@3.3.2: {} + + form-data@4.0.1: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + has-flag@4.0.0: {} + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + indent-string@4.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + + js-tokens@4.0.0: {} + + jsdom@25.0.1: + dependencies: + cssstyle: 4.1.0 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.1 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.16 + parse5: 7.2.1 + rrweb-cssom: 0.7.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.0.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.0 + ws: 8.18.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + lodash@4.17.21: {} + + loupe@3.1.2: {} + + lz-string@1.5.0: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + min-indent@1.0.1: {} + + mrmime@2.0.0: {} + + ms@2.1.3: {} + + nanoid@3.3.8: {} + + nwsapi@2.2.16: {} + + parse5@7.2.1: + dependencies: + entities: 4.5.0 + + pathe@1.1.2: {} + + pathval@2.0.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.2: {} + + postcss@8.4.49: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + punycode@2.3.1: {} + + react-is@17.0.2: {} + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + regenerator-runtime@0.14.1: {} + + rollup@4.29.2: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.29.2 + '@rollup/rollup-android-arm64': 4.29.2 + '@rollup/rollup-darwin-arm64': 4.29.2 + '@rollup/rollup-darwin-x64': 4.29.2 + '@rollup/rollup-freebsd-arm64': 4.29.2 + '@rollup/rollup-freebsd-x64': 4.29.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.29.2 + '@rollup/rollup-linux-arm-musleabihf': 4.29.2 + '@rollup/rollup-linux-arm64-gnu': 4.29.2 + '@rollup/rollup-linux-arm64-musl': 4.29.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.29.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.29.2 + '@rollup/rollup-linux-riscv64-gnu': 4.29.2 + '@rollup/rollup-linux-s390x-gnu': 4.29.2 + '@rollup/rollup-linux-x64-gnu': 4.29.2 + '@rollup/rollup-linux-x64-musl': 4.29.2 + '@rollup/rollup-win32-arm64-msvc': 4.29.2 + '@rollup/rollup-win32-ia32-msvc': 4.29.2 + '@rollup/rollup-win32-x64-msvc': 4.29.2 + fsevents: 2.3.3 + + rrweb-cssom@0.7.1: {} + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + siginfo@2.0.0: {} + + sirv@3.0.0: + dependencies: + '@polka/url': 1.0.0-next.28 + mrmime: 2.0.0 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@3.8.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + symbol-tree@3.2.4: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.10: + dependencies: + fdir: 6.4.2(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.0.2: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + + tldts-core@6.1.70: {} + + tldts@6.1.70: + dependencies: + tldts-core: 6.1.70 + + totalist@3.0.1: {} + + tough-cookie@5.0.0: + dependencies: + tldts: 6.1.70 + + tr46@5.0.0: + dependencies: + punycode: 2.3.1 + + vite-node@2.1.8: + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 1.1.2 + vite: 5.4.11 + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.11: + dependencies: + esbuild: 0.21.5 + postcss: 8.4.49 + rollup: 4.29.2 + optionalDependencies: + fsevents: 2.3.3 + + vitest@2.1.8(@vitest/ui@2.1.8)(jsdom@25.0.1): + dependencies: + '@vitest/expect': 2.1.8 + '@vitest/mocker': 2.1.8(vite@5.4.11) + '@vitest/pretty-format': 2.1.8 + '@vitest/runner': 2.1.8 + '@vitest/snapshot': 2.1.8 + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.1.2 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 1.1.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 1.2.0 + vite: 5.4.11 + vite-node: 2.1.8 + why-is-node-running: 2.3.0 + optionalDependencies: + '@vitest/ui': 2.1.8(vitest@2.1.8) + jsdom: 25.0.1 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.1.0: + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + ws@8.18.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} diff --git a/src/basic/main.basic.js b/src/basic/main.basic.js index 0efffa28..483d3bda 100644 --- a/src/basic/main.basic.js +++ b/src/basic/main.basic.js @@ -1,204 +1,196 @@ -var prodList, sel, addBtn, cartDisp, sum, stockInfo; -var lastSel, bonusPts=0, totalAmt=0, itemCnt=0; -function main() { - prodList=[ - {id: 'p1', name: '상품1', val: 10000, q: 50 }, - {id: 'p2', name: '상품2', val: 20000, q: 30 }, - {id: 'p3', name: '상품3', val: 30000, q: 20 }, - {id: 'p4', name: '상품4', val: 15000, q: 0 }, - {id: 'p5', name: '상품5', val: 25000, q: 10 } - ]; - var root=document.getElementById('app'); - let cont=document.createElement('div'); - var wrap=document.createElement('div'); - let hTxt=document.createElement('h1'); - cartDisp=document.createElement('div'); - sum=document.createElement('div'); - sel=document.createElement('select'); - addBtn=document.createElement('button'); - stockInfo=document.createElement('div'); - cartDisp.id='cart-items'; - sum.id='cart-total'; - sel.id='product-select'; - addBtn.id='add-to-cart'; - stockInfo.id='stock-status'; - cont.className='bg-gray-100 p-8'; - wrap.className='max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl p-8'; - hTxt.className='text-2xl font-bold mb-4'; - sum.className='text-xl font-bold my-4'; - sel.className='border rounded p-2 mr-2'; - addBtn.className='bg-blue-500 text-white px-4 py-2 rounded'; - stockInfo.className='text-sm text-gray-500 mt-2'; - hTxt.textContent='장바구니'; - addBtn.textContent='추가'; - updateSelOpts(); - wrap.appendChild(hTxt); - wrap.appendChild(cartDisp); - wrap.appendChild(sum); - wrap.appendChild(sel); - wrap.appendChild(addBtn); - wrap.appendChild(stockInfo); - cont.appendChild(wrap); - root.appendChild(cont); - calcCart(); - setTimeout(function () { - setInterval(function () { - var luckyItem=prodList[Math.floor(Math.random() * prodList.length)]; - if(Math.random() < 0.3 && luckyItem.q > 0) { - luckyItem.val=Math.round(luckyItem.val * 0.8); - alert('번개세일! ' + luckyItem.name + '이(가) 20% 할인 중입니다!'); - updateSelOpts(); - } - }, 30000); - }, Math.random() * 10000); - setTimeout(function () { - setInterval(function () { - if(lastSel) { - var suggest=prodList.find(function (item) { return item.id !== lastSel && item.q > 0; }); - if(suggest) { - alert(suggest.name + '은(는) 어떠세요? 지금 구매하시면 5% 추가 할인!'); - suggest.val=Math.round(suggest.val * 0.95); - updateSelOpts(); - } - } - }, 60000); - }, Math.random() * 20000); +const prodList = [ + { id: 'p1', name: '상품1', val: 10000, q: 50 }, + { id: 'p2', name: '상품2', val: 20000, q: 30 }, + { id: 'p3', name: '상품3', val: 30000, q: 20 }, + { id: 'p4', name: '상품4', val: 15000, q: 0 }, + { id: 'p5', name: '상품5', val: 25000, q: 10 }, +]; + +const itemDiscountRates = { + p1: 0.1, + p2: 0.15, + p3: 0.2, + p4: 0.05, + p5: 0.25, }; -function updateSelOpts() { - sel.innerHTML=''; - prodList.forEach(function (item) { - var opt=document.createElement('option'); - opt.value=item.id; - opt.textContent=item.name + ' - ' + item.val + '원'; - if(item.q === 0) opt.disabled=true; - sel.appendChild(opt); - }); -} -function calcCart() { - totalAmt=0; - itemCnt=0; - var cartItems=cartDisp.children; - var subTot=0; - for (var i=0; i < cartItems.length; i++) { - (function () { - var curItem; - for (var j=0; j < prodList.length; j++) { - if(prodList[j].id === cartItems[i].id) { - curItem=prodList[j]; - break; + +const main = () => ` +
+
+

장바구니

+
+
+ + +
+
+
+`; + +const handleAddCartItem = () => { + const addToCartButton = document.getElementById('add-to-cart'); + const productSelect = document.getElementById('product-select'); + const cartItems = document.getElementById('cart-items'); + + addToCartButton.addEventListener('click', () => { + const selectedProductId = productSelect.value; + const addProduct = prodList.find((item) => item.id === selectedProductId); + + if (addProduct && addProduct.q > 0) { + const cartItem = document.getElementById(addProduct.id); + + if (cartItem) { + const itemQty = + parseInt(cartItem.querySelector('span').textContent.split('x ')[1]) + + 1; + if (itemQty <= addProduct.q) { + const updateText = `${addProduct.name} - ${addProduct.val}원 x ${itemQty}`; + cartItem.querySelector('span').textContent = updateText; + + addProduct.q--; + console.log(`수량: ${itemQty}, 남은 재고: ${addProduct.q}`); + } else { + alert('재고가 부족합니다.'); } + } else { + cartItems.insertAdjacentHTML( + 'beforeend', + createNewCartItem(addProduct) + ); + addProduct.q--; } - var q=parseInt(cartItems[i].querySelector('span').textContent.split('x ')[1]); - var itemTot=curItem.val * q; - var disc=0; - itemCnt += q; - subTot += itemTot; - if(q >= 10) { - if(curItem.id === 'p1') disc=0.1; - else if(curItem.id === 'p2') disc=0.15; - else if(curItem.id === 'p3') disc=0.2; - else if(curItem.id === 'p4') disc=0.05; - else if(curItem.id === 'p5') disc=0.25; - } - totalAmt += itemTot * (1 - disc); - })(); - } - let discRate=0; - if(itemCnt >= 30) { - var bulkDisc=totalAmt * 0.25; - var itemDisc=subTot - totalAmt; - if(bulkDisc > itemDisc) { - totalAmt=subTot * (1 - 0.25); - discRate=0.25; - } else { - discRate=(subTot - totalAmt) / subTot; - } - } else { - discRate=(subTot - totalAmt) / subTot; - } - if(new Date().getDay() === 2) { - totalAmt *= (1 - 0.1); - discRate=Math.max(discRate, 0.1); - } - sum.textContent='총액: ' + Math.round(totalAmt) + '원'; - if(discRate > 0) { - var span=document.createElement('span'); - span.className='text-green-500 ml-2'; - span.textContent='(' + (discRate * 100).toFixed(1) + '% 할인 적용)'; - sum.appendChild(span); - } - updateStockInfo(); - renderBonusPts(); -} -const renderBonusPts=() => { - bonusPts = Math.floor(totalAmt / 1000); - var ptsTag=document.getElementById('loyalty-points'); - if(!ptsTag) { - ptsTag=document.createElement('span'); - ptsTag.id='loyalty-points'; - ptsTag.className='text-blue-500 ml-2'; - sum.appendChild(ptsTag); - } - ptsTag.textContent='(포인트: ' + bonusPts + ')'; -}; -function updateStockInfo() { - var infoMsg=''; - prodList.forEach(function (item) { - if(item.q < 5) {infoMsg += item.name + ': ' + (item.q > 0 ? '재고 부족 ('+item.q+'개 남음)' : '품절') + '\n'; } + calculateCartItem(); }); - stockInfo.textContent=infoMsg; -} -main(); -addBtn.addEventListener('click', function () { - var selItem=sel.value; - var itemToAdd=prodList.find(function (p) { return p.id === selItem; }); - if(itemToAdd && itemToAdd.q > 0) { - var item=document.getElementById(itemToAdd.id); - if(item) { - var newQty=parseInt(item.querySelector('span').textContent.split('x ')[1]) + 1; - if(newQty <= itemToAdd.q) { - item.querySelector('span').textContent=itemToAdd.name + ' - ' + itemToAdd.val + '원 x ' + newQty; - itemToAdd.q--; - } else {alert('재고가 부족합니다.');} - } else { - var newItem=document.createElement('div'); - newItem.id=itemToAdd.id; - newItem.className='flex justify-between items-center mb-2'; - newItem.innerHTML='' + itemToAdd.name + ' - ' + itemToAdd.val + '원 x 1
' + - '' + - '' + - '
'; - cartDisp.appendChild(newItem); - itemToAdd.q--; - } - calcCart(); - lastSel=selItem; - } -}); -cartDisp.addEventListener('click', function (event) { - var tgt=event.target; - if(tgt.classList.contains('quantity-change') || tgt.classList.contains('remove-item')) { - var prodId=tgt.dataset.productId; - var itemElem=document.getElementById(prodId); - var prod=prodList.find(function (p) { return p.id === prodId; }); - if(tgt.classList.contains('quantity-change')) { - var qtyChange=parseInt(tgt.dataset.change); - var newQty=parseInt(itemElem.querySelector('span').textContent.split('x ')[1]) + qtyChange; - if(newQty > 0 && newQty <= prod.q + parseInt(itemElem.querySelector('span').textContent.split('x ')[1])) { - itemElem.querySelector('span').textContent=itemElem.querySelector('span').textContent.split('x ')[0] + 'x ' + newQty; - prod.q -= qtyChange; - } else if(newQty <= 0) { - itemElem.remove(); - prod.q -= qtyChange; +}; + +const createNewCartItem = (addProduct) => ` +
+ ${addProduct.name} - ${addProduct.val}원 x 1 +
+ + + +
+
+`; + +const handleCartItemQuantity = () => { + const cartItems = document.getElementById('cart-items'); + + cartItems.addEventListener('click', (e) => { + const target = e.target; + const countButton = target.classList.contains('quantity-change'); + const removeButton = target.classList.contains('remove-item'); + + const targetProdctId = target.dataset.productId; + const targetProdctIdElem = document.getElementById(targetProdctId); + const currentProduct = prodList.find((prod) => prod.id === targetProdctId); + + const targetChange = parseInt(target.dataset.change); + const initialQty = parseInt( + targetProdctIdElem.querySelector('span').textContent.split('x ')[1] + ); + const changeQty = initialQty + targetChange; + + if (!countButton && !removeButton) return; + + if (countButton) { + if (changeQty > 0 && changeQty <= currentProduct.q + initialQty) { + targetProdctIdElem.querySelector('span').textContent = + targetProdctIdElem.querySelector('span').textContent.split('x ')[0] + + 'x ' + + changeQty; + currentProduct.q -= targetChange; + } else if (changeQty <= 0) { + targetProdctIdElem.remove(); + currentProduct.q -= targetChange; } else { alert('재고가 부족합니다.'); } - } else if(tgt.classList.contains('remove-item')) { - var remQty=parseInt(itemElem.querySelector('span').textContent.split('x ')[1]); - prod.q += remQty; - itemElem.remove(); } - calcCart(); + + if (removeButton) { + currentProduct.q += initialQty; + targetProdctIdElem.remove(); + } + }); +}; + +const calculateCartItem = () => { + let totalAmount = 0; + let totalAmoutDisc = 0; + let totalQuantity = 0; + let discountRate = 0; + const cartItems = document.querySelectorAll('#cart-items > div'); + const cartTotalElem = document.getElementById('cart-total'); + + cartItems.forEach((cartItem) => { + const currentItem = prodList.find((product) => product.id === cartItem.id); + const qty = parseInt( + cartItem.querySelector('span').textContent.split('x ')[1] + ); + const currentItemTotal = currentItem.val * qty; + const discount = qty > 10 ? itemDiscountRates[currentItem.id] || 0 : 0; + + totalQuantity += qty; + totalAmount += currentItemTotal; + totalAmoutDisc += currentItemTotal * (1 - discount); + }); + + let itemDiscount = totalAmount - totalAmoutDisc; + let bulkDiscount = totalAmoutDisc * 0.25; + + if (totalQuantity >= 30 && bulkDiscount > itemDiscount) { + totalAmoutDisc = totalAmount * (1 - 0.25); + discountRate = 0.25; + } else { + discountRate = (totalAmount - totalAmoutDisc) / totalAmount; } -}); \ No newline at end of file + + if (new Date().getDay() === 2) { + totalAmoutDisc *= 1 - 0.1; + discountRate = Math.max(discountRate, 0.1); + } + + cartTotalElem.innerHTML = createCartTotal(totalAmoutDisc, discountRate); + updateStockInfo(); +}; + +const createCartTotal = (totalAmoutDisc, discountRate) => ` + 총액: ${Math.round(totalAmoutDisc)} 원 + (${(discountRate * 100).toFixed( + 1 + )}% 할인 적용) +`; + +const updateStockInfo = () => { + const stockStatusElem = document.getElementById('stock-status'); + const infoMessage = prodList + .filter((item) => item.q < 5) + .map( + (item) => + `${item.name}: ${item.q > 0 ? `재고 부족 (${item.q}개 남음)` : `품절`}` + ) + .join('\n'); + stockStatusElem.textContent = infoMessage; +}; + +const init = () => { + const root = document.getElementById('app'); + root.innerHTML = main(); + handleAddCartItem(); + handleCartItemQuantity(); +}; + +document.addEventListener('DOMContentLoaded', init); From 7d0397779428dbe8835c177102b7a2912eb1ee3f Mon Sep 17 00:00:00 2001 From: suzy Date: Thu, 9 Jan 2025 22:00:26 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 24 +++---- src/basic/__tests__/basic.test.js | 95 ++++++++++++++++--------- src/basic/components/AddToCartButton.js | 35 +++++++++ src/basic/components/CartItems.js | 19 +++++ src/basic/components/CartTotal.js | 5 ++ src/basic/components/CartWrap.js | 29 ++++++++ src/basic/components/Container.js | 9 +++ src/basic/components/Header.js | 5 ++ src/basic/components/ProductSelect.js | 15 ++++ src/basic/components/StockStatus.js | 5 ++ src/basic/main.basic.js | 36 +++++----- src/basic/main.basic1.js | 8 +++ 12 files changed, 218 insertions(+), 67 deletions(-) create mode 100644 src/basic/components/AddToCartButton.js create mode 100644 src/basic/components/CartItems.js create mode 100644 src/basic/components/CartTotal.js create mode 100644 src/basic/components/CartWrap.js create mode 100644 src/basic/components/Container.js create mode 100644 src/basic/components/Header.js create mode 100644 src/basic/components/ProductSelect.js create mode 100644 src/basic/components/StockStatus.js create mode 100644 src/basic/main.basic1.js diff --git a/index.html b/index.html index 694c78b8..c6c5d69c 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,13 @@ - + - - - - 장바구니 - - - -
- - - \ No newline at end of file + + + + 장바구니 + + + +
+ + + diff --git a/src/basic/__tests__/basic.test.js b/src/basic/__tests__/basic.test.js index 1afd4a58..c1a297fe 100644 --- a/src/basic/__tests__/basic.test.js +++ b/src/basic/__tests__/basic.test.js @@ -1,28 +1,35 @@ -import { beforeAll, beforeEach, afterEach, describe, expect, it, vi } from "vitest"; +import { + beforeAll, + beforeEach, + afterEach, + describe, + expect, + it, + vi, +} from 'vitest'; describe('basic test', () => { - describe.each([ - { type: 'origin', loadFile: () => import('../../main.original.js'), }, - { type: 'basic', loadFile: () => import('../main.basic.js'), }, + { type: 'origin', loadFile: () => import('../../main.original.js') }, + { type: 'basic', loadFile: () => import('../main.basic.js') }, ])('$type 장바구니 시나리오 테스트', ({ loadFile }) => { let sel, addBtn, cartDisp, sum, stockInfo; beforeAll(async () => { // DOM 초기화 - document.body.innerHTML='
'; + document.body.innerHTML = '
'; await loadFile(); // 전역 변수 참조 - sel=document.getElementById('product-select'); - addBtn=document.getElementById('add-to-cart'); - cartDisp=document.getElementById('cart-items'); - sum=document.getElementById('cart-total'); - stockInfo=document.getElementById('stock-status'); + sel = document.getElementById('product-select'); + addBtn = document.getElementById('add-to-cart'); + cartDisp = document.getElementById('cart-items'); + sum = document.getElementById('cart-total'); + stockInfo = document.getElementById('stock-status'); }); beforeEach(() => { - vi.useRealTimers() + vi.useRealTimers(); vi.spyOn(window, 'alert').mockImplementation(() => {}); }); @@ -61,46 +68,54 @@ describe('basic test', () => { }); it('상품을 장바구니에 추가할 수 있는지 확인', () => { - sel.value='p1'; + sel.value = 'p1'; addBtn.click(); expect(cartDisp.children.length).toBe(1); - expect(cartDisp.children[0].querySelector('span').textContent).toContain('상품1 - 10000원 x 1'); + expect(cartDisp.children[0].querySelector('span').textContent).toContain( + '상품1 - 10000원 x 1' + ); }); it('장바구니에서 상품 수량을 변경할 수 있는지 확인', () => { - const increaseBtn=cartDisp.querySelector('.quantity-change[data-change="1"]'); + const increaseBtn = cartDisp.querySelector( + '.quantity-change[data-change="1"]' + ); increaseBtn.click(); - expect(cartDisp.children[0].querySelector('span').textContent).toContain('상품1 - 10000원 x 2'); + expect(cartDisp.children[0].querySelector('span').textContent).toContain( + '상품1 - 10000원 x 2' + ); }); it('장바구니에서 상품을 삭제할 수 있는지 확인', () => { - sel.value='p1'; + sel.value = 'p1'; addBtn.click(); - const removeBtn=cartDisp.querySelector('.remove-item'); + const removeBtn = cartDisp.querySelector('.remove-item'); removeBtn.click(); expect(cartDisp.children.length).toBe(0); expect(sum.textContent).toContain('총액: 0원(포인트: 0)'); }); it('총액이 올바르게 계산되는지 확인', () => { - sel.value='p1'; + sel.value = 'p1'; addBtn.click(); addBtn.click(); expect(sum.textContent).toContain('총액: 20000원(포인트: 20)'); }); it('할인이 올바르게 적용되는지 확인', () => { - sel.value='p1'; - for (let i=0; i < 10; i++) { + sel.value = 'p1'; + for (let i = 0; i < 10; i++) { addBtn.click(); } expect(sum.textContent).toContain('(10.0% 할인 적용)'); }); it('포인트가 올바르게 계산되는지 확인', () => { - sel.value='p2'; + sel.value = 'p2'; addBtn.click(); - expect(document.getElementById('loyalty-points').textContent).toContain('(포인트: 128)'); + expect(document.getElementById('loyalty-points').textContent).toContain( + '(포인트: 128)' + ); }); it('번개세일 기능이 정상적으로 동작하는지 확인', () => { @@ -112,39 +127,47 @@ describe('basic test', () => { }); it('화요일 할인이 적용되는지 확인', () => { - const mockDate=new Date('2024-10-15'); // 화요일 - vi.useFakeTimers() + const mockDate = new Date('2024-10-15'); // 화요일 + vi.useFakeTimers(); vi.setSystemTime(mockDate); - sel.value='p1'; + sel.value = 'p1'; addBtn.click(); - expect(document.getElementById('cart-total').textContent).toContain('(10.0% 할인 적용)'); + expect(document.getElementById('cart-total').textContent).toContain( + '(10.0% 할인 적용)' + ); }); it('재고가 부족한 경우 추가되지 않는지 확인', () => { // p4 상품 선택 (재고 없음) - sel.value='p4'; + sel.value = 'p4'; addBtn.click(); // p4 상품이 장바구니에 없는지 확인 - const p4InCart=Array.from(cartDisp.children).some(item => item.id === 'p4'); + const p4InCart = Array.from(cartDisp.children).some( + (item) => item.id === 'p4' + ); expect(p4InCart).toBe(false); expect(stockInfo.textContent).toContain('상품4: 품절'); }); it('재고가 부족한 경우 추가되지 않고 알림이 표시되는지 확인', () => { - sel.value='p5'; + sel.value = 'p5'; addBtn.click(); // p5 상품이 장바구니에 추가되었는지 확인 - const p5InCart=Array.from(cartDisp.children).some(item => item.id === 'p5'); + const p5InCart = Array.from(cartDisp.children).some( + (item) => item.id === 'p5' + ); expect(p5InCart).toBe(true); // 수량 증가 버튼 찾기 - const increaseBtn=cartDisp.querySelector('#p5 .quantity-change[data-change="1"]'); + const increaseBtn = cartDisp.querySelector( + '#p5 .quantity-change[data-change="1"]' + ); expect(increaseBtn).not.toBeNull(); // 수량을 10번 증가시키기 - for (let i=0; i < 10; i++) { + for (let i = 0; i < 10; i++) { increaseBtn.click(); } @@ -152,14 +175,16 @@ describe('basic test', () => { increaseBtn.click(); // 재고 부족 알림이 표시되었는지 확인 - expect(window.alert).toHaveBeenCalledWith(expect.stringContaining('재고가 부족합니다')); + expect(window.alert).toHaveBeenCalledWith( + expect.stringContaining('재고가 부족합니다') + ); // 장바구니의 상품 수량이 10개인지 확인 - const itemQuantity=cartDisp.querySelector('#p5 span').textContent; + const itemQuantity = cartDisp.querySelector('#p5 span').textContent; expect(itemQuantity).toContain('x 10'); // 재고 상태 정보에 해당 상품이 재고 부족으로 표시되는지 확인 expect(stockInfo.textContent).toContain('상품5: 품절'); }); }); -}); \ No newline at end of file +}); diff --git a/src/basic/components/AddToCartButton.js b/src/basic/components/AddToCartButton.js new file mode 100644 index 00000000..bde21ee0 --- /dev/null +++ b/src/basic/components/AddToCartButton.js @@ -0,0 +1,35 @@ +import { CartItems } from './CartItems'; + +export const AddToCartButton = ({ productList, cartItems }) => { + document.getElementById('app').addEventListener('click', (e) => { + if (e.target.id === 'add-to-cart') { + const selectedProductId = document.getElementById('product-select').value; + const addedProduct = productList.find( + (item) => item.id === selectedProductId + ); + + if (addedProduct && addedProduct.q > 0) { + const cartItem = document.getElementById(addedProduct.id); + if (cartItem) { + const itemQty = + parseInt( + cartItem.querySelector('span').textContent.split('x ')[1] + ) + 1; + console.log(itemQty); + } else { + const newCartItem = { + id: addedProduct.id, + name: addedProduct.name, + val: addedProduct.val, + q: 1, + }; + cartItems.add(newCartItem); + CartItems({ cartItems }); + } + } + } + }); + return ` + + `; +}; diff --git a/src/basic/components/CartItems.js b/src/basic/components/CartItems.js new file mode 100644 index 00000000..039bba48 --- /dev/null +++ b/src/basic/components/CartItems.js @@ -0,0 +1,19 @@ +export const CartItems = ({ cartItems }) => { + return ` +
+ ${[...cartItems].map((cartItem) => { + console.log(cartItem); + return ` +
+ ${cartItem.name} - ${cartItem.val}원 x 1 +
+ + + +
+
+ `; + })} +
+ `; +}; diff --git a/src/basic/components/CartTotal.js b/src/basic/components/CartTotal.js new file mode 100644 index 00000000..9e0b11e5 --- /dev/null +++ b/src/basic/components/CartTotal.js @@ -0,0 +1,5 @@ +export const CartTotal = () => { + return ` +
+ `; +}; diff --git a/src/basic/components/CartWrap.js b/src/basic/components/CartWrap.js new file mode 100644 index 00000000..8453e426 --- /dev/null +++ b/src/basic/components/CartWrap.js @@ -0,0 +1,29 @@ +import { AddToCartButton } from './AddToCartButton'; +import { CartItems } from './CartItems'; +import { CartTotal } from './CartTotal'; +import { Header } from './Header'; +import { ProductSelect } from './ProductSelect'; +import { StockStatus } from './StockStatus'; + +export const CartWrap = () => { + const PRODUCT_LIST = [ + { id: 'p1', name: '상품1', val: 10000, q: 50 }, + { id: 'p2', name: '상품2', val: 20000, q: 30 }, + { id: 'p3', name: '상품3', val: 30000, q: 20 }, + { id: 'p4', name: '상품4', val: 15000, q: 0 }, + { id: 'p5', name: '상품5', val: 25000, q: 10 }, + ]; + + const CART_ITEMS = new Set(); + + return ` +
+ ${Header()} + ${CartItems({ cartItems: CART_ITEMS })} + ${CartTotal()} + ${ProductSelect({ productList: PRODUCT_LIST })} + ${AddToCartButton({ productList: PRODUCT_LIST, cartItems: CART_ITEMS })} + ${StockStatus()} +
+ `; +}; diff --git a/src/basic/components/Container.js b/src/basic/components/Container.js new file mode 100644 index 00000000..6f86aa9a --- /dev/null +++ b/src/basic/components/Container.js @@ -0,0 +1,9 @@ +import { CartWrap } from './CartWrap'; + +export const Container = () => { + return ` +
+ ${CartWrap()} +
+ `; +}; diff --git a/src/basic/components/Header.js b/src/basic/components/Header.js new file mode 100644 index 00000000..06f20587 --- /dev/null +++ b/src/basic/components/Header.js @@ -0,0 +1,5 @@ +export const Header = () => { + return ` +

장바구니

+ `; +}; diff --git a/src/basic/components/ProductSelect.js b/src/basic/components/ProductSelect.js new file mode 100644 index 00000000..02bd9c93 --- /dev/null +++ b/src/basic/components/ProductSelect.js @@ -0,0 +1,15 @@ +export const ProductSelect = ({ productList }) => { + return ` + + `; +}; diff --git a/src/basic/components/StockStatus.js b/src/basic/components/StockStatus.js new file mode 100644 index 00000000..1cc8ff66 --- /dev/null +++ b/src/basic/components/StockStatus.js @@ -0,0 +1,5 @@ +export const StockStatus = () => { + return ` +
+ `; +}; diff --git a/src/basic/main.basic.js b/src/basic/main.basic.js index 483d3bda..552f8901 100644 --- a/src/basic/main.basic.js +++ b/src/basic/main.basic.js @@ -24,9 +24,9 @@ const main = () => ` ${prodList .map( (item) => ` - + ` ) .join('')} @@ -44,30 +44,30 @@ const handleAddCartItem = () => { addToCartButton.addEventListener('click', () => { const selectedProductId = productSelect.value; - const addProduct = prodList.find((item) => item.id === selectedProductId); + const addedProduct = prodList.find((item) => item.id === selectedProductId); - if (addProduct && addProduct.q > 0) { - const cartItem = document.getElementById(addProduct.id); + if (addedProduct && addedProduct.q > 0) { + const cartItem = document.getElementById(addedProduct.id); if (cartItem) { const itemQty = parseInt(cartItem.querySelector('span').textContent.split('x ')[1]) + 1; - if (itemQty <= addProduct.q) { - const updateText = `${addProduct.name} - ${addProduct.val}원 x ${itemQty}`; + if (itemQty <= addedProduct.q) { + const updateText = `${addedProduct.name} - ${addedProduct.val}원 x ${itemQty}`; cartItem.querySelector('span').textContent = updateText; - addProduct.q--; - console.log(`수량: ${itemQty}, 남은 재고: ${addProduct.q}`); + addedProduct.q--; + console.log(`수량: ${itemQty}, 남은 재고: ${addedProduct.q}`); } else { alert('재고가 부족합니다.'); } } else { cartItems.insertAdjacentHTML( 'beforeend', - createNewCartItem(addProduct) + createNewCartItem(addedProduct) ); - addProduct.q--; + addedProduct.q--; } } calculateCartItem(); @@ -186,11 +186,7 @@ const updateStockInfo = () => { stockStatusElem.textContent = infoMessage; }; -const init = () => { - const root = document.getElementById('app'); - root.innerHTML = main(); - handleAddCartItem(); - handleCartItemQuantity(); -}; - -document.addEventListener('DOMContentLoaded', init); +const root = document.getElementById('app'); +root.innerHTML = main(); +handleAddCartItem(); +handleCartItemQuantity(); diff --git a/src/basic/main.basic1.js b/src/basic/main.basic1.js new file mode 100644 index 00000000..681c3af7 --- /dev/null +++ b/src/basic/main.basic1.js @@ -0,0 +1,8 @@ +import { Container } from './components/Container'; + +export default function main() { + const root = document.getElementById('app'); + root.innerHTML = Container(); +} + +main(); From ec816e1af4285eae9f2e1b8a432c37029e367cc1 Mon Sep 17 00:00:00 2001 From: suzy Date: Fri, 10 Jan 2025 00:40:02 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EA=B8=B0=EB=B3=B8=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=9C=20=ED=86=B5=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.advanced.html | 21 +++++++++--------- src/basic/main.basic.js | 49 +++++++++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/index.advanced.html b/index.advanced.html index 1d45f718..aa7b179d 100644 --- a/index.advanced.html +++ b/index.advanced.html @@ -1,13 +1,14 @@ - - - - 장바구니 - - - -
- - + + + + 장바구니 + + + +
+ + + diff --git a/src/basic/main.basic.js b/src/basic/main.basic.js index 552f8901..84d0aa92 100644 --- a/src/basic/main.basic.js +++ b/src/basic/main.basic.js @@ -14,7 +14,7 @@ const itemDiscountRates = { p5: 0.25, }; -const main = () => ` +const createMainContainer = () => `

장바구니

@@ -124,12 +124,14 @@ const handleCartItemQuantity = () => { currentProduct.q += initialQty; targetProdctIdElem.remove(); } + + calculateCartItem(); }); }; const calculateCartItem = () => { let totalAmount = 0; - let totalAmoutDisc = 0; + let totalAmountDisc = 0; let totalQuantity = 0; let discountRate = 0; const cartItems = document.querySelectorAll('#cart-items > div'); @@ -145,34 +147,38 @@ const calculateCartItem = () => { totalQuantity += qty; totalAmount += currentItemTotal; - totalAmoutDisc += currentItemTotal * (1 - discount); + totalAmountDisc += currentItemTotal * (1 - discount); }); - let itemDiscount = totalAmount - totalAmoutDisc; - let bulkDiscount = totalAmoutDisc * 0.25; + let itemDiscount = totalAmount - totalAmountDisc; + let bulkDiscount = totalAmountDisc * 0.25; if (totalQuantity >= 30 && bulkDiscount > itemDiscount) { - totalAmoutDisc = totalAmount * (1 - 0.25); + totalAmountDisc = totalAmount * (1 - 0.25); discountRate = 0.25; } else { - discountRate = (totalAmount - totalAmoutDisc) / totalAmount; + discountRate = (totalAmount - totalAmountDisc) / totalAmount; } if (new Date().getDay() === 2) { - totalAmoutDisc *= 1 - 0.1; + totalAmountDisc *= 1 - 0.1; discountRate = Math.max(discountRate, 0.1); } - cartTotalElem.innerHTML = createCartTotal(totalAmoutDisc, discountRate); updateStockInfo(); + cartTotalElem.innerHTML = createTotal(totalAmountDisc, discountRate); }; -const createCartTotal = (totalAmoutDisc, discountRate) => ` - 총액: ${Math.round(totalAmoutDisc)} 원 - (${(discountRate * 100).toFixed( - 1 - )}% 할인 적용) -`; +const createTotal = (totalAmountDisc, discountRate) => + `총액: ${Math.round(totalAmountDisc)}원${ + discountRate > 0 + ? `(${(discountRate * 100).toFixed( + 1 + )}% 할인 적용)` + : `` + }(포인트: ${Math.floor( + totalAmountDisc / 1000 + )})`; const updateStockInfo = () => { const stockStatusElem = document.getElementById('stock-status'); @@ -186,7 +192,12 @@ const updateStockInfo = () => { stockStatusElem.textContent = infoMessage; }; -const root = document.getElementById('app'); -root.innerHTML = main(); -handleAddCartItem(); -handleCartItemQuantity(); +const init = () => { + const root = document.getElementById('app'); + root.innerHTML = createMainContainer(); + calculateCartItem(); + handleAddCartItem(); + handleCartItemQuantity(); +}; + +init(); From a66b317af1a26cd19991e95940075e1ce2e5f3ae Mon Sep 17 00:00:00 2001 From: suzy Date: Fri, 10 Jan 2025 01:14:00 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20main=20basic=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/basic/main.basic.js | 193 ++++++++++++++++++++-------------------- 1 file changed, 96 insertions(+), 97 deletions(-) diff --git a/src/basic/main.basic.js b/src/basic/main.basic.js index 84d0aa92..76409a43 100644 --- a/src/basic/main.basic.js +++ b/src/basic/main.basic.js @@ -1,4 +1,4 @@ -const prodList = [ +const PRODUCT_LIST = [ { id: 'p1', name: '상품1', val: 10000, q: 50 }, { id: 'p2', name: '상품2', val: 20000, q: 30 }, { id: 'p3', name: '상품3', val: 30000, q: 20 }, @@ -6,7 +6,7 @@ const prodList = [ { id: 'p5', name: '상품5', val: 25000, q: 10 }, ]; -const itemDiscountRates = { +const DISCOUNT_RATES = { p1: 0.1, p2: 0.15, p3: 0.2, @@ -21,15 +21,13 @@ const createMainContainer = () => `
@@ -38,40 +36,38 @@ const createMainContainer = () => ` `; const handleAddCartItem = () => { - const addToCartButton = document.getElementById('add-to-cart'); - const productSelect = document.getElementById('product-select'); - const cartItems = document.getElementById('cart-items'); - - addToCartButton.addEventListener('click', () => { - const selectedProductId = productSelect.value; - const addedProduct = prodList.find((item) => item.id === selectedProductId); - - if (addedProduct && addedProduct.q > 0) { - const cartItem = document.getElementById(addedProduct.id); - - if (cartItem) { - const itemQty = - parseInt(cartItem.querySelector('span').textContent.split('x ')[1]) + - 1; - if (itemQty <= addedProduct.q) { - const updateText = `${addedProduct.name} - ${addedProduct.val}원 x ${itemQty}`; - cartItem.querySelector('span').textContent = updateText; - - addedProduct.q--; - console.log(`수량: ${itemQty}, 남은 재고: ${addedProduct.q}`); - } else { - alert('재고가 부족합니다.'); - } - } else { - cartItems.insertAdjacentHTML( - 'beforeend', - createNewCartItem(addedProduct) - ); + const $productSelect = document.getElementById('product-select'); + const $cartItems = document.getElementById('cart-items'); + + const selectedProductId = $productSelect.value; + const addedProduct = PRODUCT_LIST.find( + (item) => item.id === selectedProductId + ); + + if (addedProduct && addedProduct.q > 0) { + const cartItem = document.getElementById(addedProduct.id); + + if (cartItem) { + const itemQty = + parseInt(cartItem.querySelector('span').textContent.split('x ')[1]) + 1; + if (itemQty <= addedProduct.q) { + const updateText = `${addedProduct.name} - ${addedProduct.val}원 x ${itemQty}`; + cartItem.querySelector('span').textContent = updateText; + addedProduct.q--; + console.log(`수량: ${itemQty}, 남은 재고: ${addedProduct.q}`); + } else { + alert('재고가 부족합니다.'); } + } else { + $cartItems.insertAdjacentHTML( + 'beforeend', + createNewCartItem(addedProduct) + ); + addedProduct.q--; } - calculateCartItem(); - }); + } + calculateCartItem(); }; const createNewCartItem = (addProduct) => ` @@ -85,48 +81,46 @@ const createNewCartItem = (addProduct) => `
`; -const handleCartItemQuantity = () => { - const cartItems = document.getElementById('cart-items'); - - cartItems.addEventListener('click', (e) => { - const target = e.target; - const countButton = target.classList.contains('quantity-change'); - const removeButton = target.classList.contains('remove-item'); - - const targetProdctId = target.dataset.productId; - const targetProdctIdElem = document.getElementById(targetProdctId); - const currentProduct = prodList.find((prod) => prod.id === targetProdctId); - - const targetChange = parseInt(target.dataset.change); - const initialQty = parseInt( - targetProdctIdElem.querySelector('span').textContent.split('x ')[1] - ); - const changeQty = initialQty + targetChange; - - if (!countButton && !removeButton) return; - - if (countButton) { - if (changeQty > 0 && changeQty <= currentProduct.q + initialQty) { - targetProdctIdElem.querySelector('span').textContent = - targetProdctIdElem.querySelector('span').textContent.split('x ')[0] + - 'x ' + - changeQty; - currentProduct.q -= targetChange; - } else if (changeQty <= 0) { - targetProdctIdElem.remove(); - currentProduct.q -= targetChange; - } else { - alert('재고가 부족합니다.'); - } - } - - if (removeButton) { - currentProduct.q += initialQty; +const handleCartItemQuantity = (e) => { + const target = e.target; + const countButton = target.classList.contains('quantity-change'); + const removeButton = target.classList.contains('remove-item'); + + const targetProdctId = target.dataset.productId; + const targetProdctIdElem = document.getElementById(targetProdctId); + const currentProduct = PRODUCT_LIST.find( + (prod) => prod.id === targetProdctId + ); + + const targetChange = parseInt(target.dataset.change); + const initialQty = parseInt( + targetProdctIdElem.querySelector('span').textContent.split('x ')[1] + ); + const changeQty = initialQty + targetChange; + + if (!countButton && !removeButton) return; + + if (countButton) { + if (changeQty > 0 && changeQty <= currentProduct.q + initialQty) { + targetProdctIdElem.querySelector('span').textContent = + targetProdctIdElem.querySelector('span').textContent.split('x ')[0] + + 'x ' + + changeQty; + currentProduct.q -= targetChange; + } else if (changeQty <= 0) { targetProdctIdElem.remove(); + currentProduct.q -= targetChange; + } else { + alert('재고가 부족합니다.'); } + } - calculateCartItem(); - }); + if (removeButton) { + currentProduct.q += initialQty; + targetProdctIdElem.remove(); + } + + calculateCartItem(); }; const calculateCartItem = () => { @@ -138,12 +132,14 @@ const calculateCartItem = () => { const cartTotalElem = document.getElementById('cart-total'); cartItems.forEach((cartItem) => { - const currentItem = prodList.find((product) => product.id === cartItem.id); + const currentItem = PRODUCT_LIST.find( + (product) => product.id === cartItem.id + ); const qty = parseInt( cartItem.querySelector('span').textContent.split('x ')[1] ); const currentItemTotal = currentItem.val * qty; - const discount = qty > 10 ? itemDiscountRates[currentItem.id] || 0 : 0; + const discount = qty > 10 ? DISCOUNT_RATES[currentItem.id] || 0 : 0; totalQuantity += qty; totalAmount += currentItemTotal; @@ -169,21 +165,21 @@ const calculateCartItem = () => { cartTotalElem.innerHTML = createTotal(totalAmountDisc, discountRate); }; -const createTotal = (totalAmountDisc, discountRate) => - `총액: ${Math.round(totalAmountDisc)}원${ - discountRate > 0 - ? `(${(discountRate * 100).toFixed( - 1 - )}% 할인 적용)` - : `` - }(포인트: ${Math.floor( - totalAmountDisc / 1000 - )})`; +const createTotal = (totalAmountDisc, discountRate) => ` + 총액: ${Math.round(totalAmountDisc)}원${ + discountRate > 0 + ? `(${(discountRate * 100).toFixed( + 1 + )}% 할인 적용)` + : `` +}(포인트: ${Math.floor( + totalAmountDisc / 1000 +)}) +`; const updateStockInfo = () => { const stockStatusElem = document.getElementById('stock-status'); - const infoMessage = prodList - .filter((item) => item.q < 5) + const infoMessage = PRODUCT_LIST.filter((item) => item.q < 5) .map( (item) => `${item.name}: ${item.q > 0 ? `재고 부족 (${item.q}개 남음)` : `품절`}` @@ -193,11 +189,14 @@ const updateStockInfo = () => { }; const init = () => { - const root = document.getElementById('app'); - root.innerHTML = createMainContainer(); + const $addToCartButton = document.getElementById('add-to-cart'); + const $cartItems = document.getElementById('cart-items'); + + $addToCartButton.addEventListener('click', handleAddCartItem); + $cartItems.addEventListener('click', handleCartItemQuantity); calculateCartItem(); - handleAddCartItem(); - handleCartItemQuantity(); }; +const $root = document.getElementById('app'); +$root.innerHTML = createMainContainer(); init(); From eb6862917066044b9c89de7eab46415d0db58b92 Mon Sep 17 00:00:00 2001 From: ddudiscoool <107531556+devsuzy@users.noreply.github.com> Date: Thu, 20 Feb 2025 15:48:00 +0900 Subject: [PATCH 5/6] Update README.md --- README.md | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c2f44a61..f05213d2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,26 @@ -# Chapter 2-1. 클린코드와 리팩토링 +# 항해 플러스 프론트엔드 4기 과제 4주차
:Chapter 2-1. 클린코드와 리팩토링 +- [과제 설명](#과제-설명) + - [기본과제: 더티코드 개선](#기본과제-더티코드-개선) + - [심화과제: 유지보수 하기 좋은 코드만들기](#심화과제-유지보수-하기-좋은-코드-만들기) +- [과제 체크포인트](#과제-체크포인트) + - [기본과제](#기본과제) + - [심화과제](#심화과제) +- [과제 셀프회고](#과제-셀프회고) + - [과제에서 좋았던 부분](#과제에서-좋았던-부분) + - [과제를 하면서 새롭게 알게된 점](#과제를-하면서-새롭게-알게된-점) + - [과제를 진행하면서 아직 애매하게 잘 모르겠다 하는 점, 혹은 뭔가 잘 안되서 아쉬운 것들](#과제를-진행하면서-아직-애매하게-잘-모르겠다-하는-점-혹은-뭔가-잘-안되서-아쉬운-것들) +- [리뷰 받고 싶은 내용이나 궁금한 것에 대한 질문](#리뷰-받고-싶은-내용이나-궁금한-것에-대한-질문) -## 기본과제: 더티코드 개선 +## 과제 설명 -이번 과제는 더티코드를 클린코드의 형태로 개선을 하는 과제입니다. 주어진 테스트를 통과하면서 원래 기능과 동일한 동작을 하는 코드를 만들어주세요. basic과제는 제공되는 더티코드를 클린코드와 리팩토링 원칙에 입각해서 더 나은 코드로 만들어보세요. 주어진 테스트를 참고삼아 좋은 이름, 좋은 모양, 좋은 구조를 가지는 코드로 만들어 보세요. +### 기본과제: 더티코드 개선 -[필수조건] +이번 과제는 더티코드를 클린코드의 형태로 개선을 하는 과제입니다.
+주어진 테스트를 통과하면서 원래 기능과 동일한 동작을 하는 코드를 만들어주세요.
+basic과제는 제공되는 더티코드를 클린코드와 리팩토링 원칙에 입각해서 더 나은 코드로 만들어보세요.
+주어진 테스트를 참고삼아 좋은 이름, 좋은 모양, 좋은 구조를 가지는 코드로 만들어 보세요. + +#### [필수조건] - Prettier와 ESLint를 설치해서 적용할 것 - 테스트 코드 모두 통과할 것 @@ -35,7 +51,109 @@ - 장바구니 내역 조회 기능 - 총액 계산 기능 -## 심확과제: 유지보수 하기 좋은 코드만들기 +### 심화과제: 유지보수 하기 좋은 코드 만들기 + +심화과제는 **기본과제에서 작성한 코드를 기술고도화를 하는 것입니다.**
+바닐라 자바스크립트로 되어 있는 코드를 유지보수하기에 유리한 기술스택(React + Typescript)으로 고도화 리팩토링을 진행해주세요.
+우리의 목표는 앞으로 유지보수를 더 잘할 수 있도록 하기 위함입니다.
+최소 React와 Typescript를 이용한 코드로 개선해주세요. 그 밖의 기술선택과 폴더/파일 구조, 테스트 코드등은 자유입니다. + +## 과제 체크포인트 + +### 기본과제 + +- [x] 코드가 Prettier를 통해 일관된 포맷팅이 적용되어 있는가? +- [x] 적절한 줄바꿈과 주석을 사용하여 코드의 논리적 단위를 명확히 구분했는가? +- [x] 변수명과 함수명이 그 역할을 명확히 나타내며, 일관된 네이밍 규칙을 따르는가? +- [x] 매직 넘버와 문자열을 의미 있는 상수로 추출했는가? +- [x] 중복 코드를 제거하고 재사용 가능한 형태로 리팩토링했는가? +- [x] 함수가 단일 책임 원칙을 따르며, 한 가지 작업만 수행하는가? +- [x] 조건문과 반복문이 간결하고 명확한가? 복잡한 조건을 함수로 추출했는가? +- [x] 코드의 배치가 의존성과 실행 흐름에 따라 논리적으로 구성되어 있는가? +- [x] 연관된 코드를 의미 있는 함수나 모듈로 그룹화했는가? +- [x] ES6+ 문법을 활용하여 코드를 더 간결하고 명확하게 작성했는가? +- [x] 전역 상태와 부수 효과(side effects)를 최소화했는가? +- [x] 에러 처리와 예외 상황을 명확히 고려하고 처리했는가? +- [x] 코드 자체가 자기 문서화되어 있어, 주석 없이도 의도를 파악할 수 있는가? +- [x] 비즈니스 로직과 UI 로직이 적절히 분리되어 있는가? +- [x] 코드의 각 부분이 테스트 가능하도록 구조화되어 있는가? +- [x] 성능 개선을 위해 불필요한 연산이나 렌더링을 제거했는가? +- [x] 새로운 기능 추가나 변경이 기존 코드에 미치는 영향을 최소화했는가? +- [x] 리팩토링 시 기존 기능을 그대로 유지하면서 점진적으로 개선했는가? +- [x] 코드 리뷰를 통해 다른 개발자들의 피드백을 반영하고 개선했는가? + +### 심화과제 + +- [ ] 변경한 구조와 코드가 기존의 코드보다 가독성이 높고 이해하기 쉬운가? +- [ ] 변경한 구조와 코드가 기존의 코드보다 기능을 수정하거나 확장하기에 용이한가? +- [ ] 변경한 구조와 코드가 기존의 코드보다 테스트를 하기에 더 용이한가? +- [ ] 변경한 구조와 코드가 기존의 모든 기능은 그대로 유지했는가? +- [ ] 변경한 구조와 코드를 새로운 한번에 새로만들지 않고 점진적으로 개선했는가? + + +## 과제 셀프회고 + +### 과제에서 좋았던 부분 + +> 🤩 클린코드와 리팩토링의 중요성 + +중요하다는 걸 알지만 평소에 기능 구현에 급급해 무심코 지나쳤던 클린코드와 리팩토링의 중요성을 다시금 깨닫게 되었습니다. + +> 👫 팀원들과의 소통을 통해 서로의 생각 공유 + +하나의 코드를 가지고 팀원분들과 클린코드에 대해 자유롭게 대화를 나누다 보니 좋은 인사이트를 많이 얻었습니다. + +"나는 이렇게 생각했는데, 이렇게 생각 해볼 수도 있구나." +"나와 똑같이 생각하셨네! 신기하다!" +"이건 생각도 못 했는데, 되게 예리하시다!" + +이렇게 다른 분들의 의견에 공감도 얻고 새로운 것도 알게되어 재밌고 유익했던 시간이었습니다. + +물론 논쟁거리가 될 만한 부분에서는 서로 의견 충돌이 있어 합의하는데 시간이 많이 소요되지만,
+이러한 과정을 통해 같이 좋은 방향으로 성장하고 있다는 느낌을 받았습니다. + + +### 과제를 하면서 새롭게 알게된 점 + +> 🍎 순수함수 만들기 + +- 함수는 한 가지 작업만 수행하도록 한다. +- 함수의 길이를 20-30줄 이내로 유지한다. +- 함수 하나에 조건문이나 반복문이 중첩되지 않도록 한다. +- 사이드 이펙트를 최소화 한다. +- 추상화 수준을 일관되게 유지한다. + +> 🍏 의미있는 이름 부여하기 + +- 엘리먼트 객체는 변수명 앞에 `$`를 붙인다. +- boolean 반환값을 출력하는 변수는 is~ 또는 has~로 시작한다. +- 이벤트 핸들러는 on~ 또는 handle~로 시작한다. +- 변수 앞에 붙이면 좋은 단어들 +image (2) +image (3) + + +### 과제를 진행하면서 아직 애매하게 잘 모르겠다 하는 점, 혹은 뭔가 잘 안되서 아쉬운 것들 + +> 🙉 리액트처럼 리팩토링 하기 + +일단 과제를 받자 마자 main.basic 안에 더티 코드들을 정리하는게 우선이고,
+그 다음으로 리액트처럼 리팩토링 해야겠다고 계획을 세웠습니다. + +그래서 어느정도 코드 정리를 한 뒤 component 폴더를 만들어 구현 기능을 기준으로 작은 단위로 컴포넌트를 쪼갠 뒤
+최대한 리액트처럼 구현하기 위해 선언형 방식으로 UI가 보여지게끔 했습니다. + +하지만 저는 자바스크립트와 리액트 둘 다 능숙하지 않다 보니 구현하는 과정에서 구동 방식이 헷갈려 혼돈이 오고 말았습니다..
+결국엔 일단 폴더 분리만 해놓은 상태에서 리액트처럼 리팩토링 하는 걸 포기하고,
+(해당 코드들은 component 폴더와 main.basic1.js에 남아있습니다.) + +한 파일 안에서 구현한 main.basic.js를 제출하게 되었습니다.🥲
+그 마저도 만족스럽게 클린코드를 끝 마치지 못해 이도저도 못한 것 같은 아쉬움이 많이 남아있습니다..😖 + +## 리뷰 받고 싶은 내용이나 궁금한 것에 대한 질문 + +기존 더티 코드에 변수가 너무 많다보니 변수 네이밍 짓기, 전역변수 줄이기 등에서 많이 고민했던 것 같습니다.
+최대한 전역변수를 사용하지 않기 위해 전역변수를 모두 지우고 지역변수로만 구현했습니다. (엘리멘트를 담은 변수들까지) -심화과제는 **기본과제에서 작성한 코드를 기술고도화를 하는 것입니다.** 바닐라 자바스크립트로 되어 있는 코드를 유지보수하기에 유리한 기술스택(React + Typescript)으로 고도화 리팩토링을 진행해주세요. -우리의 목표는 앞으로 유지보수를 더 잘할 수 있도록 하기 위함입니다. 최소 React와 Typescript를 이용한 코드로 개선해주세요. 그 밖의 기술선택과 폴더/파일 구조, 테스트 코드등은 자유입니다. \ No newline at end of file +예전에 자바스크립트를 배울 때 전역변수의 사용을 지양하는 게 좋다고 인식이 생겨서
+전역변수 선언과 참조를 가급적으로 피하고 있는데, 코치님은 어떻게 생각하시는지 궁금합니다! From 2f3fb88cbdf725d9bd7cb2b7e84bb259aa61f3d4 Mon Sep 17 00:00:00 2001 From: ddudiscoool <107531556+devsuzy@users.noreply.github.com> Date: Thu, 20 Feb 2025 15:48:49 +0900 Subject: [PATCH 6/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f05213d2..76d42264 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 항해 플러스 프론트엔드 4기 과제 4주차
:Chapter 2-1. 클린코드와 리팩토링 +# 항해 플러스 프론트엔드 4기 과제 4주차
: Chapter 2-1. 클린코드와 리팩토링 - [과제 설명](#과제-설명) - [기본과제: 더티코드 개선](#기본과제-더티코드-개선) - [심화과제: 유지보수 하기 좋은 코드만들기](#심화과제-유지보수-하기-좋은-코드-만들기)