diff --git a/1-team-front/src/App.jsx b/1-team-front/src/App.jsx
index 17cc7f6..73ef9de 100644
--- a/1-team-front/src/App.jsx
+++ b/1-team-front/src/App.jsx
@@ -1,9 +1,34 @@
-import { useState } from 'react'
+/* eslint-disable */
-import './App.css'
+import {
+ createBrowserRouter,
+ createRoutesFromElements,
+ Route,
+ RouterProvider,
+} from 'react-router-dom';
+import { QueryClient, QueryClientProvider } from 'react-query';
+
+import Auth from './pages/Auth';
+import LogIn from './components/Auth/LogIn/LogIn';
+import FindPassword from './components/Auth/FindPassword/FindPassword';
+
+const queryClient = new QueryClient();
+
+const routesDifinition = createRoutesFromElements(
+ }>
+ }>
+ }>
+ ,
+);
+
+const router = createBrowserRouter(routesDifinition);
function App() {
- return
hello
+ return (
+
+
+
+ );
}
-export default App
+export default App;
diff --git a/1-team-front/src/components/Auth/AuthStyles.jsx b/1-team-front/src/components/Auth/AuthStyles.jsx
new file mode 100644
index 0000000..9d22394
--- /dev/null
+++ b/1-team-front/src/components/Auth/AuthStyles.jsx
@@ -0,0 +1,86 @@
+import styled, { css } from 'styled-components';
+
+const buttonType = {
+ cancel: css`
+ background-color: #2f3136;
+ &:hover {
+ background-color: #40444b;
+ }
+ `,
+ confirm: css`
+ background-color: #5865f2;
+ &:hover {
+ background-color: #4752c4;
+ }
+ `,
+};
+
+const CardContainer = styled.div`
+ padding: 40px 30px;
+ background-color: #36393f;
+ border-radius: 4px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 18px;
+ & h2 {
+ color: #fff;
+ }
+ & p {
+ font-size: 0.9rem;
+ }
+`;
+
+const FormWrapper = styled.form`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 13px;
+`;
+
+const InputBox = styled.div`
+ width: 360px;
+ & label {
+ font-size: 0.8rem;
+ font-weight: 800;
+ & span {
+ color: #ed4245;
+ }
+ }
+ & input {
+ width: 95%;
+ height: 30px;
+ background-color: #18191c;
+ border-radius: 2px;
+ margin: 5px 0;
+ color: #fff;
+ padding: 5px 10px;
+ font-size: 0.9rem;
+ }
+ & p {
+ font-size: 0.8rem;
+ color: ${(props) => (props.isBlue ? '#00aff4' : '#ed4245')};
+ }
+`;
+
+const Button = styled.button`
+ width: 100%;
+ height: 40px;
+ border-radius: 2px;
+ color: #fff;
+ font-weight: 800;
+ transition: all 0.3s linear;
+ border: 0;
+ ${(props) => buttonType[props.buttonType]}
+`;
+
+const SpanButton = styled.span`
+ font-size: 0.8rem;
+ color: #00aff4;
+ cursor: pointer;
+ &:hover {
+ text-decoration-line: underline;
+ }
+`;
+
+export { CardContainer, FormWrapper, InputBox, Button, SpanButton };
diff --git a/1-team-front/src/components/Auth/FindPassword/FindPassword.jsx b/1-team-front/src/components/Auth/FindPassword/FindPassword.jsx
new file mode 100644
index 0000000..00a0f28
--- /dev/null
+++ b/1-team-front/src/components/Auth/FindPassword/FindPassword.jsx
@@ -0,0 +1,52 @@
+/*eslint-disable*/
+import { useState } from 'react';
+
+import { CardContainer, FormWrapper, InputBox, Button } from '../AuthStyles';
+import useAuthMutation from '../../../hooks/Auth/useAuthMutation';
+import useBlurHandler from '../../../hooks/Auth/useBlurHandler';
+
+const FindPassword = () => {
+ const [email, setEmail] = useBlurHandler('');
+ const [isError, setIsError] = useState(false);
+ const [isSubmit, setIsSubmit] = useState(false);
+
+ const fetchFindPassword = useAuthMutation(
+ 'http://localhost:8080/member/password-reissue',
+ );
+
+ const findPasswordSubmitHandler = (event) => {
+ event.preventDefault();
+ fetchFindPassword.mutate(
+ { email: email },
+ {
+ onSuccess: () => {
+ setIsError(false);
+ setIsSubmit(true);
+ },
+ onError: () => {
+ setIsSubmit(false);
+ setIsError(true);
+ },
+ },
+ );
+ };
+ return (
+
+ 비밀번호를 잊으셨나요 ?
+ 입력하신 이메일로 비밀번호 재설정 링크를 보내드립니다.
+
+
+
+ setEmail(event)}>
+ {isError && 이메일이 유효하지 않습니다.
}
+ {isSubmit && (
+ 입력하신 이메일로 비밀번호 재설정 링크를 보냈습니다.
+ )}
+
+
+
+
+ );
+};
+
+export default FindPassword;
diff --git a/1-team-front/src/components/Auth/LogIn/LogIn.jsx b/1-team-front/src/components/Auth/LogIn/LogIn.jsx
new file mode 100644
index 0000000..6a521a9
--- /dev/null
+++ b/1-team-front/src/components/Auth/LogIn/LogIn.jsx
@@ -0,0 +1,92 @@
+/* eslint-disable */
+import { useState } from 'react';
+import styled from 'styled-components';
+import { useNavigate } from 'react-router-dom';
+
+import {
+ CardContainer,
+ FormWrapper,
+ InputBox,
+ Button,
+ SpanButton,
+} from '../AuthStyles';
+import useAuthMutation from '../../../hooks/Auth/useAuthMutation';
+import useBlurHandler from '../../../hooks/Auth/useBlurHandler';
+
+const FooterBox = styled.div`
+ display: flex;
+ gap: 8px;
+`;
+
+const LogIn = () => {
+ const navigate = useNavigate();
+ const [email, setEmail] = useBlurHandler('');
+ const [password, setPassword] = useBlurHandler('');
+ const [isError, setIsError] = useState(false);
+
+ const fetchLogin = useAuthMutation('http://localhost:8080/login');
+
+ const loginSubmitHander = (event) => {
+ event.preventDefault();
+ fetchLogin.mutate(
+ { email: email, password: password },
+ {
+ onSuccess: (res) => {
+ setIsError(false);
+ localStorage.clear();
+ localStorage.setItem('email', res.data.email);
+ localStorage.setItem('token', res.data.token);
+ navigate('/');
+ },
+ onError: () => {
+ setIsError(true);
+ },
+ },
+ );
+ };
+
+ const findPasswordClickHandler = () => {
+ navigate('/auth/find-password');
+ };
+
+ return (
+
+ 환영합니다 !
+
+
+
+ setEmail(event)}
+ >
+
+
+
+ setPassword(event)}
+ >
+ {isError && 이메일 또는 비밀번호가 일치하지 않습니다.
}
+
+ 비밀번호를 잊으셨나요 ?
+
+
+
+
+
+ 계정이 필요한가요 ?
+ 가입하기
+
+
+ );
+};
+
+export default LogIn;
diff --git a/1-team-front/src/components/Auth/SignUp/SignUp.jsx b/1-team-front/src/components/Auth/SignUp/SignUp.jsx
new file mode 100644
index 0000000..6520e2e
--- /dev/null
+++ b/1-team-front/src/components/Auth/SignUp/SignUp.jsx
@@ -0,0 +1,157 @@
+/* eslint-disable */
+
+import { useState } from 'react';
+import styled from 'styled-components';
+
+const SignUpForm = styled.form`
+ padding: 40px 30px;
+ background-color: #36393f;
+ border-radius: 4px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 18px;
+ & h2 {
+ color: #fff;
+ }
+`;
+
+const SignUpButton = styled.button`
+ width: 100%;
+ height: 40px;
+ border-radius: 2px;
+ color: #fff;
+ background-color: #5865f2;
+ font-weight: 800;
+`;
+
+const InputBox = styled.div`
+ width: 360px;
+ & label {
+ font-size: 0.8rem;
+ font-weight: 800;
+ }
+ & input {
+ width: 95%;
+ height: 30px;
+ background-color: #18191c;
+ border-radius: 2px;
+ margin: 5px 0;
+ color: #fff;
+ padding: 5px 10px;
+ font-size: 0.9rem;
+ }
+ & p {
+ font-size: 0.8rem;
+ color: #ed4245;
+ }
+`;
+
+function SignUp() {
+ const [email, setEmail] = useState({ email: '', isWarn: false });
+ const [password, setPassword] = useState({ password: '', isWarn: false });
+ const [passwordConfirm, setPasswordConfirm] = useState({
+ passwordConfirm: '',
+ isWarn: false,
+ });
+ const [nickName, setNickName] = useState({ nickName: '', isWarn: false });
+
+ const validateEmail = (value) => value.includes('@');
+ const validatePassword = (value) => {
+ const check =
+ /^(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,20}$/;
+ return check.test(value);
+ };
+ const validatePasswordConfirm = (value) => {
+ return value === password.password;
+ };
+ const validateNickName = (value) => {
+ return value.length !== 0;
+ };
+
+ const blurHandler = (id, value, setFn, validationFn) => {
+ if (validationFn(value)) return setFn({ [id]: value, isWarn: false });
+ setFn((prev) => {
+ return { ...prev, isWarn: true };
+ });
+ };
+
+ return (
+
+ 계정 만들기
+
+
+
+ blurHandler(
+ event.target.id,
+ event.target.value,
+ setEmail,
+ validateEmail,
+ )
+ }
+ />
+ {email.isWarn && 이메일을 입력해 주세요.
}
+
+
+
+
+ blurHandler(
+ event.target.id,
+ event.target.value,
+ setPassword,
+ validatePassword,
+ )
+ }
+ >
+ {password.isWarn && (
+ 비밀번호는 대문자, 소문자, 숫자, 특수문자 조합을 사용해 주세요.
+ )}
+
+
+
+
+ blurHandler(
+ event.target.id,
+ event.target.value,
+ setPasswordConfirm,
+ validatePasswordConfirm,
+ )
+ }
+ >
+ {passwordConfirm.isWarn && 비밀번호가 일치하지 않습니다.
}
+
+
+
+
+ blurHandler(
+ event.target.id,
+ event.target.value,
+ setNickName,
+ validateNickName,
+ )
+ }
+ >
+ {nickName.isWarn && (
+
+ 입력하지 않을경우 랜덤 별명이 발급 됩니다.
+
+ )}
+
+ 계속하기
+
+ );
+}
+
+export default SignUp;
diff --git a/1-team-front/src/hooks/Auth/useAuthMutation.js b/1-team-front/src/hooks/Auth/useAuthMutation.js
new file mode 100644
index 0000000..2812a09
--- /dev/null
+++ b/1-team-front/src/hooks/Auth/useAuthMutation.js
@@ -0,0 +1,15 @@
+/* eslint-disable */
+
+import axios from 'axios';
+import { useMutation } from 'react-query';
+
+const useAuthMutation = (url) => {
+ const { mutate, ...rest } = useMutation(async (payload) => {
+ const response = await axios.post(url, payload);
+ return response.data;
+ });
+
+ return { mutate, ...rest };
+};
+
+export default useAuthMutation;
diff --git a/1-team-front/src/hooks/Auth/useBlurHandler.js b/1-team-front/src/hooks/Auth/useBlurHandler.js
new file mode 100644
index 0000000..84b65cc
--- /dev/null
+++ b/1-team-front/src/hooks/Auth/useBlurHandler.js
@@ -0,0 +1,13 @@
+import { useState } from 'react';
+
+const useBlurHandler = (initialValue) => {
+ const [inputValue, setInputValue] = useState(initialValue);
+
+ const blurHandler = (event) => {
+ setInputValue(event.target.value);
+ };
+
+ return [inputValue, blurHandler];
+};
+
+export default useBlurHandler;
diff --git a/1-team-front/src/index.css b/1-team-front/src/index.css
index 6119ad9..1373380 100644
--- a/1-team-front/src/index.css
+++ b/1-team-front/src/index.css
@@ -1,3 +1,9 @@
+* {
+ margin: 0;
+ border: 0;
+ padding: 0;
+}
+
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
diff --git a/1-team-front/src/pages/Auth.jsx b/1-team-front/src/pages/Auth.jsx
new file mode 100644
index 0000000..67eeea2
--- /dev/null
+++ b/1-team-front/src/pages/Auth.jsx
@@ -0,0 +1,21 @@
+import styled from 'styled-components';
+import { Outlet } from 'react-router-dom';
+
+const AuthContainer = styled.div`
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: #202225;
+`;
+
+function Auth() {
+ return (
+
+
+
+ );
+}
+
+export default Auth;