From 018e18272b917a563fe2cdda275accc43f5bbab7 Mon Sep 17 00:00:00 2001 From: DaegyunOh Date: Mon, 18 Sep 2023 21:49:35 +0900 Subject: [PATCH 01/20] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=B4=88=EA=B8=B0=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 컴포넌트 선언, 스타일 선언, hooks, utils, stores 디렉토리 추가 --- .prettierrc | 6 + package-lock.json | 473 ++++++++++++++++++++++++++-- package.json | 4 +- public/index.css | 131 ++++++++ public/index.html | 4 +- src/App.js | 9 - src/App.jsx | 14 + src/components/TodoDetail.jsx | 5 + src/components/TodoInput.jsx | 5 + src/components/TodoListElement.jsx | 5 + src/hooks/useAuth.js | 0 src/hooks/useTodo.js | 5 + src/stores/useAuthStore.js | 0 src/styles/App.style.js | 7 + src/styles/TodoDetail.style.js | 3 + src/styles/TodoInput.style.js | 11 + src/styles/TodoListElement.style.js | 11 + src/styles/common.style.js | 0 src/utils/common.js | 0 19 files changed, 647 insertions(+), 46 deletions(-) create mode 100644 .prettierrc create mode 100644 public/index.css delete mode 100644 src/App.js create mode 100644 src/App.jsx create mode 100644 src/components/TodoDetail.jsx create mode 100644 src/components/TodoInput.jsx create mode 100644 src/components/TodoListElement.jsx create mode 100644 src/hooks/useAuth.js create mode 100644 src/hooks/useTodo.js create mode 100644 src/stores/useAuthStore.js create mode 100644 src/styles/App.style.js create mode 100644 src/styles/TodoDetail.style.js create mode 100644 src/styles/TodoInput.style.js create mode 100644 src/styles/TodoListElement.style.js create mode 100644 src/styles/common.style.js create mode 100644 src/utils/common.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..0a725205c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": true, + "singleQuote": true +} diff --git a/package-lock.json b/package-lock.json index eb6e658d3..40dc24738 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,9 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", - "web-vitals": "^2.1.4" + "styled-components": "^6.0.8", + "web-vitals": "^2.1.4", + "zustand": "^4.4.1" } }, "node_modules/@adobe/css-tools": { @@ -34,6 +36,78 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/cli": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.22.15.tgz", + "integrity": "sha512-prtg5f6zCERIaECeTZzd2fMtVjlfjhUcO+fBLQ6DXXdq5FljN+excVitJ2nogsusdf31LeqkjAfXZ7Xq+HmN8g==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "commander": "^4.0.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" + }, + "bin": { + "babel": "bin/babel.js", + "babel-external-helpers": "bin/babel-external-helpers.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "optionalDependencies": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/cli/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@babel/cli/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/cli/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/cli/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@babel/cli/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -353,9 +427,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "engines": { "node": ">=6.9.0" } @@ -531,6 +605,20 @@ "@babel/core": "^7.13.0" } }, + "node_modules/@babel/plugin-external-helpers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.22.5.tgz", + "integrity": "sha512-ngnNEWxmykPk82mH4ajZT0qTztr3Je6hrMuKAslZVM8G1YZTENJSYwrIGtt6KOtznug3exmAtF4so/nPqJuA4A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-async-generator-functions": { "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", @@ -2131,6 +2219,24 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz", @@ -3005,6 +3111,12 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "optional": true + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -4252,6 +4364,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "node_modules/@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.5", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", @@ -5627,6 +5744,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6058,6 +6183,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", @@ -6239,6 +6372,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -6426,9 +6569,9 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -8356,6 +8499,11 @@ "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -12128,9 +12276,15 @@ } }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -12771,9 +12925,9 @@ } }, "node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "funding": [ { "type": "opencollective", @@ -12782,10 +12936,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -15129,6 +15287,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -15531,6 +15694,49 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.8.tgz", + "integrity": "sha512-AwI02MTWZwqjzfXgR5QcbmcSn5xVjY4N2TLjSuYnmuBGF3y7GicHz3ysbpUq2EMJP5M8/Nc22vcmF3V3WNZDFA==", + "dependencies": { + "@babel/cli": "^7.21.0", + "@babel/core": "^7.21.0", + "@babel/helper-module-imports": "^7.18.6", + "@babel/plugin-external-helpers": "^7.18.6", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.7", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@babel/traverse": "^7.21.2", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/unitless": "^0.8.0", + "@types/stylis": "^4.0.2", + "css-to-react-native": "^3.2.0", + "csstype": "^3.1.2", + "postcss": "^8.4.23", + "shallowequal": "^1.1.0", + "stylis": "^4.3.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "babel-plugin-styled-components": ">= 2", + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "babel-plugin-styled-components": { + "optional": true + } + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -15546,6 +15752,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", + "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -16064,16 +16275,16 @@ } }, "node_modules/typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { @@ -16209,6 +16420,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -17226,6 +17445,33 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz", + "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } }, "dependencies": { @@ -17243,6 +17489,53 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@babel/cli": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.22.15.tgz", + "integrity": "sha512-prtg5f6zCERIaECeTZzd2fMtVjlfjhUcO+fBLQ6DXXdq5FljN+excVitJ2nogsusdf31LeqkjAfXZ7Xq+HmN8g==", + "requires": { + "@jridgewell/trace-mapping": "^0.3.17", + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0", + "commander": "^4.0.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + } + } + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -17480,9 +17773,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==" }, "@babel/helper-remap-async-to-generator": { "version": "7.18.9", @@ -17601,6 +17894,14 @@ "@babel/plugin-proposal-optional-chaining": "^7.20.7" } }, + "@babel/plugin-external-helpers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.22.5.tgz", + "integrity": "sha512-ngnNEWxmykPk82mH4ajZT0qTztr3Je6hrMuKAslZVM8G1YZTENJSYwrIGtt6KOtznug3exmAtF4so/nPqJuA4A==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, "@babel/plugin-proposal-async-generator-functions": { "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", @@ -18598,6 +18899,24 @@ "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", "requires": {} }, + "@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "requires": { + "@emotion/memoize": "^0.8.1" + } + }, + "@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, "@eslint-community/eslint-utils": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz", @@ -19241,6 +19560,12 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "optional": true + }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -20180,6 +20505,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "@types/testing-library__jest-dom": { "version": "5.14.5", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", @@ -21193,6 +21523,11 @@ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" }, + "camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==" + }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -21513,6 +21848,11 @@ "postcss-selector-parser": "^6.0.9" } }, + "css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==" + }, "css-declaration-sorter": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", @@ -21620,6 +21960,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "requires": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -21758,9 +22108,9 @@ } }, "csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "damerau-levenshtein": { "version": "1.0.8", @@ -23184,6 +23534,11 @@ "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" }, + "fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -25903,9 +26258,9 @@ } }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" }, "natural-compare": { "version": "1.4.0", @@ -26358,11 +26713,11 @@ } }, "postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "requires": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -27883,6 +28238,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -28186,6 +28546,32 @@ "integrity": "sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==", "requires": {} }, + "styled-components": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.8.tgz", + "integrity": "sha512-AwI02MTWZwqjzfXgR5QcbmcSn5xVjY4N2TLjSuYnmuBGF3y7GicHz3ysbpUq2EMJP5M8/Nc22vcmF3V3WNZDFA==", + "requires": { + "@babel/cli": "^7.21.0", + "@babel/core": "^7.21.0", + "@babel/helper-module-imports": "^7.18.6", + "@babel/plugin-external-helpers": "^7.18.6", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.7", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@babel/traverse": "^7.21.2", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/unitless": "^0.8.0", + "@types/stylis": "^4.0.2", + "css-to-react-native": "^3.2.0", + "csstype": "^3.1.2", + "postcss": "^8.4.23", + "shallowequal": "^1.1.0", + "stylis": "^4.3.0", + "tslib": "^2.5.0" + } + }, "stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -28195,6 +28581,11 @@ "postcss-selector-parser": "^6.0.4" } }, + "stylis": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", + "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -28588,9 +28979,9 @@ } }, "typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "peer": true }, "unbox-primitive": { @@ -28682,6 +29073,12 @@ "requires-port": "^1.0.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -29470,6 +29867,14 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zustand": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz", + "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==", + "requires": { + "use-sync-external-store": "1.2.0" + } } } } diff --git a/package.json b/package.json index 101e3ed4b..53bce2f4d 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", - "web-vitals": "^2.1.4" + "styled-components": "^6.0.8", + "web-vitals": "^2.1.4", + "zustand": "^4.4.1" }, "scripts": { "start": "react-scripts start", diff --git a/public/index.css b/public/index.css new file mode 100644 index 000000000..7855957f7 --- /dev/null +++ b/public/index.css @@ -0,0 +1,131 @@ +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} +body { + line-height: 1; +} +ol, +ul { + list-style: none; +} +blockquote, +q { + quotes: none; +} +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +button { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: transparent; + border: none; +} diff --git a/public/index.html b/public/index.html index aa069f27c..fcccc0675 100644 --- a/public/index.html +++ b/public/index.html @@ -10,6 +10,7 @@ content="Web site created using create-react-app" /> + - React App + 시크릿 일기장 -
- 시크릿 일기장 + 시크릿 투두
diff --git a/src/components/Header.jsx b/src/components/Header.jsx index e0c0c309b..63704d3f7 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -9,6 +9,10 @@ import { PasswordButton, AuthButton, } from '../styles/Header.style'; +import lockIcon from '../images/lock.png'; +import unlockIcon from '../images/unlock.png'; +import settingIcon from '../images/setting.png'; +import resetIcon from '../images/reset.png'; const Header = () => { const { login, logout, register, reset } = useAuth(); @@ -20,15 +24,15 @@ const Header = () => { const ButtonSelector = {}; ButtonSelector[authStates.AUTHORIZED] = { clickHandler: logout, - src: '잠금', + src: unlockIcon, }; ButtonSelector[authStates.UNAUTHORIZED] = { clickHandler: login, - src: '잠금 해제', + src: lockIcon, }; ButtonSelector[authStates.NOT_REGISTERED] = { clickHandler: register, - src: '비밀번호 설정', + src: settingIcon, }; return ( @@ -65,11 +69,17 @@ const Header = () => { else setIsInputMode(true); }} > - {ButtonSelector[authState].src} + )} {!isInputMode && authState !== authStates.NOT_REGISTERED && ( - 초기화 + + + )} diff --git a/src/components/TodoDetail.jsx b/src/components/TodoDetail.jsx index e6c07f06c..7c8f3a0a3 100644 --- a/src/components/TodoDetail.jsx +++ b/src/components/TodoDetail.jsx @@ -8,7 +8,7 @@ import { DetailButtonsOuter, DetailButton, } from '../styles/TodoDetail.style'; -import { convertDate, convertPriority } from '../utils/common'; +import { convertDate, priorityMap } from '../utils/common'; const TodoDetail = ({ title, @@ -28,7 +28,7 @@ const TodoDetail = ({
-
{convertDate(todo.toDate)}
중요도:
-
{convertPriority(todo.priority)}
+
{priorityMap[todo.priority]}
{body} diff --git a/src/components/TodoInput.jsx b/src/components/TodoInput.jsx index d3fd81a28..618791cfe 100644 --- a/src/components/TodoInput.jsx +++ b/src/components/TodoInput.jsx @@ -1,22 +1,15 @@ +import { useRef } from 'react'; import { AddButton, DateInput, PriorityOuterDiv, - // PriorityInput, SecretInput, ContentTextArea, TodoInputContainer, } from '../styles/TodoInput.style'; import { useTodoInput } from '../hooks/useTodoInput'; - import { authStates, useAuthStore } from '../stores/useAuthStore'; -import { useRef } from 'react'; - -const PRIORITY = { - high: 3, - mid: 2, - low: 1, -}; +import { priorityMap } from '../utils/common'; const TodoInput = ({ addTodo }) => { const { @@ -47,7 +40,7 @@ const TodoInput = ({ addTodo }) => { >
- {Object.entries(PRIORITY).map(([key, value]) => ( + {Object.entries(priorityMap).map(([value, key]) => ( {
- {authState === authStates.AUTHORIZED && (
diff --git a/src/components/TodoListElement.jsx b/src/components/TodoListElement.jsx index 2806c4775..d84658ce3 100644 --- a/src/components/TodoListElement.jsx +++ b/src/components/TodoListElement.jsx @@ -8,7 +8,7 @@ import { DoneButton, DeleteButton, } from '../styles/TodoListElement.style'; -import { convertDate, convertPriority } from '../utils/common'; +import { convertDate, priorityMap } from '../utils/common'; import TodoDetail from './TodoDetail'; import { authStates, useAuthStore } from '../stores/useAuthStore'; @@ -38,6 +38,7 @@ const TodoListElement = ({ )} { + if (todo.isSecret && authState !== authStates.AUTHORIZED) return; setIsDetailModalOpen(true); }} > @@ -45,7 +46,7 @@ const TodoListElement = ({ 비밀글입니다. ) : (
diff --git a/src/images/lock.png b/src/images/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..bc56b93643cff8bcc95f31638236de4d7d3ad8cc GIT binary patch literal 1101 zcmV-T1hV^yP)Ar?_A{DlOYghV8XXs3mhsI6EC zRw4u~>_jYrg&uf;1UvzHYg8!#4}dc8%=(6aLCgfXzs7ZMfrG00(;r*>4*}o??yfu!oCi8muI;dO z=4-^b(b97O&;wK)#6JSN8;G;pUXzSd0eXCL?(o{3-~!N<@cjW!tLn$BL2TVKn53)G zW!vxv&plv&3((NRkcg}Y`hj6q-i{u|Yz3(7jP9=p!rSCqHV!)?eelCgxTAhYJtQWwJD&tDFN(1zZ#RT79q7K z)SCpaV8;AvML#Bony5Jm+6nI`upv?O2N+{{OS@Gt1RQ1M%C-n>sx>jLO9|MIJNW@s zeUq1Ekr0tSOt-HVVFM6`IyuUxKzTVI@h~Pay3`Q{T{NGud6Oj=sN>IcY0@s1tKzEg| z+xo7DCKFTu`~L7^N|lSYPB4V-0Y~bz*^_+-Yw!go{;t@>% zh@1F7v{!-H1Y#pi0N3s6#-fOLgo~pq7a(1LKcB$C%g zn?59EML30dlM_3ktTqRn%92~=bAt8*rn8tYJBL~R^u1Lui<#_7z+E-p8`Z^M{XGJP T18*CQ00000NkvXXu0mjfkSOX} literal 0 HcmV?d00001 diff --git a/src/images/reset.png b/src/images/reset.png new file mode 100644 index 0000000000000000000000000000000000000000..5a48c6f3017bb0d3e9697b8cbdfc0784f56e0eae GIT binary patch literal 1814 zcmV+x2kH2UP)-6_yARWRa6XOd>|qyzOX1(KoKhuV`)Xe@{(eA{c!Hm*|RgVcXw}>ez^Z+((au( zbI#12Gmrn7TSDjKbYLuS9xwtJ3iJmmfeN4$I1r>};8$P=P!D_ov=qU{$T1Fh0{9+C z0LcQO5n%`P09IL)Yy{3Nq~eG$1o#gBs*!gGlmIVTDqI2dDa7gs z(GM65d`KsBz>}8Bp2NSC6$@7Ye-durkXHv(lDP&i=EV5B4XX*igFHJQ1wDa1MkR03 zHQsy*FA}ctEkJiyCC&_FCanQx1KW}1r9A?}qr&~be&BphnZ3Z%$ViS6BSwrEF=E6> zLrIXvA@p?$hk-_5n@6?>a1}yvYqTJAR>C8H7BCFx4g7|XsXjZe1O{62_91-4G4=c+ zgo1OS3D^L#zjtjTe*~^^RPd2UE(yHqkh>1pY$@M}n_I}X?fpc_&COCil@-DSvBsk~Mss+&s7$y^=>Am?+NFMf--oQbn?N zL+;UEo35OMUFjX{!>dY_(=JeleTQh`WZ*tzx2g3$Y?RC90;>>O+=YT%paJ3CHL6s( zFsncX;sV;(QecLv?^7ecQ&$1)0?nlFsYf7hvqz3fHTobE-AZ65saM;Kn7W`jR);Yjjmfkm> z>r>)uMqWOLp=xCK`(zTx9Uv3=9EMEPsB(Ac0*iu-b~-K?q!h290ma=u_>5^*Dq zGT;GZi0ks3N#7ousLi5l4epR#Uu3mNB0Pn8s5?%Gj75A&`(BDHX+aJlCb}bp$SD@3 z&A{jQ36W|fX4gBYyU|m&1PO-P-tt~#KGB4Ti3;T2&}O8aF=E7s5hF&77+Eho-G-1M zy&U<+kX_g_6i7-$jKK&Ado=j_S3Z@DBi!L72}~)_;3#nhva3wlR_811qr^t!sI)7f zKN7O5HlEqyiJ>r)aBqJDLT{aD3`DN)nzps!j%{Xzs{<0iqe!F6ozlg{aMFi;>U2A` zkV>;~M91Z%V{t-!TWDbgJ<1tCG6#s104A>{kMye=k(Ku_QXg#WX-WCQ78^@4GeQZT{D z8;=A#U0mRD#5c`wTD};CgynZT^tB`RfnA%mywM-=@gx$xJxS$PBBR0gt=~Z;7Vr>o zHE!ZEh297wJRP~Gkc~ezv} zLs$8B0g&C-Lgdy~*e!G}sT~Q0Po!&ow~&D{{9l!ZNk*OT5C8xG literal 0 HcmV?d00001 diff --git a/src/images/setting.png b/src/images/setting.png new file mode 100644 index 0000000000000000000000000000000000000000..dbe6bf9213af39aee8ef99da5f3f4fc1b751f063 GIT binary patch literal 1554 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!n2Vh}LpV4%Za?&Y0OWEOctjR6 zFtCb(Fyk$YzAHdM$r9IylHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@`?%7?T2g zLR|m<{|{6@3TPJsGege00ZlJ03GxeOU}R!uVP#|I;N=q(6_b#ZkyB7oQP(syGB!0c zx3RN#aCCBUbNBG__Vx1*3JwiROV2AQtEjB5t8Z-U=n^{IaP>GHf zG`GK6)5F(ovbpi**Z1cOd4p#!*w)mIOqlp|x-!Gjn?`$8=SIrxd z`=L$H@ci)~bx#($JUujH+8ft{qIxRKvaj~v5cCi`q*tg`=`p#cK;T=_qZ^si5>GZa z2Si@@wn`^Tj`fsNQeeZeRmBEu-HSz@E&KfKt3zVhWd)^`-dnd^H8V=e4sFzPsStg| zY~tgj+kIkn!Oj~_j}jSbwbH}!e4h~a5cfTe-+Ulq}vmF>&k`E6e0 z<&-)zljR;sGpcxr|^#J;ge+l}qvt^R2k` zERp~Al})~aUImpOH#=p1bA8dLc580O+u#db98V+p4wPPWaay+MTfY~dM07+0@AruT z@($cEg6oc+%ZXcX&m6lwAcoT~jaHo>*V}qGVn*>&JUXmKd#`VZm;-q9sN}pySDfJxk^= zmlztJo*8}hTXSe%?725>mv$`Qc0YT~jerQ<-p6X2mQaT#fIoe7xx7o>%i;?tFEv?&i9myW)R0 zJD+h~HcQ<pt5Ys8fE&2~-w& My85}Sb4q9e0O-jumjD0& literal 0 HcmV?d00001 diff --git a/src/images/unlock.png b/src/images/unlock.png new file mode 100644 index 0000000000000000000000000000000000000000..428d5690f1bd944ce3a88b63b4bf59d332be71ab GIT binary patch literal 1109 zcmV-b1giUqP)At|C*_zMX(hD0QZXs3mhsI6EC zRw9TNb|ON8g%C&~+Gv-*K}jhp2!_-6Bcc$3KM(~ail~qakvxl;m}4`yx3@Ptb0PPI zf#c20oA3MH%7N^+hmJs#(ntd#W@k0r!AO;F+}z0R5N^a(|V6Z-IlV`pX|%{4W7u6!%pA16%;wQu?;p zHh-%47;Wl30O$th9mGEXyXx?>%l1k7nFqRka&GfFo!}zSk+3ZSr&aYsRwuUY8BEZX z=&)n>gXbCWk(K}2bHbD{T170Qd1-exAXHK76fUl0Ky{bBu$7d2gm%wg^ zZ68c%N~sT<|HtHixa`!S6)Iv2wwu0Gk}P84rG^Gme!DJ_Qhm&Bq4g^5GgQ z7kpm^R~=dc#3m3M>tYkA1yvmahKlmP=4U`lfY=0LW6cXt*TayAtOt65K~~<59>%1* zEAbF`ke^;%_zXM+UaRUt=3W^eOFMvTz#NSyIg2iHmTQNXj1VTbH`N4z)mx^3Jq-z9 z|M}Gze5DAfGojWdconneS1WokF;qp>P0((5zkm&iszqR&<&<`_U;sGE$}3wL=&ZIe zrriqn;tsw~Ro~=gStLZH2Q%#}Mc9Ce<+-a*+7cLa*v6YRcmT_7+)?F<1xOdbm3C^j zY43uIW7QPen!gil2@sn=Y(z_lh={P9-=9%mZ)oa7@EITm=P~d6r)`-Pk@HrRpop;q zMu1yDSB0(1+D1Z?2`Yene|RaS$tBw+7(&m0BQ?fsvu%PQv;aFjweg0~0zCHA#v4Kl za3Ax8Sq;B!n_vhkfU5ogj$_hRNz4N$Y@1*Rei>BNN5Bn7)eTjB5SUK1Tj6En1L^$brpzBAU4tjaNVwMEQ*LnxH!6E0n!CX9(@NmDI)E4Qwjvj?WCj1 z6$_9qz*C29J1{0919fsM9G24!z$8vg`^QP{$O00000NkvXXu0mjfMPA~p literal 0 HcmV?d00001 diff --git a/src/styles/Header.style.js b/src/styles/Header.style.js index dd7bd0ccd..55823ceb7 100644 --- a/src/styles/Header.style.js +++ b/src/styles/Header.style.js @@ -50,4 +50,9 @@ export const PasswordButton = styled.button` export const AuthButton = styled.button` margin: 0 10px; + + .authIcon { + width: 25px; + height: 25px; + } `; diff --git a/src/styles/TodoDetail.style.js b/src/styles/TodoDetail.style.js index 667c8903c..6f3131aa9 100644 --- a/src/styles/TodoDetail.style.js +++ b/src/styles/TodoDetail.style.js @@ -22,7 +22,6 @@ export const TodoDetailInner = styled.div` width: 100%; box-sizing: border-box; padding: 20px; - // margin-bottom: 100px; background-color: white; border-radius: 10px; `; @@ -65,7 +64,6 @@ export const DetailBodyDiv = styled.div` white-space: break-spaces; overflow-y: auto; margin-bottom: 40px; - // white-space: pre-wrap; `; export const DetailButtonsOuter = styled.div` diff --git a/src/styles/TodoInput.style.js b/src/styles/TodoInput.style.js index 7b0337447..da7aadde3 100644 --- a/src/styles/TodoInput.style.js +++ b/src/styles/TodoInput.style.js @@ -14,6 +14,7 @@ export const TodoInputContainer = styled.div` flex-wrap: wrap; .option { box-sizing: border-box; + height: 50px; flex: 1; font-size: 14px; background-color: white; @@ -62,8 +63,6 @@ export const TodoInputContainer = styled.div` } `; -export const ContentInput = styled.input``; - export const ContentTextArea = styled.textarea.attrs({ // 내용물에 따른 높이조절을 위한 attrs placeholder: '내용을 입력해주세요.', @@ -76,7 +75,7 @@ export const ContentTextArea = styled.textarea.attrs({ display: block; box-sizing: border-box; - padding: 10px; + padding: 15px; border-top-left-radius: 10px; border-top-right-radius: 10px; border: 1px solid #bcbcbc; @@ -93,13 +92,10 @@ export const PriorityOuterDiv = styled.div` .priorityLabel { } `; -// export const PriorityInput = styled.input.attrs({ type: 'radio' })``; export const DateInput = styled.input.attrs({ type: 'date' })` margin: 0 5px; border: none; - // width: 30px; - // background-color: black; `; export const SecretInput = styled.input.attrs({ type: 'checkbox' })``; @@ -107,7 +103,7 @@ export const SecretInput = styled.input.attrs({ type: 'checkbox' })``; export const AddButton = styled.button` box-sizing: border-box; width: 100%; - padding: 5px; + padding: 10px; text-align: center; color: white; background-color: black; diff --git a/src/utils/common.js b/src/utils/common.js index 404a0880d..2d69649a5 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -1,13 +1,8 @@ export const convertDate = (date) => `${date.getFullYear()}.${date.getMonth() + 1}.${date.getDate()}`; -export const convertPriority = (priority) => { - switch (priority) { - case 1: - return 'low'; - case 2: - return 'mid'; - default: - return 'high'; - } +export const priorityMap = { + 1: 'low', + 2: 'mid', + 3: 'high', }; From a67074a13c0574ac665c5fc94748e944b94a3401 Mon Sep 17 00:00:00 2001 From: DaegyunOh Date: Wed, 20 Sep 2023 11:40:27 +0900 Subject: [PATCH 16/20] =?UTF-8?q?chore:=20=EC=A3=BC=EC=84=9D=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/AuthProvider.jsx | 1 + src/components/Header.jsx | 15 ++++++++---- src/components/TodoInput.jsx | 38 ++++++++++++++++-------------- src/components/TodoListElement.jsx | 31 ++++++++++++------------ src/hooks/useAuth.js | 3 ++- src/hooks/useTodo.js | 3 +++ src/hooks/useTodoInput.js | 31 ++++++++++++++---------- src/index.js | 1 + src/stores/useAuthStore.js | 2 ++ src/utils/common.js | 12 ++++++---- 10 files changed, 81 insertions(+), 56 deletions(-) diff --git a/src/components/AuthProvider.jsx b/src/components/AuthProvider.jsx index 2c5b65813..85d026b91 100644 --- a/src/components/AuthProvider.jsx +++ b/src/components/AuthProvider.jsx @@ -4,6 +4,7 @@ import { authStates, useAuthStore } from '../stores/useAuthStore'; const AuthProvider = ({ children }) => { const setAuth = useAuthStore((state) => state.setAuth); const cachedAuth = JSON.parse(localStorage.getItem('auth') || 'null'); + // 인증 상태가 설정될 때까지 isInit을 통해 children 렌더링 방지 const [isInit, setIsInit] = useState(true); useEffect(() => { diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 63704d3f7..6ec0ed836 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -14,13 +14,15 @@ import unlockIcon from '../images/unlock.png'; import settingIcon from '../images/setting.png'; import resetIcon from '../images/reset.png'; +// header에 기능이 추가된다면 코드를 분리하는게 나을듯 const Header = () => { const { login, logout, register, reset } = useAuth(); const authState = useAuthStore((state) => state.auth.authState); - const [isInputMode, setIsInputMode] = useState(false); - const [password, setPassword] = useState(''); + const [isInputMode, setIsInputMode] = useState(false); // 로그인 or 가입 버튼 눌렀을 때 input display + const [password, setPassword] = useState(''); // 입력된 password + // 상태에 따라 버튼의 handler와 이미지 src를 설정하기 위한 selector const ButtonSelector = {}; ButtonSelector[authStates.AUTHORIZED] = { clickHandler: logout, @@ -49,6 +51,7 @@ const Header = () => { { ButtonSelector[authState].clickHandler(password); + setPassword(''); setIsInputMode(false); }} > @@ -56,6 +59,7 @@ const Header = () => { { + setPassword(''); setIsInputMode(false); }} > @@ -65,8 +69,10 @@ const Header = () => { ) : ( { - if (authState === authStates.AUTHORIZED) logout(); - else setIsInputMode(true); + if (authState === authStates.AUTHORIZED) { + logout(); + setPassword(''); + } else setIsInputMode(true); }} > { )} {!isInputMode && authState !== authStates.NOT_REGISTERED && ( + // 비밀번호를 설정한 적이 있다면 reset 버튼 display diff --git a/src/components/TodoInput.jsx b/src/components/TodoInput.jsx index 618791cfe..3031a4f0c 100644 --- a/src/components/TodoInput.jsx +++ b/src/components/TodoInput.jsx @@ -1,4 +1,3 @@ -import { useRef } from 'react'; import { AddButton, DateInput, @@ -19,49 +18,53 @@ const TodoInput = ({ addTodo }) => { toDate, todo, isSecret, + contentRef, toDateRef, - onChangeContent, - onClickPriority, - onChangeFromDate, - onChangeToDate, - onChangeSecret, + handleChangeContent, + handleClickPriority, + handleChangeFromDate, + handleChangeToDate, + handleChangeSecret, resetValue, } = useTodoInput(); const authState = useAuthStore((state) => state.auth.authState); - const contentRef = useRef(); return (
- {Object.entries(priorityMap).map(([value, key]) => ( + {priorityMap.map((value, key) => ( - + ))}
- - + +
- {authState === authStates.AUTHORIZED && ( + {authState === authStates.AUTHORIZED && ( // 로그인한 유저면 비밀글 작성 가능하도록
- +
비밀
)} @@ -70,7 +73,6 @@ const TodoInput = ({ addTodo }) => { onClick={() => { addTodo(todo); resetValue(); - contentRef.current.style.height = 'auto'; }} > + diff --git a/src/components/TodoListElement.jsx b/src/components/TodoListElement.jsx index d84658ce3..c83b247e9 100644 --- a/src/components/TodoListElement.jsx +++ b/src/components/TodoListElement.jsx @@ -17,28 +17,29 @@ const TodoListElement = ({ handleClickDeleteButton, handleClickDoneButton, }) => { - let [title, ...body] = todo.content.split('\n\n'); + let [title, ...body] = todo.content.split('\n\n'); // todo를 title과 body로 분리 const [isDetailModalOpen, setIsDetailModalOpen] = useState(false); - body = body.join(''); + body = body.join('\n\n'); // split된 바디 다시 join해줌 const authState = useAuthStore((state) => state.auth.authState); return ( <> - {isDetailModalOpen && body && ( - { - setIsDetailModalOpen(false); - }} - handleClickDeleteButton={handleClickDeleteButton} - handleClickDoneButton={handleClickDoneButton} - /> - )} + {isDetailModalOpen && + body && ( // body가 있는 글만 modal 열리도록 + { + setIsDetailModalOpen(false); + }} + handleClickDeleteButton={handleClickDeleteButton} + handleClickDoneButton={handleClickDoneButton} + /> + )} { - if (todo.isSecret && authState !== authStates.AUTHORIZED) return; + if (todo.isSecret && authState !== authStates.AUTHORIZED) return; // 비밀글이면 클릭 못하게 setIsDetailModalOpen(true); }} > diff --git a/src/hooks/useAuth.js b/src/hooks/useAuth.js index 377669666..9ba5a9914 100644 --- a/src/hooks/useAuth.js +++ b/src/hooks/useAuth.js @@ -2,6 +2,7 @@ import { useAuthStore } from '../stores/useAuthStore'; const DURATION = 5 * 60 * 1000; // 5분간 로그인 유지 +// 유저 인증을 위한 custom hook export const useAuth = () => { const password = useAuthStore((state) => state.auth.password); const setAuth = useAuthStore((state) => state.setAuth); @@ -57,8 +58,8 @@ export const useAuth = () => { 'todos', JSON.stringify(todos.filter((todo) => !todo.isSecret)) ); - // setAuth(null); localStorage.removeItem('auth'); + // 상태를 초기화하고 업데이트된 localStorage에서 새로 데이터 받아오기 위해 앱 전체 reload window.location.reload(); }; diff --git a/src/hooks/useTodo.js b/src/hooks/useTodo.js index 80c4d41f9..3e7bf5940 100644 --- a/src/hooks/useTodo.js +++ b/src/hooks/useTodo.js @@ -1,5 +1,6 @@ import { useState } from 'react'; +// localStorage에서 data load const TODOS = JSON.parse(localStorage.getItem('todos') || '[]').map( (todo, id) => ({ ...todo, @@ -29,9 +30,11 @@ const pushTodo = (todos, newTodo) => { } }; +// todo의 추가, 삭제, 변경을 다루기 위한 custom hook export const useTodo = () => { const [todos, setTodos] = useState(TODOS); + // 상태 변경 및 localStorage 업데이트 const saveChanges = (newTodos) => { setTodos(newTodos); localStorage.setItem('todos', JSON.stringify(newTodos)); diff --git a/src/hooks/useTodoInput.js b/src/hooks/useTodoInput.js index 5d7b0c260..74e15696d 100644 --- a/src/hooks/useTodoInput.js +++ b/src/hooks/useTodoInput.js @@ -1,13 +1,15 @@ import { useRef, useState } from 'react'; +// todo의 input data를 처리하기 위한 custom hook export const useTodoInput = () => { const [content, setContent] = useState(''); - const [priority, setPriority] = useState(3); + const [priority, setPriority] = useState(2); const [fromDate, setFromDate] = useState(''); const [toDate, setToDate] = useState(''); const [isSecret, setIsSecret] = useState(false); - const toDateRef = useRef(); + const contentRef = useRef(); // todo 추가시 textarea height 초기화를 위해 + const toDateRef = useRef(); // 시작일 설정에 따라 종료일의 min값을 설정하기 위해 const todo = { content: content.trim(), @@ -17,36 +19,38 @@ export const useTodoInput = () => { isSecret, }; - const onChangeContent = (e) => { + const handleChangeContent = (e) => { setContent(e.target.value); + // 줄바꿈에 따라 textarea의 높이도 조절되도록 e.target.style.height = 'auto'; e.target.style.height = e.target.scrollHeight + 'px'; }; - const onClickPriority = (e) => { + const handleClickPriority = (e) => { setPriority(Number(e.target.value)); }; - const onChangeFromDate = (e) => { + const handleChangeFromDate = (e) => { setFromDate(e.target.value); toDateRef.current.min = e.target.value; if (toDate < e.target.value) setToDate(''); }; - const onChangeToDate = (e) => { + const handleChangeToDate = (e) => { setToDate(e.target.value); }; - const onChangeSecret = (e) => { + const handleChangeSecret = (e) => { setIsSecret(e.target.checked); }; const resetValue = () => { setContent(''); - setPriority(3); + setPriority(2); setFromDate(''); setToDate(''); setIsSecret(false); + contentRef.current.style.height = 'auto'; // textarea 높이 초기화 }; return { @@ -56,12 +60,13 @@ export const useTodoInput = () => { toDate, todo, isSecret, + contentRef, toDateRef, - onChangeContent, - onClickPriority, - onChangeFromDate, - onChangeToDate, - onChangeSecret, + handleChangeContent, + handleClickPriority, + handleChangeFromDate, + handleChangeToDate, + handleChangeSecret, resetValue, }; }; diff --git a/src/index.js b/src/index.js index 73c8af36a..16cb924b1 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,7 @@ import AuthProvider from './components/AuthProvider'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( + // App 컴포넌트가 렌더링되기 전 auth 관련 상태 정의할 수 있도록 provider로 wrapping diff --git a/src/stores/useAuthStore.js b/src/stores/useAuthStore.js index db03744e0..ee5f950e9 100644 --- a/src/stores/useAuthStore.js +++ b/src/stores/useAuthStore.js @@ -1,6 +1,7 @@ import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; +// 인증 상태에 대한 정의 export const authStates = { INIT: 'init', NOT_REGISTERED: 'notRegistered', @@ -15,6 +16,7 @@ const initialState = { duration: null, }; +// 브라우저에서 redux devtool로 상태변화를 확인할 수 있도록 devtools middleware 사용 export const useAuthStore = create( devtools((set, get) => ({ auth: initialState, diff --git a/src/utils/common.js b/src/utils/common.js index 2d69649a5..40c7374f8 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -1,8 +1,10 @@ export const convertDate = (date) => `${date.getFullYear()}.${date.getMonth() + 1}.${date.getDate()}`; -export const priorityMap = { - 1: 'low', - 2: 'mid', - 3: 'high', -}; +// export const priorityMap = { +// 3: 'high', +// 2: 'mid', +// 1: 'low', +// }; + +export const priorityMap = ['low', 'mid', 'high']; From 709ca7214d7c2c2574e24db02b598f30fdc4c078 Mon Sep 17 00:00:00 2001 From: DaegyunOh Date: Wed, 20 Sep 2023 11:52:06 +0900 Subject: [PATCH 17/20] =?UTF-8?q?hotfix:=20=EB=AA=A8=EB=B0=94=EC=9D=BC=20?= =?UTF-8?q?=EB=B8=8C=EB=9D=BC=EC=9A=B0=EC=A0=80=EB=A1=9C=20=EC=A0=91?= =?UTF-8?q?=EC=86=8D=ED=95=98=EB=A9=B4=20input/textarea=EC=97=90=20border-?= =?UTF-8?q?radius=EA=B0=80=20=EC=84=A4=EC=A0=95=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/index.css b/public/index.css index 2931f52c8..7261a1615 100644 --- a/public/index.css +++ b/public/index.css @@ -164,3 +164,11 @@ body { padding: 10px; margin: 0; } + +input[type='button'], +input[type='text'], +input[type='password'], +textarea { + -webkit-border-radius: 0; + -webkit-appearance: none; +} From 5b0af3c86a6b37b253530529cb0e23f3f5f474dc Mon Sep 17 00:00:00 2001 From: DaegyunOh Date: Wed, 20 Sep 2023 12:15:59 +0900 Subject: [PATCH 18/20] =?UTF-8?q?docs:=20readme=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 77 +++++++++++-------------------------------------------- 1 file changed, 15 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 621d0471f..6bb54a70d 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,21 @@ -# 2주차 미션: React-Todo +# 배포 -# 서론 +[오대균의 Todo List](https://daegyuns-react-todo-list.vercel.app/) -안녕하세요 🙌🏻 18기 프론트엔드 운영진 **배성준**입니다. +# Key Features -다들 1주차 미션 Vanilla Todo 만드시느라 수고 많으셨습니다! 1주차 미션을 통해 여러분들께서 본격적인 React 사용에 앞서 Vanilla JS로 SPA를 만들때의 불편한 점을 느끼셨을 것 이라 생각합니다. +### 기존 -그리하여 이번 미션은, 1주차 스터의 미션으로 주어진 Todo list 만들기를 **React**로 리팩토링하는 것 입니다! -기존에 리액트를 잘 아시던 분들께는, 조금 더 효울적인 디자인 패턴에 대해 고민할수 있는 주차가 될 것이고, 리액트를 접해보지 못하신 분들께는 기존의 어플리케이션을 리액트로 포팅하는 과정을 통해 왜 프론트엔드 시장에 리액트가 등장하게 되었고, 리액트에서 사용하는 여러가지 방식들이 왜 바닐라에 비해 효율적인지 꺠닫는 주차가 될 것이라 생각합니다. +- todo 추가, 제거, 완료 +- 중요도 및 기한 설정 +- 중요도 및 기한(시작일)을 기반으로 정렬된 list +- 중요도에 따라 다른 글자색(중요도가 높을 수록 더 진하게) +- 완료된 todo는 list의 가장 아래에 배치, 글자색 매우 연하게 -비교적 가벼운 미션인 만큼 코드를 짜는 데 있어 여러분의 **창의성**을 충분히 발휘해보시기 바랍니다. 작동하기만 하면 되는 것보다 같은 코드를 짜는 여러가지 방식과 패턴에 대해 많이 고민해보시고, 본인이 작성할 수 있는 가장 창의적인 방법으로 코드를 작성해주셨으면 합니다. 여러분이 미션 수행을 하는 과정에서 한 생각과 고민만큼 스터디에서 더 많은 것을 얻어가실 수 있을 것입니다. +### 추가 -막히는 부분이 있더라도 우선 스스로 공부하고 찾아보면서 미션을 진행하는 방식을 권고드리지만, 미션과 관련하여 운영진의 도움이 필요하시다면 얼마든지 슬랙 Q&A 채널이나 프론트엔드 카톡방에 질문을 남겨 주세요! - -# 미션 - -## 예시 - -- [리액트 투두](https://react-todo-17th-psi.vercel.app/) - -## 미션 목표 - -- VSCode, Prettier를 이용하여 개발환경을 관리합니다. -- React의 기초를 이해합니다. -- React를 통한 어플리케이션 상태 관리 방법을 이해합니다. -- React Hooks에 대한 기초를 이해합니다. -- Styled-Components를 통한 CSS-in-JS 및 CSS Preprocessor의 사용법을 익힙니다. - -## 기한 - -- 2023년 9월 22일 금요일 - -## Key Questions - -- Virtual-DOM은 무엇이고, 이를 사용함으로서 얻는 이점은 무엇인가요? -- 미션을 진행하면서 느낀, React를 사용함으로서 얻을수 있는 장점은 무엇이었나요? -- React에서 상태란 무엇이고 어떻게 관리할 수 있을까요? -- Styled-Components 사용 후기 (CSS와 비교) - -## 필수 요건 - -- 1주차 미션의 결과물을 그대로 React로 구현합니다 -- Functional Components를 사용합니다 -- React Hooks만을 사용해 상태를 관리합니다 -- (이번주는 Redux, MobX, Recoil, SWR등의 외부 상태관리 라이브러리를 사용하지 않아도 미션 수행에 지장이 없습니다.) - -## 선택 요건 - -- 기존 Todo-list에 여러분들이 추가하고 싶은 기능과 디자인을 자유롭게 추가해보세요. - -## 로컬 실행방법 - ---- - -`npm start` : 로컬에서 react application을 자동으로 리로드하여 실행시켜줍니다. - -# 링크 및 참고자료 - ---- - -- [create react app (CRA)](https://create-react-app.dev/docs/getting-started/) -- [리액트 docs 주요 개념 1-12](https://react.dev/learn) -- [리액트 docs Hook 1-3](https://react.dev/reference/react) -- [리액트 useEffect 완벽 가이드](https://overreacted.io/ko/a-complete-guide-to-useeffect/) -- [컴포넌트 네이밍을 위한 자바스크립트 네이밍 컨벤션](https://velog.io/@cada/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8A%A4%ED%83%80%EC%9D%BC-%EA%B0%80%EC%9D%B4%EB%93%9C-%EB%84%A4%EC%9D%B4%EB%B0%8D-%EC%BB%A8%EB%B2%A4%EC%85%98-%ED%8E%B8) -- [useState, useEffect hooks](https://velog.io/@velopert/react-hooks#1-usestate) -- [styled-component](https://styled-components.com/docs/basics#getting-started) +- git commit message 처럼 제목 입력하고 enter 두 번 누르면 본문 작성 가능 +- 본문 있는 게시물 클릭하면 detail 정보 확인 가능 +- 비밀번호 설정 기능 +- 비밀글 설정 기능 +- 모바일 환경에서도 모든 기능 이용할 수 있도록 사이즈에 맞게 대응 From 880110d91295fc20fb510f7d125bf592023aac48 Mon Sep 17 00:00:00 2001 From: DaegyunOh Date: Thu, 21 Sep 2023 14:08:45 +0900 Subject: [PATCH 19/20] =?UTF-8?q?refactor:=20input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=93=A4=EC=9D=98=20=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit input value를 state로 관리하면 리렌더링이 반복해서 발생하므로, todo를 추가하는 컴포넌트를 분리한 후, memo와 useCallback을 사용하여 불필요한 리렌더링을 방지했다. --- src/components/Header.jsx | 4 +-- src/components/TodoInput.jsx | 50 +++++++------------------- src/components/TodoInputOption.jsx | 58 ++++++++++++++++++++++++++++++ src/components/TodoListElement.jsx | 4 +-- src/hooks/useTodoInput.js | 27 +++++++------- 5 files changed, 90 insertions(+), 53 deletions(-) create mode 100644 src/components/TodoInputOption.jsx diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 6ec0ed836..0f1b3ee13 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { memo, useState } from 'react'; import { useAuth } from '../hooks/useAuth'; import { authStates, useAuthStore } from '../stores/useAuthStore'; import { @@ -93,4 +93,4 @@ const Header = () => { ); }; -export default Header; +export default memo(Header); diff --git a/src/components/TodoInput.jsx b/src/components/TodoInput.jsx index 3031a4f0c..2ecc5542f 100644 --- a/src/components/TodoInput.jsx +++ b/src/components/TodoInput.jsx @@ -1,14 +1,11 @@ import { AddButton, - DateInput, - PriorityOuterDiv, - SecretInput, ContentTextArea, TodoInputContainer, } from '../styles/TodoInput.style'; import { useTodoInput } from '../hooks/useTodoInput'; -import { authStates, useAuthStore } from '../stores/useAuthStore'; -import { priorityMap } from '../utils/common'; +import { useAuthStore } from '../stores/useAuthStore'; +import TodoInputOption from './TodoInputOption'; const TodoInput = ({ addTodo }) => { const { @@ -37,38 +34,17 @@ const TodoInput = ({ addTodo }) => { onChange={handleChangeContent} value={content} > -
-
- {priorityMap.map((value, key) => ( - - - - - ))} -
-
- - -
- {authState === authStates.AUTHORIZED && ( // 로그인한 유저면 비밀글 작성 가능하도록 -
- -
비밀
-
- )} -
+ { addTodo(todo); diff --git a/src/components/TodoInputOption.jsx b/src/components/TodoInputOption.jsx new file mode 100644 index 000000000..44b334ebe --- /dev/null +++ b/src/components/TodoInputOption.jsx @@ -0,0 +1,58 @@ +import { authStates, useAuthStore } from '../stores/useAuthStore'; +import { priorityMap } from '../utils/common'; +import { + DateInput, + PriorityOuterDiv, + SecretInput, +} from '../styles/TodoInput.style'; +import { memo } from 'react'; + +const TodoInputOption = ({ + priority, + fromDate, + toDate, + isSecret, + toDateRef, + handleClickPriority, + handleChangeFromDate, + handleChangeToDate, + handleChangeSecret, +}) => { + const authState = useAuthStore((state) => state.auth.authState); + return ( +
+
+ {priorityMap.map((value, key) => ( + + + + + ))} +
+
+ + +
+ {authState === authStates.AUTHORIZED && ( // 로그인한 유저면 비밀글 작성 가능하도록 +
+ +
비밀
+
+ )} +
+ ); +}; + +export default memo(TodoInputOption); diff --git a/src/components/TodoListElement.jsx b/src/components/TodoListElement.jsx index c83b247e9..fd625a1d9 100644 --- a/src/components/TodoListElement.jsx +++ b/src/components/TodoListElement.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { memo, useState } from 'react'; import { TodoListElementContainer, ContentDiv, @@ -72,4 +72,4 @@ const TodoListElement = ({ ); }; -export default TodoListElement; +export default memo(TodoListElement); diff --git a/src/hooks/useTodoInput.js b/src/hooks/useTodoInput.js index 74e15696d..c52efc49a 100644 --- a/src/hooks/useTodoInput.js +++ b/src/hooks/useTodoInput.js @@ -1,4 +1,4 @@ -import { useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; // todo의 input data를 처리하기 위한 custom hook export const useTodoInput = () => { @@ -26,23 +26,26 @@ export const useTodoInput = () => { e.target.style.height = e.target.scrollHeight + 'px'; }; - const handleClickPriority = (e) => { + const handleClickPriority = useCallback((e) => { setPriority(Number(e.target.value)); - }; + }, []); - const handleChangeFromDate = (e) => { - setFromDate(e.target.value); - toDateRef.current.min = e.target.value; - if (toDate < e.target.value) setToDate(''); - }; + const handleChangeFromDate = useCallback( + (e) => { + setFromDate(e.target.value); + toDateRef.current.min = e.target.value; + if (toDate < e.target.value) setToDate(''); + }, + [toDate] + ); - const handleChangeToDate = (e) => { + const handleChangeToDate = useCallback((e) => { setToDate(e.target.value); - }; + }, []); - const handleChangeSecret = (e) => { + const handleChangeSecret = useCallback((e) => { setIsSecret(e.target.checked); - }; + }, []); const resetValue = () => { setContent(''); From 0540a633374ce0103837c1e85c947f8a502a5159 Mon Sep 17 00:00:00 2001 From: DaegyunOh Date: Thu, 21 Sep 2023 14:17:21 +0900 Subject: [PATCH 20/20] =?UTF-8?q?refactor:=20=EC=8B=9C=EC=9E=91=EC=9D=BC?= =?UTF-8?q?=20=EC=A2=85=EB=A3=8C=EC=9D=BC=20=EB=82=A0=EC=A7=9C=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 시작일 > 종료일이 되는 모든 경우를 차단했다. --- src/components/TodoInput.jsx | 2 ++ src/components/TodoInputOption.jsx | 7 ++++++- src/hooks/useTodoInput.js | 17 +++++++++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/components/TodoInput.jsx b/src/components/TodoInput.jsx index 2ecc5542f..db9cb9fc1 100644 --- a/src/components/TodoInput.jsx +++ b/src/components/TodoInput.jsx @@ -16,6 +16,7 @@ const TodoInput = ({ addTodo }) => { todo, isSecret, contentRef, + fromDateRef, toDateRef, handleChangeContent, handleClickPriority, @@ -39,6 +40,7 @@ const TodoInput = ({ addTodo }) => { fromDate={fromDate} toDate={toDate} isSecret={isSecret} + fromDateRef={fromDateRef} toDateRef={toDateRef} handleClickPriority={handleClickPriority} handleChangeFromDate={handleChangeFromDate} diff --git a/src/components/TodoInputOption.jsx b/src/components/TodoInputOption.jsx index 44b334ebe..522c988df 100644 --- a/src/components/TodoInputOption.jsx +++ b/src/components/TodoInputOption.jsx @@ -12,6 +12,7 @@ const TodoInputOption = ({ fromDate, toDate, isSecret, + fromDateRef, toDateRef, handleClickPriority, handleChangeFromDate, @@ -38,7 +39,11 @@ const TodoInputOption = ({ ))}
- + { const contentRef = useRef(); // todo 추가시 textarea height 초기화를 위해 const toDateRef = useRef(); // 시작일 설정에 따라 종료일의 min값을 설정하기 위해 + const fromDateRef = useRef(); const todo = { content: content.trim(), @@ -34,14 +35,21 @@ export const useTodoInput = () => { (e) => { setFromDate(e.target.value); toDateRef.current.min = e.target.value; - if (toDate < e.target.value) setToDate(''); + if (toDate < e.target.value || !toDateRef.current.value) + setToDate(e.target.value); }, [toDate] ); - const handleChangeToDate = useCallback((e) => { - setToDate(e.target.value); - }, []); + const handleChangeToDate = useCallback( + (e) => { + setToDate(e.target.value); + fromDateRef.current.max = e.target.value; + if (fromDate > e.target.value || !fromDateRef.current.value) + setFromDate(e.target.value); + }, + [fromDate] + ); const handleChangeSecret = useCallback((e) => { setIsSecret(e.target.checked); @@ -65,6 +73,7 @@ export const useTodoInput = () => { isSecret, contentRef, toDateRef, + fromDateRef, handleChangeContent, handleClickPriority, handleChangeFromDate,