From f3c2a9b444e11294de52443e84e516f82724f491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=98=84=EC=9A=B0?= <122512457+hyo-nu@users.noreply.github.com> Date: Sat, 13 Jul 2024 11:16:34 +0900 Subject: [PATCH] =?UTF-8?q?[KAN-191]=20feat(login):=20=EB=A6=AC=ED=94=84?= =?UTF-8?q?=EB=A0=88=EC=89=AC=20=ED=86=A0=ED=81=B0=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=EC=86=8C=20=EC=88=98=EC=A0=95,=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=89=AC=20=ED=86=A0=ED=81=B0=20=ED=99=9C=EC=9A=A9=20=EC=9E=AC?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package-lock.json | 229 +++++++++++++++++- client/package.json | 1 + client/src/apis/auth.js | 31 ++- client/src/apis/index.js | 54 ++++- client/src/apis/member.js | 17 ++ client/src/components/LoginForm.jsx | 49 +++- client/src/components/SignupForm.jsx | 40 +-- client/src/components/common/InputWeb.jsx | 30 --- .../common/{ => buttons}/StatusNextButton.jsx | 8 +- .../common/{ => buttons}/StatusPreButton.jsx | 7 +- .../common/{ => inputs}/AddressInput.jsx | 0 .../common/{ => inputs}/CheckBox.jsx | 0 .../components/common/{ => inputs}/Input.jsx | 0 .../src/components/common/inputs/InputWeb.jsx | 55 +++++ .../common/{ => inputs}/RadioGroup.jsx | 0 .../common/{ => member}/Loading.jsx | 0 .../common/{ => member}/Success.jsx | 0 .../components/common/{ => tables}/Table.jsx | 0 .../common/{ => tables}/TableCol.jsx | 0 .../common/{ => tables}/TableRow.jsx | 0 .../common/{ => tables}/TableSearch.jsx | 0 .../member/simpConsent/BasicInfo.jsx | 4 +- .../member/simpConsent/ContractInfo.jsx | 2 +- .../member/simpConsent/PaymentCMS.jsx | 2 +- .../member/simpConsent/PaymentCard.jsx | 2 +- .../member/simpConsent/PaymentInfo.jsx | 2 +- client/src/pages/member/InvoicePage.jsx | 4 +- .../src/pages/member/PaymentAccountPage.jsx | 8 +- client/src/pages/member/PaymentCardPage.jsx | 8 +- client/src/pages/member/PaymentChoosePage.jsx | 4 +- .../src/pages/member/PaymentVirtualPage.jsx | 8 +- client/src/pages/member/SimpConsentPage.jsx | 8 +- client/src/pages/vendor/DashBoardPage.jsx | 14 ++ .../pages/vendor/member/MemberListPage.jsx | 2 +- .../vendor/member/MemberRegisterPage.jsx | 4 +- .../pages/vendor/product/ProductListPage.jsx | 2 +- .../vendor/setting/SettingSimpConsentPage.jsx | 2 +- client/src/stores/useStatusStore.js | 2 +- client/src/utils/regex.js | 19 ++ client/src/utils/validators.js | 26 ++ .../member/controller/MemberController.java | 4 +- .../vendor/dto/VendorUserDetailsDto.java | 6 +- .../domain/vendor/jwt/JWTFilter.java | 8 +- .../domain/vendor/jwt/JWTUtil.java | 2 - .../domain/vendor/jwt/LoginFilter.java | 2 + .../domain/vendor/service/VendorService.java | 13 +- 46 files changed, 550 insertions(+), 129 deletions(-) create mode 100644 client/src/apis/member.js delete mode 100644 client/src/components/common/InputWeb.jsx rename client/src/components/common/{ => buttons}/StatusNextButton.jsx (71%) rename client/src/components/common/{ => buttons}/StatusPreButton.jsx (56%) rename client/src/components/common/{ => inputs}/AddressInput.jsx (100%) rename client/src/components/common/{ => inputs}/CheckBox.jsx (100%) rename client/src/components/common/{ => inputs}/Input.jsx (100%) create mode 100644 client/src/components/common/inputs/InputWeb.jsx rename client/src/components/common/{ => inputs}/RadioGroup.jsx (100%) rename client/src/components/common/{ => member}/Loading.jsx (100%) rename client/src/components/common/{ => member}/Success.jsx (100%) rename client/src/components/common/{ => tables}/Table.jsx (100%) rename client/src/components/common/{ => tables}/TableCol.jsx (100%) rename client/src/components/common/{ => tables}/TableRow.jsx (100%) rename client/src/components/common/{ => tables}/TableSearch.jsx (100%) create mode 100644 client/src/utils/regex.js create mode 100644 client/src/utils/validators.js diff --git a/client/package-lock.json b/client/package-lock.json index a944b1d2..a551b3b4 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,6 +12,7 @@ "axios": "^1.7.2", "qrcode.react": "^3.1.0", "react": "^18.3.1", + "react-cookie": "^7.1.4", "react-daum-postcode": "^3.1.3", "react-dom": "^18.3.1", "react-icons": "^5.2.1", @@ -3690,23 +3691,37 @@ "node": ">=10.13.0" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "license": "MIT", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "devOptional": true + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { "version": "18.3.3", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4112,6 +4127,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -4363,6 +4385,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js-compat": { "version": "3.37.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", @@ -4427,6 +4458,23 @@ "node": ">=4" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "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", @@ -4438,6 +4486,33 @@ "postcss-value-parser": "^4.0.2" } }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4451,6 +4526,42 @@ "node": ">=4" } }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -4608,6 +4719,65 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -5550,6 +5720,15 @@ "node": ">= 0.4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -6230,6 +6409,13 @@ "node": "14 || >=16.14" } }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -6374,6 +6560,19 @@ "node": ">=0.10.0" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7035,6 +7234,20 @@ "node": ">=0.10.0" } }, + "node_modules/react-cookie": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.1.4.tgz", + "integrity": "sha512-wDxxa/HYaSXSMlyWJvJ5uZTzIVtQTPf1gMksFgwAz/2/W3lCtY8r4OChCXMPE7wax0PAdMY97UkNJedGv7KnDw==", + "license": "MIT", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.5", + "hoist-non-react-statics": "^3.3.2", + "universal-cookie": "^7.0.0" + }, + "peerDependencies": { + "react": ">= 16.3.0" + } + }, "node_modules/react-daum-postcode": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/react-daum-postcode/-/react-daum-postcode-3.1.3.tgz", @@ -8200,6 +8413,16 @@ "node": ">=4" } }, + "node_modules/universal-cookie": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.1.4.tgz", + "integrity": "sha512-Q+DVJsdykStWRMtXr2Pdj3EF98qZHUH/fXv/gwFz/unyToy1Ek1w5GsWt53Pf38tT8Gbcy5QNsj61Xe9TggP4g==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^0.6.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", diff --git a/client/package.json b/client/package.json index 4becabe2..4df7eb17 100644 --- a/client/package.json +++ b/client/package.json @@ -14,6 +14,7 @@ "axios": "^1.7.2", "qrcode.react": "^3.1.0", "react": "^18.3.1", + "react-cookie": "^7.1.4", "react-daum-postcode": "^3.1.3", "react-dom": "^18.3.1", "react-icons": "^5.2.1", diff --git a/client/src/apis/auth.js b/client/src/apis/auth.js index 02e63aeb..92f1d041 100644 --- a/client/src/apis/auth.js +++ b/client/src/apis/auth.js @@ -3,7 +3,8 @@ import { publicAxios } from '.'; // 회원가입 export const postJoin = async info => { try { - return await publicAxios.post('/v1/vendor/auth/join', info); + const res = await publicAxios.post('/v1/vendor/auth/join', info); + return res; } catch (err) { console.error('회원가입 실패 =>', err.response.data); throw err; @@ -13,21 +14,45 @@ export const postJoin = async info => { // 로그인 export const postLogin = async info => { try { - return await publicAxios.post('/v1/vendor/auth/login', info); + const res = await publicAxios.post('/v1/vendor/auth/login', info); + return res; } catch (err) { console.error('로그인 실패 =>', err.response.data); throw err; } }; +// 로그아웃 +export const deleteLogout = async () => { + try { + const res = await publicAxios.delete('/v1/vendor/auth/logout'); + return res; + } catch (err) { + console.log('로그아웃 실패', err.response.data); + throw err; + } +}; + // 아이디 중복확인 export const getCheckUsername = async username => { try { - return await publicAxios.get('/v1/vendor/auth/check-username', { + const res = await publicAxios.get('/v1/vendor/auth/check-username', { params: { username: username }, }); + return res; } catch (err) { console.error('아이디 중복확인 =>', err.response.data); throw err; } }; + +// 리프레쉬 토큰 재발급 +export const postRefreshToken = async () => { + try { + const res = await publicAxios.post('/v1/vendor/auth/refresh'); + return res; + } catch (err) { + console.log('리프레쉬 토큰 재발급 실패', err.response.data); + throw err; + } +}; diff --git a/client/src/apis/index.js b/client/src/apis/index.js index 890730de..ac356bfe 100644 --- a/client/src/apis/index.js +++ b/client/src/apis/index.js @@ -1,4 +1,5 @@ import axios from 'axios'; +import { postRefreshToken } from './auth'; const BASE_URL = 'http://localhost:8080/api'; axios.defaults.withCredentials = true; @@ -20,19 +21,66 @@ export const privateAxios = axios.create({ }, }); -export const UploadFileAxios = axios.create({ +export const publicUploadFileAxios = axios.create({ baseURL: BASE_URL, headers: { 'Access-Control-Allow-Origin': `${BASE_URL}`, 'Content-Type': 'multipart/form-data', - Authorization: `Bearer ${localStorage.getItem('access_token')}`, }, }); -export const publicUploadFileAxios = axios.create({ +export const UploadFileAxios = axios.create({ baseURL: BASE_URL, headers: { 'Access-Control-Allow-Origin': `${BASE_URL}`, 'Content-Type': 'multipart/form-data', + Authorization: `Bearer ${localStorage.getItem('access_token')}`, }, }); + +// 요청 인터셉터 설정 +privateAxios.interceptors.request.use( + config => { + const token = localStorage.getItem('access_token'); + if (token) { + config.headers['Authorization'] = `Bearer ${token}`; + } + return config; + }, + error => { + return Promise.reject(error); + } +); + +// 응답 인터셉터 설정 +privateAxios.interceptors.response.use( + response => { + return response; + }, + async err => { + console.log('access_token 만료'); + const { config } = err; + const originRequest = config; + if (err.response && err.response.status === 401) { + try { + const response = await postRefreshToken(); + const newAccessToken = response.data.accessToken; + + // 원래 요청을 위한 값 셋팅 + localStorage.setItem('access_token', newAccessToken); + + axios.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`; + originRequest.headers['Authorization'] = `Bearer ${newAccessToken}`; + + // 원래 요청 재시도 + console.log('리프레시 토큰 재발급 완료'); + return axios(originRequest); + } catch (err) { + console.error('리프레시 토큰 요청 실패:', err); + localStorage.removeItem('access_token'); + window.location.href = '/'; + } + } + return Promise.reject(err); + } +); diff --git a/client/src/apis/member.js b/client/src/apis/member.js new file mode 100644 index 00000000..8c0d9007 --- /dev/null +++ b/client/src/apis/member.js @@ -0,0 +1,17 @@ +import { privateAxios } from '.'; + +// 회원 목록 조회 +export const getMemberList = async () => { + try { + const res = await privateAxios.get('/v1/vendor/management/members', { + params: { + page: 1, + size: 3, + }, + }); + return res; + } catch (err) { + console.log('회원 목록 조회', err.response); + throw err; + } +}; diff --git a/client/src/components/LoginForm.jsx b/client/src/components/LoginForm.jsx index ea3b398c..202813f2 100644 --- a/client/src/components/LoginForm.jsx +++ b/client/src/components/LoginForm.jsx @@ -1,18 +1,44 @@ import { useNavigate } from 'react-router-dom'; -import InputWeb from './common/InputWeb'; +import InputWeb from './common/inputs/InputWeb'; import { postLogin } from '@/apis/auth'; +import { useState } from 'react'; const LoginForm = () => { const navigate = useNavigate(); + const [vendorFormData, setVendorFormData] = useState({ + username: null, + password: null, + }); - const handleMoveSignup = () => { - navigate('/signup'); + // Todo + // 로그인 실패시 로그인 실패 경고 빨간글씨 + // 로그인 성공 시 Alert창 + + // 사용자 입력값 + const handleChangeValue = e => { + const { id, value } = e.target; + setVendorFormData(prev => ({ ...prev, [id]: value == '' ? null : value })); + }; + + // 공백입력 막기 + const handleKeyDown = e => { + e.key === ' ' && e.preventDefault(); }; + // 폼 제출 핸들러 + const handleSubmit = e => { + e.preventDefault(); + axiosLogin(vendorFormData); + }; + + // 로그인 API const axiosLogin = async data => { try { - console.log('!----로그인 성공----!'); // 삭제예정 const res = await postLogin(data); + console.log('!----로그인 성공----!'); // 삭제예정 + const accessToken = res.data.accessToken; + localStorage.setItem('access_token', accessToken); + navigate('/vendor/dashboard'); } catch (err) { console.error('axiosJoin => ', err.response.data); } @@ -20,7 +46,7 @@ const LoginForm = () => { return (
-
+

Hyosung CMS#

수납 및 자금 관리 통합 비즈니스 솔루션

@@ -30,12 +56,17 @@ const LoginForm = () => { type='text' placeholder='아이디를 입력해 주세요.' classInput='mb-4' + onChange={handleChangeValue} + onKeyDown={handleKeyDown} />
@@ -43,19 +74,21 @@ const LoginForm = () => { / 비밀번호 찾기
- +
아직 회원이 아니신가요? { - handleMoveSignup(); + navigate('/signup'); }}> 아이디 만들기
-
+
); }; diff --git a/client/src/components/SignupForm.jsx b/client/src/components/SignupForm.jsx index 92d0c542..39839cec 100644 --- a/client/src/components/SignupForm.jsx +++ b/client/src/components/SignupForm.jsx @@ -1,11 +1,10 @@ import { useNavigate } from 'react-router-dom'; -import InputWeb from './common/InputWeb'; +import InputWeb from './common/inputs/InputWeb'; import { getCheckUsername, postJoin } from '@/apis/auth'; import { useState } from 'react'; const SignupForm = () => { const navigate = useNavigate(); - const [showPassword, setShowPassword] = useState(false); const [checkedUsername, setCheckedUsername] = useState(''); const [vendorFormData, setVendorFormData] = useState({ name: null, @@ -28,16 +27,9 @@ const SignupForm = () => { // 유선 전화번호 정규식 // 부서명 정규식 - // 로그인 페이지 이동 - const handleMoveLogin = () => { - navigate('/login'); - }; - // 공백입력 막기 const handleKeyDown = e => { - if (e.key === ' ') { - e.preventDefault(); - } + e.key === ' ' && e.preventDefault(); }; // 사용자 입력값 @@ -102,20 +94,12 @@ const SignupForm = () => { return name && username && password && email && phone && department; }; - // 비밀번호 표시 여부 - const handleTogglePassword = () => { - setShowPassword(!showPassword); - }; - // 회원강비 API const axiosJoin = async data => { try { const res = await postJoin(data); console.log('!----회원가입 성공----!'); // 삭제예정 - if (res.status === 201) { - console.log('성공'); - handleMoveLogin(); - } + navigate('/login'); } catch (err) { console.error('axiosJoin => ', err.response.data); } @@ -159,17 +143,17 @@ const SignupForm = () => { />
-
+
{ { onKeyDown={handleKeyDown} autoComplete='off' /> - Toggle password visibility
{ { - handleMoveLogin(); + navigate('/login'); }}> 로그인
diff --git a/client/src/components/common/InputWeb.jsx b/client/src/components/common/InputWeb.jsx deleted file mode 100644 index c95749c8..00000000 --- a/client/src/components/common/InputWeb.jsx +++ /dev/null @@ -1,30 +0,0 @@ -const InputWeb = ({ - id, - label, - required, - type = 'text', - placeholder, - classContainer = '', - classLabel = '', - classInput = '', - ...props -}) => { - return ( -
- - -
- ); -}; - -export default InputWeb; diff --git a/client/src/components/common/StatusNextButton.jsx b/client/src/components/common/buttons/StatusNextButton.jsx similarity index 71% rename from client/src/components/common/StatusNextButton.jsx rename to client/src/components/common/buttons/StatusNextButton.jsx index 80ae0b97..29fe7b20 100644 --- a/client/src/components/common/StatusNextButton.jsx +++ b/client/src/components/common/buttons/StatusNextButton.jsx @@ -12,11 +12,11 @@ const NextButton = ({ onClick, status, type = '', end }) => { buttonText = '확인'; } - const width = type === 'memberRegister' ? 'w-28' : 'flex-1'; - return ( - - ); diff --git a/client/src/components/common/StatusPreButton.jsx b/client/src/components/common/buttons/StatusPreButton.jsx similarity index 56% rename from client/src/components/common/StatusPreButton.jsx rename to client/src/components/common/buttons/StatusPreButton.jsx index 00a884a6..ef91e1bd 100644 --- a/client/src/components/common/StatusPreButton.jsx +++ b/client/src/components/common/buttons/StatusPreButton.jsx @@ -3,10 +3,11 @@ const PreviousButton = ({ onClick, status, start, type = '', end }) => { return null; } - const width = type === 'memberRegister' ? 'w-28' : 'w-1/3'; - return ( - ); diff --git a/client/src/components/common/AddressInput.jsx b/client/src/components/common/inputs/AddressInput.jsx similarity index 100% rename from client/src/components/common/AddressInput.jsx rename to client/src/components/common/inputs/AddressInput.jsx diff --git a/client/src/components/common/CheckBox.jsx b/client/src/components/common/inputs/CheckBox.jsx similarity index 100% rename from client/src/components/common/CheckBox.jsx rename to client/src/components/common/inputs/CheckBox.jsx diff --git a/client/src/components/common/Input.jsx b/client/src/components/common/inputs/Input.jsx similarity index 100% rename from client/src/components/common/Input.jsx rename to client/src/components/common/inputs/Input.jsx diff --git a/client/src/components/common/inputs/InputWeb.jsx b/client/src/components/common/inputs/InputWeb.jsx new file mode 100644 index 00000000..8e6fdb08 --- /dev/null +++ b/client/src/components/common/inputs/InputWeb.jsx @@ -0,0 +1,55 @@ +import { useRef, useState } from 'react'; + +const InputWeb = ({ + id, + label, + required, + type = 'text', + placeholder, + classContainer = '', + classLabel = '', + classInput = '', + ...props +}) => { + const [showPassword, setShowPassword] = useState(false); + const inputRef = useRef(null); + + // 비밀번호 표시 여부 + const handleTogglePassword = () => { + setShowPassword(!showPassword); + if (inputRef.current) { + inputRef.current.focus(); + } + }; + + return ( +
+ +
+ + {type === 'password' && ( + Toggle password visibility + )} +
+
+ ); +}; + +export default InputWeb; diff --git a/client/src/components/common/RadioGroup.jsx b/client/src/components/common/inputs/RadioGroup.jsx similarity index 100% rename from client/src/components/common/RadioGroup.jsx rename to client/src/components/common/inputs/RadioGroup.jsx diff --git a/client/src/components/common/Loading.jsx b/client/src/components/common/member/Loading.jsx similarity index 100% rename from client/src/components/common/Loading.jsx rename to client/src/components/common/member/Loading.jsx diff --git a/client/src/components/common/Success.jsx b/client/src/components/common/member/Success.jsx similarity index 100% rename from client/src/components/common/Success.jsx rename to client/src/components/common/member/Success.jsx diff --git a/client/src/components/common/Table.jsx b/client/src/components/common/tables/Table.jsx similarity index 100% rename from client/src/components/common/Table.jsx rename to client/src/components/common/tables/Table.jsx diff --git a/client/src/components/common/TableCol.jsx b/client/src/components/common/tables/TableCol.jsx similarity index 100% rename from client/src/components/common/TableCol.jsx rename to client/src/components/common/tables/TableCol.jsx diff --git a/client/src/components/common/TableRow.jsx b/client/src/components/common/tables/TableRow.jsx similarity index 100% rename from client/src/components/common/TableRow.jsx rename to client/src/components/common/tables/TableRow.jsx diff --git a/client/src/components/common/TableSearch.jsx b/client/src/components/common/tables/TableSearch.jsx similarity index 100% rename from client/src/components/common/TableSearch.jsx rename to client/src/components/common/tables/TableSearch.jsx diff --git a/client/src/components/member/simpConsent/BasicInfo.jsx b/client/src/components/member/simpConsent/BasicInfo.jsx index 8c6dec34..857721e6 100644 --- a/client/src/components/member/simpConsent/BasicInfo.jsx +++ b/client/src/components/member/simpConsent/BasicInfo.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; -import Input from '@/components/common/Input'; -import AddressInput from '@/components/common/AddressInput'; +import Input from '@/components/common/inputs/Input'; +import AddressInput from '@/components/common/inputs/AddressInput'; const BasicInfo = ({ userData, setUserData }) => { const [localData, setLocalData] = useState({ diff --git a/client/src/components/member/simpConsent/ContractInfo.jsx b/client/src/components/member/simpConsent/ContractInfo.jsx index 240152cd..2654d514 100644 --- a/client/src/components/member/simpConsent/ContractInfo.jsx +++ b/client/src/components/member/simpConsent/ContractInfo.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import Input from '@/components/common/Input'; +import Input from '@/components/common/inputs/Input'; import SelectField from '@/components/common/SelectField'; import ProductItem from '@/components/common/ProductItem'; import { useUserDataStore } from '@/stores/useUserDataStore'; diff --git a/client/src/components/member/simpConsent/PaymentCMS.jsx b/client/src/components/member/simpConsent/PaymentCMS.jsx index 43f486e1..e07fec4c 100644 --- a/client/src/components/member/simpConsent/PaymentCMS.jsx +++ b/client/src/components/member/simpConsent/PaymentCMS.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import Input from '@/components/common/Input'; +import Input from '@/components/common/inputs/Input'; import SelectField from '@/components/common/SelectField'; import { useUserDataStore } from '@/stores/useUserDataStore'; diff --git a/client/src/components/member/simpConsent/PaymentCard.jsx b/client/src/components/member/simpConsent/PaymentCard.jsx index 39b6c0d9..6ab8b352 100644 --- a/client/src/components/member/simpConsent/PaymentCard.jsx +++ b/client/src/components/member/simpConsent/PaymentCard.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import Input from '@/components/common/Input'; +import Input from '@/components/common/inputs/Input'; import { useUserDataStore } from '@/stores/useUserDataStore'; const PaymentCard = () => { diff --git a/client/src/components/member/simpConsent/PaymentInfo.jsx b/client/src/components/member/simpConsent/PaymentInfo.jsx index b439da08..ee959176 100644 --- a/client/src/components/member/simpConsent/PaymentInfo.jsx +++ b/client/src/components/member/simpConsent/PaymentInfo.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import RadioGroup from '@/components/common/RadioGroup'; +import RadioGroup from '@/components/common/inputs/RadioGroup'; import PaymentCard from '@/components/member/simpConsent/PaymentCard'; import PaymentCMS from '@/components/member/simpConsent/PaymentCMS'; import { useUserDataStore } from '@/stores/useUserDataStore'; diff --git a/client/src/pages/member/InvoicePage.jsx b/client/src/pages/member/InvoicePage.jsx index 1977013c..04b37054 100644 --- a/client/src/pages/member/InvoicePage.jsx +++ b/client/src/pages/member/InvoicePage.jsx @@ -1,8 +1,8 @@ import Main from '@/components/member/invoice/Main'; import CheckInvoice from '@/components/member/invoice/CheckInvoice'; -import NextButton from '@/components/common/StatusNextButton'; -import PreviousButton from '@/components/common/StatusPreButton'; +import NextButton from '@/components/common/buttons/StatusNextButton'; +import PreviousButton from '@/components/common/buttons/StatusPreButton'; import useStatusStepper from '@/hooks/useStatusStepper'; import { useStatusStore } from '@/stores/useStatusStore'; diff --git a/client/src/pages/member/PaymentAccountPage.jsx b/client/src/pages/member/PaymentAccountPage.jsx index bdd582fa..2a202e40 100644 --- a/client/src/pages/member/PaymentAccountPage.jsx +++ b/client/src/pages/member/PaymentAccountPage.jsx @@ -1,10 +1,10 @@ import ChooseBank from '@/components/member/account/ChooseBank'; import AccountInfo from '@/components/member/account/AccountInfo'; -import Loading from '@/components/common/Loading'; -import Success from '@/components/common/Success'; +import Loading from '@/components/common/member/Loading'; +import Success from '@/components/common/member/Success'; -import NextButton from '@/components/common/StatusNextButton'; -import PreviousButton from '@/components/common/StatusPreButton'; +import NextButton from '@/components/common/buttons/StatusNextButton'; +import PreviousButton from '@/components/common/buttons/StatusPreButton'; import { useStatusStore } from '@/stores/useStatusStore'; import useStatusStepper from '@/hooks/useStatusStepper'; diff --git a/client/src/pages/member/PaymentCardPage.jsx b/client/src/pages/member/PaymentCardPage.jsx index 61784596..4b0db6cd 100644 --- a/client/src/pages/member/PaymentCardPage.jsx +++ b/client/src/pages/member/PaymentCardPage.jsx @@ -1,10 +1,10 @@ import CardInfo from '@/components/member/card/CardInfo'; import ChooseCard from '@/components/member/card/ChooseCard'; -import Loading from '@/components/common/Loading'; -import Success from '@/components/common/Success'; +import Loading from '@/components/common/member/Loading'; +import Success from '@/components/common/member/Success'; -import NextButton from '@/components/common/StatusNextButton'; -import PreviousButton from '@/components/common/StatusPreButton'; +import NextButton from '@/components/common/buttons/StatusNextButton'; +import PreviousButton from '@/components/common/buttons/StatusPreButton'; import useStatusStepper from '@/hooks/useStatusStepper'; import { useStatusStore } from '@/stores/useStatusStore'; diff --git a/client/src/pages/member/PaymentChoosePage.jsx b/client/src/pages/member/PaymentChoosePage.jsx index db85c16e..8678b76e 100644 --- a/client/src/pages/member/PaymentChoosePage.jsx +++ b/client/src/pages/member/PaymentChoosePage.jsx @@ -1,5 +1,5 @@ -import NextButton from '@/components/common/StatusNextButton'; -import PreviousButton from '@/components/common/StatusPreButton'; +import NextButton from '@/components/common/buttons/StatusNextButton'; +import PreviousButton from '@/components/common/buttons/StatusPreButton'; import { useStatusStore } from '@/stores/useStatusStore'; import useStatusStepper from '@/hooks/useStatusStepper'; diff --git a/client/src/pages/member/PaymentVirtualPage.jsx b/client/src/pages/member/PaymentVirtualPage.jsx index 76e3a2c5..f10a15dd 100644 --- a/client/src/pages/member/PaymentVirtualPage.jsx +++ b/client/src/pages/member/PaymentVirtualPage.jsx @@ -1,9 +1,9 @@ import CheckVirtual from '@/components/member/virtualAccount/CheckVirtual'; -import Loading from '@/components/common/Loading'; -import Success from '@/components/common/Success'; +import Loading from '@/components/common/member/Loading'; +import Success from '@/components/common/member/Success'; -import NextButton from '@/components/common/StatusNextButton'; -import PreviousButton from '@/components/common/StatusPreButton'; +import NextButton from '@/components/common/buttons/StatusNextButton'; +import PreviousButton from '@/components/common/buttons/StatusPreButton'; import { useStatusStore } from '@/stores/useStatusStore'; import useStatusStepper from '@/hooks/useStatusStepper'; diff --git a/client/src/pages/member/SimpConsentPage.jsx b/client/src/pages/member/SimpConsentPage.jsx index 911b1734..43b6f1db 100644 --- a/client/src/pages/member/SimpConsentPage.jsx +++ b/client/src/pages/member/SimpConsentPage.jsx @@ -4,11 +4,11 @@ import BasicInfo from '@/components/member/simpConsent/BasicInfo'; import ContractInfo from '@/components/member/simpConsent/ContractInfo'; import PaymentInfo from '@/components/member/simpConsent/PaymentInfo'; import Signature from '@/components/member/simpConsent/Signature'; -import Loading from '@/components/common/Loading'; -import Success from '@/components/common/Success'; +import Loading from '@/components/common/member/Loading'; +import Success from '@/components/common/member/Success'; -import NextButton from '@/components/common/StatusNextButton'; -import PreviousButton from '@/components/common/StatusPreButton'; +import NextButton from '@/components/common/buttons/StatusNextButton'; +import PreviousButton from '@/components/common/buttons/StatusPreButton'; import { useStatusStore } from '@/stores/useStatusStore'; import { useUserDataStore } from '@/stores/useUserDataStore'; diff --git a/client/src/pages/vendor/DashBoardPage.jsx b/client/src/pages/vendor/DashBoardPage.jsx index 7a0ddd35..134a636a 100644 --- a/client/src/pages/vendor/DashBoardPage.jsx +++ b/client/src/pages/vendor/DashBoardPage.jsx @@ -1,4 +1,17 @@ +import { getMemberList } from '@/apis/member'; + const DashBoardPage = () => { + // 회원 목록 조회 + const axiosMemberList = async () => { + try { + const res = await getMemberList(); + console.log('!----회원 목록 조회 성공----!'); // 삭제예정 + console.log(res.data); + } catch (err) { + console.error('axiosMemberList => ', err.response.data); + } + }; + return ( <>
@@ -17,6 +30,7 @@ const DashBoardPage = () => {

DashBoard

+
); diff --git a/client/src/pages/vendor/member/MemberListPage.jsx b/client/src/pages/vendor/member/MemberListPage.jsx index be0a74df..3ce28e3b 100644 --- a/client/src/pages/vendor/member/MemberListPage.jsx +++ b/client/src/pages/vendor/member/MemberListPage.jsx @@ -1,4 +1,4 @@ -import Table from '@/components/common/Table'; +import Table from '@/components/common/tables/Table'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; const cols = [ diff --git a/client/src/pages/vendor/member/MemberRegisterPage.jsx b/client/src/pages/vendor/member/MemberRegisterPage.jsx index e7a1ff00..00467b31 100644 --- a/client/src/pages/vendor/member/MemberRegisterPage.jsx +++ b/client/src/pages/vendor/member/MemberRegisterPage.jsx @@ -1,5 +1,5 @@ -import NextButton from '@/components/common/StatusNextButton'; -import PreviousButton from '@/components/common/StatusPreButton'; +import NextButton from '@/components/common/buttons/StatusNextButton'; +import PreviousButton from '@/components/common/buttons/StatusPreButton'; import RegisterBasicInfo from '@/components/vendor/member/RegisterBasicInfo'; import RegisterBillingInfo from '@/components/vendor/member/RegisterBillingInfo'; import RegisterContractInfo from '@/components/vendor/member/RegisterContractInfo'; diff --git a/client/src/pages/vendor/product/ProductListPage.jsx b/client/src/pages/vendor/product/ProductListPage.jsx index bd942bca..477431c5 100644 --- a/client/src/pages/vendor/product/ProductListPage.jsx +++ b/client/src/pages/vendor/product/ProductListPage.jsx @@ -1,5 +1,5 @@ import { getProductDetail, getProductList } from '@/apis/product'; -import Table from '@/components/common/Table'; +import Table from '@/components/common/tables/Table'; import ProductModal from '@/components/vendor/modal/ProductModal'; import { useEffect, useState } from 'react'; diff --git a/client/src/pages/vendor/setting/SettingSimpConsentPage.jsx b/client/src/pages/vendor/setting/SettingSimpConsentPage.jsx index 3795a708..8e6a9206 100644 --- a/client/src/pages/vendor/setting/SettingSimpConsentPage.jsx +++ b/client/src/pages/vendor/setting/SettingSimpConsentPage.jsx @@ -1,7 +1,7 @@ import SimpConsentQrUrlModal from '@/components/vendor/modal/SimpConsentQrUrlModal'; import { useState } from 'react'; import SelectField from '@/components/common/SelectField'; -import Checkbox from '@/components/common/CheckBox'; +import Checkbox from '@/components/common/inputs/CheckBox'; const SettingSimpConsentPage = () => { const [isShowModal, setIsShowModal] = useState(false); diff --git a/client/src/stores/useStatusStore.js b/client/src/stores/useStatusStore.js index bd7c8b27..ca759f65 100644 --- a/client/src/stores/useStatusStore.js +++ b/client/src/stores/useStatusStore.js @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import { persist, createJSONStorage } from 'zustand/middleware'; +import { persist } from 'zustand/middleware'; export const useStatusStore = create( persist( diff --git a/client/src/utils/regex.js b/client/src/utils/regex.js new file mode 100644 index 00000000..b82b1754 --- /dev/null +++ b/client/src/utils/regex.js @@ -0,0 +1,19 @@ +export const regex = { + // 1. 회원명 + name: /^[가-힣a-zA-Z]{1,40}$/, + + // 2. 아이디 + username: /^[a-z0-9]{5,20}$/, + + // 3. 비밀번호 + password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()\-_=+<>?]).{8,16}$/, + + // 4. 이메일 + email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, + + // 5. 휴대전화번호 + phone: /^01(?:0|1|[6-9])-(?:\d{3}|\d{4})-\d{4}$/, + + // 6. 유선전화번호 + homePhone: /^(02|0[3-6]{1}[1-5]{1})-([0-9]{3,4})-[0-9]{4}$/, +}; diff --git a/client/src/utils/validators.js b/client/src/utils/validators.js new file mode 100644 index 00000000..5242e7de --- /dev/null +++ b/client/src/utils/validators.js @@ -0,0 +1,26 @@ +import { regex } from './regex'; + +export const validateField = (id, value) => { + switch (id) { + case 'name': // 1.회원명 정규식 + return regex.name.test(value); + + case 'username': // 2.아이디 정규식 + return regex.username.test(value); + + case 'password': // 3.비밀번호 정규식 + return regex.password.test(value); + + case 'email': // 4.이메일 정규식 + return regex.email.test(value); + + case 'phone': // 5.휴대전화 정규식 + return regex.phone.test(value); + + case 'homePhone': // 4.유선전화 정규식 + return regex.homePhone.test(value); + + default: + return true; + } +}; diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/member/controller/MemberController.java b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/member/controller/MemberController.java index c7471a23..24fdf8d9 100644 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/member/controller/MemberController.java +++ b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/member/controller/MemberController.java @@ -29,8 +29,8 @@ public class MemberController { * */ @GetMapping("/members") public SortPageDto.Res getMemberList(@AuthenticationPrincipal VendorUserDetailsDto userDetails, SortPageDto.Req pageable) { -// String username1 = userDetails.getUsername(); - String username = "vendor1"; + String username = userDetails.getUsername(); +// String username = "vendor1"; return memberService.findMemberListItem(username, pageable); } diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/dto/VendorUserDetailsDto.java b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/dto/VendorUserDetailsDto.java index cf57b7b4..ea1aa64c 100644 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/dto/VendorUserDetailsDto.java +++ b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/dto/VendorUserDetailsDto.java @@ -3,14 +3,12 @@ import java.util.ArrayList; import java.util.Collection; -import lombok.ToString; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import kr.or.kosa.cmsplusmain.domain.vendor.entity.Vendor; import lombok.RequiredArgsConstructor; -@ToString @RequiredArgsConstructor public class VendorUserDetailsDto implements UserDetails { @@ -62,4 +60,8 @@ public boolean isEnabled() { public Long getId() { return vendor.getId(); } + + public String getName() { + return vendor.getName(); + } } diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/JWTFilter.java b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/JWTFilter.java index 7626de5e..ca3f1039 100644 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/JWTFilter.java +++ b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/JWTFilter.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.io.PrintWriter; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -18,6 +19,7 @@ import kr.or.kosa.cmsplusmain.domain.vendor.entity.Vendor; import lombok.RequiredArgsConstructor; +@Slf4j @RequiredArgsConstructor public class JWTFilter extends OncePerRequestFilter { @@ -28,6 +30,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse FilterChain filterChain) throws ServletException, IOException { String authorization = request.getHeader("Authorization"); + log.info("JWTFilter authorization : " + authorization); // Authorization 헤더 검증 if (authorization == null || !authorization.startsWith("Bearer ")) { @@ -41,7 +44,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse try { jwtUtil.isExpired(accessToken); } catch (ExpiredJwtException e) { - + log.info("JWTFilter access token expired"); // response body PrintWriter writer = response.getWriter(); writer.print("access token expired"); @@ -55,7 +58,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // 토큰이 access인지 확인 if (!category.equals("access")) { - + log.info("JWTFilter access token이 아님"); + // response body PrintWriter writer = response.getWriter(); writer.print("invalid access token"); diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/JWTUtil.java b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/JWTUtil.java index 9f8b53da..d322c6c8 100644 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/JWTUtil.java +++ b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/JWTUtil.java @@ -49,8 +49,6 @@ public String getRole(String token) { .get("role", String.class); } - - public String getCategory(String token) { return Jwts.parser() diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/LoginFilter.java b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/LoginFilter.java index 355cbc39..e9c60b58 100644 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/LoginFilter.java +++ b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/jwt/LoginFilter.java @@ -66,6 +66,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR Authentication authentication) throws IOException { VendorUserDetailsDto vendorUserDetails = (VendorUserDetailsDto)authentication.getPrincipal(); String username = vendorUserDetails.getUsername(); + String name = vendorUserDetails.getName(); Long id = vendorUserDetails.getId(); Collection authorities = authentication.getAuthorities(); @@ -96,6 +97,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR AccessTokenRes accessTokenRes = AccessTokenRes.builder() .accessToken(accessToken) .username(username) + .name(name) .role(role.replace("ROLE_", "")) .build(); diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/service/VendorService.java b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/service/VendorService.java index 02559337..f598fbbb 100644 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/service/VendorService.java +++ b/server/src/main/java/kr/or/kosa/cmsplusmain/domain/vendor/service/VendorService.java @@ -4,6 +4,7 @@ import java.util.concurrent.TimeUnit; import jakarta.servlet.http.Cookie; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; @@ -19,6 +20,7 @@ import kr.or.kosa.cmsplusmain.domain.vendor.repository.VendorCustomRepository; import lombok.RequiredArgsConstructor; +@Slf4j @Service @RequiredArgsConstructor @Transactional(readOnly = true) @@ -56,14 +58,14 @@ public RefreshTokenRes refresh(HttpServletRequest request, HttpServletResponse r // Cookie에서 refresh token 접근 for(Cookie cookie : cookies) { - System.out.println("cookies == " + cookie.getValue()); + System.out.println("cookies == "+ cookie.getName() + " : " + cookie.getValue()); if (cookie.getName().equals("refresh_token")) { refreshToken = cookie.getValue(); } } - - // authorizationRefresh 헤더 검증 + // refreshToken cookie 검증 if (refreshToken == null) { + log.info("refreshToken cookie 검증 : refresh token null"); throw new IllegalArgumentException("refresh token null"); } @@ -71,6 +73,7 @@ public RefreshTokenRes refresh(HttpServletRequest request, HttpServletResponse r try { jwtUtil.isExpired(refreshToken); } catch (ExpiredJwtException e) { + log.info("refreshToken 기간 만료"); throw new IllegalArgumentException("Invaild refresh token"); } @@ -78,12 +81,14 @@ public RefreshTokenRes refresh(HttpServletRequest request, HttpServletResponse r // 토큰이 refresh인지 확인 if (!category.equals("refresh")) { + log.info("refreshToken이 아님"); throw new IllegalArgumentException("Invalid refresh token"); } // 토큰이 Redis에 저장되어 있는지 확인 String storedRefreshToken = redisTemplate.opsForValue().get(jwtUtil.getUsername(refreshToken)); if (storedRefreshToken == null || !storedRefreshToken.equals(refreshToken)) { + log.info("refreshToken이 래디스에 없음"); throw new IllegalArgumentException("Invalid refresh token"); } @@ -92,7 +97,7 @@ public RefreshTokenRes refresh(HttpServletRequest request, HttpServletResponse r Long id = Long.valueOf(jwtUtil.getId(refreshToken)); // JWT 토큰 생성 - String newAccessToken = jwtUtil.createJwt("access", username, id, role, 10 * 60 * 1000L); + String newAccessToken = jwtUtil.createJwt("access", username, id, role, 30 * 60 * 1000L); String newRefreshToken = jwtUtil.createJwt("refresh", username, id, role, 24 * 60 * 60 * 1000L); // 기존 토큰을 Redis에서 제거 후 새로운 토큰 저장