- ์๋ ์๊ฑด์ ๋ง์ถฐ ์ฌ์ฉ๊ฐ๋ฅํ framework(react, vue ๋ฑ) ์ฌ์ฉํ์ฌ ์์ ๋กญ๊ฒ ๊ตฌํํด์ฃผ์ธ์
- README.md ์์ฑํด์ฃผ์ธ์ (๊ณผ์ ๊ด๋ จ ๋ด์ฉ)
- ๊ฐ๋ฐ๋ ์ฝ๋๋ ์์ถํ์ฌ ๋ฉ์ผ๋ก ์ ๋ฌ ํน์ github๋ฑ ๋งํฌ๋ฅผ ์ ๋ฌํด์ฃผ์ธ์
- ์ต์ input 2๊ฐ, button 1๊ฐ๋ก๊ทธ์ธ์ (ID, PW ์ ๋ ฅ ํ)
- Local Storage(ํน์ cookie) ๋ก๊ทธ์ธ ์ ๋ณด ์ ์ฅ (๋ค์ ์ ์ํ์์ ์ ๋ณด ์ ์ง)
- ๋ฉ์ธ ํ์ด์ง ์ด๋
์ ํจ์ฑ ๊ฒ์ฌ
- ID : ๋ฉ์ผํ์
- PWD : ์๋ฌธ์, ๋๋ฌธ์, ์ซ์ ์กฐํฉ (์ต์ 1๊ฐ์ฉ) ์์๋ฌธ์๋ ์ํ๋ฒณ
๐ฆsrc
โฃ ๐components // 1. ์ปดํฌ๋ํธ ํด๋
โ โฃ ๐AuthRoute.tsx //
โ โฃ ๐Header.tsx // ํค๋
โ โ ๐ProtectedRoute.tsx // ์์น ์์ ๋๋ฉ์ธ ๋ง๊ธฐ
โฃ ๐context
โ โ ๐AuthContext.tsx // ์ ์ญ์ํ๊ด๋ฆฌ (์ ์ ์ ๋ณด,๋ก๊ทธ์์)
โฃ ๐hooks
โ โ ๐useForm.ts // Form ์ํ๊ด๋ฆฌ ์ปค์คํ
ํ
โฃ ๐pages
โ โฃ ๐Home.tsx // ๋ฉ์ธ ํ์ด์ง
โ โ ๐SignIn.tsx // ๋ก๊ทธ์ธ ํ์ด์ง
โฃ ๐utils
โ โ ๐validate.ts // ์ ํจ์ฑ ๊ฒ์ฌ
โฃ ๐App.tsx // ๋ผ์ฐํฐ, Context Providers, Theme Provider
โฃ ๐index.tsx
โฃ ๐react-app-env.d.ts
โ ๐reportWebVitals.ts
hooks
ํด๋์์useFormController
์ ์ปค์คํ ํ ์ ํตํดForm
์ email, password, displayName ๋ฑ์value
๋ค๊ณผloading
,error
์ ๋ํ ์ํ๊ด๋ฆฌ๋ฅผ ํ๋ ํ ์ ๋๋ค.- ๋งค๊ฐ๋ณ์๋ก
initialValues
๋ ์ด๊น๊ฐ,onSubmit
๋submit
ํ ๊ฒฝ์ฐ์ ์ฝ๋ฐฑ ํจ์,validate
๋ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํ ํจ์๋ฅผ ์ฝ๋ฐฑํจ์๋ก ๊ตฌ์ฑ์ด ๋ฉ๋๋ค. - ์ด ํ๋ก์ ํธ์์
validate
์ฝ๋ฐฑํจ์๋กutils/validate.ts
์์signInValidation
ํจ์๋ฅผ ์ฌ์ฉํ์์ต๋๋ค.
*์ ๊ทํํ์
// ์๊ตฌ์ฌํญ ๊ธฐ์ค Input
// ์ด๋ฉ์ผ ํ์ - ์ด๋ฉ์ผ ํ์ ex) [email protected]
const EMAIL_REGEX =
/^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;
// ๋น๋ฐ๋ฒํธ ํ์ - ์๋ฌธ์, ๋๋ฌธ์, ์ซ์๋ก ๊ตฌ์ฑ, ๋งจ ์์ ๋ฌธ์ ex) Test1
const PWD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]+$/;
// ์ถ๊ฐ input
// ์ด๋ฆ ํ์ - ํ๊ธ๋ก ๊ตฌ์ฑ๋๋ฉฐ, ์ต์ 1๊ธ์ ์์๋ถํฐ ์ต๋ 8๊ธ์ ex) ํ๊ธธ๋
const DISPLAY_NAME_REGEX = /^[๊ฐ-ํฃ]{1,8}$/;
- ์ ํจ์ฑ ๊ฒ์ฌ ํจ์
const signInValidation = ({
displayName,
email,
password,
}: SignUpValidationProps) => {
const errors: SignUpValidationProps = {};
if (!displayName) {
errors.displayName = "์ด๋ฆ์ด ์
๋ ฅ๋์ง ์์์ต๋๋ค.";
} else if (!DISPLAY_NAME_REGEX.test(displayName)) {
errors.displayName =
"์ด๋ฆ์ ํ๊ธ๋ก ์
๋ ฅํด์ฃผ์ธ์ ์ต์ 1๊ธ์ ์ต๋ 8๊ธ์๋ก ํด์ผ ํฉ๋๋ค.";
}
if (!email) {
errors.email = "์ด๋ฉ์ผ์ด ์
๋ ฅ๋์ง ์์์ต๋๋ค.";
} else if (!EMAIL_REGEX.test(email)) {
errors.email = "์ด๋ฉ์ผ ํ์์ผ๋ก ์์ฑ ํด์ผ ํฉ๋๋ค..";
}
if (!password) {
errors.password = "๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํ์ง ์์์ต๋๋ค.";
} else if (!PWD_REGEX.test(password)) {
errors.password =
"๋น๋ฐ๋ฒํธ๋ ์๋ฌธ์,๋๋ฌธ์,์ซ์๋ก ์ฌ์ฉํ ์กฐํฉ์ด๋ฉฐ, ์์๋ฌธ์๋ ๋ฌธ์๋ก ํด์ผ ํฉ๋๋ค.";
}
return errors;
};
email, password, displayName์ ๋ฃ์ด์ฃผ๋ฉด errors
์ธ ์๋ฌ ๋ฉ์์ง๋ฅผ ๋ด์ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋๋ก ํฉ๋๋ค.
*ex. ์๋ฌ๋ฉ์์ง ์์
{
"displayName": "์ด๋ฆ์ ํ๊ธ๋ก ์
๋ ฅํด์ฃผ์ธ์ ์ต์ 1๊ธ์ ์ต๋ 8๊ธ์๋ก ํด์ผ ํฉ๋๋ค.",
"email": "์ด๋ฉ์ผ ํ์์ผ๋ก ์์ฑ ํด์ผ ํฉ๋๋ค..",
"password": "๋น๋ฐ๋ฒํธ๋ ์๋ฌธ์,๋๋ฌธ์,์ซ์๋ก ์ฌ์ฉํ ์กฐํฉ์ด๋ฉฐ, ์์๋ฌธ์๋ ๋ฌธ์๋ก ํด์ผ ํฉ๋๋ค."
}
MUI
์ธ ui ๋ผ์ด๋ธ๋ฌ๋ฆฌ์<TextField>
์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ,error
์helperTet
์ props๋ฅผ ์ฃผ์ด ์๋ฌ ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ก๊ณ ๊ตฌํํ์์ต๋๋ค.
return (
<>
<TextField
sx={textStyle}
fullWidth
placeholder="์ด๋ฉ์ผ์ ์
๋ ฅํด์ฃผ์ธ์."
type="text"
name="email"
value={email}
onChange={handleChange}
error={!!errors.email}
helperText={errors.email}
/>
</>
);
- ๋ก์ปฌ ์คํ ๋ฆฌ์ง ์ ์ฅ
form ์ํ๊ด๋ฆฌ ํ ์ ๋งค๊ฐ๋ณ์๋ก
onSubmit
์ฝ๋ฐฑ ํจ์๋ฅผ ์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ ๋ฌํฉ๋๋ค.pretest
๋ผ๋key
์display,name, password
์ ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๊ณnavigate('/')
๋ฅผ ํตํด์ ๋ฉ์ธ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.,
AuthRoute
์ปดํฌ๋ํธ์ ๊ธฐ๋ฅ์ ๋ก๊ทธ์ธ ์ํ๊ฐ ๋์์ ๋, ๋ฉ์ธ ํ์ด์ง๋ก ์ด๋ํ๊ฒ ํ๊ณ , ๋ก๊ทธ์ธ ์ํ๊ฐ ์ ์ง๋๋ ๋์ ๋ก๊ทธ์ธ ํ์ด์ง์ ์ ๊ทผํ ์ ์๋๋ก ํฉ๋๋ค.ProtectedRoute
์ปดํฌ๋ํธ์ ๊ธฐ๋ฅ์ ์์น ์๋ ๋๋ฉ์ธ์ ๋ง์ต๋๋ค.
- useEffect์ dependency ๋ฐฐ์ด์
navigate
๋ฅผ ์ฃผ์ด, url์ ๋ณ๊ฒฝ์ ์๋ํ ๋, ํ์ด์ง๊ฐ mount๋ ๋,checkUser
ํจ์๋ฅผ ์คํํ์ฌ, ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์ ์ ์ ๋ณด๊ฐ ์ ์ฅ์ด ๋์ด์์ง ์๋ค๋ฉด, ๋ค์login
๋ผ์ฐํฐ๋ก ์ด๋์ ํ๋๋ก ํฉ๋๋ค.
context
๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ ์ ์ ๋ณด์ ๋ก๊ทธ์์ ํจ์ ๋ฑ๋ฅผ ์ ์ญ์ํ๋ก ๊ด๋ฆฌํ์ฌ ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋๋ก ๊ตฌํ (context/AuthContext
)
const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const navigate = useNavigate();
const [userInfo, setUserInfo] = React.useState<UserInfo | unknown>({});
// ๋ก๊ทธ์์์์, ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋
const logOut = async () => {
await localStorage.removeItem("pretest");
setUserInfo({});
navigate("/signin");
};
React.useEffect(() => {
const userInfo = localStorage.getItem("pretest");
if (!userInfo) return;
const parsedUserInfo = JSON.parse(userInfo);
setUserInfo(parsedUserInfo);
}, [navigate]);
return (
<AuthContext.Provider
value={{
userInfo,
logOut,
}}
>
{children}
</AuthContext.Provider>
);
};
export { AuthContext, AuthProvider };