모두의 이력서
[모두의 이력서_15일차] 회원가입기능 구현
개발조각
2023. 4. 11. 17:12
728x90
반응형
로그인 기능 구현할 때 cors, react-query, axios, recoil을 세팅했으므로 로그인 기능과 똑같이 구현해 주시면 됩니다.
회원가입 기능 구성
이메일, 닉네임, 비밀번호, 비밀번호 확인 → 회원가입 버튼
- 알맞은 일메일을 작성할 경우 : 알맞은 이메일 형식입니다.
- 알맞은 닉네임을 작성할 경우 : 알맞은 닉네임 형식입니다.
- 알맞은 비밀번호를 작성할 경우 : 알맞은 비밀번호 형식입니다.
- 비밀번호, 비밀번호 확인이 일치할 경우: 비밀번호가 일치합니다.
라는 문구를 띄우게 했습니다.
api > user.ts
import instance from ".";
interface User {
email: string;
nickName: string;
password: string;
}
// 회원가입
export const register = async ({ email, nickName, password }: User) => {
const result = await instance.post("/users/register", {
email,
nickName,
password,
});
return result;
};
axios로 회원가입 api 정의해 주었습니다.
utils > regExp.ts
입력한 값(이메일, 닉네임, 비밀번호)이 알맞은 값인지 확인하기 위해 정규식을 사용했습니다.
이메일, 닉네임, 비밀번호는 회원 관련 기능을 구현할 때 자주 사용되므로 utils폴더 안에 넣어주었습니다.
로그인 기능 구현할 때도 사용했습니다.
// 이메일: (영어대소문자+숫자) + @ + (영어소문자) + (.com)
export const validateEmail = (email: string) => {
return /([\d\w])+@{1}([a-z])+(.com$)/.test(email);
};
// 닉네임: 특수문자제외, 공백제외, 2~8글자
export const validateNickname = (nickname: string) => {
return /^[가-힣\d\w]{2,8}$/.test(nickname);
};
// 비밀번호: 4~10글자
export const validatePassword = (value: string) => {
return value.length >= 4 && value.length <= 10;
};
vaidateEmail(입력한 이메일 값)을 넣어주면 true, false 값이 나옵니다.
Register.tsx
import { ChangeEvent, FormEvent, useContext, useState } from "react";
import { useRouter } from "next/router";
import { useMutation } from "@tanstack/react-query";
import Logo from "../components/common/Logo";
import { ThemeContext } from "../context/themeContext";
import { IsDark } from "../context/type";
import {
validateEmail,
validateNickname,
validatePassword,
} from "../utils/regExp";
import { register } from "../api/user";
import * as authStyled from "../styles/components/Auth";
import * as styled from "../styles/pages/Register";
// 이메일, 닉네임, 비밀번호, 비밀번호 확인 -> 회원가입
const Register = () => {
const router = useRouter();
const { isDark } = useContext(ThemeContext) as IsDark;
const mode = isDark ? "darkTheme" : "lightTheme";
const [email, setEmail] = useState("");
const [nickName, setNickName] = useState("");
const [password, setPassword] = useState("");
const [checkPassword, setCechkPassword] = useState("");
const onEmailHandler = (e: ChangeEvent<HTMLInputElement>) => {
const { value } = e.target as any;
setEmail(value);
};
const onNickNameHandler = (e: ChangeEvent<HTMLInputElement>) => {
const { value } = e.target as any;
setNickName(value);
};
const onPasswordHandler = (e: ChangeEvent<HTMLInputElement>) => {
const { value } = e.target as any;
setPassword(value);
};
const onCheckPasswordHandler = (e: ChangeEvent<HTMLInputElement>) => {
const { value } = e.target as any;
setCechkPassword(value);
};
const mutation = useMutation({
mutationFn: register,
onSuccess: (data) => {
router.replace("/Login", "/auth/register");
},
});
const onSubmitHandler = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault(); // 리프레시 막기
if (
validateEmail(email) &&
validateNickname(nickName) &&
validatePassword(password) &&
password === checkPassword
) {
mutation.mutate({ email: email, nickName: nickName, password: password });
} else {
alert("회원가입 실패했습니다. 다시 수정해주세요.");
}
};
return (
<authStyled.AuthWrap mode={mode}>
<authStyled.AuthCon mode={mode}>
<styled.TitleWrap>
<Logo />
<h2>이메일로 가입하기</h2>
</styled.TitleWrap>
<authStyled.LoginformWrap>
<form onSubmit={onSubmitHandler}>
<authStyled.InputBox mode={mode}>
<p>이메일</p>
<input
autoFocus
type="text"
value={email}
onChange={onEmailHandler}
/>
</authStyled.InputBox>
{validateEmail(email) && (
<authStyled.Msg state={"success"}>
알맞은 이메일 형식입니다.
</authStyled.Msg>
)}
<authStyled.InputBox mode={mode}>
<p>닉네임</p>
<input type="text" onChange={onNickNameHandler} />
</authStyled.InputBox>
{validateNickname(nickName) && (
<authStyled.Msg state={"success"}>
알맞은 닉네임 형식입니다.
</authStyled.Msg>
)}
<authStyled.InputBox mode={mode}>
<p>비밀번호</p>
<input type="password" onChange={onPasswordHandler} />
</authStyled.InputBox>
{validatePassword(password) && (
<authStyled.Msg state={"success"}>
알맞은 비밀번호 형식입니다.
</authStyled.Msg>
)}
<authStyled.InputBox mode={mode}>
<p>비밀번호 확인</p>
<input type="password" onChange={onCheckPasswordHandler} />
</authStyled.InputBox>
{validatePassword(password) && password === checkPassword && (
<authStyled.Msg state={"success"}>
비밀번호가 일치합니다.
</authStyled.Msg>
)}
<authStyled.btn type="submit">회원가입</authStyled.btn>
</form>
</authStyled.LoginformWrap>
</authStyled.AuthCon>
</authStyled.AuthWrap>
);
};
export default Register;
Auth.ts
Login.ts css와 유사해서 공통으로 사용되는 부분은 Auth에 넣어주었고
Register에서만 사용되는 css는 Register.ts에 넣어주었습니다.
import styled from "styled-components";
export const AuthWrap = styled.div<{ mode: "darkTheme" | "lightTheme" }>`
${({ theme }) => theme.common.flexCenter}
height: 100vh;
background: ${({ theme, mode }) => theme.colors[mode].lightBg};
`;
export const AuthCon = styled.div<{ mode: "darkTheme" | "lightTheme" }>`
${({ theme }) => theme.common.flexCenter}
flex-direction: column;
padding: 88px 0;
width: 640px;
border-radius: 10px;
background: ${({ theme, mode }) => theme.colors[mode].bg};
${({ theme }) => theme.device.mobile} {
width: 100%;
height: 100vh;
padding: 28px 24px;
}
`;
export const LoginformWrap = styled.div`
margin-top: 48px;
width: 320px;
form {
width: 100%;
}
`;
export const InputBox = styled.div<{ mode: "darkTheme" | "lightTheme" }>`
margin-top: 16px;
p {
font-weight: 700;
font-size: ${({ theme }) => theme.fonts.size.small};
}
input {
margin-top: 8px;
padding: 10px 12px;
width: 100%;
border: 1px solid ${({ theme, mode }) => theme.colors[mode].border};
border-radius: 4px;
background: ${({ theme, mode }) => theme.colors[mode].lightBg};
&:focus {
border: 1px solid ${({ theme }) => theme.colors.lightMain};
}
}
`;
export const btn = styled.button`
display: block;
margin-top: 36px;
width: 100%;
height: 48px;
border-radius: 4px;
background: ${({ theme }) => theme.colors.main};
color: ${({ theme }) => theme.colors.lightTheme.bg};
transition: all 0.1s;
&:hover {
background: ${({ theme }) => theme.colors.lightMain};
}
`;
export const Msg = styled.span<{ state: "warning" | "success" }>`
display: block;
margin-top: 12px;
font-weight: 300;
font-size: ${({ theme }) => theme.fonts.size.small};
line-height: ${({ theme }) => theme.fonts.lineHeight.base};
color: ${({ theme, state }) => theme.colors[state]};
`;
Register.ts
import styled from "styled-components";
export const TitleWrap = styled.div`
text-align: center;
h1 a img {
width: auto;
height: 16px;
}
h2 {
margin-top: 16px;
font-weight: 700;
font-size: ${({ theme }) => theme.fonts.size.title};
}
`;
728x90
반응형