diff --git a/mission/chapter05/mission_ch05/src/App.jsx b/mission/chapter05/mission_ch05/src/App.jsx
index ae1218b..23ef283 100644
--- a/mission/chapter05/mission_ch05/src/App.jsx
+++ b/mission/chapter05/mission_ch05/src/App.jsx
@@ -3,7 +3,7 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
import RootLayout from "./layout/root-layout";
import HomePage from "./pages/Default/home";
import LoginPage from "./pages/Default/Login/login";
-import SignupPage from "./pages/Default/signup";
+import SignupPage from "./pages/Default/Signup/signup";
import SearchPage from './pages/Default/search';
import CategoryListPage from "./pages/CategoryList/CategoryList";
import MovieRoute from "./pages/MoviePages/MovieRoute";
diff --git a/mission/chapter05/mission_ch05/src/hooks/use-form.js b/mission/chapter05/mission_ch05/src/hooks/use-form.js
index c64413c..96033e1 100644
--- a/mission/chapter05/mission_ch05/src/hooks/use-form.js
+++ b/mission/chapter05/mission_ch05/src/hooks/use-form.js
@@ -1,39 +1,43 @@
-//
+// 입력 필드 값을 관리하고 유효성 검사를 수행
import { useEffect, useState } from "react";
function useForm({initialValue, validate}) {
- const [values, setValue] = useState(initialValue);
- const [touched , setTouched] = useState({});
- const [errors , setErrors] = useState({});
-
- const handleChangeInput = (name, value) => {
- setValue({
- ...value,
- [name]: value
- });
- };
-
- const handleBlur = (name) => {
- setTouched({
- ...touched,
- [name]: true
- });
- };
-
- const getTextInputProps = (name) => {
- const value = values[name];
- const onChange = (event) => handleChangeInput(name, event.target.value);
- const onBlur = () => handleBlur(name);
-
- return [value, onChange, onBlur];
- };
-
- useEffect(() => {
- const newErrors = validate(values);
- setErrors(newErrors);
- }, [validate, values]);
-
- return {values, errors, touched, getTextInputProps};
+ const [values, setValues] = useState(initialValue);
+ const [touched, setTouched] = useState({});
+ const [errors, setErrors] = useState({});
+
+ const handleChangeInput = (name, value) => {
+ setValues({
+ ...values,
+ [name]: value,
+ });
+ }
+
+ const handleBlur = (name) => {
+ setTouched({
+ ...touched,
+ [name]: true,
+ });
+ }
+
+ const getTextInputProps = (name) => {
+ const value = values[name];
+ const onChange = (e) => {
+ handleChangeInput(name, e.target.value);
+ }
+ const onBlur = () => {
+ handleBlur(name);
+ }
+
+ return { value, onChange, onBlur };
+ }
+
+ useEffect(() => {
+ const newErrors = validate(values);
+ setErrors(newErrors);
+ }, [validate, values]);
+
+ return { values, errors, touched, getTextInputProps };
}
-export { useForm };
\ No newline at end of file
+export default useForm;
\ No newline at end of file
diff --git a/mission/chapter05/mission_ch05/src/pages/CategoryList/CategoryList.style.jsx b/mission/chapter05/mission_ch05/src/pages/CategoryList/CategoryList.style.jsx
index b5ccc9d..3f01b8d 100644
--- a/mission/chapter05/mission_ch05/src/pages/CategoryList/CategoryList.style.jsx
+++ b/mission/chapter05/mission_ch05/src/pages/CategoryList/CategoryList.style.jsx
@@ -17,7 +17,7 @@ export const CategoryHeaderName = styled.div`
export const CategoryBox = styled.div`
display: flex;
flex-direction: row;
- justifyContent: center;
+ justify-content: center;
gap: 20px;
flex-wrap: wrap;
margin-top: 20px;
diff --git a/mission/chapter05/mission_ch05/src/pages/Default/Login/login.jsx b/mission/chapter05/mission_ch05/src/pages/Default/Login/login.jsx
index 0401a29..d9c5986 100644
--- a/mission/chapter05/mission_ch05/src/pages/Default/Login/login.jsx
+++ b/mission/chapter05/mission_ch05/src/pages/Default/Login/login.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import * as L from './login.style';
-import { useForm } from '../../../hooks/use-form';
+import useForm from '../../../hooks/use-form';
import { validateLogin } from '../../../utils/validate';
const Login = () => {
@@ -17,24 +17,43 @@ const Login = () => {
console.log(login.values.email, login.values.password);
}
+
+ const onSubmit = (event) => { // 폼 제출 시 호출, 에러가 없으면 폼 데이터 출력
+ event.preventDefault();
+ if (!login.errors.email && !login.errors.password) {
+ console.log('폼 데이터 제출');
+ console.log(login.values);
+ }
+ };
+
+ const isFormValid = !login.errors.email && !login.errors.password && login.values.email && login.values.password;
+
return (
-
+
로그인
-
{login.touched.email && login.errors.email
&& {login.errors.email}}
-
+
+ {login.touched.password && login.errors.password
+ && {login.errors.password}}
- 로그인
+
+ 로그인
+
);
diff --git a/mission/chapter05/mission_ch05/src/pages/Default/Login/login.style.jsx b/mission/chapter05/mission_ch05/src/pages/Default/Login/login.style.jsx
index bdef994..7a60528 100644
--- a/mission/chapter05/mission_ch05/src/pages/Default/Login/login.style.jsx
+++ b/mission/chapter05/mission_ch05/src/pages/Default/Login/login.style.jsx
@@ -9,7 +9,7 @@ export const LoginBox = styled.div`
background-color: #000;
`;
-export const LoginContainer = styled.div`
+export const LoginContainer = styled.form`
display: flex;
flex-direction: column;
align-items: center;
@@ -21,7 +21,7 @@ export const LoginContainer = styled.div`
`;
export const Input = styled.input`
- width: 90%;
+ width: 80%;
padding: 10px;
margin-bottom: 15px;
font-size: 16px;
@@ -29,8 +29,8 @@ export const Input = styled.input`
outline: none;
// error가 발생하면 테두리 색을 빨간색으로 변경
- border: ${props => props.error ?
- '2px solid red' : '1px solid #ccc'};
+ border: ${props => (props.$error ?
+ '2px solid red' : '1px solid #ccc')};
&:focus { // input 창에 focus 되면 입력창의 테두리 색 변경
border-color: #007bff;
@@ -38,18 +38,21 @@ export const Input = styled.input`
`;
export const Button = styled.button`
- width: 100%;
+ width: 85%;
padding: 10px;
font-size: 16px;
color: #fff;
- background-color: #ff4d6d;
+ background-color: ${props => (props.disabled ?
+ '#ccc' : '#ff4d6d')};
border: none;
border-radius: 4px;
- cursor: pointer;
+ cursor: ${props => (props.disabled ?
+ 'not-allowed' : 'pointer')};
transition: background-color 0.3s ease;
&:hover {
- background-color: #ff2f4e;
+ background-color: ${props => (props.disabled ?
+ '#ccc' : '#ff2f4e')};
}
`;
diff --git a/mission/chapter05/mission_ch05/src/pages/Default/Signup/signup.jsx b/mission/chapter05/mission_ch05/src/pages/Default/Signup/signup.jsx
new file mode 100644
index 0000000..6e5f451
--- /dev/null
+++ b/mission/chapter05/mission_ch05/src/pages/Default/Signup/signup.jsx
@@ -0,0 +1,93 @@
+import React from 'react';
+import {useForm} from 'react-hook-form'
+import * as yup from 'yup'
+import {yupResolver} from '@hookform/resolvers/yup'
+import * as S from './signup.style';
+
+const Signup = () => {
+ const schema = yup.object().shape({
+ email: yup
+ .string()
+ .email('유효한 이메일 형식이어야 합니다.')
+ .required(
+ '이메일을 반드시 입력해주세요.'
+ ),
+ password: yup
+ .string()
+ .min(
+ 8,
+ '비밀번호는 8자 이상이어야 합니다.'
+ ).max(16,
+ '비밀번호는 16자 이하여야 합니다.'
+ ).required(
+ '비밀번호를 입력해주세요.'
+ ),
+ passwordConfirm: yup
+ .string()
+ .oneOf(
+ [yup.ref('password'), null],
+ '비밀번호가 일치하지 않습니다.'
+ )
+ .required(
+ '비밀번호 확인은 필수 입력사항입니다.'
+ )
+ });
+
+ const {
+ register,
+ handleSubmit,
+ formState: { errors, isValid },
+ watch,
+ } = useForm({
+ resolver: yupResolver(schema),
+ mode: 'onChange'
+ });
+
+ const onSubmit = (data) => {
+ console.log('폼 데이터 제출');
+ console.log(data);
+ };
+
+ // password 필드의 값이 존재하고 에러가 없으면 true
+ const passwordValue = watch('password');
+ const isPasswordValid = passwordValue && !errors.password;
+
+ return (
+
+
+ 회원가입
+
+ {errors.email?.message}
+
+
+ {errors.password?.message}
+
+
+ {errors.passwordConfirm?.message}
+
+
+ 제출
+
+
+
+ );
+};
+
+export default Signup;
\ No newline at end of file
diff --git a/mission/chapter05/mission_ch05/src/pages/Default/Signup/signup.style.jsx b/mission/chapter05/mission_ch05/src/pages/Default/Signup/signup.style.jsx
new file mode 100644
index 0000000..6db67d3
--- /dev/null
+++ b/mission/chapter05/mission_ch05/src/pages/Default/Signup/signup.style.jsx
@@ -0,0 +1,59 @@
+import styled from 'styled-components';
+
+export const SignupBox = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: flex-start; // 회원가입 영역을 화면 상단에 배치
+ height: 100vh;
+ padding-top: 10vh;
+ background-color: #000;
+`;
+
+export const SignupContainer = styled.form`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 400px;
+ padding: 40px;
+ background-color: #000;
+ border-radius: 8px;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
+`;
+
+export const Input = styled.input`
+ width: 80%;
+ padding: 10px;
+ margin-bottom: 15px;
+ font-size: 16px;
+ border-radius: 4px;
+ outline: none;
+
+ // error가 발생하면 테두리 색을 빨간색으로 변경
+ border: ${props => (props.$error ? '2px solid red' : '1px solid #ccc')};
+
+ &:focus { // input 창에 focus 되면 입력창의 테두리 색 변경
+ border-color: #007bff;
+ }
+`;
+
+export const Button = styled.button`
+ width: 85%;
+ padding: 10px;
+ font-size: 16px;
+ color: #fff;
+ background-color: ${props => (props.disabled ? '#ccc' : '#ff4d6d')};
+ border: none;
+ border-radius: 4px;
+ cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};
+ transition: background-color 0.3s ease;
+
+ &:hover {
+ background-color: ${props => (props.disabled ? '#ccc' : '#ff2f4e')};
+ }
+`;
+
+export const ErrorText = styled.p`
+ color: red;
+ font-size: 12px;
+ margin-bottom: 10px;
+`;
\ No newline at end of file
diff --git a/mission/chapter05/mission_ch05/src/pages/Default/signup.jsx b/mission/chapter05/mission_ch05/src/pages/Default/signup.jsx
deleted file mode 100644
index 51393f6..0000000
--- a/mission/chapter05/mission_ch05/src/pages/Default/signup.jsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import React from 'react';
-import {useForm} from 'react-hook-form'
-import * as yup from 'yup'
-import {yupResolver} from '@hookform/resolvers/yup'
-import styled from 'styled-components';
-
-const Signup = () => {
- const schema = yup.object().shape({
- email: yup.string().email().required(
- '이메일을 반드시 입력해주세요.'
- ),
- password: yup.string().min(
- 8,
- '비밀번호는 8자 이상이어야 합니다.'
- ).max(16, '비밀번호는 16자 이하여야 합니다.').required(),
- });
-
- const {register, handleSubmit, formState: {errors}} = useForm({
- resolver: yupResolver(schema)
- });
-
- const onSubmit = (data) => {
- console.log('폼 데이터 제출')
- console.log(data);
- }
-
- return (
- <>
-
회원가입
-
- >
- );
-};
-
-const inputEmail = styled.input`
- border: 1px solid black;
- border-radius: 5px;
- padding: 5px;
- margin: 5px;
-`;
-export default Signup;
\ No newline at end of file
diff --git a/mission/chapter05/mission_ch05/src/utils/validate.js b/mission/chapter05/mission_ch05/src/utils/validate.js
index 4d386fc..5a034d1 100644
--- a/mission/chapter05/mission_ch05/src/utils/validate.js
+++ b/mission/chapter05/mission_ch05/src/utils/validate.js
@@ -1,5 +1,4 @@
// 입력받은 email, password 값에 따라 유효성 검사 수행
-import React from 'react';
// @ 앞 : 이메일의 사용자명이 영어 대소문자, 숫자
// @ 뒤 : 도메인 이름이 영어 대소문자, 숫자