diff --git a/client/package-lock.json b/client/package-lock.json
index 4a719d1c..3e42972c 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -8,6 +8,7 @@
"name": "vite-project",
"version": "0.0.0",
"dependencies": {
+ "axios": "^1.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^6.0.7"
@@ -2833,6 +2834,21 @@
"node": ">=8"
}
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/axios": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
+ "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
+ "dependencies": {
+ "follow-redirects": "^1.15.0",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/babel-plugin-polyfill-corejs2": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz",
@@ -3052,6 +3068,17 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
@@ -3141,6 +3168,14 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -3608,6 +3643,38 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fs-readdir-recursive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
@@ -3990,6 +4057,25 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -4216,6 +4302,11 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/punycode": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
diff --git a/client/package.json b/client/package.json
index 7745298f..4c717370 100644
--- a/client/package.json
+++ b/client/package.json
@@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
+ "axios": "^1.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^6.0.7"
diff --git a/client/src/asset/images/GoogleLogo.svg b/client/src/asset/images/GoogleLogo.svg
new file mode 100644
index 00000000..3790851d
--- /dev/null
+++ b/client/src/asset/images/GoogleLogo.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/client/src/asset/images/KakaoLogo.svg b/client/src/asset/images/KakaoLogo.svg
new file mode 100644
index 00000000..e5f5d11b
--- /dev/null
+++ b/client/src/asset/images/KakaoLogo.svg
@@ -0,0 +1,21 @@
+
+
diff --git a/client/src/components/Headers/LoginHeader.tsx b/client/src/components/Headers/LoginHeader.tsx
index af416b2e..4ddd3947 100644
--- a/client/src/components/Headers/LoginHeader.tsx
+++ b/client/src/components/Headers/LoginHeader.tsx
@@ -4,7 +4,7 @@ import StockHolmLogo from "../../asset/images/StockHolmLogo.png";
import SampleProfile from "../../asset/images/ProfileSample.png";
import AlarmImage from "../../asset/images/alarm.png";
-const LoginHeader: React.FC = () => {
+const LoginHeader: React.FC = ({ onLogoutClick }) => {
const [searchValue, setSearchValue] = useState('');
const handleSearchChange = (event: React.ChangeEvent) => {
@@ -30,12 +30,18 @@ const LoginHeader: React.FC = () => {
- {logoutText}
+ {logoutText} {/* 로그아웃 버튼 클릭 시 onLogoutClick 실행 */}
);
};
+export default LoginHeader;
+
+interface LoginHeaderProps {
+ onLogoutClick: () => void; // 로그아웃 클릭 핸들러 타입 정의
+}
+
const HeaderContainer = styled.div`
display: flex;
justify-content: space-between;
@@ -124,4 +130,3 @@ const LogoutButton = styled.button`
-export default LoginHeader;
diff --git a/client/src/components/Headers/LogoutHeader.tsx b/client/src/components/Headers/LogoutHeader.tsx
index 04418fba..fe554e23 100644
--- a/client/src/components/Headers/LogoutHeader.tsx
+++ b/client/src/components/Headers/LogoutHeader.tsx
@@ -3,7 +3,7 @@ import React, { useState } from 'react';
import styled from 'styled-components';
import StockHolmLogo from "../../asset/images/StockHolmLogo.png"
-const LogoutHeader: React.FC = () => {
+const LogoutHeader: React.FC = ({ onLoginClick })=> {
const [searchValue, setSearchValue] = useState('');
const handleSearchChange = (event: React.ChangeEvent) => {
@@ -16,13 +16,15 @@ const LogoutHeader: React.FC = () => {
console.log("Logo clicked");
};
+
+
return (
- {loginText}
+ {loginText}
);
};
@@ -78,5 +80,8 @@ const LoginButton = styled.button`
background-color: #f2f2f2; // 호버 시 약간 어두운 흰색으로 변경
}
`;
+interface LogoutHeaderProps {
+ onLoginClick: () => void;
+}
export default LogoutHeader;
diff --git a/client/src/components/Logins/EmailLogin.tsx b/client/src/components/Logins/EmailLogin.tsx
new file mode 100644
index 00000000..02cae2ea
--- /dev/null
+++ b/client/src/components/Logins/EmailLogin.tsx
@@ -0,0 +1,143 @@
+import axios from 'axios';
+import styled from 'styled-components';
+import React, { useState } from 'react';
+
+const EmailLoginModal = ({ onClose, onLogin }: { onClose: () => void, onLogin: () => void }) => {
+ // 문자열 변수 정의
+ const titleText = "이메일로 로그인";
+ const emailLabelText = "이메일";
+ const passwordLabelText = "비밀번호";
+ const findPasswordText = "비밀번호 찾기";
+ const loginButtonText = "로그인";
+ const noAccountText = "계정이 없습니까?";
+ const registerButtonText = "회원가입하기";
+
+ const [email, setEmail] = useState(''); // 입력된 이메일 상태
+ const [password, setPassword] = useState(''); // 입력된 비밀번호 상태
+
+ const handleEmailChange = (event: React.ChangeEvent) => {
+ setEmail(event.target.value);
+ };
+
+ const handlePasswordChange = (event: React.ChangeEvent) => {
+ setPassword(event.target.value);
+ };
+
+ const handleLoginClick = async () => {
+ try {
+ const response = await axios.post('http://localhost:8080/login', { email, password });
+ if (response.status === 200) {
+ onLogin();
+ onClose();
+ } else {
+ console.error('Error during login');
+ }
+ } catch (error) {
+ console.error('Error during login:', error);
+ }
+ };
+
+ return (
+
+
+ ×
+ {titleText}
+
+
+
+
+ {findPasswordText}
+ {loginButtonText}
+
+ {noAccountText} {registerButtonText}
+
+
+
+ );
+};
+
+export default EmailLoginModal;
+
+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);
+`;
+
+const ModalContainer = styled.div`
+ 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;
+`;
+
+const Title = styled.h2`
+ margin-bottom: 20px;
+ font-size: 1.6rem; // 크기를 줄입니다.
+ font-weight: 400; // 굵기를 줄입니다.
+`;
+
+const Label = styled.label`
+ align-self: flex-start;
+ margin-top: 10px;
+`;
+
+const Input = styled.input`
+ width: 100%;
+ padding: 10px;
+ margin-top: 5px;
+ border: 1px solid lightgray;
+ border-radius: 5px;
+`;
+
+const RightAlignedButton = styled.button`
+ align-self: flex-end;
+ margin-top: 5px;
+ background: none;
+ border: none;
+ color: slategray; // 글자색을 변경합니다.
+ cursor: pointer;
+`;
+
+const LoginButton = styled.button`
+ width: 100%;
+ padding: 10px;
+ margin-top: 10px;
+ background-color: darkslategray; // 배경색을 변경합니다.
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+`;
+
+const BottomText = styled.div`
+ margin-top: 10px;
+ font-size: 0.9rem; // 크기를 줄입니다.
+`;
+
+const RegisterButton = styled.button`
+ background: none;
+ border: none;
+ color: slategray; // 글자색을 변경합니다.
+ cursor: pointer;
+`;
\ No newline at end of file
diff --git a/client/src/components/Logins/OAuthLogin.tsx b/client/src/components/Logins/OAuthLogin.tsx
new file mode 100644
index 00000000..665385ee
--- /dev/null
+++ b/client/src/components/Logins/OAuthLogin.tsx
@@ -0,0 +1,171 @@
+import styled from 'styled-components';
+import googleLogo from '../../asset/images/GoogleLogo.svg';
+import kakaoLogo from '../../asset/images/KakaoLogo.svg';
+import axios from 'axios';
+
+
+
+const OAuthLoginModal: React.FC = ({ onClose, onEmailLoginClick, onEmailSignupClick }) => {
+ const titleText = "로그인";
+ const googleLoginText = "구글로 로그인";
+ const kakaoLoginText = "카카오로 로그인";
+ const orText = "또는";
+ const emailLoginText = "이메일로 로그인";
+ const emailSignupText = "이메일로 회원가입";
+
+
+ const handleGoogleLogin = async () => {
+ try {
+ const response = await axios.post('http://localhost:8080/auth/google');
+ if (response.status === 200) {
+ // 로그인 처리
+ console.log("Successfully logged in with Google!");
+ onClose();
+ } else {
+ console.error("Error logging in with Google");
+ }
+ } catch (error) {
+ console.error("Error logging in with Google:", error);
+ }
+ };
+
+ const handleKakaoLogin = async () => {
+ try {
+ const response = await axios.post('http://localhost:8080/auth/kakao');
+ if (response.status === 200) {
+ // 로그인 처리
+ console.log("Successfully logged in with Kakao!");
+ onClose();
+ } else {
+ console.error("Error logging in with Kakao");
+ }
+ } catch (error) {
+ console.error("Error logging in with Kakao:", error);
+ }
+ };
+
+ return (
+
+
+ ×
+ {titleText}
+
+
+ {googleLoginText}
+
+
+
+
+ {kakaoLoginText}
+
+ {orText}
+
+ {emailLoginText}
+ {emailSignupText}
+
+
+
+ );
+};
+
+export default OAuthLoginModal;
+
+interface LoginModalProps {
+ onClose: () => void;
+ onEmailLoginClick: () => void;
+ onEmailSignupClick: () => 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);
+`;
+
+const ModalContainer = styled.div`
+ 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;
+`;
+
+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 GoogleButton = styled(OAuthButton)`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`;
+
+const KakaoButton = styled(OAuthButton)`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`;
+
+const EmailButtonsContainer = styled.div`
+ 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;
+`;
+
+
+const LogoImage = styled.img`
+ margin-right: 30px;
+ width: 60px; // 로고의 가로 크기를 조정합니다. 필요에 따라 값을 조절할 수 있습니다.
+ height: auto; // 가로 크기에 맞춰 세로 크기를 자동으로 조절합니다.
+`;
\ No newline at end of file
diff --git a/client/src/components/Signups/EmailCertify.tsx b/client/src/components/Signups/EmailCertify.tsx
new file mode 100644
index 00000000..722cd87d
--- /dev/null
+++ b/client/src/components/Signups/EmailCertify.tsx
@@ -0,0 +1,149 @@
+import axios from 'axios';
+import React, { useState } from 'react';
+import styled from 'styled-components';
+
+const strings = {
+ titleText: "이메일 인증요청",
+ emailLabelText: "인증할 이메일",
+ codeLabelText: "인증코드",
+ nextStepText: "인증 후 다음단계",
+ codeHintText: "이메일로 전송된 코드를 입력하세요",
+ termsText: "개인정보 처리방침 및 서비스 이용약관에 동의합니다"
+};
+
+const EmailVerificationModal: React.FC = ({ onClose, onNextStep }) => {
+ const [email, setEmail] = useState('sample@example.com'); // 이메일 상태
+ const [verificationCode, setVerificationCode] = useState(''); // 인증코드 상태
+
+ const handleEmailChange = (event: React.ChangeEvent) => {
+ setEmail(event.target.value);
+ };
+
+ const handleVerificationCodeChange = (event: React.ChangeEvent) => {
+ setVerificationCode(event.target.value);
+ };
+
+ const handleNextStepClick = async () => {
+ try {
+ const response = await axios.post('http://localhost:8080/email/confirm', { email, code: verificationCode });
+ if (response.status === 200) {
+ onNextStep();
+ } else {
+ console.error('Error during email confirmation');
+ }
+ } catch (error) {
+ console.error('Error during email confirmation:', error);
+ }
+ };
+
+ return (
+
+
+ ×
+ {strings.titleText}
+
+
+
+
+ {strings.codeHintText}
+
+
+
+
+
+ {strings.nextStepText}
+
+
+
+ );
+};
+
+export default EmailVerificationModal;
+
+type EmailVerificationModalProps = {
+ onClose: () => void;
+ onNextStep: () => void;
+};
+
+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);
+`;
+
+const ModalContainer = styled.div`
+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;
+`;
+
+const Title = styled.h2`
+ margin-bottom: 20px;
+ font-size: 1.6rem; // 크기를 줄입니다.
+ font-weight: 400; // 굵기를 줄입니다.
+`;
+
+const Label = styled.label`
+ align-self: flex-start;
+ margin-top: 10px;
+`;
+
+const Input = styled.input`
+ width: 100%;
+ padding: 10px;
+ margin-top: 5px;
+ border: 1px solid lightgray;
+ border-radius: 5px;
+`;
+
+const SignupButton = styled.button`
+ width: 100%;
+ padding: 10px;
+ margin-top: 10px;
+ background-color: darkslategray; // 배경색을 변경합니다.
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+`;
+
+const HintText = styled.p`
+ color: red;
+ font-size: 0.8rem;
+ margin-top: 5px;
+`;
+
+const TermsCheckbox = styled.div`
+ margin-top: 10px;
+ display: flex;
+ align-items: center;
+
+ input[type="checkbox"] {
+ margin-right: 5px;
+ }
+
+ label {
+ font-size: 0.9rem;
+ }
+`;
\ No newline at end of file
diff --git a/client/src/components/Signups/EmailSignup.tsx b/client/src/components/Signups/EmailSignup.tsx
new file mode 100644
index 00000000..c5483f73
--- /dev/null
+++ b/client/src/components/Signups/EmailSignup.tsx
@@ -0,0 +1,115 @@
+import axios from 'axios';
+import styled from 'styled-components';
+import React, { useState } from 'react';
+
+const strings = {
+ titleText: "이메일로 회원가입",
+ emailLabelText: "이메일",
+ requestVerificationText: "이메일 인증요청"
+};
+
+const EmailSignupModal: React.FC = ({ onClose, onRequestVerification }) => {
+ const [email, setEmail] = useState(''); // 입력된 이메일을 관리하는 상태
+
+ const handleEmailChange = (event: React.ChangeEvent) => {
+ setEmail(event.target.value);
+ };
+
+ const handleVerificationRequest = async () => {
+ try {
+ const response = await axios.post('http://localhost:8080/email/send', { email });
+ if (response.status === 200) {
+ onRequestVerification();
+ } else {
+ console.error('Error sending verification email');
+ }
+ } catch (error) {
+ console.error('Error sending verification email:', error);
+ }
+ };
+
+ return (
+
+
+ ×
+ {strings.titleText}
+
+
+
+ {strings.requestVerificationText}
+
+
+
+ );
+};
+
+export default EmailSignupModal;
+
+
+type EmailSignupModalProps = {
+ onClose: () => void;
+ onRequestVerification: () => void;
+};
+
+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);
+`;
+
+const ModalContainer = styled.div`
+ 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;
+`;
+
+const Title = styled.h2`
+ margin-bottom: 20px;
+ font-size: 1.6rem; // 크기를 줄입니다.
+ font-weight: 400; // 굵기를 줄입니다.
+`;
+
+const Label = styled.label`
+ align-self: flex-start;
+ margin-top: 10px;
+`;
+
+const Input = styled.input`
+ width: 100%;
+ padding: 10px;
+ margin-top: 5px;
+ border: 1px solid lightgray;
+ border-radius: 5px;
+`;
+
+const SignupButton = styled.button`
+ width: 100%;
+ padding: 10px;
+ margin-top: 10px;
+ background-color: darkslategray; // 배경색을 변경합니다.
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+`;
diff --git a/client/src/components/Signups/Password.tsx b/client/src/components/Signups/Password.tsx
new file mode 100644
index 00000000..1a135960
--- /dev/null
+++ b/client/src/components/Signups/Password.tsx
@@ -0,0 +1,131 @@
+import axios from 'axios';
+import React, { useState } from 'react';
+import styled from 'styled-components';
+
+const strings = {
+ titleText: "비밀번호 설정",
+ passwordLabelText: "비밀번호",
+ confirmPasswordLabelText: "비밀번호 확인",
+ nicknameLabelText: "닉네임",
+ confirmButtonText: "확인"
+ };
+
+ const PasswordSettingModal: React.FC<{ onClose: () => void }> = ({ onClose }) => {
+ const [password, setPassword] = useState('');
+ const [confirmPassword, setConfirmPassword] = useState('');
+ const [nickname, setNickname] = useState('');
+
+ const handlePasswordChange = (e: React.ChangeEvent) => {
+ setPassword(e.target.value);
+ };
+
+ const handleConfirmPasswordChange = (e: React.ChangeEvent) => {
+ setConfirmPassword(e.target.value);
+ };
+
+ const handleNicknameChange = (e: React.ChangeEvent) => {
+ setNickname(e.target.value);
+ };
+
+ const handleConfirmClick = async () => {
+ try {
+ const response = await axios.post('http://localhost:8080/members', {
+ password,
+ confirmPassword,
+ nickname
+ });
+
+ if (response.status === 200) {
+ console.log('Data sent successfully');
+ onClose();
+ } else {
+ console.error('Error sending data');
+ }
+ } catch (error) {
+ console.error('Error sending data:', error);
+ }
+ };
+
+ return (
+
+
+ ×
+ {strings.titleText}
+
+
+
+
+
+
+ {strings.confirmButtonText}
+
+
+ );
+};
+
+ export default PasswordSettingModal;
+
+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);
+`;
+
+const ModalContainer = styled.div`
+ position: relative;
+ background-color: white;
+ padding: 20px;
+ width: 400px;
+ border-radius: 10px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+`;
+
+const Title = styled.h2`
+ margin-bottom: 20px;
+ font-size: 1.6rem;
+ font-weight: 400;
+`;
+
+
+const CloseButton = styled.button`
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ background: #FFFFFF;
+ border: 1px solid lightgray;
+ font-size: 1.5rem;
+ cursor: pointer;
+`;
+
+const Label = styled.label`
+ align-self: flex-start;
+ margin-top: 10px;
+`;
+
+const Input = styled.input`
+ width: 100%;
+ padding: 10px;
+ margin-top: 5px;
+ border: 1px solid lightgray;
+ border-radius: 5px;
+`;
+
+
+const ConfirmButton = styled.button`
+ width: 100%;
+ padding: 10px;
+ margin-top: 10px;
+ background-color: darkslategray;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+`;
\ No newline at end of file
diff --git a/client/src/page/MainPage.tsx b/client/src/page/MainPage.tsx
index cc799abc..65d20768 100644
--- a/client/src/page/MainPage.tsx
+++ b/client/src/page/MainPage.tsx
@@ -1,17 +1,117 @@
-import { styled } from "styled-components";
-import LogoutHeader from "../components/Headers/LogoutHeader"
-import LoginHeader from "../components/Headers/LoginHeader"
+import { useState, useCallback } from 'react';
+import styled from "styled-components";
+import LogoutHeader from "../components/Headers/LogoutHeader";
+import LoginHeader from "../components/Headers/LoginHeader";
+import OAuthLoginModal from "../components/Logins/OAuthLogin";
+import EmailLoginModal from "../components/Logins/EmailLogin";
+import EmailSignupModal from "../components/Signups/EmailSignup";
+import EmailVerificationModal from '../components/Signups/EmailCertify';
+import PasswordSettingModal from '../components/Signups/Password';
const MainPage = () => {
+ const [isOAuthModalOpen, setOAuthModalOpen] = useState(false);
+ const [isEmailLoginModalOpen, setEmailLoginModalOpen] = useState(false);
+ const [isEmailSignupModalOpen, setEmailSignupModalOpen] = useState(false);
+
+ const openOAuthModal = useCallback(() => {
+ setOAuthModalOpen(true);
+ }, []);
+
+ const closeOAuthModal = useCallback(() => {
+ setOAuthModalOpen(false);
+ }, []);
+
+ const openEmailLoginModal = useCallback(() => {
+ setOAuthModalOpen(false);
+ setEmailLoginModalOpen(true);
+ }, []);
+
+ const closeEmailLoginModal = useCallback(() => {
+ setEmailLoginModalOpen(false);
+ }, []);
+
+ const openEmailSignupModal = useCallback(() => {
+ setOAuthModalOpen(false);
+ setEmailSignupModalOpen(true);
+ }, []);
+
+ const closeEmailSignupModal = useCallback(() => {
+ setEmailSignupModalOpen(false);
+ }, []);
+
+ const [isEmailVerificationModalOpen, setEmailVerificationModalOpen] = useState(false);
+
+ const openEmailVerificationModal = useCallback(() => {
+ setEmailSignupModalOpen(false); // 이메일 회원가입 모달 닫기
+ setEmailVerificationModalOpen(true); // 이메일 인증 모달 열기
+ }, []);
+
+ const closeEmailVerificationModal = useCallback(() => {
+ setEmailVerificationModalOpen(false);
+ }, []);
+
+ const [isPasswordSettingModalOpen, setPasswordSettingModalOpen] = useState(false);
+
+ const openPasswordSettingModal = useCallback(() => {
+ setEmailVerificationModalOpen(false); // 이메일 인증 모달 닫기
+ setPasswordSettingModalOpen(true); // 비밀번호 설정 모달 열기
+ }, []);
+
+ const closePasswordSettingModal = useCallback(() => {
+ setPasswordSettingModalOpen(false);
+ }, []);
+
+ const [isLoggedIn, setIsLoggedIn] = useState(false); // 로그인 상태 관리
+
+ const handleLogin = () => {
+ setIsLoggedIn(true);
+ };
+
+ const handleLogout = () => {
+ setIsLoggedIn(false);
+ };
+
return (
-
-
+ {isLoggedIn ?
+ : // 로그아웃 버튼 클릭 핸들러 추가
+
+ }
+ {isOAuthModalOpen && }
+ {isEmailLoginModalOpen &&
+
+ }
+
+ {isEmailSignupModalOpen &&
+
+ }
+
+ {isEmailVerificationModalOpen &&
+
+ }
+
+ {isPasswordSettingModalOpen &&
+ { handleLogin(); closePasswordSettingModal(); }} />
+ }
+
);
};