From a63c16a7507fbcb5334bbfe2a6fca79e8e8eaa68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=91=ED=98=84?= Date: Mon, 11 Sep 2023 16:03:20 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=8B=9C=20?= =?UTF-8?q?=EC=97=91=EC=84=B8=EC=8A=A4=ED=86=A0=ED=81=B0=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=97=91=EC=84=B8=EC=8A=A4=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=EC=9D=98=20=EB=B0=B8=EB=A5=98=EB=A5=BC=20=EB=94=94=EC=BD=94?= =?UTF-8?q?=EB=94=A9=20=EB=94=94=EC=BD=94=EB=94=A9=20=EB=90=9C=20=EC=9E=90?= =?UTF-8?q?=EB=A3=8C=EC=97=90=EC=84=9C=20memberId=EB=A5=BC=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=ED=95=B4=20=EC=A0=84=EC=97=AD=EB=B3=80=EC=88=98?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A4=EC=A0=95=20Issues=20#15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package-lock.json | 102 ++----- client/package.json | 2 + client/src/components/Logins/EmailLogin.tsx | 28 +- client/src/components/Logins/OAuthLogin.tsx | 248 +++++++++--------- .../src/components/Profile/profileModal.tsx | 18 +- 5 files changed, 170 insertions(+), 228 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index bd08fe18..e91c2976 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -13,8 +13,10 @@ "boxicons": "^2.1.4", "echarts": "^5.4.3", "echarts-for-react": "^3.0.2", + "gapi-script": "^1.2.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-google-login": "^5.2.2", "react-query": "^3.39.3", "react-redux": "^8.1.2", "react-router-dom": "^6.15.0", @@ -2425,18 +2427,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", @@ -2538,14 +2528,6 @@ "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, - "node_modules/@types/node": { - "version": "20.5.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", - "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", @@ -2565,7 +2547,7 @@ "version": "18.2.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", - "devOptional": true, + "dev": true, "dependencies": { "@types/react": "*" } @@ -3123,14 +3105,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3243,14 +3217,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3929,6 +3895,11 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/gapi-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gapi-script/-/gapi-script-1.2.0.tgz", + "integrity": "sha512-NKTVKiIwFdkO1j1EzcrWu/Pz7gsl1GmBmgh+qhuV2Ytls04W/Eg5aiBL91SCiBM9lU0PMu7p1hTVxhh1rPT5Lw==" + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -4696,6 +4667,20 @@ "react": "^18.2.0" } }, + "node_modules/react-google-login": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/react-google-login/-/react-google-login-5.2.2.tgz", + "integrity": "sha512-JUngfvaSMcOuV0lFff7+SzJ2qviuNMQdqlsDJkUM145xkGPVIfqWXq9Ui+2Dr6jdJWH5KYdynz9+4CzKjI5u6g==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dependencies": { + "@types/react": "*", + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": "^16 || ^17", + "react-dom": "^16 || ^17" + } + }, "node_modules/react-interactive": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-interactive/-/react-interactive-0.8.3.tgz", @@ -5092,17 +5077,6 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -5111,18 +5085,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -5217,26 +5179,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/terser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", - "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/client/package.json b/client/package.json index eb5081ea..9ddb83fe 100644 --- a/client/package.json +++ b/client/package.json @@ -15,8 +15,10 @@ "boxicons": "^2.1.4", "echarts": "^5.4.3", "echarts-for-react": "^3.0.2", + "gapi-script": "^1.2.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-google-login": "^5.2.2", "react-query": "^3.39.3", "react-redux": "^8.1.2", "react-router-dom": "^6.15.0", diff --git a/client/src/components/Logins/EmailLogin.tsx b/client/src/components/Logins/EmailLogin.tsx index 32d615f5..6c30d0f9 100644 --- a/client/src/components/Logins/EmailLogin.tsx +++ b/client/src/components/Logins/EmailLogin.tsx @@ -35,27 +35,33 @@ const EmailLoginModal: React.FC = ({ onClose, onLogin }) = // 로그인 버튼 클릭 핸들러 const handleLoginClick = async () => { try { - // 백엔드에 로그인 요청 const response = await axios.post("http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/members/login", { email, password, }); if (response.status === 200) { - - // 로그인 상태로 만들기 - dispatch(setLoginState(true)); - - // memberId 상태 업데이트하기 - dispatch(updateMemberId(response.data.memberId)); - const authToken = response.headers['authorization']; - const accessToken = response.headers['accessToken']; + + // JWT 토큰 디코딩 + const base64Url = authToken.split('.')[1]; + const base64 = base64Url.replace('-', '+').replace('_', '/'); + const decodedToken = JSON.parse(window.atob(base64)); + + // memberId 상태 업데이트하기 + if (decodedToken && decodedToken.memberId) { + dispatch(updateMemberId(decodedToken.memberId)); + } + const refreshToken = response.headers['refreshToken']; - + + // 로그인 상태로 만들기 + dispatch(setLoginState(true)); + // 토큰들을 로컬 스토리지에 저장 if(authToken) localStorage.setItem('authToken', authToken); - if(accessToken) localStorage.setItem('accessToken', accessToken); + if(refreshToken) localStorage.setItem('refreshToken', refreshToken); + onLogin(); onClose(); } else { diff --git a/client/src/components/Logins/OAuthLogin.tsx b/client/src/components/Logins/OAuthLogin.tsx index b40f5615..a38e6052 100644 --- a/client/src/components/Logins/OAuthLogin.tsx +++ b/client/src/components/Logins/OAuthLogin.tsx @@ -1,12 +1,11 @@ -// client/src/components/Logins/OAuthLogin.tsx +import React from 'react'; import styled from 'styled-components'; import googleLogo from '../../asset/images/GoogleLogo.svg'; import kakaoLogo from '../../asset/images/KakaoLogo.svg'; import axios from 'axios'; +import { GoogleLogin, GoogleLoginResponse, GoogleLoginResponseOffline } from 'react-google-login'; -// OAuth 로그인 모달 컴포넌트 const OAuthLoginModal: React.FC = ({ onClose, onEmailLoginClick, onEmailSignupClick }) => { - // 텍스트 상수 const titleText = "로그인"; const googleLoginText = "구글로 로그인"; const kakaoLoginText = "카카오로 로그인"; @@ -14,66 +13,55 @@ const OAuthLoginModal: React.FC = ({ onClose, onEmailLoginClick const emailLoginText = "이메일로 로그인"; const emailSignupText = "이메일로 회원가입"; - // 카카오 로그인 핸들러 - const handleGoogleLogin = async () => { - try { - const response = await axios.get('/oauth2/authorization/google'); - if (response.status === 200) { - // 200 상태 코드를 받으면 주소 데이터를 사용하여 리다이렉트 - const redirectUri = response.data.uri; // 백엔드에서 'url' 키로 주소 데이터를 제공한다고 가정 - window.location.href = redirectUri; - } else { - console.error("Error logging in with Google, unexpected status code:", response.status); - } - } catch (error) { - console.error("Error logging in with Google:", error); - } - }; - - - // 카카오 로그인 핸들러 + const responseGoogle = (response: GoogleLoginResponse | GoogleLoginResponseOffline) => { + console.log(response); + // 여기서 응답 데이터를 처리하거나 백엔드에 전송할 수 있습니다. + } + const handleKakaoLogin = async () => { - try { - const response = await axios.get('/oauth2/authorization/kakao'); - if (response.status === 200) { - // 200 상태 코드를 받으면 주소 데이터를 사용하여 리다이렉트 - const redirectUri = response.data.uri; // 백엔드에서 'url' 키로 주소 데이터를 제공한다고 가정 - window.location.href = redirectUri; - } else { - console.error("Error logging in with Kakao, unexpected status code:", response.status); - } - } catch (error) { - console.error("Error logging in with Kakao:", error); - } - }; - - // 모달 반환 + try { + const response = await axios.get('/oauth2/authorization/kakao'); + if (response.status === 200) { + const redirectUri = response.data.uri; + window.location.href = redirectUri; + } else { + console.error("Error logging in with Kakao, unexpected status code:", response.status); + } + } catch (error) { + console.error("Error logging in with Kakao:", error); + } + }; + return ( - - - × - {titleText} - - - {googleLoginText} - - - - {kakaoLoginText} - - {orText} - - {emailLoginText} - {emailSignupText} - - - + + + × + {titleText} + + + + + + {kakaoLoginText} + + {orText} + + {emailLoginText} + {emailSignupText} + + + ); }; -export default OAuthLoginModal; - -// 로그인 모달 인터페이스 interface LoginModalProps { onClose: () => void; onEmailLoginClick: () => void; @@ -82,96 +70,96 @@ interface LoginModalProps { onHoldingsClick: () => void; } -// 스타일 컴포넌트 -const OrText = styled.span` - margin: 20px 0; - color: grey; -`; - -//제목 "로그인" -const Title = styled.h2` - margin-bottom: 20px; - font-size: 1.6rem; - font-weight: 400; -`; - -//배경 어둡게 const ModalBackground = styled.div` - display: flex; - justify-content: center; - align-items: center; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); `; -//모달 창 CSS const ModalContainer = styled.div` - position: relative; - background-color: white; - padding: 20px; - width: 400px; - border-radius: 10px; - display: flex; - flex-direction: column; - align-items: center; + position: relative; + background-color: white; + padding: 20px; + width: 400px; + border-radius: 10px; + display: flex; + flex-direction: column; + align-items: center; `; -//닫기 버튼 const CloseButton = styled.button` - position: absolute; - top: 10px; - right: 10px; - background: #FFFFFF; - border: 1px solid lightgray; - font-size: 1.5rem; - cursor: pointer; + position: absolute; + top: 10px; + right: 10px; + background: #FFFFFF; + border: 1px solid lightgray; + font-size: 1.5rem; + cursor: pointer; `; -//OAuth 버튼 -const OAuthButton = styled.button` - margin: 10px 0; - padding: 10px 20px; - background-color: #FFFFFF; - border: 1px solid lightgray; - border-radius: 5px; - cursor: pointer; - width: 300px; - display: flex; - align-items: center; - justify-content: center; +const Title = styled.h2` + margin-bottom: 20px; + font-size: 1.6rem; + font-weight: 400; `; -const GoogleButton = styled(OAuthButton)``; +const OrText = styled.span` + margin: 20px 0; + color: grey; +`; -const KakaoButton = styled(OAuthButton)``; +const StyledGoogleLogin = styled(GoogleLogin)` + margin: 10px 0; + padding: 10px 20px; + background-color: #FFFFFF; + border: 1px solid lightgray; + border-radius: 5px; + cursor: pointer; + width: 300px; + display: flex; + align-items: center; + justify-content: center; +`; -//구글과 카카오 로고 이미지 크기 -const LogoImage = styled.img` - margin-right: 30px; - width: 60px; - height: auto; +const KakaoButton = styled.button` + margin: 10px 0; + padding: 10px 20px; + background-color: #FFFFFF; + border: 1px solid lightgray; + border-radius: 5px; + cursor: pointer; + width: 300px; + display: flex; + align-items: center; + justify-content: center; `; -// +const LogoImage = styled.img` + margin-right: 30px; + width: 60px; + height: auto; +`; const EmailButtonsContainer = styled.div` - display: flex; - justify-content: space-around; - width: 100%; - margin: 5px 0; + display: flex; + justify-content: space-around; + width: 100%; + margin: 5px 0; `; -//이메일로 회원가입하기, 이메일로 로그인하기 버튼 + const EmailButton = styled.button` - margin: 5px 0; - padding: 10px 20px; - background-color: #FFFFFF; - border: 1px solid lightgray; - border-radius: 5px; - cursor: pointer; + margin: 5px 0; + padding: 10px 20px; + background-color: #FFFFFF; + border: 1px solid lightgray; + border-radius: 5px; + cursor: pointer; `; - +export default OAuthLoginModal; diff --git a/client/src/components/Profile/profileModal.tsx b/client/src/components/Profile/profileModal.tsx index 97601a83..c5caaf25 100644 --- a/client/src/components/Profile/profileModal.tsx +++ b/client/src/components/Profile/profileModal.tsx @@ -1,12 +1,16 @@ import React, { useState } from 'react'; import styled from 'styled-components'; import { useSelector } from 'react-redux'; -import MemberInfoModal from './memberInfoModal'; // 경로는 실제 파일 위치에 따라 수정해야 합니다. -import MemberWithdrawalModal from './memberWithdrawalModal'; // 경로는 실제 파일 위치에 따라 수정해야 합니다. +import MemberInfoModal from './memberInfoModal'; +import MemberWithdrawalModal from './memberWithdrawalModal'; import CashModal from './cashModal'; import { RootState } from '../../store/config'; const ProfileModal: React.FC = ({ onClose }) => { + + const memberInfoText = "회원정보"; + const cashText = "현금"; + const memberWithdrawText = "회원탈퇴" const [selectedTab, setSelectedTab] = useState(1); const cashId = useSelector((state: RootState) => state.cash.cashId); // Get cashId from Redux store @@ -19,14 +23,14 @@ const ProfileModal: React.FC = ({ onClose }) => { × - handleTabChange(1)}>회원정보 - handleTabChange(2)}>현금 - handleTabChange(3)}>회원탈퇴 + handleTabChange(1)}>{memberInfoText} + handleTabChange(2)}>{cashText} + handleTabChange(3)}>{memberWithdrawText} {selectedTab === 1 && } - - {selectedTab === 3 && } {/* 회원탈퇴 모달 추가 */} + {selectedTab === 2 && } + {selectedTab === 3 && }